From 3b263b8c4f153da772e28f6f97a004a218a3db3c Mon Sep 17 00:00:00 2001 From: Devis Lucato Date: Fri, 16 Feb 2024 14:49:13 -0800 Subject: [PATCH] Load handlers list from configuration --- KernelMemory.sln.DotSettings | 1 + .../Program.cs | 17 ++++++ .../Program.cs | 6 +- extensions/AzureOpenAI/AzureOpenAIConfig.cs | 14 ++--- .../Core/AppBuilders/DependencyInjection.cs | 61 ++++++++++++++++++- service/Core/AppBuilders/HandlerTypeLoader.cs | 60 ++++++++++++++++++ service/Core/Configuration/HandlerConfig.cs | 28 +++++++++ .../Configuration/KernelMemoryConfig.cs | 0 .../ServiceAuthorizationConfig.cs | 0 .../Configuration/ServiceConfig.cs | 4 ++ service/Core/KernelMemoryBuilder.cs | 25 +------- service/Service/Auth/HttpAuthHandler.cs | 1 + service/Service/OpenAPI.cs | 58 +++++++++--------- service/Service/Program.cs | 38 ++++++++---- service/Service/ServiceConfiguration.cs | 5 -- service/Service/WebAPIEndpoints.cs | 1 + service/Service/appsettings.json | 47 +++++++++++++- .../DefaultTestCases/IndexListTest.cs | 8 +-- .../InteractiveSetup/InteractiveSetup.csproj | 4 +- 19 files changed, 290 insertions(+), 88 deletions(-) create mode 100644 service/Core/AppBuilders/HandlerTypeLoader.cs create mode 100644 service/Core/Configuration/HandlerConfig.cs rename service/{Abstractions => Core}/Configuration/KernelMemoryConfig.cs (100%) rename service/{Abstractions => Core}/Configuration/ServiceAuthorizationConfig.cs (100%) rename service/{Abstractions => Core}/Configuration/ServiceConfig.cs (87%) diff --git a/KernelMemory.sln.DotSettings b/KernelMemory.sln.DotSettings index 987c4a5d3..c1c45a758 100644 --- a/KernelMemory.sln.DotSettings +++ b/KernelMemory.sln.DotSettings @@ -270,6 +270,7 @@ public void It$SOMENAME$() DO_NOT_SHOW True True + True True True True diff --git a/examples/004-dotnet-ServerlessCustomPipeline/Program.cs b/examples/004-dotnet-ServerlessCustomPipeline/Program.cs index 83d3483d4..b62eb464a 100644 --- a/examples/004-dotnet-ServerlessCustomPipeline/Program.cs +++ b/examples/004-dotnet-ServerlessCustomPipeline/Program.cs @@ -1,5 +1,22 @@ // Copyright (c) Microsoft. All rights reserved. +/* + * The ingestion pipeline is composed by a set of default STEPS to process documents: + * - extract + * - partition + * - gen_embeddings + * - save_records + * + * Each step is managed by a HANDLER, see the Core/Handlers for a list of available handlers. + * + * You can create new handlers, and customize the pipeline in multiple ways: + * + * - If you are using the Memory Web Service, the list of handlers can be configured in appsettings.json and appsettings..json + * - You can create new handlers and load them in the configuration, passing the path to the assembly and the handler class. + * - If you are using the Serverless Memory, see the example below. + * - You can also remove the default handlers calling .WithoutDefaultHandlers() in the memory builder. + */ + using Microsoft.KernelMemory; using Microsoft.KernelMemory.Handlers; diff --git a/examples/202-dotnet-CustomHandlerAsAService/Program.cs b/examples/202-dotnet-CustomHandlerAsAService/Program.cs index b574bf5ff..60995976b 100644 --- a/examples/202-dotnet-CustomHandlerAsAService/Program.cs +++ b/examples/202-dotnet-CustomHandlerAsAService/Program.cs @@ -2,9 +2,9 @@ using Microsoft.KernelMemory; -/* The following code shows how to create a custom handler, attached - * to a queue and listening for work to do. You can also add multiple handlers - * the same way. +/* The following code shows how to create a custom handler and run it as a standalone service. + * The handler will automatically attach to a queue and listen for work to do. + * You can also add multiple handlers the same way. */ // Usual .NET web app builder diff --git a/extensions/AzureOpenAI/AzureOpenAIConfig.cs b/extensions/AzureOpenAI/AzureOpenAIConfig.cs index 25efa3195..d0796d506 100644 --- a/extensions/AzureOpenAI/AzureOpenAIConfig.cs +++ b/extensions/AzureOpenAI/AzureOpenAIConfig.cs @@ -84,7 +84,7 @@ public void SetCredential(TokenCredential credential) public TokenCredential GetTokenCredential() { return this._tokenCredential - ?? throw new ConfigurationException("TokenCredential not defined"); + ?? throw new ConfigurationException("Azure OpenAI TokenCredential not defined"); } /// @@ -94,33 +94,33 @@ public void Validate() { if (this.Auth == AuthTypes.Unknown) { - throw new ArgumentOutOfRangeException(nameof(this.Auth), "The authentication type is not defined"); + throw new ArgumentOutOfRangeException(nameof(this.Auth), "The Azure OpenAI Authentication Type is not defined"); } if (this.Auth == AuthTypes.APIKey && string.IsNullOrWhiteSpace(this.APIKey)) { - throw new ArgumentOutOfRangeException(nameof(this.APIKey), "The API Key is empty"); + throw new ArgumentOutOfRangeException(nameof(this.APIKey), "The Azure OpenAI API Key is empty"); } if (string.IsNullOrWhiteSpace(this.Endpoint)) { - throw new ArgumentOutOfRangeException(nameof(this.Endpoint), "The endpoint value is empty"); + throw new ArgumentOutOfRangeException(nameof(this.Endpoint), "The Azure OpenAI Endpoint value is empty"); } if (!this.Endpoint.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { - throw new ArgumentOutOfRangeException(nameof(this.Endpoint), "The endpoint value must start with https://"); + throw new ArgumentOutOfRangeException(nameof(this.Endpoint), "The Azure OpenAI Endpoint value must start with https://"); } if (string.IsNullOrWhiteSpace(this.Deployment)) { - throw new ArgumentOutOfRangeException(nameof(this.Deployment), "The deployment value is empty"); + throw new ArgumentOutOfRangeException(nameof(this.Deployment), "The Azure OpenAI Deployment Name is empty"); } if (this.MaxTokenTotal < 1) { throw new ArgumentOutOfRangeException(nameof(this.MaxTokenTotal), - $"{nameof(this.MaxTokenTotal)} cannot be less than 1"); + $"Azure OpenAI: {nameof(this.MaxTokenTotal)} cannot be less than 1"); } } } diff --git a/service/Core/AppBuilders/DependencyInjection.cs b/service/Core/AppBuilders/DependencyInjection.cs index 8508d06d4..7b564f9d9 100644 --- a/service/Core/AppBuilders/DependencyInjection.cs +++ b/service/Core/AppBuilders/DependencyInjection.cs @@ -1,6 +1,10 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.KernelMemory.Configuration; +using Microsoft.KernelMemory.Handlers; using Microsoft.KernelMemory.Pipeline; namespace Microsoft.KernelMemory; @@ -16,8 +20,63 @@ public static partial class DependencyInjection public static void AddHandlerAsHostedService(this IServiceCollection services, string stepName) where THandler : class, IPipelineStepHandler { services.AddTransient(serviceProvider => ActivatorUtilities.CreateInstance(serviceProvider, stepName)); - services.AddHostedService>( serviceProvider => ActivatorUtilities.CreateInstance>(serviceProvider, stepName)); } + + /// + /// Register the handler as a hosted service, passing the step name to the handler ctor + /// + /// Application builder service collection + /// Handler class + /// Pipeline step name + public static void AddHandlerAsHostedService(this IServiceCollection services, Type tHandler, string stepName) + { + if (!typeof(IPipelineStepHandler).IsAssignableFrom(tHandler)) + { + throw new ArgumentException($"'{tHandler.FullName}' doesn't implement interface '{nameof(IPipelineStepHandler)}'", nameof(tHandler)); + } + + if (tHandler == null) + { + throw new ArgumentNullException(nameof(tHandler), $"Handler type for '{stepName}' is NULL"); + } + + services.AddTransient(tHandler, serviceProvider => ActivatorUtilities.CreateInstance(serviceProvider, tHandler, stepName)); + + // Build generic type: HandlerAsAHostedService + Type handlerAsAHostedServiceTHandler = typeof(HandlerAsAHostedService<>).MakeGenericType(tHandler); + + Func implementationFactory = + serviceProvider => (IHostedService)ActivatorUtilities.CreateInstance(serviceProvider, handlerAsAHostedServiceTHandler, stepName); + + // See https://github.com/dotnet/runtime/issues/38751 for troubleshooting + services.Add(ServiceDescriptor.Singleton(implementationFactory)); + } + + /// + /// Register the handler as a hosted service, passing the step name to the handler ctor + /// + /// Application builder service collection + /// Handler type configuration + /// Pipeline step name + public static void AddHandlerAsHostedService(this IServiceCollection services, HandlerConfig config, string stepName) + { + if (HandlerTypeLoader.TryGetHandlerType(config, out var handlerType)) + { + services.AddHandlerAsHostedService(handlerType, stepName); + } + } + + /// + /// Register the handler as a hosted service, passing the step name to the handler ctor + /// + /// Application builder service collection + /// Path to assembly containing handler class + /// Handler type, within the assembly + /// Pipeline step name + public static void AddHandlerAsHostedService(this IServiceCollection services, string assemblyFile, string typeFullName, string stepName) + { + services.AddHandlerAsHostedService(new HandlerConfig(assemblyFile, typeFullName), stepName); + } } diff --git a/service/Core/AppBuilders/HandlerTypeLoader.cs b/service/Core/AppBuilders/HandlerTypeLoader.cs new file mode 100644 index 000000000..8967d68a8 --- /dev/null +++ b/service/Core/AppBuilders/HandlerTypeLoader.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using Microsoft.KernelMemory.Configuration; +using Microsoft.KernelMemory.Pipeline; + +namespace Microsoft.KernelMemory.Handlers; + +internal static class HandlerTypeLoader +{ + internal static bool TryGetHandlerType(HandlerConfig config, [NotNullWhen(true)] out Type? handlerType) + { + handlerType = null; + + // If part of the config is empty, the handler is disabled + if (string.IsNullOrEmpty(config.Class) || string.IsNullOrEmpty(config.Assembly)) + { + return false; + } + + // Search the assembly in a few directories + var path = string.Empty; + var paths = new HashSet + { + config.Assembly, + Path.Join(Environment.CurrentDirectory, config.Assembly), + Path.Join(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), config.Assembly), + }; + + foreach (var p in paths) + { + if (!File.Exists(p)) { continue; } + + path = p; + break; + } + + // Check if the assembly exists + if (string.IsNullOrEmpty(path)) + { + throw new ConfigurationException($"Handler assembly not found: {config.Assembly}"); + } + + Assembly assembly = Assembly.LoadFrom(path); + + // IPipelineStepHandler + handlerType = assembly.GetType(config.Class); + + if (!typeof(IPipelineStepHandler).IsAssignableFrom(handlerType)) + { + throw new ConfigurationException($"Handler assembly not found: {config.Class} doesn't implement interface {nameof(IPipelineStepHandler)}"); + } + + return true; + } +} diff --git a/service/Core/Configuration/HandlerConfig.cs b/service/Core/Configuration/HandlerConfig.cs new file mode 100644 index 000000000..54cf34d62 --- /dev/null +++ b/service/Core/Configuration/HandlerConfig.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.KernelMemory.Configuration; + +public class HandlerConfig +{ + /// + /// .NET assembly containing the handler class + /// + public string Assembly { get; set; } + + /// + /// .NET class in the assembly, containing the handler logic + /// + public string Class { get; set; } + + public HandlerConfig() + { + this.Assembly = string.Empty; + this.Class = string.Empty; + } + + public HandlerConfig(string assembly, string className) + { + this.Assembly = assembly; + this.Class = className; + } +} diff --git a/service/Abstractions/Configuration/KernelMemoryConfig.cs b/service/Core/Configuration/KernelMemoryConfig.cs similarity index 100% rename from service/Abstractions/Configuration/KernelMemoryConfig.cs rename to service/Core/Configuration/KernelMemoryConfig.cs diff --git a/service/Abstractions/Configuration/ServiceAuthorizationConfig.cs b/service/Core/Configuration/ServiceAuthorizationConfig.cs similarity index 100% rename from service/Abstractions/Configuration/ServiceAuthorizationConfig.cs rename to service/Core/Configuration/ServiceAuthorizationConfig.cs diff --git a/service/Abstractions/Configuration/ServiceConfig.cs b/service/Core/Configuration/ServiceConfig.cs similarity index 87% rename from service/Abstractions/Configuration/ServiceConfig.cs rename to service/Core/Configuration/ServiceConfig.cs index 0cdacbd8c..2821504a4 100644 --- a/service/Abstractions/Configuration/ServiceConfig.cs +++ b/service/Core/Configuration/ServiceConfig.cs @@ -1,5 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. +using System.Collections.Generic; + namespace Microsoft.KernelMemory.Configuration; public class ServiceConfig @@ -20,4 +22,6 @@ public class ServiceConfig /// Web service settings, e.g. whether to expose OpenAPI swagger docs. /// public bool OpenApiEnabled { get; set; } = false; + + public Dictionary Handlers { get; set; } = new(); } diff --git a/service/Core/KernelMemoryBuilder.cs b/service/Core/KernelMemoryBuilder.cs index bef80b15d..1f6a4ceb4 100644 --- a/service/Core/KernelMemoryBuilder.cs +++ b/service/Core/KernelMemoryBuilder.cs @@ -37,7 +37,7 @@ private enum ClientTypes // Services required to build the memory client class private readonly IServiceCollection _memoryServiceCollection; - // Services of the host application, when hosting pipeline handlers, e.g. in service mode + // Services of the host application private readonly IServiceCollection? _hostServiceCollection; // List of all the embedding generators to use during ingestion @@ -296,29 +296,6 @@ private MemoryService BuildAsyncClient() var orchestrator = serviceProvider.GetService() ?? throw new ConfigurationException("Unable to build orchestrator"); var searchClient = serviceProvider.GetService() ?? throw new ConfigurationException("Unable to build search client"); - if (this._useDefaultHandlers) - { - if (this._hostServiceCollection == null) - { - const string ClassName = nameof(KernelMemoryBuilder); - const string MethodName = nameof(this.WithoutDefaultHandlers); - throw new ConfigurationException("Host service collection not available: unable to register default handlers. " + - $"If you'd like using the default handlers use `new {ClassName}()`, " + - $"otherwise use `new {ClassName}(...).{MethodName}()` to manage the list of handlers manually."); - } - - // Handlers - Register these handlers to run as hosted services in the caller app. - // At start each hosted handler calls IPipelineOrchestrator.AddHandlerAsync() to register in the orchestrator. - this._hostServiceCollection.AddHandlerAsHostedService(Constants.PipelineStepsExtract); - this._hostServiceCollection.AddHandlerAsHostedService(Constants.PipelineStepsPartition); - this._hostServiceCollection.AddHandlerAsHostedService(Constants.PipelineStepsGenEmbeddings); - this._hostServiceCollection.AddHandlerAsHostedService(Constants.PipelineStepsSaveRecords); - this._hostServiceCollection.AddHandlerAsHostedService(Constants.PipelineStepsSummarize); - this._hostServiceCollection.AddHandlerAsHostedService(Constants.PipelineStepsDeleteDocument); - this._hostServiceCollection.AddHandlerAsHostedService(Constants.PipelineStepsDeleteIndex); - this._hostServiceCollection.AddHandlerAsHostedService(Constants.PipelineStepsDeleteGeneratedFiles); - } - this.CheckForMissingDependencies(); return new MemoryService(orchestrator, searchClient); diff --git a/service/Service/Auth/HttpAuthHandler.cs b/service/Service/Auth/HttpAuthHandler.cs index c05a6aea0..7ef89655f 100644 --- a/service/Service/Auth/HttpAuthHandler.cs +++ b/service/Service/Auth/HttpAuthHandler.cs @@ -13,6 +13,7 @@ public class HttpAuthEndpointFilter : IEndpointFilter public HttpAuthEndpointFilter(ServiceAuthorizationConfig config) { + config.Validate(); this._config = config; } diff --git a/service/Service/OpenAPI.cs b/service/Service/OpenAPI.cs index 75cb4eb01..70df0031d 100644 --- a/service/Service/OpenAPI.cs +++ b/service/Service/OpenAPI.cs @@ -11,42 +11,42 @@ internal static class OpenAPI { public static void ConfigureSwagger(this WebApplicationBuilder appBuilder, KernelMemoryConfig config) { - if (!config.Service.RunWebService) { return; } + if (!config.Service.RunWebService || !config.Service.OpenApiEnabled) { return; } appBuilder.Services.AddEndpointsApiExplorer(); - if (config.ServiceAuthorization.Enabled) + // Note: this call is required even if service auth is disabled + appBuilder.Services.AddSwaggerGen(c => { - appBuilder.Services.AddSwaggerGen(c => + if (!config.ServiceAuthorization.Enabled) { return; } + + const string ReqName = "auth"; + c.AddSecurityDefinition(ReqName, new OpenApiSecurityScheme { - const string ReqName = "auth"; - c.AddSecurityDefinition(ReqName, new OpenApiSecurityScheme - { - Description = "The API key to access the API", - Type = SecuritySchemeType.ApiKey, - Scheme = "ApiKeyScheme", - Name = config.ServiceAuthorization.HttpHeaderName, - In = ParameterLocation.Header, - }); - - var scheme = new OpenApiSecurityScheme - { - Reference = new OpenApiReference - { - Id = ReqName, - Type = ReferenceType.SecurityScheme, - }, - In = ParameterLocation.Header - }; - - var requirement = new OpenApiSecurityRequirement + Description = "The API key to access the API", + Type = SecuritySchemeType.ApiKey, + Scheme = "ApiKeyScheme", + Name = config.ServiceAuthorization.HttpHeaderName, + In = ParameterLocation.Header, + }); + + var scheme = new OpenApiSecurityScheme + { + Reference = new OpenApiReference { - { scheme, new List() } - }; + Id = ReqName, + Type = ReferenceType.SecurityScheme, + }, + In = ParameterLocation.Header + }; - c.AddSecurityRequirement(requirement); - }); - } + var requirement = new OpenApiSecurityRequirement + { + { scheme, new List() } + }; + + c.AddSecurityRequirement(requirement); + }); } public static void UseSwagger(this WebApplication app, KernelMemoryConfig config) diff --git a/service/Service/Program.cs b/service/Service/Program.cs index af60165c1..e8c8b231d 100644 --- a/service/Service/Program.cs +++ b/service/Service/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Collections.Generic; using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; @@ -52,35 +53,51 @@ public static void Main(string[] args) // *************************** APP BUILD ******************************* - // Usual .NET web app builder + // Usual .NET web app builder with settings from appsettings.json, appsettings..json, and env vars WebApplicationBuilder appBuilder = WebApplication.CreateBuilder(); appBuilder.Configuration.AddKMConfigurationSources(); - // Read settings and validate. Settings are needed before building the app. + // Read KM settings, needed before building the app. KernelMemoryConfig config = appBuilder.Configuration.GetSection("KernelMemory").Get() ?? throw new ConfigurationException("Unable to load configuration"); - config.ServiceAuthorization.Validate(); + + // Register pipeline handlers if enabled + if (config.Service.RunHandlers) + { + // You can add handlers in the configuration or manually here using one of these syntaxes: + // appBuilder.Services.AddHandlerAsHostedService<...CLASS...>("...STEP NAME..."); + // appBuilder.Services.AddHandlerAsHostedService("...assembly file name...", "...type full name...", "...STEP NAME..."); + + // Register all pipeline handlers defined in the configuration to run as hosted services + foreach (KeyValuePair handlerConfig in config.Service.Handlers) + { + appBuilder.Services.AddHandlerAsHostedService(config: handlerConfig.Value, stepName: handlerConfig.Key); + } + } // Some OpenAPI Explorer/Swagger dependencies appBuilder.ConfigureSwagger(config); // Inject memory client and its dependencies // Note: pass the current service collection to the builder, in order to start the pipeline handlers - IKernelMemory memory = new KernelMemoryBuilder(appBuilder.Services) - .FromAppSettings() - // .With...() // in case you need to set something not already defined by `.FromAppSettings()` - .Build(); + var memoryBuilder = new KernelMemoryBuilder(appBuilder.Services) + .FromAppSettings(); - appBuilder.Services.AddSingleton(memory); + // Build the memory client and make it available for dependency injection + appBuilder.Services.AddSingleton(memoryBuilder.Build()); - // Build .NET web app as usual, add HTTP endpoints using minimal API (https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis) + // Build .NET web app as usual WebApplication app = appBuilder.Build(); + + // Add HTTP endpoints using minimal API (https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis) app.ConfigureMinimalAPI(config); // *************************** START *********************************** + var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); + Console.WriteLine("***************************************************************************************************************************"); - Console.WriteLine($"* Environment : " + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "WARNING: ASPNETCORE_ENVIRONMENT env var not defined"); + Console.WriteLine($"* Environment : " + (string.IsNullOrEmpty(env) ? "WARNING: ASPNETCORE_ENVIRONMENT env var not defined" : env)); Console.WriteLine($"* Web service : " + (config.Service.RunWebService ? "Enabled" : "Disabled")); Console.WriteLine($"* Web service auth : " + (config.ServiceAuthorization.Enabled ? "Enabled" : "Disabled")); Console.WriteLine($"* Pipeline handlers : " + (config.Service.RunHandlers ? "Enabled" : "Disabled")); @@ -92,7 +109,6 @@ public static void Main(string[] args) Console.WriteLine($"* Text generation : {app.Services.GetService()?.GetType().FullName}"); Console.WriteLine("***************************************************************************************************************************"); - var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? string.Empty; app.Logger.LogInformation( "Starting Kernel Memory service, .NET Env: {0}, Log Level: {1}, Web service: {2}, Auth: {3}, Pipeline handlers: {4}", env, diff --git a/service/Service/ServiceConfiguration.cs b/service/Service/ServiceConfiguration.cs index 32f6d6f62..b98812da1 100644 --- a/service/Service/ServiceConfiguration.cs +++ b/service/Service/ServiceConfiguration.cs @@ -77,11 +77,6 @@ private IKernelMemoryBuilder BuildUsingConfiguration(IKernelMemoryBuilder builde // Required by ctors expecting KernelMemoryConfig via DI builder.AddSingleton(this._memoryConfiguration); - if (!this._memoryConfiguration.Service.RunHandlers) - { - builder.WithoutDefaultHandlers(); - } - this.ConfigureMimeTypeDetectionDependency(builder); this.ConfigureTextPartitioning(builder); diff --git a/service/Service/WebAPIEndpoints.cs b/service/Service/WebAPIEndpoints.cs index fde76f82b..7c30ec826 100644 --- a/service/Service/WebAPIEndpoints.cs +++ b/service/Service/WebAPIEndpoints.cs @@ -31,6 +31,7 @@ public static void ConfigureMinimalAPI(this WebApplication app, KernelMemoryConf app.UseDeleteDocumentsEndpoint(config, authFilter); app.UseAskEndpoint(config, authFilter); app.UseSearchEndpoint(config, authFilter); + app.UseUploadStatusEndpoint(config, authFilter); } public static void UseGetStatusEndpoint(this WebApplication app, KernelMemoryConfig config, HttpAuthEndpointFilter authFilter) diff --git a/service/Service/appsettings.json b/service/Service/appsettings.json index c72ae4d91..018398e2b 100644 --- a/service/Service/appsettings.json +++ b/service/Service/appsettings.json @@ -29,11 +29,54 @@ // Whether to run the web service that allows to upload files and search memory // Use these booleans to deploy the web service and the handlers on same/different VMs "RunWebService": true, + // Whether to expose OpenAPI swagger UI at http://127.0.0.1:9001/swagger/index.html + "OpenApiEnabled": false, // Whether to run the asynchronous pipeline handlers // Use these booleans to deploy the web service and the handlers on same/different VMs "RunHandlers": true, - // Whether to expose OpenAPI swagger UI at http://127.0.0.1:9001/swagger/index.html - "OpenApiEnabled": false + // Handlers to load for callers (use "steps" to choose which handlers + // to use to process a document during the ingestion) + "Handlers": { + // The key, e.g. "extract", is the name used when starting a pipeline with specific steps + "extract": { + "Assembly": "Microsoft.KernelMemory.Core.dll", + "Class": "Microsoft.KernelMemory.Handlers.TextExtractionHandler" + }, + "partition": { + "Assembly": "Microsoft.KernelMemory.Core.dll", + "Class": "Microsoft.KernelMemory.Handlers.TextPartitioningHandler" + }, + "gen_embeddings": { + "Assembly": "Microsoft.KernelMemory.Core.dll", + "Class": "Microsoft.KernelMemory.Handlers.GenerateEmbeddingsHandler" + }, + "save_records": { + "Assembly": "Microsoft.KernelMemory.Core.dll", + "Class": "Microsoft.KernelMemory.Handlers.SaveRecordsHandler" + }, + "summarize": { + "Assembly": "Microsoft.KernelMemory.Core.dll", + "Class": "Microsoft.KernelMemory.Handlers.SummarizationHandler" + }, + "delete_generated_files": { + "Assembly": "Microsoft.KernelMemory.Core.dll", + "Class": "Microsoft.KernelMemory.Handlers.DeleteGeneratedFilesHandler" + }, + "private_delete_document": { + "Assembly": "Microsoft.KernelMemory.Core.dll", + "Class": "Microsoft.KernelMemory.Handlers.DeleteDocumentHandler" + }, + "private_delete_index": { + "Assembly": "Microsoft.KernelMemory.Core.dll", + "Class": "Microsoft.KernelMemory.Handlers.DeleteIndexHandler" + }, + "disabled_handler_example": { + // Setting Class or Assembly to "" in appsettings.Development.json or appsettings.Production.json + // allows to remove a handler defined in appsettings.json + "Class": "", + "Assembly": "" + } + } }, "ServiceAuthorization": { // Whether clients must provide some credentials to interact with the HTTP API diff --git a/service/tests/Core.FunctionalTests/DefaultTestCases/IndexListTest.cs b/service/tests/Core.FunctionalTests/DefaultTestCases/IndexListTest.cs index 87293c2ce..9275b1eef 100644 --- a/service/tests/Core.FunctionalTests/DefaultTestCases/IndexListTest.cs +++ b/service/tests/Core.FunctionalTests/DefaultTestCases/IndexListTest.cs @@ -58,25 +58,25 @@ public static async Task ItListsIndexes(IKernelMemory memory, Action log while (!await memory.IsDocumentReadyAsync(documentId: id1, index: indexName1)) { - log("Waiting for memory ingestion to complete..."); + log($"[id1: {id1}] Waiting for memory ingestion to complete..."); await Task.Delay(TimeSpan.FromSeconds(2)); } while (!await memory.IsDocumentReadyAsync(documentId: id2, index: indexName2)) { - log("Waiting for memory ingestion to complete..."); + log($"[id2: {id2}] Waiting for memory ingestion to complete..."); await Task.Delay(TimeSpan.FromSeconds(2)); } while (!await memory.IsDocumentReadyAsync(documentId: id3, index: indexNameWithDashes)) { - log("Waiting for memory ingestion to complete..."); + log($"[id3: {id3}] Waiting for memory ingestion to complete..."); await Task.Delay(TimeSpan.FromSeconds(2)); } while (!await memory.IsDocumentReadyAsync(documentId: id4, index: indexNameWithUnderscores)) { - log("Waiting for memory ingestion to complete..."); + log($"[id4: {id4}] Waiting for memory ingestion to complete..."); await Task.Delay(TimeSpan.FromSeconds(2)); } diff --git a/tools/InteractiveSetup/InteractiveSetup.csproj b/tools/InteractiveSetup/InteractiveSetup.csproj index bd9e04fb8..7c8474a77 100644 --- a/tools/InteractiveSetup/InteractiveSetup.csproj +++ b/tools/InteractiveSetup/InteractiveSetup.csproj @@ -9,8 +9,8 @@ - - + +