Repository navigation
Expand file tree
/
Copy pathVectorStore_DynamicDataModel_Interop_Chroma.cs
More file actions
194 lines (164 loc) · 9.05 KB
/
Copy pathVectorStore_DynamicDataModel_Interop_Chroma.cs
File metadata and controls
194 lines (164 loc) · 9.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using Azure.AI.OpenAI;
using Azure.Identity;
using ChromaDB.Client;
using CommunityToolkit.VectorData.Chroma;
using Memory.VectorStoreFixtures;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.VectorData;
namespace Memory;
/// <summary>
/// Semantic Kernel support dynamic data modeling for vector stores that can be used with any
/// schema. The schema still has to be provided in the form of a record definition, but no
/// custom .NET data model is required; a simple dictionary can be used.
///
/// The sample shows how to, with a Chroma database
/// 1. Upsert data using dynamic data modeling and retrieve it from the vector store using a custom data model.
/// 2. Upsert data using a custom data model and retrieve it from the vector store using the dynamic data modeling.
///
/// To run this sample, you need a local instance of Docker running, since the associated fixture will try and start a Chroma container in the local docker instance.
/// </summary>
public class VectorStore_DynamicDataModel_Interop_Chroma(ITestOutputHelper output, VectorStoreChromaContainerFixture chromaFixture) : BaseTest(output), IClassFixture<VectorStoreChromaContainerFixture>
{
private static readonly JsonSerializerOptions s_indentedSerializerOptions = new() { WriteIndented = true };
// Chroma supports string and Guid keys.
private static readonly VectorStoreCollectionDefinition s_definition = new()
{
Properties =
[
new VectorStoreKeyProperty("Key", typeof(string)),
new VectorStoreDataProperty("Term", typeof(string)),
new VectorStoreDataProperty("Definition", typeof(string)),
new VectorStoreVectorProperty("DefinitionEmbedding", typeof(ReadOnlyMemory<float>), 1536)
]
};
[Fact]
public async Task UpsertWithDynamicRetrieveWithCustomAsync()
{
// Create an embedding generation service.
var embeddingGenerator = new AzureOpenAIClient(new Uri(TestConfiguration.AzureOpenAIEmbeddings.Endpoint), new AzureCliCredential())
.GetEmbeddingClient(TestConfiguration.AzureOpenAIEmbeddings.DeploymentName)
.AsIEmbeddingGenerator(1536);
// Initiate the docker container and construct the vector store.
await chromaFixture.ManualInitializeAsync();
using var httpClient = new HttpClient();
using var vectorStore = new ChromaVectorStore(new ChromaClient(new ChromaConfigurationOptions("http://localhost:8000"), httpClient), ownsClient: true);
// Get and create collection if it doesn't exist using the dynamic data model and record definition that defines the schema.
var dynamicDataModelCollection = vectorStore.GetDynamicCollection("skglossary", s_definition);
await dynamicDataModelCollection.EnsureCollectionExistsAsync();
// Create glossary entries and generate embeddings for them.
var glossaryEntries = CreateDynamicGlossaryEntries().ToList();
var tasks = glossaryEntries.Select(entry => Task.Run(async () =>
{
entry["DefinitionEmbedding"] = (await embeddingGenerator.GenerateAsync((string)entry["Definition"]!)).Vector;
}));
await Task.WhenAll(tasks);
// Upsert the glossary entries into the collection.
await dynamicDataModelCollection.UpsertAsync(glossaryEntries);
// Get the collection using the custom data model.
var customDataModelCollection = vectorStore.GetCollection<string, Glossary>("skglossary");
// Retrieve one of the upserted records from the collection.
var upsertedRecord = await customDataModelCollection.GetAsync((string)glossaryEntries.First()["Key"]!, new() { IncludeVectors = true });
// Write one of the upserted records to the console.
Console.WriteLine($"Upserted record: {JsonSerializer.Serialize(upsertedRecord, s_indentedSerializerOptions)}");
}
[Fact]
public async Task UpsertWithCustomRetrieveWithDynamicAsync()
{
// Create an embedding generation service.
var embeddingGenerator = new AzureOpenAIClient(new Uri(TestConfiguration.AzureOpenAIEmbeddings.Endpoint), new AzureCliCredential())
.GetEmbeddingClient(TestConfiguration.AzureOpenAIEmbeddings.DeploymentName)
.AsIEmbeddingGenerator(1536);
// Initiate the docker container and construct the vector store.
await chromaFixture.ManualInitializeAsync();
using var httpClient = new HttpClient();
using var vectorStore = new ChromaVectorStore(new ChromaClient(new ChromaConfigurationOptions("http://localhost:8000"), httpClient), ownsClient: true);
// Get and create collection if it doesn't exist using the custom data model.
var customDataModelCollection = vectorStore.GetCollection<string, Glossary>("skglossary");
await customDataModelCollection.EnsureCollectionExistsAsync();
// Create glossary entries and generate embeddings for them.
var glossaryEntries = CreateCustomGlossaryEntries().ToList();
var tasks = glossaryEntries.Select(entry => Task.Run(async () =>
{
entry.DefinitionEmbedding = (await embeddingGenerator.GenerateAsync(entry.Definition)).Vector;
}));
await Task.WhenAll(tasks);
// Upsert the glossary entries into the collection and return their keys.
await customDataModelCollection.UpsertAsync(glossaryEntries);
// Get the collection using the dynamic data model.
var dynamicDataModelCollection = vectorStore.GetDynamicCollection("skglossary", s_definition);
// Retrieve one of the upserted records from the collection.
var upsertedRecord = await dynamicDataModelCollection.GetAsync(glossaryEntries.First().Key, new() { IncludeVectors = true });
// Write one of the upserted records to the console.
Console.WriteLine($"Upserted record: {JsonSerializer.Serialize(upsertedRecord, s_indentedSerializerOptions)}");
}
/// <summary>
/// Sample model class that represents a glossary entry.
/// </summary>
/// <remarks>
/// Note that each property is decorated with an attribute that specifies how the property should be treated by the vector store.
/// This allows us to create a collection in the vector store and upsert and retrieve instances of this class without any further configuration.
/// </remarks>
private sealed class Glossary
{
[VectorStoreKey]
public string Key { get; set; }
[VectorStoreData]
public string Term { get; set; }
[VectorStoreData]
public string Definition { get; set; }
[VectorStoreVector(1536)]
public ReadOnlyMemory<float> DefinitionEmbedding { get; set; }
}
/// <summary>
/// Create some sample glossary entries using the custom data model.
/// </summary>
/// <returns>A list of sample glossary entries.</returns>
private static IEnumerable<Glossary> CreateCustomGlossaryEntries()
{
yield return new Glossary
{
Key = "1",
Term = "API",
Definition = "Application Programming Interface. A set of rules and specifications that allow software components to communicate and exchange data.",
};
yield return new Glossary
{
Key = "2",
Term = "Connectors",
Definition = "Connectors allow you to integrate with various services provide AI capabilities, including LLM, AudioToText, TextToAudio, Embedding generation, etc.",
};
yield return new Glossary
{
Key = "3",
Term = "RAG",
Definition = "Retrieval Augmented Generation - a term that refers to the process of retrieving additional data to provide as context to an LLM to use when generating a response (completion) to a user’s question (prompt).",
};
}
/// <summary>
/// Create some sample glossary entries using dynamic data modeling.
/// </summary>
/// <returns>A list of sample glossary entries.</returns>
private static IEnumerable<Dictionary<string, object?>> CreateDynamicGlossaryEntries()
{
yield return new Dictionary<string, object?>
{
["Key"] = "1",
["Term"] = "API",
["Definition"] = "Application Programming Interface. A set of rules and specifications that allow software components to communicate and exchange data."
};
yield return new Dictionary<string, object?>
{
["Key"] = "2",
["Term"] = "Connectors",
["Definition"] = "Connectors allow you to integrate with various services provide AI capabilities, including LLM, AudioToText, TextToAudio, Embedding generation, etc."
};
yield return new Dictionary<string, object?>
{
["Key"] = "3",
["Term"] = "RAG",
["Definition"] = "Retrieval Augmented Generation - a term that refers to the process of retrieving additional data to provide as context to an LLM to use when generating a response (completion) to a user’s question (prompt)."
};
}
}