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(builder.Configuration, "AzureOpenAI")!; var appSettings = builder.Services.ConfigureAndGet(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(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(MediaTypeNames.Application.Pdf); builder.Services.AddKeyedSingleton("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); builder.Services.AddKeyedSingleton(MediaTypeNames.Text.Plain); builder.Services.AddKeyedSingleton(MediaTypeNames.Text.Markdown); builder.Services.AddKeyedSingleton(KeyedService.AnyKey); builder.Services.AddKeyedSingleton(MediaTypeNames.Text.Markdown); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddOpenApi(options => { //options.RemoveServerList(); //options.AddDefaultProblemDetailsResponse(); }); ValidatorOptions.Global.LanguageManager.Enabled = false; builder.Services.AddValidatorsFromAssemblyContaining(); 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", createScopeForErrors: true); // 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() .AddInteractiveServerRenderMode(); app.MapEndpoints(); app.Run(); static async Task ConfigureDatabaseAsync(IServiceProvider serviceProvider) { await using var scope = serviceProvider.CreateAsyncScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); await dbContext.Database.MigrateAsync(); }