diff --git a/Directory.Build.props b/Directory.Build.props index 7c8a168c5..72ca146b4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ - 0.95.0 + 0.96.0 12 diff --git a/examples/001-dotnet-WebClient/Properties/launchSettings.json b/examples/001-dotnet-WebClient/Properties/launchSettings.json new file mode 100644 index 000000000..dea57200c --- /dev/null +++ b/examples/001-dotnet-WebClient/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "console": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/examples/002-dotnet-Serverless/Properties/launchSettings.json b/examples/002-dotnet-Serverless/Properties/launchSettings.json new file mode 100644 index 000000000..dea57200c --- /dev/null +++ b/examples/002-dotnet-Serverless/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "console": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/examples/007-dotnet-serverless-azure/Properties/launchSettings.json b/examples/007-dotnet-serverless-azure/Properties/launchSettings.json new file mode 100644 index 000000000..dea57200c --- /dev/null +++ b/examples/007-dotnet-serverless-azure/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "console": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/service/Abstractions/IKernelMemoryBuilder.cs b/service/Abstractions/IKernelMemoryBuilder.cs index 7878003e7..b0d552f75 100644 --- a/service/Abstractions/IKernelMemoryBuilder.cs +++ b/service/Abstractions/IKernelMemoryBuilder.cs @@ -26,17 +26,20 @@ public interface IKernelMemoryBuilder /// /// Build the memory instance, using defaults and the provided dependencies /// and overrides. Depending on the dependencies provided, the resulting - /// memory might use either an synchronous or asynchronous pipeline. + /// memory might use either a synchronous or asynchronous pipeline. /// - public IKernelMemory Build(); + /// Options for the build process + /// A memory instance + public IKernelMemory Build(KernelMemoryBuilderBuildOptions? options = null); /// /// Build a specific type of memory instance, e.g. explicitly choosing /// between synchronous or asynchronous (queue based) pipeline. /// /// Type of memory derived from IKernelMemory + /// Options for the build process /// A memory instance - public T Build() where T : class, IKernelMemory; + public T Build(KernelMemoryBuilderBuildOptions? options = null) where T : class, IKernelMemory; /// /// Add a singleton to the builder service collection pool. diff --git a/service/Abstractions/KernelMemoryBuilderBuildOptions.cs b/service/Abstractions/KernelMemoryBuilderBuildOptions.cs new file mode 100644 index 000000000..e0739b6af --- /dev/null +++ b/service/Abstractions/KernelMemoryBuilderBuildOptions.cs @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft. All rights reserved. + +namespace Microsoft.KernelMemory; + +public sealed class KernelMemoryBuilderBuildOptions +{ + public bool AllowMixingVolatileAndPersistentData { get; set; } = false; +} diff --git a/service/Core/KernelMemoryBuilder.cs b/service/Core/KernelMemoryBuilder.cs index 305d67663..e6babd817 100644 --- a/service/Core/KernelMemoryBuilder.cs +++ b/service/Core/KernelMemoryBuilder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.KernelMemory.AI; using Microsoft.KernelMemory.AppBuilders; @@ -116,21 +117,21 @@ public KernelMemoryBuilder(IServiceCollection? hostServiceCollection = null) } /// - public IKernelMemory Build() + public IKernelMemory Build(KernelMemoryBuilderBuildOptions? options = null) { var type = this.GetBuildType(); switch (type) { case ClientTypes.SyncServerless: - return this.BuildServerlessClient(); + return this.BuildServerlessClient(options); case ClientTypes.AsyncService: - return this.BuildAsyncClient(); + return this.BuildAsyncClient(options); case ClientTypes.Undefined: - throw new KernelMemoryException("Missing dependencies or insufficient configuration provided. " + - "Try using With...() methods " + - $"and other configuration methods before calling {nameof(this.Build)}(...)"); + throw new ConfigurationException("Missing dependencies or insufficient configuration provided. " + + "Try using With...() methods " + + $"and other configuration methods before calling {nameof(this.Build)}(...)"); default: throw new ArgumentOutOfRangeException(nameof(type), $"Unsupported memory type '{type}'"); @@ -138,11 +139,11 @@ public IKernelMemory Build() } /// - public T Build() where T : class, IKernelMemory + public T Build(KernelMemoryBuilderBuildOptions? options = null) where T : class, IKernelMemory { if (typeof(T) == typeof(MemoryServerless)) { - if (this.BuildServerlessClient() is not T result) + if (this.BuildServerlessClient(options) is not T result) { throw new InvalidOperationException($"Unable to instantiate '{typeof(MemoryServerless)}'. The instance is NULL."); } @@ -152,7 +153,7 @@ public T Build() where T : class, IKernelMemory if (typeof(T) == typeof(MemoryService)) { - if (this.BuildAsyncClient() is not T result) + if (this.BuildAsyncClient(options) is not T result) { throw new InvalidOperationException($"Unable to instantiate '{typeof(MemoryService)}'. The instance is NULL."); } @@ -226,7 +227,7 @@ private static void CopyServiceCollection( } } - private MemoryServerless BuildServerlessClient() + private MemoryServerless BuildServerlessClient(KernelMemoryBuilderBuildOptions? options) { try { @@ -240,6 +241,7 @@ private MemoryServerless BuildServerlessClient() // Recreate the service provider, in order to have the latest dependencies just configured serviceProvider = this._memoryServiceCollection.BuildServiceProvider(); + this.CheckStoragePersistence(options, serviceProvider); var memoryClientInstance = ActivatorUtilities.CreateInstance(serviceProvider); // Load handlers in the memory client @@ -257,7 +259,7 @@ private MemoryServerless BuildServerlessClient() } } - private MemoryService BuildAsyncClient() + private MemoryService BuildAsyncClient(KernelMemoryBuilderBuildOptions? options) { // Add handlers to DI service collection if (this._useDefaultHandlers) @@ -282,9 +284,62 @@ private MemoryService BuildAsyncClient() // Recreate the service provider, in order to have the latest dependencies just configured serviceProvider = this._memoryServiceCollection.BuildServiceProvider(); + this.CheckStoragePersistence(options, serviceProvider); return ActivatorUtilities.CreateInstance(serviceProvider); } + private void CheckStoragePersistence(KernelMemoryBuilderBuildOptions? options, ServiceProvider serviceProvider) + { + if (options is { AllowMixingVolatileAndPersistentData: true }) { return; } + + ServiceDescriptor docStoreType = this._memoryServiceCollection.Last(x => x.ServiceType == typeof(IDocumentStorage)); + ServiceDescriptor memStoreType = this._memoryServiceCollection.Last(x => x.ServiceType == typeof(IMemoryDb)); + SimpleFileStorageConfig? simpleFileStorageConfig = serviceProvider.GetService(); + SimpleVectorDbConfig? simpleVectorDbConfig = serviceProvider.GetService(); + SimpleTextDbConfig? simpleTextDbConfig = serviceProvider.GetService(); + + bool persistentDocStore = docStoreType.ImplementationType != typeof(SimpleFileStorage) || simpleFileStorageConfig?.StorageType == FileSystemTypes.Disk; + bool persistentMemStore = memStoreType.ImplementationType != typeof(SimpleVectorDb) && memStoreType.ImplementationType != typeof(SimpleTextDb) + || (memStoreType.ImplementationType == typeof(SimpleVectorDb) && simpleVectorDbConfig?.StorageType == FileSystemTypes.Disk) + || (memStoreType.ImplementationType == typeof(SimpleTextDb) && simpleTextDbConfig?.StorageType == FileSystemTypes.Disk); + + // No error if both services are volatile or persistent, + if (persistentMemStore == persistentDocStore) + { + return; + } + + // Show a service name for a helpful error message + var docStoreName = docStoreType.ImplementationType != typeof(SimpleFileStorage) + ? docStoreType.ImplementationType?.Name + : $"{nameof(SimpleFileStorage)} {simpleFileStorageConfig?.StorageType}"; + + var memStoreName = memStoreType.ImplementationType != typeof(SimpleVectorDb) && memStoreType.ImplementationType != typeof(SimpleTextDb) + ? memStoreType.ImplementationType?.Name + : memStoreType.ImplementationType == typeof(SimpleVectorDb) + ? $"{nameof(SimpleVectorDb)} {simpleVectorDbConfig?.StorageType}" + : $"{nameof(SimpleTextDb)} {simpleTextDbConfig?.StorageType}"; + + // Different error message depending on which service is volatile + if (persistentMemStore && !persistentDocStore) + { + throw new ConfigurationException( + $"Using a persistent memory store ({memStoreName}) with a volatile document store ({docStoreName}) will lead to duplicate memory records over multiple executions. " + + $"Set up Kernel Memory to use a persistent document store like Azure Blobs, AWS S3, {nameof(SimpleFileStorage)} on disk, etc. " + + $"Otherwise, use {nameof(KernelMemoryBuilderBuildOptions)}.{nameof(KernelMemoryBuilderBuildOptions.AllowMixingVolatileAndPersistentData)} " + + "to suppress this exception when invoking kernelMemoryBuilder.Build(). "); + } + + if (persistentDocStore && !persistentMemStore) + { + throw new ConfigurationException( + $"Using a volatile memory store ({memStoreName}) with a persistent document store ({docStoreName}) will lead to missing memory records over multiple executions. " + + $"Set up Kernel Memory to use a persistent memory store like Azure AI Search, Postgres, Qdrant, {nameof(SimpleVectorDb)} on disk, etc. " + + $"Otherwise, use {nameof(KernelMemoryBuilderBuildOptions)}.{nameof(KernelMemoryBuilderBuildOptions.AllowMixingVolatileAndPersistentData)} " + + "to suppress this exception when invoking kernelMemoryBuilder.Build(). "); + } + } + private KernelMemoryBuilder CompleteServerlessClient(ServiceProvider serviceProvider) { this.UseDefaultSearchClientIfNecessary(serviceProvider); diff --git a/service/Service/Program.cs b/service/Service/Program.cs index 05dd48e48..a169c690d 100644 --- a/service/Service/Program.cs +++ b/service/Service/Program.cs @@ -60,7 +60,7 @@ public static void Main(string[] args) // Run `dotnet run setup` to run this code and set up the service if (new[] { "setup", "-setup", "config" }.Contains(args.FirstOrDefault(), StringComparer.OrdinalIgnoreCase)) { - InteractiveSetup.Main.InteractiveSetup(args.Skip(1).ToArray()); + InteractiveSetup.Program.Main(args.Skip(1).ToArray()); } // *************************** APP BUILD ******************************* diff --git a/tools/InteractiveSetup/AppSettings.cs b/tools/InteractiveSetup/AppSettings.cs index 9cbf517cd..a31a5c778 100644 --- a/tools/InteractiveSetup/AppSettings.cs +++ b/tools/InteractiveSetup/AppSettings.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Collections.Generic; using System.IO; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -37,6 +38,11 @@ public static void Change(Action configChanges) File.WriteAllText(DevelopmentSettingsFile, json); } + public static void AddService(string serviceName, Dictionary config) + { + Change(x => { x.Services.Add(serviceName, config); }); + } + public static void GlobalChange(Action configChanges) { CreateFileIfNotExists(); diff --git a/tools/InteractiveSetup/Context.cs b/tools/InteractiveSetup/Context.cs index 0e539c4ba..053147bb5 100644 --- a/tools/InteractiveSetup/Context.cs +++ b/tools/InteractiveSetup/Context.cs @@ -27,8 +27,10 @@ internal sealed class Context public readonly BoundedBoolean CfgAzureOpenAIText = new(); public readonly BoundedBoolean CfgAzureOpenAIEmbedding = new(); public readonly BoundedBoolean CfgOpenAI = new(); - public readonly BoundedBoolean CfgLlamaSharp = new(); - public readonly BoundedBoolean CfgOllama = new(); + public readonly BoundedBoolean CfgLlamaSharpEmbedding = new(); + public readonly BoundedBoolean CfgLlamaSharpText = new(); + public readonly BoundedBoolean CfgOllamaEmbedding = new(); + public readonly BoundedBoolean CfgOllamaText = new(); public readonly BoundedBoolean CfgAzureAIDocIntel = new(); // Vectors diff --git a/tools/InteractiveSetup/InteractiveSetup.csproj b/tools/InteractiveSetup/InteractiveSetup.csproj index 2e2e14932..53fc29bb0 100644 --- a/tools/InteractiveSetup/InteractiveSetup.csproj +++ b/tools/InteractiveSetup/InteractiveSetup.csproj @@ -2,6 +2,7 @@ net8.0 + Exe Microsoft.KernelMemory.InteractiveSetup Microsoft.KernelMemory.InteractiveSetup $(NoWarn);CA1031;CA1303;CA1724; diff --git a/tools/InteractiveSetup/Main.cs b/tools/InteractiveSetup/Program.cs similarity index 96% rename from tools/InteractiveSetup/Main.cs rename to tools/InteractiveSetup/Program.cs index 0819dbbbe..d58433cea 100644 --- a/tools/InteractiveSetup/Main.cs +++ b/tools/InteractiveSetup/Program.cs @@ -7,9 +7,9 @@ namespace Microsoft.KernelMemory.InteractiveSetup; -public static class Main +public static class Program { - public static void InteractiveSetup(string[] args) + public static void Main(string[] args) { var ctx = new Context(); @@ -207,7 +207,19 @@ private static void EmbeddingGeneratorSetup(Context ctx) ? [x.Retrieval.EmbeddingGeneratorType] : []; }); - ctx.CfgOllama.Value = true; + ctx.CfgOllamaEmbedding.Value = true; + }), + + new("LlamaSharp library", config.Retrieval.EmbeddingGeneratorType == "LlamaSharp", () => + { + AppSettings.Change(x => + { + x.Retrieval.EmbeddingGeneratorType = "LlamaSharp"; + x.DataIngestion.EmbeddingGeneratorTypes = ctx.CfgEmbeddingGenerationEnabled.Value + ? [x.Retrieval.EmbeddingGeneratorType] + : []; + }); + ctx.CfgLlamaSharpEmbedding.Value = true; }), new("None/Custom (manually set with code)", string.IsNullOrEmpty(config.Retrieval.EmbeddingGeneratorType), () => @@ -248,13 +260,13 @@ private static void TextGeneratorTypeSetup(Context ctx) new("Ollama service", config.TextGeneratorType == "Ollama", () => { AppSettings.Change(x => { x.TextGeneratorType = "Ollama"; }); - ctx.CfgOllama.Value = true; + ctx.CfgOllamaText.Value = true; }), new("LlamaSharp library", config.TextGeneratorType == "LlamaSharp", () => { AppSettings.Change(x => { x.TextGeneratorType = "LlamaSharp"; }); - ctx.CfgLlamaSharp.Value = true; + ctx.CfgLlamaSharpText.Value = true; }), new("None/Custom (manually set with code)", string.IsNullOrEmpty(config.TextGeneratorType), () => diff --git a/tools/InteractiveSetup/Properties/launchSettings.json b/tools/InteractiveSetup/Properties/launchSettings.json new file mode 100644 index 000000000..dea57200c --- /dev/null +++ b/tools/InteractiveSetup/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "console": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/tools/InteractiveSetup/Services/AWSS3.cs b/tools/InteractiveSetup/Services/AWSS3.cs index 569304a67..21d3d3425 100644 --- a/tools/InteractiveSetup/Services/AWSS3.cs +++ b/tools/InteractiveSetup/Services/AWSS3.cs @@ -24,6 +24,7 @@ public static void Setup(Context ctx) { "BucketName", "" }, { "Endpoint", "https://s3.amazonaws.com" }, }; + AppSettings.AddService(ServiceName, config); } // Required to avoid exceptions. "Endpoint" is optional and not defined in appsettings.json diff --git a/tools/InteractiveSetup/Services/AzureAIDocIntel.cs b/tools/InteractiveSetup/Services/AzureAIDocIntel.cs index 7a20786f7..0ee7f661f 100644 --- a/tools/InteractiveSetup/Services/AzureAIDocIntel.cs +++ b/tools/InteractiveSetup/Services/AzureAIDocIntel.cs @@ -22,6 +22,7 @@ public static void Setup(Context ctx) { "Auth", "ApiKey" }, { "APIKey", "" }, }; + AppSettings.AddService(ServiceName, config); } SetupUI.AskQuestionWithOptions(new QuestionWithOptions diff --git a/tools/InteractiveSetup/Services/AzureAISearch.cs b/tools/InteractiveSetup/Services/AzureAISearch.cs index 2e929e869..bd5d4964d 100644 --- a/tools/InteractiveSetup/Services/AzureAISearch.cs +++ b/tools/InteractiveSetup/Services/AzureAISearch.cs @@ -24,6 +24,7 @@ public static void Setup(Context ctx, bool force = false) { "UseHybridSearch", false }, { "UseStickySessions", false } }; + AppSettings.AddService(ServiceName, config); } SetupUI.AskQuestionWithOptions(new QuestionWithOptions diff --git a/tools/InteractiveSetup/Services/AzureBlobs.cs b/tools/InteractiveSetup/Services/AzureBlobs.cs index af16ea9a7..6e3d8061d 100644 --- a/tools/InteractiveSetup/Services/AzureBlobs.cs +++ b/tools/InteractiveSetup/Services/AzureBlobs.cs @@ -23,6 +23,7 @@ public static void Setup(Context ctx) { "Auth", "ConnectionString" }, { "ConnectionString", "" }, }; + AppSettings.AddService(ServiceName, config); } SetupUI.AskQuestionWithOptions(new QuestionWithOptions diff --git a/tools/InteractiveSetup/Services/AzureOpenAIEmbedding.cs b/tools/InteractiveSetup/Services/AzureOpenAIEmbedding.cs index 0d07c531f..b9da996b0 100644 --- a/tools/InteractiveSetup/Services/AzureOpenAIEmbedding.cs +++ b/tools/InteractiveSetup/Services/AzureOpenAIEmbedding.cs @@ -25,6 +25,7 @@ public static void Setup(Context ctx, bool force = false) { "Auth", "ApiKey" }, { "APIKey", "" }, }; + AppSettings.AddService(ServiceName, config); } SetupUI.AskQuestionWithOptions(new QuestionWithOptions diff --git a/tools/InteractiveSetup/Services/AzureOpenAIText.cs b/tools/InteractiveSetup/Services/AzureOpenAIText.cs index cdc3e035a..33f661ba1 100644 --- a/tools/InteractiveSetup/Services/AzureOpenAIText.cs +++ b/tools/InteractiveSetup/Services/AzureOpenAIText.cs @@ -25,6 +25,7 @@ public static void Setup(Context ctx, bool force = false) { "Auth", "ApiKey" }, { "APIKey", "" }, }; + AppSettings.AddService(ServiceName, config); } SetupUI.AskQuestionWithOptions(new QuestionWithOptions diff --git a/tools/InteractiveSetup/Services/AzureQueue.cs b/tools/InteractiveSetup/Services/AzureQueue.cs index db70cba73..52364b229 100644 --- a/tools/InteractiveSetup/Services/AzureQueue.cs +++ b/tools/InteractiveSetup/Services/AzureQueue.cs @@ -18,10 +18,11 @@ public static void Setup(Context ctx) { config = new Dictionary { - { "Account", "" }, { "Auth", "ConnectionString" }, { "ConnectionString", "" }, + { "Account", "" }, }; + AppSettings.AddService(ServiceName, config); } SetupUI.AskQuestionWithOptions(new QuestionWithOptions diff --git a/tools/InteractiveSetup/Services/LlamaSharp.cs b/tools/InteractiveSetup/Services/LlamaSharp.cs index 390ca4e0b..22b53ca87 100644 --- a/tools/InteractiveSetup/Services/LlamaSharp.cs +++ b/tools/InteractiveSetup/Services/LlamaSharp.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Microsoft.KernelMemory.InteractiveSetup.UI; +using Newtonsoft.Json.Linq; namespace Microsoft.KernelMemory.InteractiveSetup.Services; @@ -9,24 +10,76 @@ internal static class LlamaSharp { public static void Setup(Context ctx, bool force = false) { - if (!ctx.CfgLlamaSharp.Value && !force) { return; } + if (!ctx.CfgLlamaSharpText.Value && !ctx.CfgLlamaSharpEmbedding.Value && !force) { return; } - ctx.CfgLlamaSharp.Value = false; const string ServiceName = "LlamaSharp"; - if (!AppSettings.GetCurrentConfig().Services.TryGetValue(ServiceName, out var config)) + Dictionary textModel = []; + Dictionary embeddingModel = []; + + if (AppSettings.GetCurrentConfig().Services.TryGetValue(ServiceName, out var config)) { - config = new Dictionary + if (config.TryGetValue("TextModel", out object? tm) && tm is JObject jtm) + { + textModel = jtm.ToObject>() ?? []; + } + + if (config.TryGetValue("EmbeddingModel", out object? em) && em is JObject jem) + { + embeddingModel = jem.ToObject>() ?? []; + } + } + else + { + textModel = new Dictionary + { + { "ModelPath", "" }, + { "MaxTokenTotal", 4096 }, + }; + + embeddingModel = new Dictionary { { "ModelPath", "" }, { "MaxTokenTotal", 4096 }, }; + + config = new Dictionary + { + { "TextModel", textModel }, + { "EmbeddingModel", embeddingModel } + }; + AppSettings.AddService(ServiceName, config); } - AppSettings.Change(x => x.Services[ServiceName] = new Dictionary + if (ctx.CfgLlamaSharpText.Value) { - { "ModelPath", SetupUI.AskOpenQuestion("Path to model .gguf file", config.TryGet("ModelPath")) }, - { "MaxTokenTotal", SetupUI.AskOpenQuestion("Max tokens supported by the model", config.TryGet("MaxTokenTotal")) }, - }); + AppSettings.Change(x => x.Services[ServiceName] = new Dictionary + { + { + "TextModel", new Dictionary + { + { "ModelPath", SetupUI.AskOpenQuestion("Path to text model .gguf file", textModel.TryGet("ModelPath")) }, + { "MaxTokenTotal", SetupUI.AskOpenQuestion("Max tokens supported by the text model", textModel.TryGet("MaxTokenTotal")) }, + } + } + }); + } + + if (ctx.CfgLlamaSharpEmbedding.Value) + { + AppSettings.Change(x => x.Services[ServiceName] = new Dictionary + { + { + "EmbeddingModel", new Dictionary + { + { "ModelPath", SetupUI.AskOpenQuestion("Path to embedding model .gguf file", embeddingModel.TryGet("ModelPath")) }, + { "MaxTokenTotal", SetupUI.AskOpenQuestion("Max tokens supported by the embedding model", embeddingModel.TryGet("MaxTokenTotal")) }, + } + } + }); + } + + ctx.CfgLlamaSharpText.Value = false; + ctx.CfgLlamaSharpEmbedding.Value = false; } } diff --git a/tools/InteractiveSetup/Services/MongoDbAtlasDocumentStorage.cs b/tools/InteractiveSetup/Services/MongoDbAtlasDocumentStorage.cs index ac93b6cb1..5cefc7b2d 100644 --- a/tools/InteractiveSetup/Services/MongoDbAtlasDocumentStorage.cs +++ b/tools/InteractiveSetup/Services/MongoDbAtlasDocumentStorage.cs @@ -18,8 +18,9 @@ public static void Setup(Context ctx, bool force = false) { config = new Dictionary { - { "ConnectionString", "" }, + { "ConnectionString", "" } }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary diff --git a/tools/InteractiveSetup/Services/MongoDbAtlasMemoryDb.cs b/tools/InteractiveSetup/Services/MongoDbAtlasMemoryDb.cs index a1ce5b8f6..0803ec4d2 100644 --- a/tools/InteractiveSetup/Services/MongoDbAtlasMemoryDb.cs +++ b/tools/InteractiveSetup/Services/MongoDbAtlasMemoryDb.cs @@ -20,6 +20,7 @@ public static void Setup(Context ctx, bool force = false) { { "ConnectionString", "" }, }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary diff --git a/tools/InteractiveSetup/Services/Ollama.cs b/tools/InteractiveSetup/Services/Ollama.cs index 5470f4318..bc82fea03 100644 --- a/tools/InteractiveSetup/Services/Ollama.cs +++ b/tools/InteractiveSetup/Services/Ollama.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using Microsoft.KernelMemory.InteractiveSetup.UI; +using Newtonsoft.Json.Linq; namespace Microsoft.KernelMemory.InteractiveSetup.Services; @@ -10,15 +11,26 @@ internal static class Ollama { public static void Setup(Context ctx, bool force = false) { - if (!ctx.CfgOllama.Value && !force) { return; } + if (!ctx.CfgOllamaText.Value && !ctx.CfgOllamaEmbedding.Value && !force) { return; } - ctx.CfgOllama.Value = false; const string ServiceName = "Ollama"; Dictionary textModel = []; Dictionary embeddingModel = []; - if (!AppSettings.GetCurrentConfig().Services.TryGetValue(ServiceName, out var config)) + if (AppSettings.GetCurrentConfig().Services.TryGetValue(ServiceName, out var config)) + { + if (config.TryGetValue("TextModel", out object? tm) && tm is JObject jtm) + { + textModel = jtm.ToObject>() ?? []; + } + + if (config.TryGetValue("EmbeddingModel", out object? em) && em is JObject jem) + { + embeddingModel = jem.ToObject>() ?? []; + } + } + else { textModel = new Dictionary { @@ -38,6 +50,7 @@ public static void Setup(Context ctx, bool force = false) { "TextModel", textModel }, { "EmbeddingModel", embeddingModel }, }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary @@ -45,17 +58,26 @@ public static void Setup(Context ctx, bool force = false) { "Endpoint", SetupUI.AskOpenQuestion("Ollama endpoint", config.TryGet("Endpoint")) } }); - AppSettings.Change(x => x.Services[ServiceName]["TextModel"] = new Dictionary + if (ctx.CfgOllamaText.Value) { - { "ModelName", SetupUI.AskOpenQuestion("Ollama text model name", textModel.TryGet("ModelName")) }, - { "MaxTokenTotal", SetupUI.AskOpenQuestionInt("Ollama text model max tokens", StrToInt(textModel.TryGet("MaxTokenTotal"))) }, - }); + AppSettings.Change(x => x.Services[ServiceName]["TextModel"] = new Dictionary + { + { "ModelName", SetupUI.AskOpenQuestion("Ollama text model name", textModel.TryGet("ModelName")) }, + { "MaxTokenTotal", SetupUI.AskOpenQuestionInt("Ollama text model max tokens", StrToInt(textModel.TryGet("MaxTokenTotal"))) }, + }); + } - AppSettings.Change(x => x.Services[ServiceName]["EmbeddingModel"] = new Dictionary + if (ctx.CfgOllamaEmbedding.Value) { - { "ModelName", SetupUI.AskOpenQuestion("Ollama text embedding model name", embeddingModel.TryGet("ModelName")) }, - { "MaxTokenTotal", SetupUI.AskOpenQuestionInt("Ollama text embedding model max tokens", StrToInt(embeddingModel.TryGet("MaxTokenTotal"))) }, - }); + AppSettings.Change(x => x.Services[ServiceName]["EmbeddingModel"] = new Dictionary + { + { "ModelName", SetupUI.AskOpenQuestion("Ollama text embedding model name", embeddingModel.TryGet("ModelName")) }, + { "MaxTokenTotal", SetupUI.AskOpenQuestionInt("Ollama text embedding model max tokens", StrToInt(embeddingModel.TryGet("MaxTokenTotal"))) }, + }); + } + + ctx.CfgOllamaText.Value = false; + ctx.CfgOllamaEmbedding.Value = false; } private static int StrToInt(string s) diff --git a/tools/InteractiveSetup/Services/OpenAI.cs b/tools/InteractiveSetup/Services/OpenAI.cs index d4ea1f95f..6a12f906e 100644 --- a/tools/InteractiveSetup/Services/OpenAI.cs +++ b/tools/InteractiveSetup/Services/OpenAI.cs @@ -24,6 +24,7 @@ public static void Setup(Context ctx, bool force = false) { "OrgId", "" }, { "MaxRetries", 10 }, }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary diff --git a/tools/InteractiveSetup/Services/Postgres.cs b/tools/InteractiveSetup/Services/Postgres.cs index ce42b92cd..9c7040565 100644 --- a/tools/InteractiveSetup/Services/Postgres.cs +++ b/tools/InteractiveSetup/Services/Postgres.cs @@ -20,6 +20,7 @@ public static void Setup(Context ctx, bool force = false) { { "ConnectionString", "" }, }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary diff --git a/tools/InteractiveSetup/Services/Qdrant.cs b/tools/InteractiveSetup/Services/Qdrant.cs index 34fcf53a6..ac81a8c46 100644 --- a/tools/InteractiveSetup/Services/Qdrant.cs +++ b/tools/InteractiveSetup/Services/Qdrant.cs @@ -21,6 +21,7 @@ public static void Setup(Context ctx, bool force = false) { "Endpoint", "http://127.0.0.1:6333" }, { "APIKey", "" }, }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary diff --git a/tools/InteractiveSetup/Services/RabbitMQ.cs b/tools/InteractiveSetup/Services/RabbitMQ.cs index 803ae7e4c..0887e55dd 100644 --- a/tools/InteractiveSetup/Services/RabbitMQ.cs +++ b/tools/InteractiveSetup/Services/RabbitMQ.cs @@ -25,6 +25,7 @@ public static void Setup(Context ctx, bool force = false) { "VirtualHost", "/" }, { "SslEnabled", false }, }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary diff --git a/tools/InteractiveSetup/Services/Redis.cs b/tools/InteractiveSetup/Services/Redis.cs index ccd016986..7f1cdad1c 100644 --- a/tools/InteractiveSetup/Services/Redis.cs +++ b/tools/InteractiveSetup/Services/Redis.cs @@ -21,6 +21,7 @@ public static void Setup(Context ctx, bool force = false) { ["ConnectionString"] = "" }; + AppSettings.AddService(ServiceName, config); } var connectionString = SetupUI.AskPassword("Redis connection string (e.g. 'localhost:6379,password=..')", config["ConnectionString"].ToString(), optional: true); diff --git a/tools/InteractiveSetup/Services/SimpleFileStorage.cs b/tools/InteractiveSetup/Services/SimpleFileStorage.cs index a359b57e3..5030c9175 100644 --- a/tools/InteractiveSetup/Services/SimpleFileStorage.cs +++ b/tools/InteractiveSetup/Services/SimpleFileStorage.cs @@ -21,8 +21,10 @@ public static void Setup(Context ctx) { config = new Dictionary { - { "Directory", "" } + { "Directory", "" }, + { "StorageType", "Disk" } }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary diff --git a/tools/InteractiveSetup/Services/SimpleQueues.cs b/tools/InteractiveSetup/Services/SimpleQueues.cs index ab80fbaec..2472fa51d 100644 --- a/tools/InteractiveSetup/Services/SimpleQueues.cs +++ b/tools/InteractiveSetup/Services/SimpleQueues.cs @@ -23,6 +23,7 @@ public static void Setup(Context ctx) { { "Directory", "" } }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary diff --git a/tools/InteractiveSetup/Services/SimpleVectorDb.cs b/tools/InteractiveSetup/Services/SimpleVectorDb.cs index dada65529..2bbb8c7e3 100644 --- a/tools/InteractiveSetup/Services/SimpleVectorDb.cs +++ b/tools/InteractiveSetup/Services/SimpleVectorDb.cs @@ -22,7 +22,9 @@ public static void Setup(Context ctx, bool force = false) config = new Dictionary { { "Directory", "" }, + { "StorageType", "Disk" } }; + AppSettings.AddService(ServiceName, config); } AppSettings.Change(x => x.Services[ServiceName] = new Dictionary