Skip to content

Commit

Permalink
Merge branch 'main' into feature/pages-in-memoryrecords
Browse files Browse the repository at this point in the history
  • Loading branch information
dluc authored Jan 15, 2025
2 parents c220010 + dd89a8e commit 37bc0da
Show file tree
Hide file tree
Showing 29 changed files with 388 additions and 81 deletions.
19 changes: 19 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,25 @@ dotnet_naming_rule.private_fields_underscored.symbols = private_fields
dotnet_naming_rule.private_fields_underscored.style = underscored
dotnet_naming_rule.private_fields_underscored.severity = error

#####################################
# Resharper #
#####################################

# disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
resharper_Condition_Is_Always_True_Or_False_According_To_Nullable_API_Contract_highlighting = none

# disable RedundantTypeArgumentsOfMethod
resharper_Redundant_Type_Arguments_Of_Method_highlighting = none

# disable NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
resharper_Null_Coalescing_Condition_Is_Always_Not_Null_According_To_API_Contract_highlighting = none

# disable PartialTypeWithSinglePart
resharper_Partial_Type_With_Single_Part_highlighting = none

# disable RedundantDefaultMemberInitializer
resharper_Redundant_Default_Member_Initializer_highlighting = none

#####################################################################################################
# Naming Conventions by folder #
# See also https://www.jetbrains.com/help/resharper/Coding_Assistance__Naming_Style.html#configure #
Expand Down
15 changes: 15 additions & 0 deletions KernelMemory.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,19 @@
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">512</s:Int64>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LINES/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">Copyright (c) Microsoft. All rights reserved.</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AAC/@EntryIndexedValue">AAC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ACS/@EntryIndexedValue">ACS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AI/@EntryIndexedValue">AI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AIGPT/@EntryIndexedValue">AIGPT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AMQP/@EntryIndexedValue">AMQP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=API/@EntryIndexedValue">API</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AWS/@EntryIndexedValue">AWS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AWSS/@EntryIndexedValue">AWSS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AWSS3/@EntryIndexedValue">AWSS3</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BOM/@EntryIndexedValue">BOM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CL/@EntryIndexedValue">CL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CORS/@EntryIndexedValue">CORS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CSV/@EntryIndexedValue">CSV</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DB/@EntryIndexedValue">DB</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DI/@EntryIndexedValue">DI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GPT/@EntryIndexedValue">GPT</s:String>
Expand All @@ -106,28 +111,38 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IO/@EntryIndexedValue">IO</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IOS/@EntryIndexedValue">IOS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JSON/@EntryIndexedValue">JSON</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JSONLD/@EntryIndexedValue">JSONLD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=JWT/@EntryIndexedValue">JWT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=KM/@EntryIndexedValue">KM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=LLM/@EntryIndexedValue">LLM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MP/@EntryIndexedValue">MP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MPEG/@EntryIndexedValue">MPEG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MQ/@EntryIndexedValue">MQ</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MQTT/@EntryIndexedValue">MQTT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MS/@EntryIndexedValue">MS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MSAL/@EntryIndexedValue">MSAL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=NLF/@EntryIndexedValue">NLF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OCR/@EntryIndexedValue">OCR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OGG/@EntryIndexedValue">OGG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OID/@EntryIndexedValue">OID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OK/@EntryIndexedValue">OK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OS/@EntryIndexedValue">OS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PR/@EntryIndexedValue">PR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QA/@EntryIndexedValue">QA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QL/@EntryIndexedValue">QL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RTF/@EntryIndexedValue">RTF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SHA/@EntryIndexedValue">SHA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SK/@EntryIndexedValue">SK</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SKHTTP/@EntryIndexedValue">SKHTTP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSE/@EntryIndexedValue">SSE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSL/@EntryIndexedValue">SSL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SVG/@EntryIndexedValue">SVG</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TTL/@EntryIndexedValue">TTL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UID/@EntryIndexedValue">UID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=URL/@EntryIndexedValue">URL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=WEBM/@EntryIndexedValue">WEBM</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XHTML/@EntryIndexedValue">XHTML</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XML/@EntryIndexedValue">XML</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=YAML/@EntryIndexedValue">YAML</s:String>
<s:Boolean x:Key="/Default/CodeStyle/Naming/CSharpNaming/ApplyAutoDetectedRules/@EntryValue">False</s:Boolean>
Expand Down
43 changes: 35 additions & 8 deletions examples/001-dotnet-WebClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,31 +253,58 @@ private static async Task AskSimpleQuestionStreamingTheAnswer()
{
var question = "What's E = m*c^2?";
Console.WriteLine($"Question: {question}");
Console.WriteLine($"Expected result: formula explanation using the information loaded");
Console.WriteLine("Expected result: formula explanation using the information loaded");

Console.Write("\nAnswer: ");
var tokenUsage = new List<TokenUsage>();
var answerStream = s_memory.AskStreamingAsync(question, options: new SearchOptions { Stream = true });

await foreach (var answer in answerStream)
{
// Print token received by LLM
Console.Write(answer.Result);

// Collect token usage
if (answer.TokenUsage?.Count > 0)
{
tokenUsage = tokenUsage.Union(answer.TokenUsage).ToList();
}

// Slow down the stream for demo purpose
await Task.Delay(25);
}

Console.WriteLine("\n\nToken usage report:");
foreach (var report in tokenUsage)
{
Console.WriteLine($"{report.ServiceType}: {report.ModelName} [{report.ModelType}]");
Console.WriteLine($"- Input : {report.TokenizerTokensIn} tokens (measured by KM tokenizer)");
Console.WriteLine($"- Input : {report.ServiceTokensIn} tokens (measured by remote service)");
Console.WriteLine($"- Output: {report.ServiceTokensOut} tokens (measured by remote service)");
Console.WriteLine($"- Output: {report.TokenizerTokensOut} tokens (measured by KM tokenizer)");
Console.WriteLine();
}

Console.WriteLine("\n\n====================================\n");

/* OUTPUT
Question: What's E = m*c^2?
Answer: E = m*c^2 is the formula representing the principle of mass-energy equivalence, which was introduced by Albert Einstein. In this equation,
E stands for energy, m represents mass, and c is the speed of light in a vacuum, which is approximately 299,792,458 meters per second (m/s).
The equation states that the energy (E) of a system in its rest frame is equal to its mass (m) multiplied by the square of the speed of light (c^2).
This implies that mass and energy are interchangeable; a small amount of mass can be converted into a large amount of energy and vice versa,
due to the speed of light being a very large number when squared. This concept is a fundamental principle in physics and has important implications
in various fields, including nuclear physics and cosmology.
Expected result: formula explanation using the information loaded
Answer: E = m*c^2 is a formula derived by the physicist Albert Einstein, which describes the principle of
mass–energy equivalence. In this equation, E represents energy, m represents mass, and c represents the
speed of light in a vacuum (approximately 3 x 10^8 meters per second). The formula indicates that mass and
energy are interchangeable; they are different forms of the same thing and can be converted into each other.
This principle is fundamental in physics and has significant implications in various fields, including nuclear
physics and cosmology.
Token usage report:
Azure OpenAI: gpt-4o [TextGeneration]
- Input : 15657 tokens (measured by KM tokenizer)
- Input : 15664 tokens (measured by remote service)
- Output: 110 tokens (measured by remote service)
- Output: 110 tokens (measured by KM tokenizer)
*/
}
Expand Down
42 changes: 34 additions & 8 deletions examples/002-dotnet-Serverless/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,31 +311,57 @@ private static async Task AskSimpleQuestionStreamingTheAnswer()
{
var question = "What's E = m*c^2?";
Console.WriteLine($"Question: {question}");
Console.WriteLine($"Expected result: formula explanation using the information loaded");
Console.WriteLine("Expected result: formula explanation using the information loaded");

Console.Write("\nAnswer: ");
var tokenUsage = new List<TokenUsage>();
var answerStream = s_memory.AskStreamingAsync(question, options: new SearchOptions { Stream = true });

await foreach (var answer in answerStream)
{
// Print token received by LLM
Console.Write(answer.Result);

// Collect token usage
if (answer.TokenUsage?.Count > 0)
{
tokenUsage = tokenUsage.Union(answer.TokenUsage).ToList();
}

// Slow down the stream for demo purpose
await Task.Delay(25);
}

Console.WriteLine("\n\nToken usage report:");
foreach (var report in tokenUsage)
{
Console.WriteLine($"{report.ServiceType}: {report.ModelName} [{report.ModelType}]");
Console.WriteLine($"- Input : {report.TokenizerTokensIn} tokens (measured by KM tokenizer)");
Console.WriteLine($"- Input : {report.ServiceTokensIn} tokens (measured by remote service)");
Console.WriteLine($"- Output: {report.ServiceTokensOut} tokens (measured by remote service)");
Console.WriteLine($"- Output: {report.TokenizerTokensOut} tokens (measured by KM tokenizer)");
Console.WriteLine();
}

Console.WriteLine("\n\n====================================\n");

/* OUTPUT
Question: What's E = m*c^2?
Answer: E = m*c^2 is the formula representing the principle of mass-energy equivalence, which was introduced by Albert Einstein. In this equation,
E stands for energy, m represents mass, and c is the speed of light in a vacuum, which is approximately 299,792,458 meters per second (m/s).
The equation states that the energy (E) of a system in its rest frame is equal to its mass (m) multiplied by the square of the speed of light (c^2).
This implies that mass and energy are interchangeable; a small amount of mass can be converted into a large amount of energy and vice versa,
due to the speed of light being a very large number when squared. This concept is a fundamental principle in physics and has important implications
in various fields, including nuclear physics and cosmology.
Expected result: formula explanation using the information loaded
Answer: E = m*c^2 is a formula derived by physicist Albert Einstein, which expresses the principle of
mass–energy equivalence. In this equation, E represents energy, m represents mass, and c represents the
speed of light in a vacuum (approximately 3 x 10^8 meters per second). The formula indicates that mass and
energy are interchangeable; a small amount of mass can be converted into a large amount of energy, and vice
versa, differing only by a multiplicative constant (c^2).
Token usage report:
Azure OpenAI: gpt-4o [TextGeneration]
- Input : 24349 tokens (measured by KM tokenizer)
- Input : 24356 tokens (measured by remote service)
- Output: 103 tokens (measured by remote service)
- Output: 103 tokens (measured by KM tokenizer)
*/
}
Expand Down
2 changes: 1 addition & 1 deletion examples/104-dotnet-custom-LLM/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public IReadOnlyList<string> GetTokens(string text)
}

