mirror of
https://github.com/marcominerva/SqlDatabaseVectorSearch.git
synced 2026-08-04 09:48:57 +00:00
Refactor embedding request creation and processing
Consolidate embedding request logic into a new EmbeddingRequest record with static factory methods for IFormFile and stream input. Remove FormFileToEmbeddingRequestExecutor and FormFileEmbeddingRequest, updating all usages and dependency injection accordingly. Clean up related code in Program.cs, VectorSearchService, and _Imports.razor. Enhance XML documentation for EmbeddingRequest to clarify intent and usage.
This commit is contained in:
@@ -177,7 +177,7 @@ else
|
||||
var vectorSearchService = scope.ServiceProvider.GetRequiredService<VectorSearchService>();
|
||||
|
||||
var documentId = string.IsNullOrWhiteSpace(Model.DocumentId) ? null : (Guid?)Guid.Parse(Model.DocumentId);
|
||||
//await vectorSearchService.ImportAsync(stream, fileName, MimeUtility.GetMimeMapping(fileName), documentId);
|
||||
await vectorSearchService.ImportAsync(EmbeddingRequest.Create(stream, fileName, documentId));
|
||||
|
||||
ToastService.Notify(await CreateToastMessageAsync(ToastType.Success, "Upload document", $"The document {fileName} has been successfully uploaded and indexed."));
|
||||
|
||||
|
||||
@@ -13,4 +13,5 @@
|
||||
@using SqlDatabaseVectorSearch.Extensions
|
||||
@using SqlDatabaseVectorSearch.Models
|
||||
@using SqlDatabaseVectorSearch.Services
|
||||
@using SqlDatabaseVectorSearch.Workflows
|
||||
@using BlazorBootstrap
|
||||
@@ -23,7 +23,7 @@ public class DocumentEndpoints : IEndpointRouteHandlerBuilder
|
||||
documentsApiGroup.MapPost(string.Empty, async (IFormFile file, VectorSearchService vectorSearchService, CancellationToken cancellationToken,
|
||||
[Description("The unique identifier of the document. If not provided, a new one will be generated. If you specify an existing documentId, the corresponding document will be overwritten.")] Guid? documentId = null) =>
|
||||
{
|
||||
var result = await vectorSearchService.ImportAsync(new FormFileEmbeddingRequest(file, documentId), cancellationToken);
|
||||
var result = await vectorSearchService.ImportAsync(EmbeddingRequest.FromFormFile(file, documentId), cancellationToken);
|
||||
return TypedResults.Ok(result);
|
||||
})
|
||||
.DisableAntiforgery()
|
||||
|
||||
@@ -92,18 +92,15 @@ builder.Services.AddScoped<DocumentService>();
|
||||
builder.Services.AddScoped<VectorSearchService>();
|
||||
builder.Services.AddScoped<ContextProvider>();
|
||||
|
||||
builder.Services.AddSingleton<FormFileToEmbeddingRequestExecutor>();
|
||||
builder.Services.AddSingleton<GenerateEmbeddingExecutor>();
|
||||
builder.Services.AddScoped<StoreEmbeddingExecutor>(); // This executor is registered as scoped because it uses the DbContext, which is also scoped.
|
||||
|
||||
builder.AddWorkflow("EmbeddingWorkflow", (services, key) =>
|
||||
{
|
||||
var formfileToConversionRequestExecutor = services.GetRequiredService<FormFileToEmbeddingRequestExecutor>();
|
||||
var generateEmbeddingExecutor = services.GetRequiredService<GenerateEmbeddingExecutor>();
|
||||
var storeEmbeddingExecutor = services.GetRequiredService<StoreEmbeddingExecutor>();
|
||||
|
||||
var workflow = new WorkflowBuilder(formfileToConversionRequestExecutor).WithName(key)
|
||||
.AddEdge(formfileToConversionRequestExecutor, generateEmbeddingExecutor)
|
||||
var workflow = new WorkflowBuilder(generateEmbeddingExecutor).WithName(key)
|
||||
.AddEdge(generateEmbeddingExecutor, storeEmbeddingExecutor)
|
||||
.WithOutputFrom(storeEmbeddingExecutor)
|
||||
.Build(validateOrphans: true);
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace SqlDatabaseVectorSearch.Services;
|
||||
public partial class VectorSearchService([FromKeyedServices("EmbeddingWorkflow")] Workflow workflow, [FromKeyedServices("ReformulationAgent")] AIAgent reformulationAgent, [FromKeyedServices("RagAgent")] AIAgent ragAgent,
|
||||
[FromKeyedServices("RagAgent")] AgentSessionStore sessionStore)
|
||||
{
|
||||
public async Task<StoreEmbeddingResponse> ImportAsync(FormFileEmbeddingRequest request, CancellationToken cancellationToken = default)
|
||||
public async Task<StoreEmbeddingResponse> ImportAsync(EmbeddingRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var run = await InProcessExecution.RunAsync(workflow, request, cancellationToken: cancellationToken);
|
||||
var events = run.NewEvents.ToList();
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace SqlDatabaseVectorSearch.Workflows;
|
||||
|
||||
public record class EmbeddingRequest(Stream Content, string FileName, string ContentType, Guid? DocumentId)
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="EmbeddingRequest"/> from an uploaded <see cref="IFormFile"/>.
|
||||
/// </summary>
|
||||
/// <param name="file">The uploaded file.</param>
|
||||
/// <param name="documentId">The optional identifier of the document to overwrite.</param>
|
||||
public static EmbeddingRequest FromFormFile(IFormFile file, Guid? documentId = null) => Create(file.OpenReadStream(), Path.GetFileName(file.FileName), documentId);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="EmbeddingRequest"/> from a content stream, inferring the content type from the file name.
|
||||
/// </summary>
|
||||
/// <param name="content">The stream that contains the document content.</param>
|
||||
/// <param name="fileName">The name of the document.</param>
|
||||
/// <param name="documentId">The optional identifier of the document to overwrite.</param>
|
||||
/// <remarks>
|
||||
/// The content type is inferred from the file name because the content type declared by the client is not always reliable (for example, for Markdown files).
|
||||
/// </remarks>
|
||||
public static EmbeddingRequest Create(Stream content, string fileName, Guid? documentId = null)
|
||||
{
|
||||
var name = Path.GetFileName(fileName);
|
||||
return new EmbeddingRequest(content, name, MimeMapping.MimeUtility.GetMimeMapping(name), documentId);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Microsoft.Agents.AI.Workflows;
|
||||
|
||||
namespace SqlDatabaseVectorSearch.Workflows;
|
||||
|
||||
public partial class FormFileToEmbeddingRequestExecutor() : Executor(nameof(FormFileToEmbeddingRequestExecutor))
|
||||
{
|
||||
[MessageHandler]
|
||||
private ValueTask<EmbeddingRequest> HandleAsync(FormFileEmbeddingRequest request, IWorkflowContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
// Note: file.ContentType is not 100% reliable (for example, for markdown file).
|
||||
var embeddingRequest = new EmbeddingRequest(request.File.OpenReadStream(), Path.GetFileName(request.File.FileName), MimeMapping.MimeUtility.GetMimeMapping(request.File.FileName), request.DocumentId);
|
||||
return ValueTask.FromResult(embeddingRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public record class FormFileEmbeddingRequest(IFormFile File, Guid? DocumentId);
|
||||
|
||||
public record class EmbeddingRequest(Stream Content, string FileName, string ContentType, Guid? DocumentId);
|
||||
Reference in New Issue
Block a user