Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Project>
<PropertyGroup>
<!-- Central version prefix - applies to all nuget packages. -->
<Version>0.95.0</Version>
<Version>0.96.0</Version>

<!-- C# lang version, https://learn.microsoft.com/dotnet/csharp/whats-new -->
<LangVersion>12</LangVersion>
Expand Down
11 changes: 11 additions & 0 deletions examples/001-dotnet-WebClient/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"profiles": {
"console": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
11 changes: 11 additions & 0 deletions examples/002-dotnet-Serverless/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"profiles": {
"console": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"profiles": {
"console": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
9 changes: 6 additions & 3 deletions service/Abstractions/IKernelMemoryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,20 @@ public interface IKernelMemoryBuilder
/// <summary>
/// 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.
/// </summary>
public IKernelMemory Build();
/// <param name="options">Options for the build process</param>
/// <returns>A memory instance</returns>
public IKernelMemory Build(KernelMemoryBuilderBuildOptions? options = null);

/// <summary>
/// Build a specific type of memory instance, e.g. explicitly choosing
/// between synchronous or asynchronous (queue based) pipeline.
/// </summary>
/// <typeparam name="T">Type of memory derived from IKernelMemory</typeparam>
/// <param name="options">Options for the build process</param>
/// <returns>A memory instance</returns>
public T Build<T>() where T : class, IKernelMemory;
public T Build<T>(KernelMemoryBuilderBuildOptions? options = null) where T : class, IKernelMemory;

/// <summary>
/// Add a singleton to the builder service collection pool.
Expand Down
8 changes: 8 additions & 0 deletions service/Abstractions/KernelMemoryBuilderBuildOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.KernelMemory;

public sealed class KernelMemoryBuilderBuildOptions
{
public bool AllowMixingVolatileAndPersistentData { get; set; } = false;
}
77 changes: 66 additions & 11 deletions service/Core/KernelMemoryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -116,33 +117,33 @@ public KernelMemoryBuilder(IServiceCollection? hostServiceCollection = null)
}

///<inheritdoc />
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}'");
}
}

///<inheritdoc />
public T Build<T>() where T : class, IKernelMemory
public T Build<T>(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.");
}
Expand All @@ -152,7 +153,7 @@ public T Build<T>() 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.");
}
Expand Down Expand Up @@ -226,7 +227,7 @@ private static void CopyServiceCollection(
}
}

private MemoryServerless BuildServerlessClient()
private MemoryServerless BuildServerlessClient(KernelMemoryBuilderBuildOptions? options)
{
try
{
Expand All @@ -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<MemoryServerless>(serviceProvider);

// Load handlers in the memory client
Expand All @@ -257,7 +259,7 @@ private MemoryServerless BuildServerlessClient()
}
}

private MemoryService BuildAsyncClient()
private MemoryService BuildAsyncClient(KernelMemoryBuilderBuildOptions? options)
{
// Add handlers to DI service collection
if (this._useDefaultHandlers)
Expand All @@ -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<MemoryService>(serviceProvider);
}

private void CheckStoragePersistence(KernelMemoryBuilderBuildOptions? options, ServiceProvider serviceProvider)
{
if (options is { AllowMixingVolatileAndPersistentData: true }) { return; }

ServiceDescriptor docStoreType = this._memoryServiceCollection.Last<ServiceDescriptor>(x => x.ServiceType == typeof(IDocumentStorage));
ServiceDescriptor memStoreType = this._memoryServiceCollection.Last<ServiceDescriptor>(x => x.ServiceType == typeof(IMemoryDb));
SimpleFileStorageConfig? simpleFileStorageConfig = serviceProvider.GetService<SimpleFileStorageConfig>();
SimpleVectorDbConfig? simpleVectorDbConfig = serviceProvider.GetService<SimpleVectorDbConfig>();
SimpleTextDbConfig? simpleTextDbConfig = serviceProvider.GetService<SimpleTextDbConfig>();

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(<options here>). ");
}

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(<options here>). ");
}
}

private KernelMemoryBuilder CompleteServerlessClient(ServiceProvider serviceProvider)
{
this.UseDefaultSearchClientIfNecessary(serviceProvider);
Expand Down
2 changes: 1 addition & 1 deletion service/Service/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 *******************************
Expand Down
6 changes: 6 additions & 0 deletions tools/InteractiveSetup/AppSettings.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -37,6 +38,11 @@ public static void Change(Action<KernelMemoryConfig> configChanges)
File.WriteAllText(DevelopmentSettingsFile, json);
}

public static void AddService(string serviceName, Dictionary<string, object> config)
{
Change(x => { x.Services.Add(serviceName, config); });
}

public static void GlobalChange(Action<JObject> configChanges)
{
CreateFileIfNotExists();
Expand Down
6 changes: 4 additions & 2 deletions tools/InteractiveSetup/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tools/InteractiveSetup/InteractiveSetup.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<AssemblyName>Microsoft.KernelMemory.InteractiveSetup</AssemblyName>
<RootNamespace>Microsoft.KernelMemory.InteractiveSetup</RootNamespace>
<NoWarn>$(NoWarn);CA1031;CA1303;CA1724;</NoWarn>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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), () =>
Expand Down Expand Up @@ -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), () =>
Expand Down
11 changes: 11 additions & 0 deletions tools/InteractiveSetup/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"profiles": {
"console": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
1 change: 1 addition & 0 deletions tools/InteractiveSetup/Services/AWSS3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tools/InteractiveSetup/Services/AzureAIDocIntel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static void Setup(Context ctx)
{ "Auth", "ApiKey" },
{ "APIKey", "" },
};
AppSettings.AddService(ServiceName, config);
}

SetupUI.AskQuestionWithOptions(new QuestionWithOptions
Expand Down
1 change: 1 addition & 0 deletions tools/InteractiveSetup/Services/AzureAISearch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tools/InteractiveSetup/Services/AzureBlobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public static void Setup(Context ctx)
{ "Auth", "ConnectionString" },
{ "ConnectionString", "" },
};
AppSettings.AddService(ServiceName, config);
}

SetupUI.AskQuestionWithOptions(new QuestionWithOptions
Expand Down
1 change: 1 addition & 0 deletions tools/InteractiveSetup/Services/AzureOpenAIEmbedding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public static void Setup(Context ctx, bool force = false)
{ "Auth", "ApiKey" },
{ "APIKey", "" },
};
AppSettings.AddService(ServiceName, config);
}

SetupUI.AskQuestionWithOptions(new QuestionWithOptions
Expand Down
1 change: 1 addition & 0 deletions tools/InteractiveSetup/Services/AzureOpenAIText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public static void Setup(Context ctx, bool force = false)
{ "Auth", "ApiKey" },
{ "APIKey", "" },
};
AppSettings.AddService(ServiceName, config);
}

SetupUI.AskQuestionWithOptions(new QuestionWithOptions
Expand Down
Loading
Loading