Skip to content

Commit

Permalink
Merge pull request #139 from drasticactions/embeddings-semantickernel
Browse files Browse the repository at this point in the history
Add TextEmbedding for Semantic Kernel
  • Loading branch information
AsakusaRinne authored Sep 5, 2023
2 parents 037472c + ab63308 commit f3e23d6
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 0 deletions.
1 change: 1 addition & 0 deletions LLama.Examples/LLama.Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="Microsoft.SemanticKernel" Version="0.21.230828.2-preview" />
</ItemGroup>

Expand Down
172 changes: 172 additions & 0 deletions LLama.Examples/NewVersion/SemanticKernelMemory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
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
};

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<ITextEmbeddingGeneration>("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
*/

await SearchMemoryAsync(kernel, "Jupyter notebook");

await SearchMemoryAsync(kernel, "README: README associated with a sample chat summary react-based webapp");

await SearchMemoryAsync(kernel, "Jupyter notebook describing how to pass prompts from a file to a semantic skill or function");
}

private static async Task SearchMemoryAsync(IKernel kernel, string query)
{
Console.WriteLine("\nQuery: " + query + "\n");

var memories = kernel.Memory.SearchAsync(MemoryCollectionName, query, limit: 10, 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<string, string> SampleData()
{
return new Dictionary<string, string>
{
["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",
};
}
}
}
5 changes: 5 additions & 0 deletions LLama.Examples/NewVersion/TestRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static async Task Run()
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.");

while (true)
{
Expand Down Expand Up @@ -78,6 +79,10 @@ public static async Task Run()
{
await SemanticKernelChat.Run();
}
else if (choice == 13)
{
await SemanticKernelMemory.Run();
}
else
{
Console.WriteLine("Cannot parse your choice. Please select again.");
Expand Down
40 changes: 40 additions & 0 deletions LLama.Examples/RepoUtils.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Basic logger printing to console
/// </summary>
internal static class ConsoleLogger
{
internal static ILogger Logger => LoggerFactory.CreateLogger<object>();

internal static ILoggerFactory LoggerFactory => s_loggerFactory.Value;

private static readonly Lazy<ILoggerFactory> 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();
});
}
}
}
12 changes: 12 additions & 0 deletions LLama.SemanticKernel/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ For reference on how to implement it, view the following examples:

- [SemanticKernelChat](../LLama.Examples/NewVersion/SemanticKernelChat.cs)
- [SemanticKernelPrompt](../LLama.Examples/NewVersion/SemanticKernelPrompt.cs)
- [SemanticKernelMemory](../LLama.Examples/NewVersion/SemanticKernelMemory.cs)

## ITextCompletion
```csharp
Expand All @@ -24,3 +25,14 @@ using var context = model.CreateContext(parameters);
var ex = new InteractiveExecutor(context);
var chatGPT = new LLamaSharpChatCompletion(ex);
```

## ITextEmbeddingGeneration
```csharp
using var model = LLamaWeights.LoadFromFile(parameters);
var embedding = new LLamaEmbedder(model, parameters);
var kernelWithCustomDb = Kernel.Builder
.WithLoggerFactory(ConsoleLogger.LoggerFactory)
.WithAIService<ITextEmbeddingGeneration>("local-llama-embed", new LLamaSharpEmbeddingGeneration(embedding), true)
.WithMemoryStorage(new VolatileMemoryStore())
.Build();
```
Original file line number Diff line number Diff line change
@@ -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;
}

/// <inheritdoc/>
public async Task<IList<ReadOnlyMemory<float>>> GenerateEmbeddingsAsync(IList<string> data, CancellationToken cancellationToken = default)
{
return data.Select(text => new ReadOnlyMemory<float>(_embedder.GetEmbeddings(text))).ToList();
}
}

0 comments on commit f3e23d6

Please sign in to comment.