Repository navigation
Releases
2.20.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.20.0, released together.
New
SupportsSparseVectorIndexesAsync()andSupportsSparseVectorIndexes()ofChromaClientsay whether the server takes sparse vector indexes, like the BM25 index ofWithBm25Index: whether the client sends the indexes added withifSupported. Distributed Chroma, the one Chroma Cloud runs on, takes them: on Chroma Cloud, and elsewhere when the options name it withWithDistributedChroma(). So do the servers that declare them inpre-flight-checks, like VecturaDB. A single Chroma server does not. When the server does not answer within 5 seconds, or its answer cannot be read, the answer is what the options say: the asynchronous method asks again at the next call, the synchronous one keeps it for 30 seconds.- The synchronous method is for the callers that cannot await, like a synchronous
GetService. Its request runs on the thread pool, so it does not deadlock under the synchronization context of a UI or of ASP.NET on .NET Framework. After an answer of the server it answers at once, and asks again in the background when that answer is two minutes old. DistributedChromaofChromaConfigurationOptionsis whatWithDistributedChroma()orDistributed=truein the connection string set, also on the address of Chroma Cloud, for code that passes the setting on.IsDistributedChromastays whether the client takes the server for distributed Chroma, Chroma Cloud included.
Fixes
- The comments of
GetCollectionByCrnAsync,UpdateTenantAsyncandChromaTenant.ResourceName, and the docs, gave the resource names of the tenants and the get of a collection by its CRN to Chroma Cloud only. They now name distributed Chroma, the one Chroma Cloud runs on.
Docs
docs/sparse-vectors-and-schema.md: the two methods.docs/connecting.md:DistributedChromanext toIsDistributedChroma.docs/chroma-cloud.md: the resource names on distributed Chroma.docs/COMPATIBILITY.md:- Chroma 1.5.9 answers
add_inputwith404without a body, soAddFunctionInputAsyncthrows aChromaException"NotFound: POST …/attached_functions/stats/add_input"; - distributed Chroma keeps the resource name of a tenant and gets a collection by its CRN. It answers
500"collection not found" for a CRN without a collection, and refuses the name of a database with fewer than 3 characters.
- Chroma 1.5.9 answers
In our tests on 6 November 2026, both methods answered false on Chroma 1.5.9, and true on Chroma Cloud, on VecturaDB 1.0.0-preview.8 and on distributed Chroma at 52ce844, installed with ChromaDB.Operator 0.1.3, with WithDistributedChroma(). The client created the collection of the test with the index added with ifSupported exactly where they answered true.
Nothing breaks for the existing code: the public API only gains members.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.20.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.20.0
2.19.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.19.0, released together.
New
- Conditional transactions:
collectionClient.RunConditionalTransactionAsync(operation)reads records and writes them only if none of them changed after its reads, ascollection.conditional()of the Python and JavaScript clients of Chroma. After a conflict, a stale read or a429Backoff, it runs the operation again in a new transaction, up tomaxRetriestimes, 3 by default.BeginConditionalTransaction()gives a transaction to commit without a run, withGetAsync,AddAsync,UpdateAsync,UpsertAsync,DeleteAsyncby ids, andCommitAsync. A conflict throws aChromaConditionalWriteConflictException(409), a stale read aChromaStaleReadException(412). Distributed Chroma, the one Chroma Cloud runs on, has conditional transactions for the tenants that the configuration of its frontend enables, and so does VecturaDB; Chroma 1.5.9 does not. AddFunctionInputAsync(name, inputCollectionId)adds another input collection to an asynchronous attached function, asAttachedFunction.add_inputof the Python client, and says whether the input was added now: on distributed Chroma, the one Chroma Cloud runs on, and on VecturaDB.- Writes refused for compaction: distributed Chroma, the one Chroma Cloud runs on, refuses a write to a collection whose log holds too many records not yet compacted, with
429"log needs compaction before accepting more writes; please backoff exponentially and retry". Each request ofAddAsync,UpdateAsync,UpsertAsync,DeleteAsyncandDeleteAllAsyncnow goes again after that refusal only, and the batches that went are not sent again.ChromaWriteRetries.Defaultmakes 12 attempts, with a base delay of 1 second and a largest delay of 10 seconds: from 42.5 to 85 seconds in all. ARetry-Afterof the refusal is waited instead; VecturaDB sends one.WithWriteRetriessets other attempts and delays, andChromaWriteRetries.Noneturns them off. Any other error of a write, also another429, stands at once. - Distributed Chroma, the one Chroma Cloud runs on, installed elsewhere with the Helm chart of Chroma or an operator:
WithDistributedChroma(), orDistributed=truein the connection string, says it. The client then throws anArgumentExceptionbefore a filter on the documents that distributed Chroma gets wrong (KD-56, KD-57), also in a delete, sends the indexes added withifSupportedwithout the key quota of Chroma Cloud, and deletes a collection or a database in one request. Chroma Cloud is distributed Chroma whatever the options say, and keeps its quotas.Distributedwith a value other thantrueorfalsethrows anArgumentException. - The server block of
pre-flight-checks, as VecturaDB sends it:ChromaPreFlightChecks.Serverhas its name, version and features. A server that declaressparse_vector_indexgets the indexes added withifSupported, without the key quota of Chroma Cloud. A server that says what it is in that block is what it says, whatever the options say. Chroma and Chroma Cloud send no block, so nothing changes for them.
Fixes
- The messages, the comments, the README and the docs about the Search API, fork, the fork count, the indexing status, the read levels, the SPANN index, sparse vector indexes, attached functions and conditional transactions now name the servers that have them: distributed Chroma, the one Chroma Cloud runs on, and VecturaDB. They said Chroma Cloud only. The ones on the filters on the documents of KD-56 and KD-57 say that the client throws when it knows the server is distributed Chroma: on Chroma Cloud, or elsewhere with
WithDistributedChroma(). - On nuget.org the README showed the badges of OpenSSF Scorecard, OpenSSF Best Practices and GitHub Codespaces blank: they come from img.shields.io now, which nuget.org shows.
THIRD-PARTY-NOTICES.md, in the package of ChromaDotNet.Client, credits Chroma for the port of its BM25 function, with its license, the Apache License 2.0.
Docs
docs/conditional-transactions.md: conditional transactions.docs/errors.md: writes refused for compaction and the exceptions of a conditional transaction.docs/chroma-cloud.md:AddFunctionInputAsyncand conditional transactions.docs/connecting.md: distributed Chroma.docs/COMPATIBILITY.md: KD-59, agetduring concurrent upserts of the same record that returns metadata mixed from two versions on Chroma 1.5.9, the measures of KD-56 and KD-57 on distributed Chroma, and the order of the databases that distributed Chroma lists.docs/embeddings.mdand the README: ChromaDotNet.Embeddings, the embeddings of the Python client. The README points to ChromaDotNet.Local.ChromaDB, the package with the engine.docs/about-this-fork.md: the original ChromaDB.Client package is deprecated on NuGet in favor of ChromaDotNet.Client, and the repository left the fork network of ssone95/ChromaDB.Client.
Nothing breaks for the existing code: the public API only gains members. A write refused because the log needs compaction now goes again, and with ChromaWriteRetries.None it throws at once, as before.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.19.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.19.0
2.18.1
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.18.1, released together: the same code as 2.18.0.
Packages
- The new icon of ChromaDotNet, without the colors of Chroma: the dots of the C in teal and the final dot in indigo, on a dark tile.
- The package descriptions end with: Independent project. Not affiliated with, endorsed by or sponsored by Chroma Inc. CHROMA is a registered trademark of Chroma Inc., used here only to describe compatibility.
- The README says who we are, with the same sentences.
Packages: ChromaDotNet.Client 2.18.1, ChromaDotNet.Client.DependencyInjection 2.18.1
2.18.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.18.0, released together.
New
ChromaReadRetries.MinDelay: a read never goes again at once. The delay before a new attempt is a random time fromMinDelayto the smaller ofMaxDelayandBaseDelaytimes 2 to the attempts that went again before.new ChromaReadRetries(maxAttempts, minDelay, baseDelay, maxDelay)takes it, from zero to the base; the constructor without it takes 0.8 times the base, the lower bound of the jitter of Azure.Core. The default now waits 40 to 50 ms, then 40 to 100 ms and 40 to 200 ms;ChromaReadRetries.Nonedoes not change.- Collections created when missing:
client.GetCollectionClient(name, createIfMissing: true)gets or creates the collection at its first operation, with one request that the operations starting before it ends share, andawait client.GetOrCreateCollectionClientAsync(name)does it at once. Both take aChromaCollectionDefinitiontoo, keep the collection, and create it again, empty, when it is deleted elsewhere. AddChromaClientFromServicesandAddKeyedChromaClientFromServicesmake the options from the services when the client is resolved, so the address can come fromIConfigurationorIOptionsof the application.
Fixes
- A value that is not a text under the metadata key
chroma:document, where Chroma keeps the document of a record, throws anArgumentExceptionbefore the request, inAddAsync,UpdateAsyncandUpsertAsync. Such a value, like a number, a Boolean, a list, or a null in an update or an upsert, makes Chroma 1.0.0 to 1.5.9 fail every add and get of the server, on every collection, until it restarts (KD-58). The same holds for the null of a deleted document or the mark thatWithDocumentCopyKeyorWithDocumentDeletionKeywould write under that key. - A text under
chroma:documentgoes as before and sets the document, also aGuid, a date or abyte[], which the client writes as text. The other keys with the prefixchroma:go as before too. - The release notes of the packages link the GitHub release of their version. The ones of 2.15.0 to 2.17.0 were still those of 2.14.0.
Docs
docs/COMPATIBILITY.md: KD-58.docs/metadata.md: the keychroma:document.docs/errors.md:MinDelay.docs/collections.md: collections created when missing.docs/dependency-injection.md: options made from the services.- The migration guide from 1.x says what a real migration had to do: the URI, the collection client, the handlers to remove, dependency injection and the behaviors that differ from 1.x.
Nothing breaks for the existing code: every setting of ChromaReadRetries that was valid stays valid.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.18.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.18.0
2.17.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.17.0, released together.
New
ChromaCollectionClient.WithEmbeddingGenerator(generator)takes anIEmbeddingGenerator<string, Embedding<float>>of Microsoft.Extensions.AI. The client of the collection then computes the embeddings, as an embedding function does in the Python client of Chroma:var records = client.GetCollectionClient(collection).WithEmbeddingGenerator(generator); await records.AddAsync(["a", "b"], documents: ["apples are red", "kiwis are green"]); var nearest = await records.QueryAsync("a red fruit", nResults: 1);
AddAsync,UpdateAsyncandUpsertAsynccompute the embeddings of the records given with documents and without embeddings. Records given with embeddings keep them.QueryAsync(queryText)andQueryTextsAsync(queryTexts)query by text, asquery_textsin the Python client.ChromaCollectionDefinition.EmbeddingGeneratordeclares the default embedding function of Chroma: with a generator whose metadata has the providerchromaand the modelall-MiniLM-L6-v2, a new collection stores the functiondefault, as the Python client does, so the Python client reads it with its default embedding function. Another generator declares nothing.- The new API is in the builds for .NET 8 and .NET Framework, which reference Microsoft.Extensions.AI.Abstractions 9.5.0. The .NET Standard 2.0 build, for .NET Core 3.1 to 7, does not change.
Docs
docs/embeddings.mdis new, anddocs/records.mdlinks it.- README: under Installation, ChromaDotNet.Local, to run Chroma on your machine without Docker.
SECURITY.md: the Cosign command matches the dots of the signing identity as dots, so it accepts only this repository.
The API of the client does not break: without a generator, the requests do not change. One change is visible: on .NET 8 and .NET Framework an application that uses a preview of Microsoft.Extensions.AI before 9.5.0 now gets Microsoft.Extensions.AI.Abstractions 9.5.0, the first stable release, and may need the names of that release, like GenerateVectorAsync for GenerateEmbeddingVectorAsync.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.17.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.17.0
2.16.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.16.0, released together.
New
ChromaCollectionClient.DeleteAllAsync()deletes every record of the collection, which stays, and returns how many it deleted. It reads a page of ids from the start, as large as the batch size of the server and of Chroma Cloud, deletes them, and goes on until none is left.ChromaWhereDocumentOperator.FromDictionarytakes a filter on the documents written as the dictionary of thewhere_documentof the Chroma API, asChromaWhereOperator.FromDictionarydoes forwhere. Get, query and delete send it as it is; the Search API gets it on#document.- An empty metadata goes as no metadata (null) in
AddAsync,UpdateAsyncandUpsertAsync, where it threw anArgumentException: every tested Chroma, 0.4.10 to 1.5.9 and Chroma Cloud, treats the two the same.
Fixes
- The checks of Chroma Cloud on the filters of the documents (KD-56, KD-57) read the filters given as dictionaries whatever the types of their values: JSON, a typed dictionary, a
JsonNode. A pattern like^$throws before any request, also in a delete. This holds forChromaWhereOperator.FromDictionaryof 2.15.0 too. - A get in pages, a get by ids and
DeleteAllAsynclearn the quotas of the reads and of the deletes of Chroma Cloud from its422answer, and read or delete again in smaller batches, without doing a delete twice. Before, a client withWithBatchSplittingabove 300 failed on its first read. - A long
$andor$orin a filter given as a dictionary, as JSON or as aList<object>, goes split as the same filter written with the operators (KD-4). - On Chroma Cloud, a regex pattern beyond the quota of the tenant, 256 bytes in UTF-8 by default, gets a
ChromaExceptionthat names the pattern, its bytes and the limit. The client still sends every pattern: a tenant can have a higher quota.
Docs
docs/records.md,docs/metadata.md,docs/queries-and-filters.mdanddocs/COMPATIBILITY.mdsay all of the above.- README, Used by: LlmTornado.VectorDatabases.ChromaDB, the Chroma connector of LLM Tornado.
Nothing breaks for the existing code. One change is visible: FromDictionary writes the dictionary to JSON when it gets it, so a value the client cannot write, like a type outside its JSON context under NativeAOT, throws there instead of when the filter goes, and later changes to the dictionary do not change the filter.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.16.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.16.0
2.15.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.15.0, released together.
New
ChromaWhereOperator.FromDictionarytakes a filter already written as the dictionary of thewhereof the Chroma API, as the Python client takes it, and sends it as it is:FromDictionary(new Dictionary<string, object> { ["year"] = new Dictionary<string, object> { ["$gte"] = 2020 } }).&and|join it to the other filters;Notthrows aNotSupportedExceptionon it, as Chroma has no$notand the client does not read the dictionary.
Docs
docs/queries-and-filters.mdsays how to useFromDictionary.- README, Used by:
- BotSharp.Plugin.Chroma, the Chroma vector database plugin of BotSharp;
- LangChain.Databases in two entries: its integration tests under "Used by", its README, which recommends the provider for Chroma, under "Cited in".
Nothing changes for the existing code.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.15.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.15.0
2.14.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.14.0, released together.
New
-
Reads go again after an error that can pass. Each request of
GetAsync,PeekAsync,QueryAsync,SearchAsyncandCountAsyncgoes again after:- an answer
429,500,502,503or504, other than a missing collection; - a request that fails on the network;
- an answer whose lists do not have one item for every record, or, for a query or a search, one list of ids for every query embedding or search.
So do the reads that a write makes first: the get of
ChromaUpsertStrategy.SkipUnchangedEmbeddings, the get ofWithReadsBeyondKeyQuota()on Chroma Cloud, and the pages of ids thatDeleteCollectionAsyncreads withdeleteRecordsFirst. - an answer
-
Before each new attempt the client waits with the exponential backoff with full jitter of AWS: a random time between zero and the smaller of the largest delay and the base delay times 2 to the attempts that went again before. By default, 4 attempts at most, a base delay of 50 ms and a largest delay of 1 second: a read that keeps failing waits 350 ms at most before its error.
-
ChromaConfigurationOptions.ReadRetriesandWithReadRetries(ChromaReadRetries)set the attempts, withChromaReadRetries.Default, orMaxAttempts,BaseDelayandMaxDelayof your own.ChromaReadRetries.Noneturns them off, as in 2.13.0.
Changes
- After the last attempt the error stands. An answer whose lists do not have one item for every record throws a
ChromaExceptionthat says it, like "The answer of the server to a get has 1 id and 0 embeddings", instead of anArgumentOutOfRangeException. - A write never goes again: one that failed may have gone. A timeout of the
HttpClientdoes not go again either. - A query with ids takes an internal error to the workaround of KD-24 at once, without the delays. When every id has a record, the query goes again with the attempts, at most
MaxAttemptsqueries in all. - A collection client made by name reads its collection again after the attempts. On Chroma 0.4.23, where the next count of such a client answers
500after its collection is deleted and created again, that count takes 239 ms with the default, as a median of 20, and 6 ms withChromaReadRetries.None. - In our measurement on Chroma 1.5.9, 14,000 upserts, deletes and gets with embeddings of the same records at the same time gave 28 errors with 2.13.0, and none with 2.14.0.
Docs
docs/errors.mdsays which reads go again, after which errors, and how to change the attempts.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.14.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.14.0
2.13.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.13.0, released together.
New
- Two more ways to delete a document with
NullDocumentsDelete, next toWithDocumentCopyKey:WithDocumentDeletionKey(key)writes an empty document withtrueunder a metadata key, and reads it back as null.WithEmptyDocumentDeletions()writes an empty document alone, which reads back as an empty string.
ChromaConfigurationOptions.WithReadsBeyondKeyQuota(): on Chroma Cloud, a write with nulls that would take a record beyond the quota of 32 metadata keys reads the record first and sends only the nulls of its keys, instead of throwing. A write of another client between the read and the write can mix with this one.
Changes
-
UpdateAsyncandUpsertAsyncdelete metadata keys, with a null or an empty list, and documents, withNullDocumentsDelete, without reading the records first: the nulls and the empty document go in the same write. Two writes of the same record at the same time now leave the record of one of them. In our measurement of concurrent writes on 300 records, 131 records mixed the two writes with 2.12.0, and none with 2.13.0. -
A collection client chooses once how it deletes documents:
- A null document with
NullDocumentsDelete, from a client withoutWithDocumentCopyKey,WithDocumentDeletionKeyorWithEmptyDocumentDeletions, throws anArgumentExceptionbefore any request. - Another of the three, or the same one with another key, throws an
InvalidOperationException. Up to 2.12.0,WithDocumentCopyKeywith another key replaced the key. - When
NullDocumentsDeletedeletes documents, a record without a document gets the empty document too, so awhere_documentfilter that matches an empty text finds it.
- A null document with
-
On Chroma Cloud, a write with nulls that would take a record beyond the quota of 32 metadata keys throws an
ArgumentExceptionbefore it goes, unlessWithReadsBeyondKeyQuota(). -
On Chroma Cloud, a filter on the documents that gives the wrong records there throws an
ArgumentExceptionbefore the request, in every read and delete:- a
$regexor$not_regexwhose pattern takes an empty text, like^$,^or^.*$, or that the client cannot read as Chroma does (KD-56); - a
$containsor$not_containsof fewer than 3 characters (KD-57).
Regex("(?s).")finds the records with a document that is not empty,NotRegex("(?s).")the others, andRegexorNotRegexwith a short text, its special characters escaped, does whatContainsandNotContainswould. - a
Fixes
- An empty text, in a metadata key with a BM25 index or as the document, gets its empty BM25 vector, as with the Python client: the vector of the old text no longer stays, to be found by a search of the old text.
- A connection string whose
Endpointis not an http or https URI says so, instead of saying that the endpoint is missing. GetAsyncwith a limit of 0, from a collection client made by name, reads the collection first, so a collection that does not exist throws.- The error for a collection created with a schema whose space the server ignores says that Chroma 1.3.0 applies the space only when the schema also turns an index on or off.
Docs
- The documentation moved from the README to
docs/. docs/COMPATIBILITY.mddocuments the limits of Chroma 1.5.9 and Chroma Cloud verified with this client, verifies the known defects again on 10 October 2026, removes KD-2, KD-31 and KD-32, which that verification did not reproduce, and adds KD-52 to KD-57.UpdateAsyncandUpsertAsyncsay what a null document does on each server: Chroma 1.0.0 and later and Chroma Cloud keep the stored document, and every 0.x server deletes it.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.13.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.13.0
2.12.0
ChromaDotNet.Client and ChromaDotNet.Client.DependencyInjection 2.12.0, released together.
New
ChromaConfigurationOptions.WithUpsertStrategysays howUpdateAsyncandUpsertAsyncwrite the embeddings of records that exist. Chroma 1.0.21 to 1.5.9, installed on your own servers, may lose a record from the vector index after such a write (KD-49).Server, the default, writes as before.SkipUnchangedEmbeddingsupdates without the embedding a record whose embedding does not change, which does not touch the vector index. It costs a get of the records first, and a record whose embedding changes stays exposed.
- On the server,
RAYON_NUM_THREADS=1made Chroma 1.5.9 lose no record in our measurements, with upserts of records that exist about 5 times slower. The README says when to use which.
Fixes
- The creation of a collection sends
get_or_createonce.
Docs
docs/COMPATIBILITY.mdadds KD-48 to KD-51: wrong neighbors after records are deleted and written again on 1.0.0 to 1.0.5; records lost by the vector index after updates on 1.0.21 to 1.5.9; the space of a collection that 1.0.0 to 1.0.5 cannot tell; a record missing from a query after some upserts on 0.5.15, 0.5.16, 0.5.20 and 0.6.3, the versions tested.
NuGet: https://www.nuget.org/packages/ChromaDotNet.Client/2.12.0 and https://www.nuget.org/packages/ChromaDotNet.Client.DependencyInjection/2.12.0