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 @@
-
-
+
+