diff --git a/KernelMemory.sln b/KernelMemory.sln
index d11ab1451..59c2ae5bf 100644
--- a/KernelMemory.sln
+++ b/KernelMemory.sln
@@ -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
@@ -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
@@ -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
diff --git a/examples/107-dotnet-SemanticKernel-TextCompletion/107-dotnet-SemanticKernel-TextCompletion.csproj b/examples/107-dotnet-SemanticKernel-TextCompletion/107-dotnet-SemanticKernel-TextCompletion.csproj
new file mode 100644
index 000000000..cfa574100
--- /dev/null
+++ b/examples/107-dotnet-SemanticKernel-TextCompletion/107-dotnet-SemanticKernel-TextCompletion.csproj
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net8.0
+ _107_dotnet_SemanticKernel_TextCompletion
+ enable
+ enable
+ 5ee045b0-aea3-4f08-8d31-32d1a6f8fed0
+ $(NoWarn);SKEXP0001;SKEXP0010;
+
+
+
+
+
+
+
diff --git a/examples/107-dotnet-SemanticKernel-TextCompletion/Program.cs b/examples/107-dotnet-SemanticKernel-TextCompletion/Program.cs
new file mode 100644
index 000000000..a05c5b7e3
--- /dev/null
+++ b/examples/107-dotnet-SemanticKernel-TextCompletion/Program.cs
@@ -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();
+
+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}]");
+}
diff --git a/service/Core/Core.csproj b/service/Core/Core.csproj
index cee218b80..8b9259efd 100644
--- a/service/Core/Core.csproj
+++ b/service/Core/Core.csproj
@@ -5,7 +5,7 @@
LatestMajor
Microsoft.KernelMemory.Core
Microsoft.KernelMemory
- $(NoWarn);SKEXP0011;CA2208;CA1308;CA1724;CS1591;
+ $(NoWarn);SKEXP0001;SKEXP0011;CA2208;CA1308;CA1724;CS1591;
$(DefineConstants);KernelMemoryDev
diff --git a/service/Core/SemanticKernel/KernelMemoryBuilderExtensions.cs b/service/Core/SemanticKernel/KernelMemoryBuilderExtensions.cs
new file mode 100644
index 000000000..0695b2df9
--- /dev/null
+++ b/service/Core/SemanticKernel/KernelMemoryBuilderExtensions.cs
@@ -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;
+
+///
+/// Kernel Memory builder extensions.
+///
+public static partial class KernelMemoryBuilderExtensions
+{
+ ///
+ /// Inject an implementation of SK text generation service
+ /// for local dependencies on
+ ///
+ /// KM builder
+ /// SK text generation service instance
+ /// SK text generator settings
+ /// Tokenizer used to count tokens used by prompts
+ /// .NET logger factory
+ /// KM builder
+ 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(new SemanticKernelTextGenerator(service, config, tokenizer, loggerFactory));
+ }
+
+ ///
+ /// Inject an implementation ofSK text embedding generation service
+ /// for local dependencies on
+ ///
+ /// KM builder
+ /// SK text embedding generation instance
+ /// SK text embedding generator settings
+ /// Tokenizer used to count tokens sent to the embedding generator
+ /// .NET logger factory
+ /// Whether to use this embedding generator only during data ingestion, and not for retrieval (search and ask API)
+ /// KM builder
+ 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(generator);
+
+ if (!onlyForRetrieval)
+ {
+ builder.AddIngestionEmbeddingGenerator(generator);
+ }
+
+ return builder;
+ }
+}
diff --git a/service/Core/SemanticKernel/SemanticKernelConfig.cs b/service/Core/SemanticKernel/SemanticKernelConfig.cs
new file mode 100644
index 000000000..b20d90059
--- /dev/null
+++ b/service/Core/SemanticKernel/SemanticKernelConfig.cs
@@ -0,0 +1,14 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+namespace Microsoft.KernelMemory.SemanticKernel;
+
+///
+/// Semantic Kernel TextGenerator And TextEmbeddingGenerator Config
+///
+public class SemanticKernelConfig
+{
+ ///
+ /// Max size of the LLM attention window, ie max tokens that can be processed.
+ ///
+ public int MaxTokenTotal { get; set; } = 8191;
+}
diff --git a/service/Core/SemanticKernel/SemanticKernelTextEmbeddingGenerator.cs b/service/Core/SemanticKernel/SemanticKernelTextEmbeddingGenerator.cs
new file mode 100644
index 000000000..561181017
--- /dev/null
+++ b/service/Core/SemanticKernel/SemanticKernelTextEmbeddingGenerator.cs
@@ -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 _log;
+
+ ///
+ public int MaxTokens { get; }
+
+ ///
+ 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();
+ this._log = log ?? DefaultLogger.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;
+ }
+
+ ///
+ public Task GenerateEmbeddingAsync(string text, CancellationToken cancellationToken = default)
+ {
+ this._log.LogTrace("Generating embedding with SK embedding generator service");
+
+ return this._service.GenerateEmbeddingAsync(text, cancellationToken);
+ }
+}
diff --git a/service/Core/SemanticKernel/SemanticKernelTextGenerator.cs b/service/Core/SemanticKernel/SemanticKernelTextGenerator.cs
new file mode 100644
index 000000000..1699705b4
--- /dev/null
+++ b/service/Core/SemanticKernel/SemanticKernelTextGenerator.cs
@@ -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 _log;
+
+ ///
+ public int MaxTokenTotal { get; }
+
+ ///
+ 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();
+ this._log = log ?? DefaultLogger.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;
+ }
+
+ ///
+ public async IAsyncEnumerable 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()
+ {
+ [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;
+ }
+}
diff --git a/service/Service/Service.csproj b/service/Service/Service.csproj
index 1e965c999..f1f6b5667 100644
--- a/service/Service/Service.csproj
+++ b/service/Service/Service.csproj
@@ -7,6 +7,7 @@
false
5ee045b0-aea3-4f08-8d31-32d1a6f8fed0
$(NoWarn);CA2007,CA1724,CA2254,CA1031,CS1591,CA1861,CA1303,SKEXP0001
+ $(DefineConstants);KernelMemoryDev