diff --git a/LLama.Examples/LLama.Examples.csproj b/LLama.Examples/LLama.Examples.csproj index a8abe3ae..865a6321 100644 --- a/LLama.Examples/LLama.Examples.csproj +++ b/LLama.Examples/LLama.Examples.csproj @@ -27,6 +27,7 @@ + diff --git a/LLama.Examples/NewVersion/SemanticKernelMemory.cs b/LLama.Examples/NewVersion/SemanticKernelMemory.cs new file mode 100644 index 00000000..24e6f1fb --- /dev/null +++ b/LLama.Examples/NewVersion/SemanticKernelMemory.cs @@ -0,0 +1,167 @@ +using Microsoft.SemanticKernel.Memory; +using Microsoft.SemanticKernel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using LLama.Common; +using LLamaSharp.SemanticKernel.TextEmbedding; +using Microsoft.SemanticKernel.AI.Embeddings; + +namespace LLama.Examples.NewVersion +{ + public class SemanticKernelMemory + { + private const string MemoryCollectionName = "SKGitHub"; + + public static async Task Run() + { + var loggerFactory = ConsoleLogger.LoggerFactory; + Console.WriteLine("Example from: https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/KernelSyntaxExamples/Example14_SemanticMemory.cs"); + Console.Write("Please input your model path: "); + var modelPath = Console.ReadLine(); + + var seed = 1337; + // Load weights into memory + var parameters = new ModelParams(modelPath) + { + Seed = seed, + EmbeddingMode = true, + GpuLayerCount = 50, + }; + + using var model = LLamaWeights.LoadFromFile(parameters); + var embedding = new LLamaEmbedder(model, parameters); + + Console.WriteLine("===================================================="); + Console.WriteLine("======== Semantic Memory (volatile, in RAM) ========"); + Console.WriteLine("===================================================="); + + /* You can build your own semantic memory combining an Embedding Generator + * with a Memory storage that supports search by similarity (ie semantic search). + * + * In this example we use a volatile memory, a local simulation of a vector DB. + * + * You can replace VolatileMemoryStore with Qdrant (see QdrantMemoryStore connector) + * or implement your connectors for Pinecone, Vespa, Postgres + pgvector, SQLite VSS, etc. + */ + + var kernelWithCustomDb = Kernel.Builder + .WithLoggerFactory(ConsoleLogger.LoggerFactory) + .WithAIService("local-llama-embed", new LLamaSharpEmbeddingGeneration(embedding), true) + .WithMemoryStorage(new VolatileMemoryStore()) + .Build(); + + await RunExampleAsync(kernelWithCustomDb); + } + + private static async Task RunExampleAsync(IKernel kernel) + { + await StoreMemoryAsync(kernel); + + await SearchMemoryAsync(kernel, "How do I get started?"); + + /* + Output: + + Query: How do I get started? + + Result 1: + URL: : https://github.com/microsoft/semantic-kernel/blob/main/README.md + Title : README: Installation, getting started, and how to contribute + + Result 2: + URL: : https://github.com/microsoft/semantic-kernel/blob/main/samples/dotnet-jupyter-notebooks/00-getting-started.ipynb + Title : Jupyter notebook describing how to get started with the Semantic Kernel + + */ + + await SearchMemoryAsync(kernel, "Can I build a chat with SK?"); + + /* + Output: + + Query: Can I build a chat with SK? + + Result 1: + URL: : https://github.com/microsoft/semantic-kernel/tree/main/samples/skills/ChatSkill/ChatGPT + Title : Sample demonstrating how to create a chat skill interfacing with ChatGPT + + Result 2: + URL: : https://github.com/microsoft/semantic-kernel/blob/main/samples/apps/chat-summary-webapp-react/README.md + Title : README: README associated with a sample chat summary react-based webapp + + */ + } + + private static async Task SearchMemoryAsync(IKernel kernel, string query) + { + Console.WriteLine("\nQuery: " + query + "\n"); + + var memories = kernel.Memory.SearchAsync(MemoryCollectionName, query, limit: 2, minRelevanceScore: 0.5); + + int i = 0; + await foreach (MemoryQueryResult memory in memories) + { + Console.WriteLine($"Result {++i}:"); + Console.WriteLine(" URL: : " + memory.Metadata.Id); + Console.WriteLine(" Title : " + memory.Metadata.Description); + Console.WriteLine(" Relevance: " + memory.Relevance); + Console.WriteLine(); + } + + Console.WriteLine("----------------------"); + } + + private static async Task StoreMemoryAsync(IKernel kernel) + { + /* Store some data in the semantic memory. + * + * When using Azure Cognitive Search the data is automatically indexed on write. + * + * When using the combination of VolatileStore and Embedding generation, SK takes + * care of creating and storing the index + */ + + Console.WriteLine("\nAdding some GitHub file URLs and their descriptions to the semantic memory."); + var githubFiles = SampleData(); + var i = 0; + foreach (var entry in githubFiles) + { + var result = await kernel.Memory.SaveReferenceAsync( + collection: MemoryCollectionName, + externalSourceName: "GitHub", + externalId: entry.Key, + description: entry.Value, + text: entry.Value); + + Console.WriteLine($"#{++i} saved."); + Console.WriteLine(result); + } + + Console.WriteLine("\n----------------------"); + } + + private static Dictionary SampleData() + { + return new Dictionary + { + ["https://github.com/microsoft/semantic-kernel/blob/main/README.md"] + = "README: Installation, getting started, and how to contribute", + ["https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks/02-running-prompts-from-file.ipynb"] + = "Jupyter notebook describing how to pass prompts from a file to a semantic skill or function", + ["https://github.com/microsoft/semantic-kernel/blob/main/dotnet/notebooks//00-getting-started.ipynb"] + = "Jupyter notebook describing how to get started with the Semantic Kernel", + ["https://github.com/microsoft/semantic-kernel/tree/main/samples/skills/ChatSkill/ChatGPT"] + = "Sample demonstrating how to create a chat skill interfacing with ChatGPT", + ["https://github.com/microsoft/semantic-kernel/blob/main/dotnet/src/SemanticKernel/Memory/VolatileMemoryStore.cs"] + = "C# class that defines a volatile embedding store", + ["https://github.com/microsoft/semantic-kernel/blob/main/samples/dotnet/KernelHttpServer/README.md"] + = "README: How to set up a Semantic Kernel Service API using Azure Function Runtime v4", + ["https://github.com/microsoft/semantic-kernel/blob/main/samples/apps/chat-summary-webapp-react/README.md"] + = "README: README associated with a sample chat summary react-based webapp", + }; + } + } +} diff --git a/LLama.Examples/NewVersion/SemanticKernelMemorySkill.cs b/LLama.Examples/NewVersion/SemanticKernelMemorySkill.cs new file mode 100644 index 00000000..c1c9bd48 --- /dev/null +++ b/LLama.Examples/NewVersion/SemanticKernelMemorySkill.cs @@ -0,0 +1,184 @@ +using System.Reflection.Metadata; +using System.Security.Cryptography; +using System.Text; +using LLama.Abstractions; +using LLama.Common; +using LLamaSharp.SemanticKernel.TextCompletion; +using LLamaSharp.SemanticKernel.TextEmbedding; +using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel; +using Microsoft.SemanticKernel.AI.ChatCompletion; +using Microsoft.SemanticKernel.AI.Embeddings; +using Microsoft.SemanticKernel.AI.TextCompletion; +using Microsoft.SemanticKernel.Memory; +using Microsoft.SemanticKernel.Skills.Core; + +namespace LLama.Examples.NewVersion +{ + public class SemanticKernelMemorySkill + { + private const string MemoryCollectionName = "aboutMe"; + + public static async Task Run() + { + var loggerFactory = ConsoleLogger.LoggerFactory; + Console.WriteLine("Example from: https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/KernelSyntaxExamples/Example15_MemorySkill.cs"); + Console.Write("Please input your model path: "); + var modelPath = Console.ReadLine(); + + var seed = 1337; + // Load weights into memory + var parameters = new ModelParams(modelPath) + { + Seed = seed, + EmbeddingMode = true, + GpuLayerCount = 50, + }; + + using var model = LLamaWeights.LoadFromFile(parameters); + var embedding = new LLamaEmbedder(model, parameters); + using var context = model.CreateContext(parameters); + // TODO: This executor is most likely incorrect. + var ex = new InteractiveExecutor(context); + var builder = new KernelBuilder(); + builder.WithLoggerFactory(loggerFactory); + + builder.WithAIService("local-llama-text", new LLamaSharpTextCompletion(ex), true); + builder.WithAIService("local-llama-embed", new LLamaSharpEmbeddingGeneration(embedding), true); + builder.WithMemoryStorage(new VolatileMemoryStore()); + var kernel = builder.Build(); + // ========= Store memories using the kernel ========= + + await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info1", text: "My name is Andrea"); + await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info2", text: "I work as a tourist operator"); + await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info3", text: "I've been living in Seattle since 2005"); + await kernel.Memory.SaveInformationAsync(MemoryCollectionName, id: "info4", text: "I visited France and Italy five times since 2015"); + + // ========= Store memories using semantic function ========= + + // Add Memory as a skill for other functions + var memorySkill = new TextMemorySkill(kernel.Memory); + kernel.ImportSkill(memorySkill); + + // Build a semantic function that saves info to memory + const string SaveFunctionDefinition = "{{save $info}}"; + var memorySaver = kernel.CreateSemanticFunction(SaveFunctionDefinition); + + //await kernel.RunAsync(memorySaver, new() + //{ + // [TextMemorySkill.CollectionParam] = MemoryCollectionName, + // [TextMemorySkill.KeyParam] = "info5", + // ["info"] = "My family is from New York" + //}); + + // ========= Test memory remember ========= + Console.WriteLine("========= Example: Recalling a Memory ========="); + + var answer = await memorySkill.RetrieveAsync(MemoryCollectionName, "info1", loggerFactory); + Console.WriteLine("Memory associated with 'info1': {0}", answer); + + answer = await memorySkill.RetrieveAsync(MemoryCollectionName, "info2", loggerFactory); + Console.WriteLine("Memory associated with 'info2': {0}", answer); + + answer = await memorySkill.RetrieveAsync(MemoryCollectionName, "info3", loggerFactory); + Console.WriteLine("Memory associated with 'info3': {0}", answer); + + answer = await memorySkill.RetrieveAsync(MemoryCollectionName, "info4", loggerFactory); + Console.WriteLine("Memory associated with 'info4': {0}", answer); + + //answer = await memorySkill.RetrieveAsync(MemoryCollectionName, "info5", loggerFactory); + //Console.WriteLine("Memory associated with 'info5': {0}", answer); + + // ========= Test memory recall ========= + Console.WriteLine("========= Example: Recalling an Idea ========="); + + answer = await memorySkill.RecallAsync("where did I grow up?", MemoryCollectionName, relevance: null, limit: 2, null); + Console.WriteLine("Ask: where did I grow up?"); + Console.WriteLine("Answer:\n{0}", answer); + + answer = await memorySkill.RecallAsync("where do I live?", MemoryCollectionName, relevance: null, limit: 2, null); + Console.WriteLine("Ask: where do I live?"); + Console.WriteLine("Answer:\n{0}", answer); + + /* + Output: + + Ask: where did I grow up? + Answer: + ["My family is from New York","I\u0027ve been living in Seattle since 2005"] + + Ask: where do I live? + Answer: + ["I\u0027ve been living in Seattle since 2005","My family is from New York"] + */ + + // ========= Use memory in a semantic function ========= + Console.WriteLine("========= Example: Using Recall in a Semantic Function ========="); + + // Build a semantic function that uses memory to find facts + const string RecallFunctionDefinition = @" +Consider only the facts below when answering questions. + +About me: {{recall 'where did I grow up?'}} +About me: {{recall 'where do I live?'}} + +Question: {{$input}} + +Answer: +"; + + var aboutMeOracle = kernel.CreateSemanticFunction(RecallFunctionDefinition, maxTokens: 100); + + var result = await kernel.RunAsync(aboutMeOracle, new("Do I live in the same town where I grew up?") + { + [TextMemorySkill.CollectionParam] = MemoryCollectionName, + [TextMemorySkill.RelevanceParam] = "0.8" + }); + + Console.WriteLine("Do I live in the same town where I grew up?\n"); + Console.WriteLine(result); + + /* + Output: + + Do I live in the same town where I grew up? + + No, I do not live in the same town where I grew up since my family is from New York and I have been living in Seattle since 2005. + */ + + // ========= Remove a memory ========= + Console.WriteLine("========= Example: Forgetting a Memory ========="); + + result = await kernel.RunAsync(aboutMeOracle, new("Tell me a bit about myself") + { + ["fact1"] = "What is my name?", + ["fact2"] = "What do I do for a living?", + [TextMemorySkill.RelevanceParam] = ".75" + }); + + Console.WriteLine("Tell me a bit about myself\n"); + Console.WriteLine(result); + + /* + Approximate Output: + Tell me a bit about myself + + My name is Andrea and my family is from New York. I work as a tourist operator. + */ + + await memorySkill.RemoveAsync(MemoryCollectionName, "info1", loggerFactory); + + result = await kernel.RunAsync(aboutMeOracle, new("Tell me a bit about myself")); + + Console.WriteLine("Tell me a bit about myself\n"); + Console.WriteLine(result); + + /* + Approximate Output: + Tell me a bit about myself + + I'm from a family originally from New York and I work as a tourist operator. I've been living in Seattle since 2005. + */ + } + } +} diff --git a/LLama.Examples/NewVersion/TestRunner.cs b/LLama.Examples/NewVersion/TestRunner.cs index 83316510..7d27f931 100644 --- a/LLama.Examples/NewVersion/TestRunner.cs +++ b/LLama.Examples/NewVersion/TestRunner.cs @@ -20,6 +20,8 @@ Console.WriteLine("10: Constrain response to json format using grammar."); Console.WriteLine("11: Semantic Kernel Prompt."); Console.WriteLine("12: Semantic Kernel Chat."); + Console.WriteLine("13: Semantic Kernel Memory."); + Console.WriteLine("14: Semantic Kernel Memory Skill."); while (true) { @@ -78,6 +80,14 @@ { await SemanticKernelChat.Run(); } + else if (choice == 13) + { + await SemanticKernelMemory.Run(); + } + else if (choice == 14) + { + await SemanticKernelMemorySkill.Run(); + } else { Console.WriteLine("Cannot parse your choice. Please select again."); diff --git a/LLama.Examples/RepoUtils.cs b/LLama.Examples/RepoUtils.cs new file mode 100644 index 00000000..8e728339 --- /dev/null +++ b/LLama.Examples/RepoUtils.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LLama.Examples +{ + /// + /// Basic logger printing to console + /// + internal static class ConsoleLogger + { + internal static ILogger Logger => LoggerFactory.CreateLogger(); + + internal static ILoggerFactory LoggerFactory => s_loggerFactory.Value; + + private static readonly Lazy s_loggerFactory = new(LogBuilder); + + private static ILoggerFactory LogBuilder() + { + return Microsoft.Extensions.Logging.LoggerFactory.Create(builder => + { + builder.SetMinimumLevel(LogLevel.Warning); + + builder.AddFilter("Microsoft", LogLevel.Trace); + builder.AddFilter("Microsoft", LogLevel.Debug); + builder.AddFilter("Microsoft", LogLevel.Information); + builder.AddFilter("Microsoft", LogLevel.Warning); + builder.AddFilter("Microsoft", LogLevel.Error); + + builder.AddFilter("Microsoft", LogLevel.Warning); + builder.AddFilter("System", LogLevel.Warning); + + builder.AddConsole(); + }); + } + } +} diff --git a/LLama.SemanticKernel/TextEmbedding/LLamaSharpEmbeddingGeneration.cs b/LLama.SemanticKernel/TextEmbedding/LLamaSharpEmbeddingGeneration.cs new file mode 100644 index 00000000..c8f23c2d --- /dev/null +++ b/LLama.SemanticKernel/TextEmbedding/LLamaSharpEmbeddingGeneration.cs @@ -0,0 +1,20 @@ +using LLama; +using Microsoft.SemanticKernel.AI.Embeddings; + +namespace LLamaSharp.SemanticKernel.TextEmbedding; + +public sealed class LLamaSharpEmbeddingGeneration : ITextEmbeddingGeneration +{ + private LLamaEmbedder _embedder; + + public LLamaSharpEmbeddingGeneration(LLamaEmbedder embedder) + { + _embedder = embedder; + } + + /// + public async Task>> GenerateEmbeddingsAsync(IList data, CancellationToken cancellationToken = default) + { + return data.Select(text => new ReadOnlyMemory(_embedder.GetEmbeddings(text))).ToList(); + } +}