diff --git a/README.md b/README.md index b5258fe..ba62f15 100644 --- a/README.md +++ b/README.md @@ -5,40 +5,18 @@ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/ChromaDotNet/ChromaDB.VectorData/badge)](https://scorecard.dev/viewer/?uri=github.com/ChromaDotNet/ChromaDB.VectorData) [![OpenSSF Best Practices](https://www.bestpractices.dev/projects/15279/badge)](https://www.bestpractices.dev/projects/15279) -A [Chroma](https://www.trychroma.com/) provider for [Microsoft.Extensions.VectorData](https://learn.microsoft.com/dotnet/ai/vector-stores/overview). It is built on [ChromaDB.Client](https://github.com/ChromaDotNet/ChromaDB.Client) and published as the `ChromaDotNet.VectorData` package. It has the same code as `CommunityToolkit.VectorData.Chroma`, accepted in the AI Community Toolkit ([CommunityToolkit/AI#58](https://github.com/CommunityToolkit/AI/pull/58)), and moves to it when that package ships. +> **Moved to [CommunityToolkit.VectorData.Chroma](https://www.nuget.org/packages/CommunityToolkit.VectorData.Chroma),** in the [AI Community Toolkit](https://github.com/CommunityToolkit/AI), where the provider is developed now. The `ChromaDotNet.VectorData` package is deprecated, and this repository will be archived. -> This is a community project. It is not affiliated with or endorsed by Chroma. - -See the [package README](src/ChromaDB.VectorData/README.md) for how to use it, what is supported, and the limitations. - -Website: [chromadotnet.org](https://chromadotnet.org) - -## Building and testing - -```bash -dotnet build ChromaDB.VectorData.slnx -dotnet test test/ChromaDB.VectorData.UnitTests -dotnet test test/ChromaDB.VectorData.ConformanceTests -``` - -The conformance tests start Chroma in a container with [Testcontainers](https://dotnet.testcontainers.org/), so they need Docker. +## Moving -By default they use `chromadb/chroma:1.5.9`. Set `CHROMA_IMAGE` to pick another Chroma release, like `chromadb/chroma:1.5.0`. +The API is the same: change the package reference, from `ChromaDotNet.VectorData` to `CommunityToolkit.VectorData.Chroma`, and the namespace, from `ChromaDB.VectorData` to `CommunityToolkit.VectorData.Chroma`. The [package README](src/ChromaDB.VectorData/README.md) shows the commands. -To run them against a server that is already running, like Chroma Cloud, set the same variables as the ChromaDB.Client tests: +## History -```bash -CHROMA_TEST_URI=https://api.trychroma.com CHROMA_TEST_TOKEN= CHROMA_TEST_TENANT= CHROMA_TEST_DATABASE= dotnet test test/ChromaDB.VectorData.ConformanceTests -``` - -The tests delete only the collections they create. - -Hybrid search needs the Search API and the sparse vector indexes of Chroma Cloud. Its tests run only against Chroma Cloud, and are skipped otherwise. - -`dotnet publish test/NativeAot -c Release -o native-aot` builds the NativeAOT check. The check runs against `CHROMA_URI` (default `http://localhost:8000`). - -## Origin +This repository published the `ChromaDotNet.VectorData` package, a [Chroma](https://www.trychroma.com/) provider for [Microsoft.Extensions.VectorData](https://learn.microsoft.com/dotnet/ai/vector-stores/overview) built on [ChromaDB.Client](https://github.com/ChromaDotNet/ChromaDB.Client), from 0.1.0 to 0.4.4. From 0.4.0 it had the same code as `CommunityToolkit.VectorData.Chroma`, which the AI Community Toolkit accepted in [CommunityToolkit/AI#58](https://github.com/CommunityToolkit/AI/pull/58). The provider started as a copy, under the MIT license, of the Qdrant provider in [CommunityToolkit/AI](https://github.com/CommunityToolkit/AI). It was then adapted to Chroma. -It is now in the AI Community Toolkit, as `CommunityToolkit.VectorData.Chroma` ([CommunityToolkit/AI#58](https://github.com/CommunityToolkit/AI/pull/58)), and it is developed there. This repository publishes it until the toolkit ships that package. Then this repository will be archived, and the package deprecated in favor of that one. The API is the same, so moving changes only the package reference and the namespace, from `ChromaDB.VectorData` to `CommunityToolkit.VectorData.Chroma`. +> This is a community project. It is not affiliated with or endorsed by Chroma. + +Website: [chromadotnet.org](https://chromadotnet.org) diff --git a/src/ChromaDB.VectorData/ChromaDB.VectorData.csproj b/src/ChromaDB.VectorData/ChromaDB.VectorData.csproj index 5ea7c36..b211caa 100644 --- a/src/ChromaDB.VectorData/ChromaDB.VectorData.csproj +++ b/src/ChromaDB.VectorData/ChromaDB.VectorData.csproj @@ -9,9 +9,9 @@ true Chroma provider for Microsoft.Extensions.VectorData - Microsoft.Extensions.VectorData provider for Chroma and Chroma Cloud, built on ChromaDotNet.Client. It has the same code as CommunityToolkit.VectorData.Chroma, proposed to the AI Community Toolkit, and moves to it when that package ships. Community project, not affiliated with Chroma. + Deprecated: use CommunityToolkit.VectorData.Chroma. chroma chromadb chroma-cloud vector-database vectordata ai - 0.4.3: Auto-generation of keys is supported only for GUIDs, as in the provider proposed to the AI Community Toolkit: a string key with IsAutoGenerated throws a NotSupportedException when the collection is built. The NuGet description says that the provider has the same code as CommunityToolkit.VectorData.Chroma, and moves to it when that package ships. All the releases: https://github.com/ChromaDotNet/ChromaDB.VectorData/releases + 0.4.4: The last release: the provider moved to CommunityToolkit.VectorData.Chroma, in the AI Community Toolkit, with the same API. Change the package reference, and the namespace from ChromaDB.VectorData to CommunityToolkit.VectorData.Chroma. The code of 0.4.3 is unchanged. All the releases: https://github.com/ChromaDotNet/ChromaDB.VectorData/releases diff --git a/src/ChromaDB.VectorData/README.md b/src/ChromaDB.VectorData/README.md index 480b1cf..f6d6460 100644 --- a/src/ChromaDB.VectorData/README.md +++ b/src/ChromaDB.VectorData/README.md @@ -2,165 +2,33 @@ # ChromaDotNet.VectorData -A [Chroma](https://www.trychroma.com/) provider for [Microsoft.Extensions.VectorData](https://learn.microsoft.com/dotnet/ai/vector-stores/overview), built on [ChromaDotNet.Client](https://github.com/ChromaDotNet/ChromaDB.Client). It has the same code as `CommunityToolkit.VectorData.Chroma`, proposed in [CommunityToolkit/AI#58](https://github.com/CommunityToolkit/AI/pull/58), and moves to it when that package ships. +> **Moved to [CommunityToolkit.VectorData.Chroma](https://www.nuget.org/packages/CommunityToolkit.VectorData.Chroma).** This package is deprecated and gets no more releases. -> This is a community project. It is not affiliated with or endorsed by Chroma. - -Website: [chromadotnet.org](https://chromadotnet.org) - -## Quick start +## Moving -1. Run Chroma with Docker: +The API is the same. Change the package reference and the namespace: ```bash -docker run -d --name chroma -p 8000:8000 chromadb/chroma:1.5.9 +dotnet remove package ChromaDotNet.VectorData +dotnet add package CommunityToolkit.VectorData.Chroma --prerelease ``` -2. Install the NuGet package: - -```bash -dotnet add package ChromaDotNet.VectorData -``` - -3. Store and search records: - ```csharp -using ChromaDB.Client; +// Before: using ChromaDB.VectorData; -using Microsoft.Extensions.VectorData; - -using var httpClient = new HttpClient(); -var client = new ChromaClient(new ChromaConfigurationOptions("http://localhost:8000"), httpClient); -using var vectorStore = new ChromaVectorStore(client, ownsClient: false); - -var collection = vectorStore.GetCollection("hotels"); -await collection.EnsureCollectionExistsAsync(); - -await collection.UpsertAsync(new Hotel { Id = "h1", Name = "Grand", Rating = 5, Embedding = new float[] { 0.1f, 0.2f, 0.3f, 0.4f } }); - -await foreach (var result in collection.SearchAsync(new float[] { 0.1f, 0.2f, 0.3f, 0.4f }, top: 3, new() { Filter = h => h.Rating >= 4 })) -{ - Console.WriteLine($"{result.Record.Name}: {result.Score}"); -} - -public sealed class Hotel -{ - [VectorStoreKey] - public string Id { get; set; } = ""; - - [VectorStoreData(IsFullTextIndexed = true)] - public string? Name { get; set; } - - [VectorStoreData] - public int Rating { get; set; } - - [VectorStoreVector(4)] - public ReadOnlyMemory Embedding { get; set; } -} -``` - -With dependency injection, the vector store takes the `ChromaClient` from the container. That can be the singleton that [ChromaDotNet.Client.DependencyInjection](https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection) registers, which gets its `HttpClient` from `IHttpClientFactory`: - -```csharp -// dotnet add package ChromaDotNet.Client.DependencyInjection -using ChromaDB.Client; -using ChromaDB.Client.DependencyInjection; - -services.AddChromaClient(_ => new ChromaConfigurationOptions("http://localhost:8000")); -services.AddChromaVectorStore(); -``` - -Or the vector store creates its own client from a connection string: a URI, or the endpoint, token, tenant and database of Chroma Cloud: - -```csharp -services.AddChromaVectorStore("http://localhost:8000"); -services.AddChromaVectorStore("Endpoint=https://api.trychroma.com;Token=;Tenant=;Database="); +// After: +using CommunityToolkit.VectorData.Chroma; ``` -`AddChromaCollection(name, …)` registers one collection in the same two ways. `AddKeyedChromaVectorStore` and `AddKeyedChromaCollection` register them under a key. - -With these registrations, the embedding generator is the `EmbeddingGenerator` set in `ChromaVectorStoreOptions` or `ChromaCollectionOptions`. If none is set, it is an `IEmbeddingGenerator` registered in the container. - -Without dependency injection, `ChromaVectorStore` and `ChromaCollection` take a `ChromaClient`. With `ownsClient: true`, they dispose it. - -`GetService(typeof(ChromaClient))` on the vector store or on a collection returns the client it uses. - -## Chroma Cloud - -Connect with the endpoint, the API key, and the tenant and the database shown in the Chroma Cloud dashboard. The client sends the API key in the `X-Chroma-Token` header. - -Chroma Cloud reads and writes at most 300 records per request. On Chroma Cloud addresses, the client reads and writes in batches of 300 by itself. A search returns at most 300 results, counting both `top` and `Skip`. +The registrations, like `AddChromaVectorStore`, keep their names and their namespace, `Microsoft.Extensions.DependencyInjection`. -Chroma Cloud also limits metadata and documents: +## History -- A record has at most 32 metadata keys. -- A key has at most 36 bytes. -- A value has at most 8,182 bytes. -- A document has at most 16,384 bytes. +ChromaDotNet.VectorData was a [Chroma](https://www.trychroma.com/) provider for [Microsoft.Extensions.VectorData](https://learn.microsoft.com/dotnet/ai/vector-stores/overview), built on [ChromaDotNet.Client](https://github.com/ChromaDotNet/ChromaDB.Client). From 0.4.0 it had the same code as `CommunityToolkit.VectorData.Chroma`, which the AI Community Toolkit accepted in [CommunityToolkit/AI#58](https://github.com/CommunityToolkit/AI/pull/58). The provider is developed there now. -Each property with a value is a key, and so is the BM25 vector of a full-text indexed `string` property: see hybrid search below. - -If the text of the property stored as the document is longer than 8,182 bytes, it is stored in the document only. `Contains` finds it, but `==` does not. - -## Hybrid search - -On Chroma Cloud, `HybridSearchAsync` searches with a vector and keywords together: - -```csharp -var results = collection.HybridSearchAsync(new float[] { 0.1f, 0.2f, 0.3f, 0.4f }, ["pool", "spa"], top: 5); -``` +It started as a copy, under the MIT license, of the Qdrant provider in [CommunityToolkit/AI](https://github.com/CommunityToolkit/AI), and was then adapted to Chroma. The releases and their notes are on [GitHub](https://github.com/ChromaDotNet/ChromaDB.VectorData/releases). -It fuses the ranks of two searches: the vector search, and a BM25 search of the keywords in a full-text indexed `string` property. The BM25 search needs a BM25 index on the text of the property. A BM25 index is a Chroma sparse vector index. - -On Chroma Cloud, creating the collection creates a BM25 index for each full-text indexed `string` property, like `Name` in `Hotel` above. The client then computes the BM25 vectors of the records as it writes them. The index of the property stored as the document is on the documents, under the key `document_bm25`; the index of another property is under its storage name with `_bm25` added. An index whose key would have more than 36 bytes is left out. - -A collection answers `IKeywordHybridSearchable` from `GetService` only on Chroma Cloud, and only when it has a full-text indexed `string` property. The `TextSearchStore` of Semantic Kernel asks for this interface to choose hybrid search over vector search. `AddChromaCollection` registers the collection as `IKeywordHybridSearchable` in any case, so resolve it from the container only on Chroma Cloud. - -The score of a hybrid result is the reciprocal rank fusion score (k = 60) of the two searches. Higher is better, and the score is well below 1. `ScoreThreshold` applies to it. - -A collection created by another Chroma client, like the Python one, works too when it has a `chroma_bm25` index on the text of the property. For the property stored as the document, the index goes on the documents. - -A record without any of the keywords gets nothing from the BM25 search, as in a keyword search. - -A single Chroma server has neither the Search API nor sparse vector indexes: there, the collection is created without BM25 indexes. - -## Supported - -- Keys: `string` and `Guid`. -- One vector per record: `ReadOnlyMemory`, `Embedding` or `float[]`, or any type with an embedding generator. -- Data properties, stored as Chroma metadata: - - `string`, `int`, `long`, `double`, `float`, `bool`, `DateTime`, `DateTimeOffset` and `DateOnly` (.NET 8 and later) - - their nullable forms - - arrays or `List` of the non-nullable ones -- Dates are stored as ISO 8601 strings. A `DateTimeOffset` is stored in UTC, so it comes back as the same instant with offset zero. A `DateTime` is stored with its `Kind`. -- Targets .NET 10, .NET 8, .NET Standard 2.0 and .NET Framework 4.6.2. NativeAOT needs .NET 8 or later. -- Distance functions: `CosineSimilarity` (the default), `CosineDistance`, `DotProductSimilarity`, `NegativeDotProductSimilarity`, `EuclideanDistance` and `EuclideanSquaredDistance`, with the HNSW index. -- An existing collection must use the space of the distance function. A collection created by another Chroma client without a space uses l2. If the space differs, creating or searching the collection throws, rather than turning the distances of another space into scores. On Chroma 1.0.0 to 1.0.5 the server does not report the space, so the check cannot be done: use Chroma 1.0.6 or later. -- Filters: - - `==` and `!=` - - `<`, `<=`, `>` and `>=` on numbers - - `&&`, `||` and `!` - - `Contains` over an inline list or an array property - - `Any` with `Contains` over an inline list -- Filters on the key: `==`, and `Contains` over a list of keys, joined to the other conditions with `&&`. Chroma looks the records up by id. -- Full-text: the only full-text indexed `string` property is also stored as the Chroma document. That is where other Chroma clients store their text. - - `Contains` and `!Contains` on it filter the text of the document, joined to the other conditions with `&&`. - - When a record has its text in the document only, the provider reads it into that property. - - A null text deletes the document of a record that exists, and reads back as null. An empty text reads back as empty. -- NativeAOT and trimming: the dynamic collection works without reflection. You get it from `GetDynamicCollection` with a `VectorStoreCollectionDefinition`. `ChromaCollection` maps the properties of the record type by reflection. `AddChromaVectorStore` and `AddKeyedChromaVectorStore` work with trimming and NativeAOT. `AddChromaCollection` and `AddKeyedChromaCollection` map a record type, so they are marked as incompatible. There, register the vector store and get the collection from it with `GetDynamicCollection`. - -## Limitations - -- Chroma metadata has no null values. A null property is not stored, and filtering on null is not supported. -- Upserting a record that exists replaces it: a value that is now null, or an empty list, is deleted, and a null text deletes the document. -- Chroma does not store empty lists. An empty array or list is not stored, and comes back as null. -- `GetAsync` with a filter does not support ordering. -- Comparisons work on numbers only. A negated comparison, like `!(r.Rating > 3)`, is not supported on a nullable property: Chroma would leave out the records where the property is null. -- A `DateTime` filter finds the same ticks with the same `Kind`. -- Array and list properties, like the namespaces of the `TextSearchStore` of Semantic Kernel, need Chroma 1.5.0 or later. -- Hybrid search needs Chroma Cloud. -- On Chroma Cloud, a vector search returns at most 300 results, `Skip` included, the default quota of Chroma Cloud: beyond that, Chroma Cloud answers with a quota error. -- Only one property can be the document. With more than one full-text indexed string property, none is. +> This is a community project. It is not affiliated with or endorsed by Chroma. -In CI, the provider runs the Microsoft.Extensions.VectorData conformance tests against Chroma 1.5.0, 1.5.9 and the latest release. On Chroma Cloud, the tests are run by hand, hybrid search included, and they pass. +Website: [chromadotnet.org](https://chromadotnet.org)