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
6 changes: 6 additions & 0 deletions KernelMemory.sln
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDbAtlas", "extensions\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDbAtlas.FunctionalTests", "extensions\MongoDbAtlas\MongoDbAtlas.FunctionalTests\MongoDbAtlas.FunctionalTests.csproj", "{8A602227-B291-4F1B-ACB8-237F49501B6A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "107-dotnet-SemanticKernel-TextCompletion", "examples\107-dotnet-SemanticKernel-TextCompletion\107-dotnet-SemanticKernel-TextCompletion.csproj", "{494B8590-F0B2-4D40-A895-F9D7BDF26250}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -298,6 +300,7 @@ Global
{A5CE3CB2-F746-4832-B239-1A3226F9BD48} = {0A43C65C-6007-4BB4-B3FE-8D439FC91841}
{4A539320-EB49-4688-82E1-4FA0ADBB3810} = {155DA079-E267-49AF-973A-D1D44681970F}
{8A602227-B291-4F1B-ACB8-237F49501B6A} = {3C17F42B-CFC8-4900-8CFB-88936311E919}
{494B8590-F0B2-4D40-A895-F9D7BDF26250} = {0A43C65C-6007-4BB4-B3FE-8D439FC91841}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A9FA587-7EBA-4D43-BE47-38D798B1C74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -486,5 +489,8 @@ Global
{8A602227-B291-4F1B-ACB8-237F49501B6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A602227-B291-4F1B-ACB8-237F49501B6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A602227-B291-4F1B-ACB8-237F49501B6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{494B8590-F0B2-4D40-A895-F9D7BDF26250}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{494B8590-F0B2-4D40-A895-F9D7BDF26250}.Debug|Any CPU.Build.0 = Debug|Any CPU
{494B8590-F0B2-4D40-A895-F9D7BDF26250}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>_107_dotnet_SemanticKernel_TextCompletion</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
<NoWarn>$(NoWarn);SKEXP0001;SKEXP0010;</NoWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\service\Core\Core.csproj" />
</ItemGroup>

</Project>
35 changes: 35 additions & 0 deletions examples/107-dotnet-SemanticKernel-TextCompletion/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.KernelMemory;
using Microsoft.KernelMemory.AI.OpenAI;
using Microsoft.KernelMemory.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;

var endpoint = Env.Var("AOAI_ENDPOINT");
var apiKey = Env.Var("AOAI_API_KEY");
var chatDeployment = Env.Var("AOAI_DEPLOYMENT_CHAT");
var embeddingDeployment = Env.Var("AOAI_DEPLOYMENT_EMBEDDING");

var config = new SemanticKernelConfig();
var tokenizer = new DefaultGPTTokenizer();

var memory = new KernelMemoryBuilder()
.WithSemanticKernelTextGenerationService(
new AzureOpenAIChatCompletionService(chatDeployment, endpoint, apiKey), config, tokenizer)
.WithSemanticKernelTextEmbeddingGenerationService(
new AzureOpenAITextEmbeddingGenerationService(embeddingDeployment, endpoint, apiKey), config, tokenizer)
.Build<MemoryServerless>();

await memory.ImportWebPageAsync("https://raw.githubusercontent.com/microsoft/kernel-memory/main/COMMUNITY.md", documentId: "doc001");

var question = "How can I join Kernel Memory's Discord?";
Console.WriteLine($"\n\nQuestion: {question}");

var answer = await memory.AskAsync(question);
Console.WriteLine($"\nAnswer: {answer.Result}");

Console.WriteLine("\n\n Sources:\n");
foreach (var x in answer.RelevantSources)
{
Console.WriteLine($" - {x.SourceName} - {x.Link} [{x.Partitions.First().LastUpdate:D}]");
}
2 changes: 1 addition & 1 deletion service/Core/Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<RollForward>LatestMajor</RollForward>
<AssemblyName>Microsoft.KernelMemory.Core</AssemblyName>
<RootNamespace>Microsoft.KernelMemory</RootNamespace>
<NoWarn>$(NoWarn);SKEXP0011;CA2208;CA1308;CA1724;CS1591;</NoWarn>
<NoWarn>$(NoWarn);SKEXP0001;SKEXP0011;CA2208;CA1308;CA1724;CS1591;</NoWarn>
<DefineConstants Condition="'$(SolutionName)' == 'KernelMemoryDev'">$(DefineConstants);KernelMemoryDev</DefineConstants>
</PropertyGroup>

Expand Down
72 changes: 72 additions & 0 deletions service/Core/SemanticKernel/KernelMemoryBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.Extensions.Logging;
using Microsoft.KernelMemory.AI;
using Microsoft.KernelMemory.Configuration;
using Microsoft.KernelMemory.SemanticKernel;
using Microsoft.SemanticKernel.Embeddings;
using Microsoft.SemanticKernel.TextGeneration;

#pragma warning disable IDE0130 // reduce number of "using" statements
// ReSharper disable once CheckNamespace - reduce number of "using" statements
namespace Microsoft.KernelMemory;

/// <summary>
/// Kernel Memory builder extensions.
/// </summary>
public static partial class KernelMemoryBuilderExtensions
{
/// <summary>
/// Inject an implementation of <see cref="ITextGenerationService">SK text generation service</see>
/// for local dependencies on <see cref="ITextGenerator"/>
/// </summary>
/// <param name="builder">KM builder</param>
/// <param name="service">SK text generation service instance</param>
/// <param name="config">SK text generator settings</param>
/// <param name="tokenizer">Tokenizer used to count tokens used by prompts</param>
/// <param name="loggerFactory">.NET logger factory</param>
/// <returns>KM builder</returns>
public static IKernelMemoryBuilder WithSemanticKernelTextGenerationService(
this IKernelMemoryBuilder builder,
ITextGenerationService service,
SemanticKernelConfig config,
ITextTokenizer? tokenizer = null,
ILoggerFactory? loggerFactory = null)
{
if (service == null) { throw new ConfigurationException("The semantic kernel text generation service instance is NULL"); }

return builder.AddSingleton<ITextGenerator>(new SemanticKernelTextGenerator(service, config, tokenizer, loggerFactory));
}

/// <summary>
/// Inject an implementation of<see cref="ITextEmbeddingGenerationService">SK text embedding generation service</see>
/// for local dependencies on <see cref="ITextEmbeddingGenerator"/>
/// </summary>
/// <param name="builder">KM builder</param>
/// <param name="service">SK text embedding generation instance</param>
/// <param name="config">SK text embedding generator settings</param>
/// <param name="tokenizer">Tokenizer used to count tokens sent to the embedding generator</param>
/// <param name="loggerFactory">.NET logger factory</param>
/// <param name="onlyForRetrieval">Whether to use this embedding generator only during data ingestion, and not for retrieval (search and ask API)</param>
/// <returns>KM builder</returns>
public static IKernelMemoryBuilder WithSemanticKernelTextEmbeddingGenerationService(
this IKernelMemoryBuilder builder,
ITextEmbeddingGenerationService service,
SemanticKernelConfig config,
ITextTokenizer? tokenizer = null,
ILoggerFactory? loggerFactory = null,
bool onlyForRetrieval = false)
{
if (service == null) { throw new ConfigurationException("The semantic kernel text embedding generation service instance is NULL"); }

var generator = new SemanticKernelTextEmbeddingGenerator(service, config, tokenizer, loggerFactory);
builder.AddSingleton<ITextEmbeddingGenerator>(generator);

if (!onlyForRetrieval)
{
builder.AddIngestionEmbeddingGenerator(generator);
}

return builder;
}
}
14 changes: 14 additions & 0 deletions service/Core/SemanticKernel/SemanticKernelConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft. All rights reserved.

namespace Microsoft.KernelMemory.SemanticKernel;

/// <summary>
/// Semantic Kernel TextGenerator And TextEmbeddingGenerator Config
/// </summary>
public class SemanticKernelConfig
{
/// <summary>
/// Max size of the LLM attention window, ie max tokens that can be processed.
/// </summary>
public int MaxTokenTotal { get; set; } = 8191;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.KernelMemory.AI;
using Microsoft.KernelMemory.AI.OpenAI;
using Microsoft.KernelMemory.Diagnostics;
using Microsoft.SemanticKernel.AI.Embeddings;
using Microsoft.SemanticKernel.Embeddings;

namespace Microsoft.KernelMemory.SemanticKernel;

internal class SemanticKernelTextEmbeddingGenerator : ITextEmbeddingGenerator
{
private readonly ITextEmbeddingGenerationService _service;
private readonly ITextTokenizer _tokenizer;
private readonly ILogger<SemanticKernelTextEmbeddingGenerator> _log;

/// <inheritdoc />
public int MaxTokens { get; }

/// <inheritdoc />
public int CountTokens(string text) => this._tokenizer.CountTokens(text);

public SemanticKernelTextEmbeddingGenerator(
ITextEmbeddingGenerationService textEmbeddingGenerationService,
SemanticKernelConfig config,
ITextTokenizer? textTokenizer = null,
ILoggerFactory? loggerFactory = null)
{
this._service = textEmbeddingGenerationService ?? throw new ArgumentNullException(nameof(textEmbeddingGenerationService));
this.MaxTokens = config.MaxTokenTotal;

var log = loggerFactory?.CreateLogger<SemanticKernelTextEmbeddingGenerator>();
this._log = log ?? DefaultLogger<SemanticKernelTextEmbeddingGenerator>.Instance;

if (textTokenizer == null)
{
this._log.LogWarning(
"Tokenizer not specified, will use {0}. The token count might be incorrect, causing unexpected errors",
nameof(DefaultGPTTokenizer));
textTokenizer = new DefaultGPTTokenizer();
}

this._tokenizer = textTokenizer;
}

/// <inheritdoc />
public Task<Embedding> GenerateEmbeddingAsync(string text, CancellationToken cancellationToken = default)
{
this._log.LogTrace("Generating embedding with SK embedding generator service");

return this._service.GenerateEmbeddingAsync(text, cancellationToken);
}
}
95 changes: 95 additions & 0 deletions service/Core/SemanticKernel/SemanticKernelTextGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.KernelMemory.AI;
using Microsoft.KernelMemory.AI.OpenAI;
using Microsoft.KernelMemory.Diagnostics;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.TextGeneration;

namespace Microsoft.KernelMemory.SemanticKernel;

internal class SemanticKernelTextGenerator : ITextGenerator
{
private readonly ITextGenerationService _service;
private readonly ITextTokenizer _tokenizer;
private readonly ILogger<SemanticKernelTextGenerator> _log;

/// <inheritdoc />
public int MaxTokenTotal { get; }

/// <inheritdoc />
public int CountTokens(string text) => this._tokenizer.CountTokens(text);

public SemanticKernelTextGenerator(
ITextGenerationService textGenerationService,
SemanticKernelConfig config,
ITextTokenizer? textTokenizer = null,
ILoggerFactory? loggerFactory = null)
{
this._service = textGenerationService ?? throw new ArgumentNullException(nameof(textGenerationService));
this.MaxTokenTotal = config.MaxTokenTotal;

var log = loggerFactory?.CreateLogger<SemanticKernelTextGenerator>();
this._log = log ?? DefaultLogger<SemanticKernelTextGenerator>.Instance;

if (textTokenizer == null)
{
this._log.LogWarning(
"Tokenizer not specified, will use {0}. The token count might be incorrect, causing unexpected errors",
nameof(DefaultGPTTokenizer));
textTokenizer = new DefaultGPTTokenizer();
}

this._tokenizer = textTokenizer;
}

/// <inheritdoc />
public async IAsyncEnumerable<string> GenerateTextAsync(
string prompt,
TextGenerationOptions options,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
this._log.LogTrace("Generating text with SK text generator service");

var contents = this._service.GetStreamingTextContentsAsync(
prompt, ToPromptExecutionSettings(options), null, cancellationToken).ConfigureAwait(false);

await foreach (StreamingTextContent? content in contents)
{
if (content != null)
{
yield return content.ToString();
}
}
}

private static PromptExecutionSettings ToPromptExecutionSettings(TextGenerationOptions options)
{
var settings = new PromptExecutionSettings
{
ExtensionData = new Dictionary<string, object>()
{
[nameof(options.Temperature)] = options.Temperature,
[nameof(options.TopP)] = options.TopP,
[nameof(options.PresencePenalty)] = options.PresencePenalty,
[nameof(options.FrequencyPenalty)] = options.FrequencyPenalty,
[nameof(options.StopSequences)] = options.StopSequences,
[nameof(options.ResultsPerPrompt)] = options.ResultsPerPrompt,
[nameof(options.TokenSelectionBiases)] = options.TokenSelectionBiases
}
};

if (options.MaxTokens != null)
{
settings.ExtensionData[nameof(options.MaxTokens)] = options.MaxTokens;
}

return settings;
}
}
1 change: 1 addition & 0 deletions service/Service/Service.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
<NoWarn>$(NoWarn);CA2007,CA1724,CA2254,CA1031,CS1591,CA1861,CA1303,SKEXP0001</NoWarn>
<DefineConstants Condition="'$(SolutionName)' == 'KernelMemoryDev'">$(DefineConstants);KernelMemoryDev</DefineConstants>
</PropertyGroup>

<ItemGroup>
Expand Down