Skip to content

Commit

Permalink
XpoMemoryStore Test
Browse files Browse the repository at this point in the history
  • Loading branch information
egarim committed Sep 3, 2024
1 parent 8eff611 commit 0284dd6
Show file tree
Hide file tree
Showing 9 changed files with 574 additions and 18 deletions.
2 changes: 1 addition & 1 deletion dotnet/SK-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AIModelRouter", "samples\De
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.AzureCosmosDBNoSQL.UnitTests", "src\Connectors\Connectors.AzureCosmosDBNoSQL.UnitTests\Connectors.AzureCosmosDBNoSQL.UnitTests.csproj", "{385A8FE5-87E2-4458-AE09-35E10BD2E67F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.Memory.Xpo", "src\Connectors\Connectors.Memory.Xpo\Connectors.Memory.Xpo.csproj", "{B2F02FB7-CCC2-4BBD-869E-238E31F893FA}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Memory.Xpo", "src\Connectors\Connectors.Memory.Xpo\Connectors.Memory.Xpo.csproj", "{B2F02FB7-CCC2-4BBD-869E-238E31F893FA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
229 changes: 229 additions & 0 deletions dotnet/XpoMemoryDemo/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
// Copyright (c) Microsoft. All rights reserved.

using DevExpress.Xpo.DB;
using Microsoft.SemanticKernel;

using Microsoft.SemanticKernel.Connectors.Xpo;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.Plugins.Memory;


namespace XpoMemoryDemo;

internal class Program
{

private static async Task Main(string[] args)
{
#pragma warning disable IDE0039

// Volatile Memory Store - an in-memory store that is not persisted
//IMemoryStore store = provider switch
//{
// "AzureAISearch" => CreateSampleAzureAISearchMemoryStore(),
// _ => new VolatileMemoryStore(),
//};

// Xpo Memory Store - using InMemoryDataStore, an in-memory store that is not persisted
string cnx = DevExpress.Xpo.DB.InMemoryDataStore.GetConnectionStringInMemory(true);
cnx = "Integrated Security=SSPI;Pooling=false;Data Source=(localdb)\\mssqllocaldb;Initial Catalog=XpoKernelMemory";
XpoMemoryStore store = await XpoMemoryStore.ConnectAsync(cnx);

var EmbeddingModelId = "text-embedding-3-small";
var ChatModelId = "gpt-4o";
//var kernel = Kernel.CreateBuilder()
// .AddOpenAIChatCompletion(TestConfiguration.OpenAI.ChatModelId, TestConfiguration.OpenAI.ApiKey)
// .AddOpenAITextEmbeddingGeneration(EmbeddingModelId, TestConfiguration.OpenAI.ApiKey)
// .Build();

#pragma warning disable IDE0039
var GetKey = () => Environment.GetEnvironmentVariable("OpenAiTestKey", EnvironmentVariableTarget.Machine);
var kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(ChatModelId, GetKey.Invoke())
.AddOpenAITextEmbeddingGeneration(EmbeddingModelId, GetKey.Invoke())
.Build();

// Create an embedding generator to use for semantic memory.
//var embeddingGenerator = new OpenAITextEmbeddingGenerationService(EmbeddingModelId, TestConfiguration.OpenAI.ApiKey);
var embeddingGenerator = new OpenAITextEmbeddingGenerationService(EmbeddingModelId, GetKey.Invoke());

// The combination of the text embedding generator and the memory store makes up the 'SemanticTextMemory' object used to
// store and retrieve memories.
SemanticTextMemory textMemory = new(memoryStore, embeddingGenerator);

/////////////////////////////////////////////////////////////////////////////////////////////////////
// PART 1: Store and retrieve memories using the ISemanticTextMemory (textMemory) object.
//
// This is a simple way to store memories from a code perspective, without using the Kernel.
/////////////////////////////////////////////////////////////////////////////////////////////////////
Console.WriteLine("== PART 1a: Saving Memories through the ISemanticTextMemory object ==");

Console.WriteLine("Saving memory with key 'info1': \"My name is Andrea\"");
await textMemory.SaveInformationAsync(MemoryCollectionName, id: "info1", text: "My name is Andrea");

Console.WriteLine("Saving memory with key 'info2': \"I work as a tourist operator\"");
await textMemory.SaveInformationAsync(MemoryCollectionName, id: "info2", text: "I work as a tourist operator");

Console.WriteLine("Saving memory with key 'info3': \"I've been living in Seattle since 2005\"");
await textMemory.SaveInformationAsync(MemoryCollectionName, id: "info3", text: "I've been living in Seattle since 2005");

Console.WriteLine("Saving memory with key 'info4': \"I visited France and Italy five times since 2015\"");
await textMemory.SaveInformationAsync(MemoryCollectionName, id: "info4", text: "I visited France and Italy five times since 2015");

// Retrieve a memory
Console.WriteLine("== PART 1b: Retrieving Memories through the ISemanticTextMemory object ==");
MemoryQueryResult? lookup = await textMemory.GetAsync(MemoryCollectionName, "info1");
Console.WriteLine("Memory with key 'info1':" + lookup?.Metadata.Text ?? "ERROR: memory not found");
Console.WriteLine();

/////////////////////////////////////////////////////////////////////////////////////////////////////
// PART 2: Create TextMemoryPlugin, store and retrieve memories through the Kernel.
//
// This enables prompt functions and the AI (via Planners) to access memories
/////////////////////////////////////////////////////////////////////////////////////////////////////

Console.WriteLine("== PART 2a: Saving Memories through the Kernel with TextMemoryPlugin and the 'Save' function ==");

// Import the TextMemoryPlugin into the Kernel for other functions
var memoryPlugin = kernel.ImportPluginFromObject(new TextMemoryPlugin(textMemory));

// Save a memory with the Kernel
Console.WriteLine("Saving memory with key 'info5': \"My family is from New York\"");
await kernel.InvokeAsync(memoryPlugin["Save"], new()
{
[TextMemoryPlugin.InputParam] = "My family is from New York",
[TextMemoryPlugin.CollectionParam] = MemoryCollectionName,
[TextMemoryPlugin.KeyParam] = "info5",
});

// Retrieve a specific memory with the Kernel
Console.WriteLine("== PART 2b: Retrieving Memories through the Kernel with TextMemoryPlugin and the 'Retrieve' function ==");
var result = await kernel.InvokeAsync(memoryPlugin["Retrieve"], new KernelArguments()
{
[TextMemoryPlugin.CollectionParam] = MemoryCollectionName,
[TextMemoryPlugin.KeyParam] = "info5"
});

Console.WriteLine("Memory with key 'info5':" + result.GetValue<string>() ?? "ERROR: memory not found");
Console.WriteLine();

/////////////////////////////////////////////////////////////////////////////////////////////////////
// PART 3: Recall similar ideas with semantic search
//
// Uses AI Embeddings for fuzzy lookup of memories based on intent, rather than a specific key.
/////////////////////////////////////////////////////////////////////////////////////////////////////

Console.WriteLine("== PART 3: Recall (similarity search) with AI Embeddings ==");

Console.WriteLine("== PART 3a: Recall (similarity search) with ISemanticTextMemory ==");
Console.WriteLine("Ask: where did I grow up?");

await foreach (var answer in textMemory.SearchAsync(
collection: MemoryCollectionName,
query: "where did I grow up?",
limit: 2,
minRelevanceScore: 0.79,
withEmbeddings: true))
{
Console.WriteLine($"Answer: {answer.Metadata.Text}");
}

Console.WriteLine("== PART 3b: Recall (similarity search) with Kernel and TextMemoryPlugin 'Recall' function ==");
Console.WriteLine("Ask: where do I live?");

result = await kernel.InvokeAsync(memoryPlugin["Recall"], new()
{
[TextMemoryPlugin.InputParam] = "Ask: where do I live?",
[TextMemoryPlugin.CollectionParam] = MemoryCollectionName,
[TextMemoryPlugin.LimitParam] = "2",
[TextMemoryPlugin.RelevanceParam] = "0.79",
});

Console.WriteLine($"Answer: {result.GetValue<string>()}");
Console.WriteLine();

/*
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"]
*/

/////////////////////////////////////////////////////////////////////////////////////////////////////
// PART 4: TextMemoryPlugin Recall in a Prompt Function
//
// Looks up related memories when rendering a prompt template, then sends the rendered prompt to
// the text generation model to answer a natural language query.
/////////////////////////////////////////////////////////////////////////////////////////////////////

Console.WriteLine("== PART 4: Using TextMemoryPlugin 'Recall' function in a Prompt Function ==");

// Build a prompt function that uses memory to find facts
const string RecallFunctionDefinition = @"
Consider only the facts below when answering questions:
BEGIN FACTS
About me: {{recall 'where did I grow up?'}}
About me: {{recall 'where do I live now?'}}
END FACTS
Question: {{$input}}
Answer:
";

var aboutMeOracle = kernel.CreateFunctionFromPrompt(RecallFunctionDefinition, new OpenAIPromptExecutionSettings() { MaxTokens = 100 });

result = await kernel.InvokeAsync(aboutMeOracle, new()
{
[TextMemoryPlugin.InputParam] = "Do I live in the same town where I grew up?",
[TextMemoryPlugin.CollectionParam] = MemoryCollectionName,
[TextMemoryPlugin.LimitParam] = "2",
[TextMemoryPlugin.RelevanceParam] = "0.79",
});

Console.WriteLine("Ask: Do I live in the same town where I grew up?");
Console.WriteLine($"Answer: {result.GetValue<string>()}");

/*
Approximate Output:
Answer: 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.
*/

/////////////////////////////////////////////////////////////////////////////////////////////////////
// PART 5: Cleanup, deleting database collection
//
/////////////////////////////////////////////////////////////////////////////////////////////////////

Console.WriteLine("== PART 5: Cleanup, deleting database collection ==");

Console.WriteLine("Printing Collections in DB...");
var collections = memoryStore.GetCollectionsAsync();
await foreach (var collection in collections)
{
Console.WriteLine(collection);
}
Console.WriteLine();

Console.WriteLine($"Removing Collection {MemoryCollectionName}");
await memoryStore.DeleteCollectionAsync(MemoryCollectionName);
Console.WriteLine();

Console.WriteLine($"Printing Collections in DB (after removing {MemoryCollectionName})...");
collections = memoryStore.GetCollectionsAsync();
await foreach (var collection in collections)
{
Console.WriteLine(collection);
}




Console.WriteLine("Hello, World!");
}
}
71 changes: 71 additions & 0 deletions dotnet/XpoMemoryDemo/XpoMemoryDemo.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\src\Connectors\Connectors.Memory.Xpo\Connectors.Memory.Xpo.csproj" />
<ProjectReference Include="..\src\Plugins\Plugins.Memory\Plugins.Memory.csproj" />

<PackageReference Include="Azure.Identity" />
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Microsoft.Extensions.Http.Resilience" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="Microsoft.ML.Tokenizers" />
<PackageReference Include="Microsoft.OpenApi" />
<PackageReference Include="System.Linq.Async" />
<PackageReference Include="System.Numerics.Tensors" />
<PackageReference Include="System.Text.Json" />

<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Xpo\Connectors.Memory.Xpo.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.MistralAI\Connectors.MistralAI.csproj" />
<ProjectReference Include="..\..\src\Agents\Abstractions\Agents.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Agents\Core\Agents.Core.csproj" />
<ProjectReference Include="..\..\src\Agents\OpenAI\Agents.OpenAI.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Google\Connectors.Google.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.HuggingFace\Connectors.HuggingFace.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.AzureAISearch\Connectors.Memory.AzureAISearch.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.AzureCosmosDBMongoDB\Connectors.Memory.AzureCosmosDBMongoDB.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Chroma\Connectors.Memory.Chroma.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.DuckDB\Connectors.Memory.DuckDB.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Kusto\Connectors.Memory.Kusto.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.MongoDB\Connectors.Memory.MongoDB.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Pinecone\Connectors.Memory.Pinecone.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Postgres\Connectors.Memory.Postgres.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Qdrant\Connectors.Memory.Qdrant.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Redis\Connectors.Memory.Redis.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Sqlite\Connectors.Memory.Sqlite.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Weaviate\Connectors.Memory.Weaviate.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.OpenAI\Connectors.OpenAI.csproj" />
<ProjectReference Include="..\..\src\Experimental\Agents\Experimental.Agents.csproj" />
<ProjectReference Include="..\..\src\Experimental\Orchestration.Flow\Experimental.Orchestration.Flow.csproj" />
<ProjectReference Include="..\..\src\Extensions\PromptTemplates.Handlebars\PromptTemplates.Handlebars.csproj" />
<ProjectReference Include="..\..\src\Extensions\PromptTemplates.Liquid\PromptTemplates.Liquid.csproj" />
<ProjectReference Include="..\..\src\Functions\Functions.Grpc\Functions.Grpc.csproj" />
<ProjectReference Include="..\..\src\Functions\Functions.OpenApi.Extensions\Functions.OpenApi.Extensions.csproj" />
<ProjectReference Include="..\..\src\Functions\Functions.OpenApi\Functions.OpenApi.csproj" />
<ProjectReference Include="..\..\src\Functions\Functions.Prompty\Functions.Prompty.csproj" />
<ProjectReference Include="..\..\src\Planners\Planners.Handlebars\Planners.Handlebars.csproj" />
<ProjectReference Include="..\..\src\Planners\Planners.OpenAI\Planners.OpenAI.csproj" />
<ProjectReference Include="..\..\src\Plugins\Plugins.Core\Plugins.Core.csproj" />
<ProjectReference Include="..\..\src\Plugins\Plugins.Memory\Plugins.Memory.csproj" />
<ProjectReference Include="..\..\src\Plugins\Plugins.MsGraph\Plugins.MsGraph.csproj" />
<ProjectReference Include="..\..\src\Plugins\Plugins.Web\Plugins.Web.csproj" />
<ProjectReference Include="..\..\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />

</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions dotnet/samples/Concepts/Concepts.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/samples/SamplesInternalUtilities.props" />

<ItemGroup>
<ProjectReference Include="..\..\src\Connectors\Connectors.Memory.Xpo\Connectors.Memory.Xpo.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.MistralAI\Connectors.MistralAI.csproj" />
<ProjectReference Include="..\..\src\Agents\Abstractions\Agents.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Agents\Core\Agents.Core.csproj" />
Expand Down
Loading

0 comments on commit 0284dd6

Please sign in to comment.