/// <inheritdoc />
public async IAsyncEnumerable<string> GenerateTextAsync(
public async IAsyncEnumerable<GeneratedTextContent> GenerateTextAsync(
string prompt,
TextGenerationOptions options,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
Expand Down
2 changes: 1 addition & 1 deletion extensions/Anthropic/AnthropicTextGeneration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public IReadOnlyList<string> GetTokens(string text)
}

/// <inheritdoc />
public async IAsyncEnumerable<string> GenerateTextAsync(
public async IAsyncEnumerable<GeneratedTextContent> GenerateTextAsync(
string prompt,
TextGenerationOptions options,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
Expand Down
37 changes: 33 additions & 4 deletions extensions/AzureOpenAI/AzureOpenAI/AzureOpenAITextGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
Expand All @@ -12,6 +13,7 @@
using Microsoft.KernelMemory.Diagnostics;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.AzureOpenAI;
using OpenAI.Chat;

namespace Microsoft.KernelMemory.AI.AzureOpenAI;

Expand All @@ -28,6 +30,8 @@ public sealed class AzureOpenAITextGenerator : ITextGenerator
private readonly ITextTokenizer _textTokenizer;
private readonly ILogger<AzureOpenAITextGenerator> _log;

private readonly string _deployment;

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

Expand Down Expand Up @@ -87,6 +91,7 @@ public AzureOpenAITextGenerator(
{
this._client = skClient;
this._log = (loggerFactory ?? DefaultLogger.Factory).CreateLogger<AzureOpenAITextGenerator>();
this._deployment = config.Deployment;
this.MaxTokenTotal = config.MaxTokenTotal;

textTokenizer ??= TokenizerFactory.GetTokenizerForEncoding(config.Tokenizer);
Expand Down Expand Up @@ -114,7 +119,7 @@ public IReadOnlyList<string> GetTokens(string text)
}

/// <inheritdoc/>
public async IAsyncEnumerable<string> GenerateTextAsync(
public async IAsyncEnumerable<GeneratedTextContent> GenerateTextAsync(
string prompt,
TextGenerationOptions options,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
Expand Down Expand Up @@ -153,9 +158,33 @@ public async IAsyncEnumerable<string> GenerateTextAsync(

await foreach (StreamingTextContent x in result.WithCancellation(cancellationToken))
{
if (x.Text == null) { continue; }

yield return x.Text;
TokenUsage? tokenUsage = null;

// The last message includes tokens usage metadata.
// https://platform.openai.com/docs/api-reference/chat/create#chat-create-stream_options
if (x.Metadata?["Usage"] is ChatTokenUsage usage)
{
this._log.LogTrace("Usage report: input tokens: {InputTokenCount}, output tokens: {OutputTokenCount}, output reasoning tokens: {ReasoningTokenCount}",
usage.InputTokenCount, usage.OutputTokenCount, usage.OutputTokenDetails?.ReasoningTokenCount ?? 0);

tokenUsage = new TokenUsage
{
Timestamp = (DateTimeOffset?)x.Metadata["CreatedAt"] ?? DateTimeOffset.UtcNow,
ServiceType = "Azure OpenAI",
ModelType = Constants.ModelType.TextGeneration,
ModelName = this._deployment,
ServiceTokensIn = usage.InputTokenCount,
ServiceTokensOut = usage.OutputTokenCount,
ServiceReasoningTokens = usage.OutputTokenDetails?.ReasoningTokenCount
};
}

// NOTE: as stated at https://platform.openai.com/docs/api-reference/chat/streaming#chat/streaming-choices,
// the Choice can also be empty for the last chunk if we set stream_options: { "include_usage": true} to get token counts, so it is possible that
// x.Text is null, but tokenUsage is not (token usage statistics for the entire request are included in the last chunk).
if (x.Text is null && tokenUsage is null) { continue; }

yield return new(x.Text ?? string.Empty, tokenUsage);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void ItCountsTokens()

// Assert
Console.WriteLine("Phi3 token count: " + tokenCount);
Console.WriteLine("GPT4 token count: " + (new CL100KTokenizer()).CountTokens(text));
Console.WriteLine("GPT4 token count: " + new CL100KTokenizer().CountTokens(text));
Console.WriteLine($"Time: {this._timer.ElapsedMilliseconds / 1000} secs");

// Expected result with Phi-3-mini-4k-instruct-q4.gguf, without BoS (https://huggingface.co/microsoft/Phi-3-mini-4k-instruct-gguf)
Expand Down Expand Up @@ -90,9 +90,8 @@ public async Task ItGeneratesText()
this._timer.Restart();
var tokens = this._target.GenerateTextAsync(prompt, options);
var result = new StringBuilder();
await foreach (string token in tokens)
await foreach (var token in tokens)
{
// Console.WriteLine(token);
result.Append(token);
}

Expand Down
Loading

0 comments on commit 37bc0da

Please sign in to comment.