* LLama.Examples: disable console logging * LLama.Examples: rename titles to signal grouped topics * LLama.Examples: add additional PDF for Q&A * LLama.Examples: improve kernel memory demo multi-document ingestion * LLama.Examples: improve message before resetting to main menu * LLama.Examples: document Q&A with local memorytags/0.11.0
| @@ -5,26 +5,27 @@ public class ExampleRunner | |||
| { | |||
| private static readonly Dictionary<string, Func<Task>> Examples = new() | |||
| { | |||
| { "Run a chat session with history.", ChatSessionWithHistory.Run }, | |||
| { "Run a chat session without stripping the role names.", ChatSessionWithRoleName.Run }, | |||
| { "Run a chat session with the role names stripped.", ChatSessionStripRoleName.Run }, | |||
| { "Run a chat session in Chinese GB2312 encoding", ChatChineseGB2312.Run }, | |||
| { "Interactive mode chat by using executor.", InteractiveModeExecute.Run }, | |||
| { "Instruct mode chat by using executor.", InstructModeExecute.Run }, | |||
| { "Stateless mode chat by using executor.", StatelessModeExecute.Run }, | |||
| { "Load and save chat session.", SaveAndLoadSession.Run }, | |||
| { "Load and save state of model and executor.", LoadAndSaveState.Run }, | |||
| { "Get embeddings from LLama model.", () => Task.Run(GetEmbeddings.Run) }, | |||
| { "Quantize the model.", () => Task.Run(QuantizeModel.Run) }, | |||
| { "Automatic conversation.", TalkToYourself.Run }, | |||
| { "Constrain response to json format using grammar.", GrammarJsonResponse.Run }, | |||
| { "Semantic Kernel Prompt.", SemanticKernelPrompt.Run }, | |||
| { "Semantic Kernel Chat.", SemanticKernelChat.Run }, | |||
| { "Semantic Kernel Memory.", SemanticKernelMemory.Run }, | |||
| { "Coding Assistant.", CodingAssistant.Run }, | |||
| { "Batched Executor (Fork)", BatchedExecutorFork.Run }, | |||
| { "Batched Executor (Rewind)", BatchedExecutorRewind.Run }, | |||
| { "SK Kernel Memory.", KernelMemory.Run }, | |||
| { "Chat Session: History", ChatSessionWithHistory.Run }, | |||
| { "Chat Session: Role names", ChatSessionWithRoleName.Run }, | |||
| { "Chat Session: Role names stripped", ChatSessionStripRoleName.Run }, | |||
| { "Chat Session: Coding Assistant", CodingAssistant.Run }, | |||
| { "Chat Session: Automatic conversation", TalkToYourself.Run }, | |||
| { "Chat Session: Chinese characters", ChatChineseGB2312.Run }, | |||
| { "Executor: Interactive mode chat", InteractiveModeExecute.Run }, | |||
| { "Executor: Instruct mode chat", InstructModeExecute.Run }, | |||
| { "Executor: Stateless mode chat", StatelessModeExecute.Run }, | |||
| { "Save and Load: chat session", SaveAndLoadSession.Run }, | |||
| { "Save and Load: state of model and executor", LoadAndSaveState.Run }, | |||
| { "LLama Model: Get embeddings", () => Task.Run(GetEmbeddings.Run) }, | |||
| { "LLama Model: Quantize", () => Task.Run(QuantizeModel.Run) }, | |||
| { "Grammar: Constrain response to json format", GrammarJsonResponse.Run }, | |||
| { "Kernel Memory: Document Q&A", KernelMemory.Run }, | |||
| { "Kernel Memory: Save and Load", KernelMemorySaveAndLoad.Run }, | |||
| { "Semantic Kernel: Prompt", SemanticKernelPrompt.Run }, | |||
| { "Semantic Kernel: Chat", SemanticKernelChat.Run }, | |||
| { "Semantic Kernel: Store", SemanticKernelMemory.Run }, | |||
| { "Batched Executor: Fork", BatchedExecutorFork.Run }, | |||
| { "Batched Executor: Rewind", BatchedExecutorRewind.Run }, | |||
| { "Exit", () => { Environment.Exit(0); return Task.CompletedTask; } } | |||
| }; | |||
| @@ -44,8 +45,10 @@ public class ExampleRunner | |||
| AnsiConsole.Write(new Rule(choice)); | |||
| await example(); | |||
| } | |||
| Console.WriteLine("Press any key to continue..."); | |||
| Console.ReadKey(); | |||
| Console.WriteLine("Press ENTER to go to the main menu..."); | |||
| Console.ReadLine(); | |||
| AnsiConsole.Clear(); | |||
| } | |||
| } | |||
| @@ -1,58 +1,110 @@ | |||
| using LLamaSharp.KernelMemory; | |||
| using Microsoft.KernelMemory; | |||
| using Microsoft.KernelMemory.Configuration; | |||
| using System.Diagnostics; | |||
| namespace LLama.Examples.Examples | |||
| { | |||
| // This example is from Microsoft's official kernel memory "custom prompts" example: | |||
| // https://github.com/microsoft/kernel-memory/blob/6d516d70a23d50c6cb982e822e6a3a9b2e899cfa/examples/101-dotnet-custom-Prompts/Program.cs#L1-L86 | |||
| // Microsoft.KernelMemory has more features than Microsoft.SemanticKernel. | |||
| // See https://microsoft.github.io/kernel-memory/ for details. | |||
| public class KernelMemory | |||
| { | |||
| public static async Task Run() | |||
| { | |||
| Console.ForegroundColor = ConsoleColor.Yellow; | |||
| Console.WriteLine( | |||
| """ | |||
| This program uses the Microsoft.KernelMemory package to ingest documents | |||
| and answer questions about them in an interactive chat prompt. | |||
| """); | |||
| // Setup the kernel memory with the LLM model | |||
| string modelPath = UserSettings.GetModelPath(); | |||
| IKernelMemory memory = CreateMemory(modelPath); | |||
| Console.ForegroundColor = ConsoleColor.Yellow; | |||
| Console.WriteLine("This example is from : \n" + | |||
| "https://github.com/microsoft/kernel-memory/blob/main/examples/101-using-core-nuget/Program.cs"); | |||
| // Ingest documents (format is automatically detected from the filename) | |||
| string[] filesToIngest = [ | |||
| Path.GetFullPath(@"./Assets/sample-SK-Readme.pdf"), | |||
| Path.GetFullPath(@"./Assets/sample-KM-Readme.pdf"), | |||
| ]; | |||
| var searchClientConfig = new SearchClientConfig | |||
| for (int i = 0; i < filesToIngest.Length; i++) | |||
| { | |||
| MaxMatchesCount = 1, | |||
| AnswerTokens = 100, | |||
| }; | |||
| string path = filesToIngest[i]; | |||
| Stopwatch sw = Stopwatch.StartNew(); | |||
| Console.ForegroundColor = ConsoleColor.Blue; | |||
| Console.WriteLine($"Importing {i + 1} of {filesToIngest.Length}: {path}"); | |||
| await memory.ImportDocumentAsync(path, steps: Constants.PipelineWithoutSummary); | |||
| Console.WriteLine($"Completed in {sw.Elapsed}\n"); | |||
| } | |||
| var memory = new KernelMemoryBuilder() | |||
| .WithLLamaSharpDefaults(new LLamaSharpConfig(modelPath) | |||
| { | |||
| DefaultInferenceParams = new Common.InferenceParams | |||
| { | |||
| AntiPrompts = new List<string> { "\n\n" } | |||
| } | |||
| }) | |||
| .WithSearchClientConfig(searchClientConfig) | |||
| .With(new TextPartitioningOptions | |||
| { | |||
| MaxTokensPerParagraph = 300, | |||
| MaxTokensPerLine = 100, | |||
| OverlappingTokens = 30 | |||
| }) | |||
| .Build(); | |||
| // Ask a predefined question | |||
| Console.ForegroundColor = ConsoleColor.Green; | |||
| string question1 = "What formats does KM support"; | |||
| Console.WriteLine($"Question: {question1}"); | |||
| await AnswerQuestion(memory, question1); | |||
| // Let the user ask additional questions | |||
| while (true) | |||
| { | |||
| Console.ForegroundColor = ConsoleColor.Green; | |||
| Console.Write("Question: "); | |||
| string question = Console.ReadLine()!; | |||
| if (string.IsNullOrEmpty(question)) | |||
| return; | |||
| await memory.ImportDocumentAsync(@"./Assets/sample-SK-Readme.pdf", steps: Constants.PipelineWithoutSummary); | |||
| await AnswerQuestion(memory, question); | |||
| } | |||
| } | |||
| private static IKernelMemory CreateMemory(string modelPath) | |||
| { | |||
| Common.InferenceParams infParams = new() { AntiPrompts = ["\n\n"] }; | |||
| var question = "What's Semantic Kernel?"; | |||
| LLamaSharpConfig lsConfig = new(modelPath) { DefaultInferenceParams = infParams }; | |||
| SearchClientConfig searchClientConfig = new() | |||
| { | |||
| MaxMatchesCount = 1, | |||
| AnswerTokens = 100, | |||
| }; | |||
| Console.WriteLine($"\n\nQuestion: {question}"); | |||
| TextPartitioningOptions parseOptions = new() | |||
| { | |||
| MaxTokensPerParagraph = 300, | |||
| MaxTokensPerLine = 100, | |||
| OverlappingTokens = 30 | |||
| }; | |||
| var answer = await memory.AskAsync(question); | |||
| return new KernelMemoryBuilder() | |||
| .WithLLamaSharpDefaults(lsConfig) | |||
| .WithSearchClientConfig(searchClientConfig) | |||
| .With(parseOptions) | |||
| .Build(); | |||
| } | |||
| Console.WriteLine($"\nAnswer: {answer.Result}"); | |||
| private static async Task AnswerQuestion(IKernelMemory memory, string question) | |||
| { | |||
| Stopwatch sw = Stopwatch.StartNew(); | |||
| Console.ForegroundColor = ConsoleColor.DarkGray; | |||
| Console.WriteLine($"Generating answer..."); | |||
| Console.WriteLine("\n\n Sources:\n"); | |||
| MemoryAnswer answer = await memory.AskAsync(question); | |||
| Console.WriteLine($"Answer generated in {sw.Elapsed}"); | |||
| foreach (var x in answer.RelevantSources) | |||
| Console.ForegroundColor = ConsoleColor.Gray; | |||
| Console.WriteLine($"Answer: {answer.Result}"); | |||
| foreach (var source in answer.RelevantSources) | |||
| { | |||
| Console.WriteLine($" - {x.SourceName} - {x.Link} [{x.Partitions.First().LastUpdate:D}]"); | |||
| Console.WriteLine($"Source: {source.SourceName}"); | |||
| } | |||
| Console.WriteLine(); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,161 @@ | |||
| using LLamaSharp.KernelMemory; | |||
| using Microsoft.KernelMemory; | |||
| using Microsoft.KernelMemory.Configuration; | |||
| using Microsoft.KernelMemory.ContentStorage.DevTools; | |||
| using Microsoft.KernelMemory.FileSystem.DevTools; | |||
| using Microsoft.KernelMemory.MemoryStorage.DevTools; | |||
| using System.Diagnostics; | |||
| namespace LLama.Examples.Examples; | |||
| public class KernelMemorySaveAndLoad | |||
| { | |||
| static string StorageFolder => Path.GetFullPath($"./storage-{nameof(KernelMemorySaveAndLoad)}"); | |||
| static bool StorageExists => Directory.Exists(StorageFolder) && Directory.GetDirectories(StorageFolder).Length > 0; | |||
| public static async Task Run() | |||
| { | |||
| Console.ForegroundColor = ConsoleColor.Yellow; | |||
| Console.WriteLine( | |||
| """ | |||
| This program uses the Microsoft.KernelMemory package to ingest documents | |||
| and store the embeddings as local files so they can be quickly recalled | |||
| when this application is launched again. | |||
| """); | |||
| string modelPath = UserSettings.GetModelPath(); | |||
| IKernelMemory memory = CreateMemoryWithLocalStorage(modelPath); | |||
| Console.ForegroundColor = ConsoleColor.Yellow; | |||
| if (StorageExists) | |||
| { | |||
| Console.WriteLine( | |||
| """ | |||
| Kernel memory files have been located! | |||
| Information about previously analyzed documents has been loaded. | |||
| """); | |||
| } | |||
| else | |||
| { | |||
| Console.WriteLine( | |||
| $""" | |||
| Existing kernel memory was not found. | |||
| Documents will be analyzed (slow) and information saved to disk. | |||
| Analysis will not be required the next time this program is run. | |||
| Press ENTER to proceed... | |||
| """); | |||
| Console.ReadLine(); | |||
| await IngestDocuments(memory); | |||
| } | |||
| await AskSingleQuestion(memory, "What formats does KM support?"); | |||
| await StartUserChatSession(memory); | |||
| } | |||
| private static IKernelMemory CreateMemoryWithLocalStorage(string modelPath) | |||
| { | |||
| Common.InferenceParams infParams = new() { AntiPrompts = ["\n\n"] }; | |||
| LLamaSharpConfig lsConfig = new(modelPath) { DefaultInferenceParams = infParams }; | |||
| SearchClientConfig searchClientConfig = new() | |||
| { | |||
| MaxMatchesCount = 1, | |||
| AnswerTokens = 100, | |||
| }; | |||
| TextPartitioningOptions parseOptions = new() | |||
| { | |||
| MaxTokensPerParagraph = 300, | |||
| MaxTokensPerLine = 100, | |||
| OverlappingTokens = 30 | |||
| }; | |||
| SimpleFileStorageConfig storageConfig = new() | |||
| { | |||
| Directory = StorageFolder, | |||
| StorageType = FileSystemTypes.Disk, | |||
| }; | |||
| SimpleVectorDbConfig vectorDbConfig = new() | |||
| { | |||
| Directory = StorageFolder, | |||
| StorageType = FileSystemTypes.Disk, | |||
| }; | |||
| Console.ForegroundColor = ConsoleColor.Blue; | |||
| Console.WriteLine($"Kernel memory folder: {StorageFolder}"); | |||
| Console.ForegroundColor = ConsoleColor.DarkGray; | |||
| return new KernelMemoryBuilder() | |||
| .WithSimpleFileStorage(storageConfig) | |||
| .WithSimpleVectorDb(vectorDbConfig) | |||
| .WithLLamaSharpDefaults(lsConfig) | |||
| .WithSearchClientConfig(searchClientConfig) | |||
| .With(parseOptions) | |||
| .Build(); | |||
| } | |||
| private static async Task AskSingleQuestion(IKernelMemory memory, string question) | |||
| { | |||
| Console.ForegroundColor = ConsoleColor.Green; | |||
| Console.WriteLine($"Question: {question}"); | |||
| await ShowAnswer(memory, question); | |||
| } | |||
| private static async Task StartUserChatSession(IKernelMemory memory) | |||
| { | |||
| while (true) | |||
| { | |||
| Console.ForegroundColor = ConsoleColor.Green; | |||
| Console.Write("Question: "); | |||
| string question = Console.ReadLine()!; | |||
| if (string.IsNullOrEmpty(question)) | |||
| return; | |||
| await ShowAnswer(memory, question); | |||
| } | |||
| } | |||
| private static async Task IngestDocuments(IKernelMemory memory) | |||
| { | |||
| string[] filesToIngest = [ | |||
| Path.GetFullPath(@"./Assets/sample-SK-Readme.pdf"), | |||
| Path.GetFullPath(@"./Assets/sample-KM-Readme.pdf"), | |||
| ]; | |||
| for (int i = 0; i < filesToIngest.Length; i++) | |||
| { | |||
| string path = filesToIngest[i]; | |||
| Stopwatch sw = Stopwatch.StartNew(); | |||
| Console.ForegroundColor = ConsoleColor.Blue; | |||
| Console.WriteLine($"Importing {i + 1} of {filesToIngest.Length}: {path}"); | |||
| await memory.ImportDocumentAsync(path, steps: Constants.PipelineWithoutSummary); | |||
| Console.WriteLine($"Completed in {sw.Elapsed}\n"); | |||
| } | |||
| } | |||
| private static async Task ShowAnswer(IKernelMemory memory, string question) | |||
| { | |||
| Stopwatch sw = Stopwatch.StartNew(); | |||
| Console.ForegroundColor = ConsoleColor.DarkGray; | |||
| Console.WriteLine($"Generating answer..."); | |||
| MemoryAnswer answer = await memory.AskAsync(question); | |||
| Console.WriteLine($"Answer generated in {sw.Elapsed}"); | |||
| Console.ForegroundColor = ConsoleColor.Gray; | |||
| Console.WriteLine($"Answer: {answer.Result}"); | |||
| foreach (var source in answer.RelevantSources) | |||
| { | |||
| Console.WriteLine($"Source: {source.SourceName}"); | |||
| } | |||
| Console.WriteLine(); | |||
| } | |||
| } | |||
| @@ -58,6 +58,9 @@ | |||
| <None Update="Assets\reason-act.txt"> | |||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |||
| </None> | |||
| <None Update="Assets\sample-KM-Readme.pdf"> | |||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |||
| </None> | |||
| <None Update="Assets\sample-SK-Readme.pdf"> | |||
| <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | |||
| </None> | |||