Files
SqlDatabaseVectorSearch/SqlDatabaseVectorSearch/Program.cs
T
Marco Minerva 404cd7565a Switch to SqlVector<float> for embeddings
Updated the application to use SQL Server's native vector data type (`SqlVector<float>`) for embeddings, replacing the previous `float[]` or `string` representations.

- Updated `.editorconfig` with new code style preferences and diagnostic rule severities.
- Modified `DocumentChunk.cs` to use `SqlVector<float>` for the `Embedding` property.
- Updated migrations and `ApplicationDbContextModelSnapshot` to reflect the new `SqlVector<float>` type.
- Replaced `AddAzureSql` with `AddSqlServer` in `Program.cs` and removed `UseVectorSearch`.
- Adjusted `DocumentService` and `VectorSearchService` to handle `SqlVector<float>` and updated vector search logic.
- Removed the `EFCore.SqlServer.VectorSearch` package and upgraded EF Core to `10.0.0-rc.1`.
- Made minor adjustments to OpenAPI configuration and dependency management.
2025-09-10 16:45:16 +02:00

146 lines
5.0 KiB
C#

using System.Net.Mime;
using System.Text.Json.Serialization;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Microsoft.SemanticKernel;
using SqlDatabaseVectorSearch.Components;
using SqlDatabaseVectorSearch.ContentDecoders;
using SqlDatabaseVectorSearch.Data;
using SqlDatabaseVectorSearch.Extensions;
using SqlDatabaseVectorSearch.Services;
using SqlDatabaseVectorSearch.Settings;
using SqlDatabaseVectorSearch.TextChunkers;
using TinyHelpers.AspNetCore.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.local.json", optional: true, reloadOnChange: true);
// Add services to the container.
var aiSettings = builder.Services.ConfigureAndGet<AzureOpenAISettings>(builder.Configuration, "AzureOpenAI")!;
var appSettings = builder.Services.ConfigureAndGet<AppSettings>(builder.Configuration, nameof(AppSettings))!;
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddBlazorBootstrap();
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
builder.Services.AddSingleton(TimeProvider.System);
builder.Services.AddSqlServer<ApplicationDbContext>(builder.Configuration.GetConnectionString("SqlConnection"), optionsAction: options =>
{
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
});
builder.Services.AddHybridCache(options =>
{
options.DefaultEntryOptions = new()
{
LocalCacheExpiration = appSettings.MessageExpiration
};
});
builder.Services.ConfigureHttpClientDefaults(configure =>
{
configure.AddStandardResilienceHandler(options =>
{
options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(15);
options.TotalRequestTimeout.Timeout = TimeSpan.FromMinutes(2);
});
});
// Semantic Kernel is used to generate embeddings and to reformulate questions taking into account all the previous interactions,
// so that embeddings themselves can be generated more accurately.
builder.Services.AddKernel()
.AddAzureOpenAIEmbeddingGenerator(aiSettings.Embedding.Deployment, aiSettings.Embedding.Endpoint, aiSettings.Embedding.ApiKey, modelId: aiSettings.Embedding.ModelId, dimensions: aiSettings.Embedding.Dimensions)
.AddAzureOpenAIChatCompletion(aiSettings.ChatCompletion.Deployment, aiSettings.ChatCompletion.Endpoint, aiSettings.ChatCompletion.ApiKey, modelId: aiSettings.ChatCompletion.ModelId);
builder.Services.AddKeyedSingleton<IContentDecoder, PdfContentDecoder>(MediaTypeNames.Application.Pdf);
builder.Services.AddKeyedSingleton<IContentDecoder, DocxContentDecoder>("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
builder.Services.AddKeyedSingleton<IContentDecoder, TextContentDecoder>(MediaTypeNames.Text.Plain);
builder.Services.AddKeyedSingleton<IContentDecoder, TextContentDecoder>(MediaTypeNames.Text.Markdown);
builder.Services.AddKeyedSingleton<ITextChunker, DefaultTextChunker>(KeyedService.AnyKey);
builder.Services.AddKeyedSingleton<ITextChunker, MarkdownTextChunker>(MediaTypeNames.Text.Markdown);
builder.Services.AddSingleton<TokenizerService>();
builder.Services.AddSingleton<ChatService>();
builder.Services.AddScoped<DocumentService>();
builder.Services.AddScoped<VectorSearchService>();
builder.Services.AddOpenApi(options =>
{
//options.RemoveServerList();
//options.AddDefaultProblemDetailsResponse();
});
ValidatorOptions.Global.LanguageManager.Enabled = false;
builder.Services.AddValidatorsFromAssemblyContaining<Program>();
builder.Services.AddDefaultProblemDetails();
builder.Services.AddDefaultExceptionHandler();
var app = builder.Build();
await ConfigureDatabaseAsync(app.Services);
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseWhen(context => context.IsWebRequest(), builder =>
{
if (!app.Environment.IsDevelopment())
{
builder.UseExceptionHandler("/error");
// The default HSTS value is 30 days.
builder.UseHsts();
}
builder.UseStatusCodePagesWithRedirects("/error?code={0}");
});
app.UseWhen(context => context.IsApiRequest(), builder =>
{
app.UseExceptionHandler(new ExceptionHandlerOptions
{
StatusCodeSelector = exception => exception switch
{
NotSupportedException => StatusCodes.Status501NotImplemented,
_ => StatusCodes.Status500InternalServerError
}
});
builder.UseStatusCodePages();
});
app.MapOpenApi();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/openapi/v1.json", builder.Environment.ApplicationName);
});
app.UseRouting();
app.UseRequestLocalization();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.MapEndpoints();
app.Run();
static async Task ConfigureDatabaseAsync(IServiceProvider serviceProvider)
{
await using var scope = serviceProvider.CreateAsyncScope();
var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
await dbContext.Database.MigrateAsync();
}