From d656dbb0608e83c56b3e9801a91716990cb1228a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Oct 2026 08:31:21 +0000 Subject: [PATCH 1/4] Remove migrated .NET Chroma provider Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- dotnet/MEVD.slnx | 2 - dotnet/src/VectorData/Chroma/AssemblyInfo.cs | 6 - dotnet/src/VectorData/Chroma/Chroma.csproj | 25 -- dotnet/src/VectorData/Chroma/ChromaClient.cs | 210 ----------- .../Chroma/ChromaMemoryBuilderExtensions.cs | 55 --- .../VectorData/Chroma/ChromaMemoryStore.cs | 337 ------------------ .../Http/ApiSchema/ChromaCollectionModel.cs | 23 -- .../Http/ApiSchema/ChromaEmbeddingsModel.cs | 30 -- .../Http/ApiSchema/ChromaQueryResultModel.cs | 36 -- .../Internal/CreateCollectionRequest.cs | 34 -- .../Internal/DeleteCollectionRequest.cs | 31 -- .../Internal/DeleteEmbeddingsRequest.cs | 35 -- .../Internal/GetCollectionRequest.cs | 31 -- .../Internal/GetEmbeddingsRequest.cs | 39 -- .../Internal/ListCollectionsRequest.cs | 25 -- .../Internal/QueryEmbeddingsRequest.cs | 44 --- .../Internal/UpsertEmbeddingsRequest.cs | 44 --- dotnet/src/VectorData/Chroma/IChromaClient.cs | 82 ----- dotnet/src/VectorData/Chroma/README.md | 35 +- .../Chroma.UnitTests/Chroma.UnitTests.csproj | 38 -- .../ChromaMemoryStoreTests.cs | 335 ----------------- 21 files changed, 2 insertions(+), 1495 deletions(-) delete mode 100644 dotnet/src/VectorData/Chroma/AssemblyInfo.cs delete mode 100644 dotnet/src/VectorData/Chroma/Chroma.csproj delete mode 100644 dotnet/src/VectorData/Chroma/ChromaClient.cs delete mode 100644 dotnet/src/VectorData/Chroma/ChromaMemoryBuilderExtensions.cs delete mode 100644 dotnet/src/VectorData/Chroma/ChromaMemoryStore.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaCollectionModel.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaEmbeddingsModel.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaQueryResultModel.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/CreateCollectionRequest.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/DeleteCollectionRequest.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/DeleteEmbeddingsRequest.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/GetCollectionRequest.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/GetEmbeddingsRequest.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/ListCollectionsRequest.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/QueryEmbeddingsRequest.cs delete mode 100644 dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/UpsertEmbeddingsRequest.cs delete mode 100644 dotnet/src/VectorData/Chroma/IChromaClient.cs delete mode 100644 dotnet/test/VectorData/Chroma.UnitTests/Chroma.UnitTests.csproj delete mode 100644 dotnet/test/VectorData/Chroma.UnitTests/ChromaMemoryStoreTests.cs diff --git a/dotnet/MEVD.slnx b/dotnet/MEVD.slnx index 46b584ee589a..da53ff8db8d6 100644 --- a/dotnet/MEVD.slnx +++ b/dotnet/MEVD.slnx @@ -20,12 +20,10 @@ - - diff --git a/dotnet/src/VectorData/Chroma/AssemblyInfo.cs b/dotnet/src/VectorData/Chroma/AssemblyInfo.cs deleted file mode 100644 index d174fc92303c..000000000000 --- a/dotnet/src/VectorData/Chroma/AssemblyInfo.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Diagnostics.CodeAnalysis; - -// This assembly is currently experimental. -[assembly: Experimental("SKEXP0020")] diff --git a/dotnet/src/VectorData/Chroma/Chroma.csproj b/dotnet/src/VectorData/Chroma/Chroma.csproj deleted file mode 100644 index d283d594dcb3..000000000000 --- a/dotnet/src/VectorData/Chroma/Chroma.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - - Microsoft.SemanticKernel.Connectors.Chroma - $(AssemblyName) - net10.0;net8.0;netstandard2.0;net462 - alpha - - - - - - - - - Semantic Kernel - Chroma Connector - Chroma connector for Semantic Kernel plugins and semantic memory - - - - - - - \ No newline at end of file diff --git a/dotnet/src/VectorData/Chroma/ChromaClient.cs b/dotnet/src/VectorData/Chroma/ChromaClient.cs deleted file mode 100644 index 1dae0d846ab5..000000000000 --- a/dotnet/src/VectorData/Chroma/ChromaClient.cs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Runtime.CompilerServices; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.SemanticKernel.Http; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -/// -/// An implementation of a client for the Chroma Vector DB. This class is used to -/// create, delete, and get embeddings data from Chroma Vector DB instance. -/// -#pragma warning disable CA1001 // Types that own disposable fields should be disposable. Explanation - In this case, there is no need to dispose because either the NonDisposableHttpClientHandler or a custom HTTP client is being used. -public class ChromaClient : IChromaClient -#pragma warning restore CA1001 // Types that own disposable fields should be disposable. Explanation - In this case, there is no need to dispose because either the NonDisposableHttpClientHandler or a custom HTTP client is being used. -{ - /// - /// Initializes a new instance of the class. - /// - /// Chroma server endpoint URL. - /// The to use for logging. If null, no logging will be performed. - public ChromaClient(string endpoint, ILoggerFactory? loggerFactory = null) - { - Verify.NotNull(endpoint); - - this._httpClient = HttpClientProvider.GetHttpClient(); - this._endpoint = endpoint; - this._logger = loggerFactory?.CreateLogger(typeof(ChromaClient)) ?? NullLogger.Instance; - } - - /// - /// Initializes a new instance of the class. - /// - /// The instance used for making HTTP requests. - /// Chroma server endpoint URL. - /// The to use for logging. If null, no logging will be performed. - /// Occurs when doesn't have base address and endpoint parameter is not provided. - public ChromaClient(HttpClient httpClient, string? endpoint = null, ILoggerFactory? loggerFactory = null) - { - if (string.IsNullOrEmpty(httpClient.BaseAddress?.AbsoluteUri) && string.IsNullOrEmpty(endpoint)) - { - throw new ArgumentException($"The {nameof(httpClient)}.{nameof(HttpClient.BaseAddress)} and {nameof(endpoint)} are both null or empty. Please ensure at least one is provided."); - } - - this._httpClient = httpClient; - this._endpoint = endpoint; - this._logger = loggerFactory?.CreateLogger(typeof(ChromaClient)) ?? NullLogger.Instance; - } - - /// - public async Task CreateCollectionAsync(string collectionName, CancellationToken cancellationToken = default) - { - this._logger.LogDebug("Creating collection {0}", collectionName); - - using var request = CreateCollectionRequest.Create(collectionName).Build(); - - await this.ExecuteHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - } - - /// - public async Task GetCollectionAsync(string collectionName, CancellationToken cancellationToken = default) - { - this._logger.LogDebug("Getting collection {0}", collectionName); - - using var request = GetCollectionRequest.Create(collectionName).Build(); - - (HttpResponseMessage response, string responseContent) = await this.ExecuteHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - - var collection = JsonSerializer.Deserialize(responseContent); - - return collection; - } - - /// - public async Task DeleteCollectionAsync(string collectionName, CancellationToken cancellationToken = default) - { - this._logger.LogDebug("Deleting collection {0}", collectionName); - - using var request = DeleteCollectionRequest.Create(collectionName).Build(); - - await this.ExecuteHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - } - - /// - public async IAsyncEnumerable ListCollectionsAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) - { - this._logger.LogDebug("Listing collections"); - - using var request = ListCollectionsRequest.Create().Build(); - - (HttpResponseMessage response, string responseContent) = await this.ExecuteHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - - var collections = JsonSerializer.Deserialize>(responseContent); - - foreach (var collection in collections!) - { - yield return collection.Name; - } - } - - /// - public async Task UpsertEmbeddingsAsync(string collectionId, string[] ids, ReadOnlyMemory[] embeddings, object[]? metadatas = null, CancellationToken cancellationToken = default) - { - this._logger.LogDebug("Upserting embeddings to collection with id: {0}", collectionId); - - using var request = UpsertEmbeddingsRequest.Create(collectionId, ids, embeddings, metadatas).Build(); - - await this.ExecuteHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - } - - /// - public async Task GetEmbeddingsAsync(string collectionId, string[] ids, string[]? include = null, CancellationToken cancellationToken = default) - { - this._logger.LogDebug("Getting embeddings from collection with id: {0}", collectionId); - - using var request = GetEmbeddingsRequest.Create(collectionId, ids, include).Build(); - - (HttpResponseMessage response, string responseContent) = await this.ExecuteHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - - var embeddings = JsonSerializer.Deserialize(responseContent); - - return embeddings ?? new ChromaEmbeddingsModel(); - } - - /// - public async Task DeleteEmbeddingsAsync(string collectionId, string[] ids, CancellationToken cancellationToken = default) - { - this._logger.LogDebug("Deleting embeddings from collection with id: {0}", collectionId); - - using var request = DeleteEmbeddingsRequest.Create(collectionId, ids).Build(); - - await this.ExecuteHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - } - - /// - public async Task QueryEmbeddingsAsync(string collectionId, ReadOnlyMemory[] queryEmbeddings, int nResults, string[]? include = null, CancellationToken cancellationToken = default) - { - this._logger.LogDebug("Query embeddings in collection with id: {0}", collectionId); - - using var request = QueryEmbeddingsRequest.Create(collectionId, queryEmbeddings, nResults, include).Build(); - - (HttpResponseMessage response, string responseContent) = await this.ExecuteHttpRequestAsync(request, cancellationToken).ConfigureAwait(false); - - var queryResult = JsonSerializer.Deserialize(responseContent); - - return queryResult ?? new ChromaQueryResultModel(); - } - - #region private ================================================================================ - - private const string ApiRoute = "api/v1/"; - - private readonly ILogger _logger; - private readonly HttpClient _httpClient; - private readonly string? _endpoint = null; - - private async Task<(HttpResponseMessage response, string responseContent)> ExecuteHttpRequestAsync( - HttpRequestMessage request, - CancellationToken cancellationToken = default) - { - string endpoint = this._endpoint ?? this._httpClient.BaseAddress!.ToString(); - endpoint = this.SanitizeEndpoint(endpoint); - - string operationName = request.RequestUri!.ToString(); - - request.RequestUri = new Uri(new Uri(endpoint), operationName); - - HttpResponseMessage? response = null; - - string? responseContent = null; - - try - { - response = await this._httpClient.SendWithSuccessCheckAsync(request, cancellationToken).ConfigureAwait(false); - - responseContent = await response.Content.ReadAsStringWithExceptionMappingAsync(cancellationToken).ConfigureAwait(false); - } - catch (HttpOperationException e) - { - this._logger.LogError(e, "{Method} {Path} operation failed: {Message}, {Response}", request.Method.Method, operationName, e.Message, e.ResponseContent); - throw; - } - - return (response, responseContent); - } - - private string SanitizeEndpoint(string endpoint) - { - StringBuilder builder = new(endpoint); - - if (!endpoint.EndsWith("/", StringComparison.Ordinal)) - { - builder.Append('/'); - } - - builder.Append(ApiRoute); - - return builder.ToString(); - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/ChromaMemoryBuilderExtensions.cs b/dotnet/src/VectorData/Chroma/ChromaMemoryBuilderExtensions.cs deleted file mode 100644 index 67476dd72f19..000000000000 --- a/dotnet/src/VectorData/Chroma/ChromaMemoryBuilderExtensions.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Net.Http; -using Microsoft.SemanticKernel.Http; -using Microsoft.SemanticKernel.Memory; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -/// -/// Provides extension methods for the class to configure Chroma memory connector. -/// -public static class ChromaMemoryBuilderExtensions -{ - /// - /// Registers Chroma memory connector. - /// - /// The instance. - /// Chroma server endpoint URL. - /// Updated Memory builder including Chroma memory connector. - public static MemoryBuilder WithChromaMemoryStore(this MemoryBuilder builder, string endpoint) - { - builder.WithMemoryStore((loggerFactory, injectedClient) => - { - return new ChromaMemoryStore( - HttpClientProvider.GetHttpClient(injectedClient), - endpoint, - loggerFactory); - }); - - return builder; - } - - /// - /// Registers Chroma memory connector. - /// - /// The instance. - /// The instance used for making HTTP requests. - /// Chroma server endpoint URL. If not specified, the base address of the HTTP client is used. - /// Updated Memory builder including Chroma memory connector. - public static MemoryBuilder WithChromaMemoryStore( - this MemoryBuilder builder, - HttpClient httpClient, - string? endpoint = null) - { - builder.WithMemoryStore((loggerFactory, injectedClient) => - { - return new ChromaMemoryStore( - HttpClientProvider.GetHttpClient(httpClient ?? injectedClient), - endpoint, - loggerFactory); - }); - - return builder; - } -} diff --git a/dotnet/src/VectorData/Chroma/ChromaMemoryStore.cs b/dotnet/src/VectorData/Chroma/ChromaMemoryStore.cs deleted file mode 100644 index 958ebce207f3..000000000000 --- a/dotnet/src/VectorData/Chroma/ChromaMemoryStore.cs +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net.Http; -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.SemanticKernel.Memory; -using Microsoft.SemanticKernel.Text; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -/// -/// An implementation of for Chroma. -/// -public class ChromaMemoryStore : IMemoryStore -{ - /// - /// Initializes a new instance of the class. - /// - /// Chroma server endpoint URL. - /// The to use for logging. If null, no logging will be performed. - public ChromaMemoryStore(string endpoint, ILoggerFactory? loggerFactory = null) - : this(new ChromaClient(endpoint, loggerFactory), loggerFactory) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The instance used for making HTTP requests. - /// Chroma server endpoint URL. - /// The to use for logging. If null, no logging will be performed. - public ChromaMemoryStore(HttpClient httpClient, string? endpoint = null, ILoggerFactory? loggerFactory = null) - : this(new ChromaClient(httpClient, endpoint, loggerFactory), loggerFactory) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// Instance of implementation. - /// The to use for logging. If null, no logging will be performed. - public ChromaMemoryStore(IChromaClient client, ILoggerFactory? loggerFactory = null) - { - this._chromaClient = client; - this._logger = loggerFactory?.CreateLogger(typeof(ChromaMemoryStore)) ?? NullLogger.Instance; - } - - /// - public async Task CreateCollectionAsync(string collectionName, CancellationToken cancellationToken = default) - { - Verify.NotNullOrWhiteSpace(collectionName); - - await this._chromaClient.CreateCollectionAsync(collectionName, cancellationToken).ConfigureAwait(false); - } - - /// - public async Task DeleteCollectionAsync(string collectionName, CancellationToken cancellationToken = default) - { - Verify.NotNullOrWhiteSpace(collectionName); - - try - { - await this._chromaClient.DeleteCollectionAsync(collectionName, cancellationToken).ConfigureAwait(false); - } - catch (HttpOperationException e) when (VerifyCollectionDoesNotExistMessage(e.ResponseContent, collectionName)) - { - this._logger.LogError("Cannot delete non-existent collection {0}", collectionName); - throw new KernelException($"Cannot delete non-existent collection {collectionName}", e); - } - } - - /// - public async Task DoesCollectionExistAsync(string collectionName, CancellationToken cancellationToken = default) - { - Verify.NotNullOrWhiteSpace(collectionName); - - var collection = await this.GetCollectionAsync(collectionName, cancellationToken).ConfigureAwait(false); - - return collection is not null; - } - - /// - public async Task GetAsync(string collectionName, string key, bool withEmbedding = false, CancellationToken cancellationToken = default) - { - return await this.GetBatchAsync(collectionName, [key], withEmbedding, cancellationToken) - .FirstOrDefaultAsync(cancellationToken) - .ConfigureAwait(false); - } - - /// - public async IAsyncEnumerable GetBatchAsync(string collectionName, IEnumerable keys, bool withEmbeddings = false, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - Verify.NotNullOrWhiteSpace(collectionName); - - var collection = await this.GetCollectionOrThrowAsync(collectionName, cancellationToken).ConfigureAwait(false); - - var ids = keys.ToArray(); - var include = this.GetEmbeddingIncludeTypes(withEmbeddings: withEmbeddings); - - var embeddingsModel = await this._chromaClient.GetEmbeddingsAsync(collection.Id, ids, include, cancellationToken).ConfigureAwait(false); - - var recordCount = embeddingsModel.Ids?.Count ?? 0; - - for (var recordIndex = 0; recordIndex < recordCount; recordIndex++) - { - yield return this.GetMemoryRecordFromEmbeddingsModel(embeddingsModel, recordIndex); - } - } - - /// - public IAsyncEnumerable GetCollectionsAsync(CancellationToken cancellationToken = default) - { - return this._chromaClient.ListCollectionsAsync(cancellationToken); - } - - /// - public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, ReadOnlyMemory embedding, double minRelevanceScore = 0, bool withEmbedding = false, CancellationToken cancellationToken = default) - { - var results = this.GetNearestMatchesAsync( - collectionName, - embedding, - minRelevanceScore: minRelevanceScore, - limit: 1, - withEmbeddings: withEmbedding, - cancellationToken: cancellationToken); - - (MemoryRecord memoryRecord, double similarityScore) = await results.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); - - return (memoryRecord, similarityScore); - } - - /// - public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync(string collectionName, ReadOnlyMemory embedding, int limit, double minRelevanceScore = 0, bool withEmbeddings = false, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - Verify.NotNullOrWhiteSpace(collectionName); - - var collection = await this.GetCollectionOrThrowAsync(collectionName, cancellationToken).ConfigureAwait(false); - - ReadOnlyMemory[] queryEmbeddings = [embedding]; - var include = this.GetEmbeddingIncludeTypes(withEmbeddings: withEmbeddings, withDistances: true); - - var queryResultModel = await this._chromaClient.QueryEmbeddingsAsync(collection.Id, queryEmbeddings, limit, include, cancellationToken).ConfigureAwait(false); - - var recordCount = queryResultModel.Ids?.FirstOrDefault()?.Count ?? 0; - - for (var recordIndex = 0; recordIndex < recordCount; recordIndex++) - { - (MemoryRecord memoryRecord, double similarityScore) = this.GetMemoryRecordFromQueryResultModel(queryResultModel, recordIndex); - - if (similarityScore >= minRelevanceScore) - { - yield return (memoryRecord, similarityScore); - } - } - } - - /// - public async Task RemoveAsync(string collectionName, string key, CancellationToken cancellationToken = default) - { - await this.RemoveBatchAsync(collectionName, [key], cancellationToken).ConfigureAwait(false); - } - - /// - public async Task RemoveBatchAsync(string collectionName, IEnumerable keys, CancellationToken cancellationToken = default) - { - Verify.NotNullOrWhiteSpace(collectionName); - - var collection = await this.GetCollectionOrThrowAsync(collectionName, cancellationToken).ConfigureAwait(false); - - await this._chromaClient.DeleteEmbeddingsAsync(collection.Id, keys.ToArray(), cancellationToken).ConfigureAwait(false); - } - - /// - public async Task UpsertAsync(string collectionName, MemoryRecord record, CancellationToken cancellationToken = default) - { - Verify.NotNullOrWhiteSpace(collectionName); - - var key = await this.UpsertBatchAsync(collectionName, [record], cancellationToken) - .FirstOrDefaultAsync(cancellationToken) - .ConfigureAwait(false); - - return key ?? string.Empty; - } - - /// - public async IAsyncEnumerable UpsertBatchAsync(string collectionName, IEnumerable records, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - Verify.NotNullOrWhiteSpace(collectionName); - - var collection = await this.GetCollectionOrThrowAsync(collectionName, cancellationToken).ConfigureAwait(false); - - var recordsArray = records.ToArray(); - var recordsLength = recordsArray.Length; - - var ids = new string[recordsLength]; - var embeddings = new ReadOnlyMemory[recordsLength]; - var metadatas = new object[recordsLength]; - - for (var i = 0; i < recordsLength; i++) - { - ids[i] = recordsArray[i].Metadata.Id; - embeddings[i] = recordsArray[i].Embedding; - metadatas[i] = recordsArray[i].Metadata; - } - - await this._chromaClient.UpsertEmbeddingsAsync(collection.Id, ids, embeddings, metadatas, cancellationToken).ConfigureAwait(false); - - foreach (var record in recordsArray) - { - yield return record.Metadata.Id; - } - } - - #region private ================================================================================ - - private const string IncludeMetadatas = "metadatas"; - private const string IncludeEmbeddings = "embeddings"; - private const string IncludeDistances = "distances"; - - private readonly ILogger _logger; - private readonly IChromaClient _chromaClient; - private readonly List _defaultEmbeddingIncludeTypes = [IncludeMetadatas]; - - private async Task GetCollectionOrThrowAsync(string collectionName, CancellationToken cancellationToken) - { - return - await this.GetCollectionAsync(collectionName, cancellationToken).ConfigureAwait(false) ?? - throw new KernelException($"Collection {collectionName} does not exist"); - } - - private async Task GetCollectionAsync(string collectionName, CancellationToken cancellationToken) - { - try - { - return await this._chromaClient.GetCollectionAsync(collectionName, cancellationToken).ConfigureAwait(false); - } - catch (HttpOperationException e) when (VerifyCollectionDoesNotExistMessage(e.ResponseContent, collectionName)) - { - this._logger.LogDebug("Collection {0} does not exist", collectionName); - - return null; - } - } - - private string[] GetEmbeddingIncludeTypes(bool withEmbeddings = false, bool withDistances = false) - { - var includeList = new List(this._defaultEmbeddingIncludeTypes); - - if (withEmbeddings) - { - includeList.Add(IncludeEmbeddings); - } - - if (withDistances) - { - includeList.Add(IncludeDistances); - } - - return [.. includeList]; - } - - private MemoryRecord GetMemoryRecordFromEmbeddingsModel(ChromaEmbeddingsModel embeddingsModel, int recordIndex) - { - return this.GetMemoryRecordFromModel(embeddingsModel.Metadatas, embeddingsModel.Embeddings, embeddingsModel.Ids, recordIndex); - } - - private (MemoryRecord, double) GetMemoryRecordFromQueryResultModel(ChromaQueryResultModel queryResultModel, int recordIndex) - { - var ids = queryResultModel.Ids?.FirstOrDefault(); - var embeddings = queryResultModel.Embeddings?.FirstOrDefault(); - var metadatas = queryResultModel.Metadatas?.FirstOrDefault(); - var distances = queryResultModel.Distances?.FirstOrDefault(); - - var memoryRecord = this.GetMemoryRecordFromModel(metadatas, embeddings, ids, recordIndex); - var similarityScore = this.GetSimilarityScore(distances, recordIndex); - - return (memoryRecord, similarityScore); - } - - private MemoryRecord GetMemoryRecordFromModel(List>? metadatas, List? embeddings, List? ids, int recordIndex) - { - var metadata = this.GetMetadataForMemoryRecord(metadatas, recordIndex); - var embeddingsVector = this.GetEmbeddingForMemoryRecord(embeddings, recordIndex); - var key = ids?[recordIndex]; - - return MemoryRecord.FromMetadata( - metadata: metadata, - embedding: embeddingsVector, - key: key); - } - - private MemoryRecordMetadata GetMetadataForMemoryRecord(List>? metadatas, int recordIndex) - { - var serializedMetadata = metadatas is not null ? JsonSerializer.Serialize(metadatas[recordIndex], JsonOptionsCache.Default) : string.Empty; - - return - JsonSerializer.Deserialize(serializedMetadata, JsonOptionsCache.Default) ?? - throw new KernelException("Unable to deserialize memory record metadata."); - } - - private ReadOnlyMemory GetEmbeddingForMemoryRecord(List? embeddings, int recordIndex) - { - return embeddings is not null ? embeddings[recordIndex] : ReadOnlyMemory.Empty; - } - - private double GetSimilarityScore(List? distances, int recordIndex) - { - var similarityScore = distances is not null ? 1.0 / (1.0 + distances[recordIndex]) : default; - - if (similarityScore < 0) - { - similarityScore = 0; - } - - return similarityScore; - } - - /// - /// Checks if Chroma API error means that collection does not exist. - /// - /// Response content. - /// Collection name. - private static bool VerifyCollectionDoesNotExistMessage(string? responseContent, string collectionName) - { - return responseContent?.Contains(string.Format(CultureInfo.InvariantCulture, "Collection {0} does not exist", collectionName)) ?? false; - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaCollectionModel.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaCollectionModel.cs deleted file mode 100644 index 40a8b1f4c1e2..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaCollectionModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -/// -/// Chroma collection model. -/// -public class ChromaCollectionModel -{ - /// - /// Identifier of collection generated by Chroma. - /// - [JsonPropertyName("id")] - public string Id { get; set; } = string.Empty; - - /// - /// Collection name provided by client. - /// - [JsonPropertyName("name")] - public string Name { get; set; } = string.Empty; -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaEmbeddingsModel.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaEmbeddingsModel.cs deleted file mode 100644 index ea53cb9cd03f..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaEmbeddingsModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -/// -/// Chroma embeddings model. -/// -public class ChromaEmbeddingsModel -{ - /// - /// Embedding identifiers. - /// - [JsonPropertyName("ids")] - public List Ids { get; set; } = []; - - /// - /// Embedding vectors. - /// - [JsonPropertyName("embeddings")] - public List Embeddings { get; set; } = []; - - /// - /// Embedding metadatas. - /// - [JsonPropertyName("metadatas")] - public List> Metadatas { get; set; } = []; -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaQueryResultModel.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaQueryResultModel.cs deleted file mode 100644 index fddebeb8b063..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/ChromaQueryResultModel.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -/// -/// Chroma query result model. Contains result sets after search operation. -/// -public class ChromaQueryResultModel -{ - /// - /// List of embedding identifiers. - /// - [JsonPropertyName("ids")] - public List> Ids { get; set; } = []; - - /// - /// List of embedding vectors. - /// - [JsonPropertyName("embeddings")] - public List> Embeddings { get; set; } = []; - - /// - /// List of embedding metadatas. - /// - [JsonPropertyName("metadatas")] - public List>> Metadatas { get; set; } = []; - - /// - /// List of embedding distances. - /// - [JsonPropertyName("distances")] - public List> Distances { get; set; } = []; -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/CreateCollectionRequest.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/CreateCollectionRequest.cs deleted file mode 100644 index 40cf511ef629..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/CreateCollectionRequest.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Net.Http; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -internal sealed class CreateCollectionRequest -{ - [JsonPropertyName("name")] - public string CollectionName { get; set; } - - [JsonPropertyName("get_or_create")] - public bool GetOrCreate => true; - - public static CreateCollectionRequest Create(string collectionName) - { - return new CreateCollectionRequest(collectionName); - } - - public HttpRequestMessage Build() - { - return HttpRequest.CreatePostRequest("collections", this); - } - - #region private ================================================================================ - - private CreateCollectionRequest(string collectionName) - { - this.CollectionName = collectionName; - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/DeleteCollectionRequest.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/DeleteCollectionRequest.cs deleted file mode 100644 index 4e8ed2066aaf..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/DeleteCollectionRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Net.Http; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -internal sealed class DeleteCollectionRequest -{ - [JsonIgnore] - public string CollectionName { get; set; } - - public static DeleteCollectionRequest Create(string collectionName) - { - return new DeleteCollectionRequest(collectionName); - } - - public HttpRequestMessage Build() - { - return HttpRequest.CreateDeleteRequest($"collections/{this.CollectionName}"); - } - - #region private ================================================================================ - - private DeleteCollectionRequest(string collectionName) - { - this.CollectionName = collectionName; - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/DeleteEmbeddingsRequest.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/DeleteEmbeddingsRequest.cs deleted file mode 100644 index 4d2a20a1988d..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/DeleteEmbeddingsRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Net.Http; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -internal sealed class DeleteEmbeddingsRequest -{ - [JsonIgnore] - public string CollectionId { get; set; } - - [JsonPropertyName("ids")] - public string[] Ids { get; set; } - - public static DeleteEmbeddingsRequest Create(string collectionId, string[] ids) - { - return new DeleteEmbeddingsRequest(collectionId, ids); - } - - public HttpRequestMessage Build() - { - return HttpRequest.CreatePostRequest($"collections/{this.CollectionId}/delete", this); - } - - #region private ================================================================================ - - private DeleteEmbeddingsRequest(string collectionId, string[] ids) - { - this.CollectionId = collectionId; - this.Ids = ids; - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/GetCollectionRequest.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/GetCollectionRequest.cs deleted file mode 100644 index 08f9bb6d9482..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/GetCollectionRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Net.Http; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -internal sealed class GetCollectionRequest -{ - [JsonIgnore] - public string CollectionName { get; set; } - - public static GetCollectionRequest Create(string collectionName) - { - return new GetCollectionRequest(collectionName); - } - - public HttpRequestMessage Build() - { - return HttpRequest.CreateGetRequest($"collections/{this.CollectionName}"); - } - - #region private ================================================================================ - - private GetCollectionRequest(string collectionName) - { - this.CollectionName = collectionName; - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/GetEmbeddingsRequest.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/GetEmbeddingsRequest.cs deleted file mode 100644 index db1437ff5781..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/GetEmbeddingsRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Net.Http; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -internal sealed class GetEmbeddingsRequest -{ - [JsonIgnore] - public string CollectionId { get; set; } - - [JsonPropertyName("ids")] - public string[] Ids { get; set; } - - [JsonPropertyName("include")] - public string[]? Include { get; set; } - - public static GetEmbeddingsRequest Create(string collectionId, string[] ids, string[]? include = null) - { - return new GetEmbeddingsRequest(collectionId, ids, include); - } - - public HttpRequestMessage Build() - { - return HttpRequest.CreatePostRequest($"collections/{this.CollectionId}/get", this); - } - - #region private ================================================================================ - - private GetEmbeddingsRequest(string collectionId, string[] ids, string[]? include = null) - { - this.CollectionId = collectionId; - this.Ids = ids; - this.Include = include; - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/ListCollectionsRequest.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/ListCollectionsRequest.cs deleted file mode 100644 index 55275b910e8a..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/ListCollectionsRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System.Net.Http; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -internal sealed class ListCollectionsRequest -{ - public static ListCollectionsRequest Create() - { - return new ListCollectionsRequest(); - } - - public HttpRequestMessage Build() - { - return HttpRequest.CreateGetRequest("collections"); - } - - #region private ================================================================================ - - private ListCollectionsRequest() - { } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/QueryEmbeddingsRequest.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/QueryEmbeddingsRequest.cs deleted file mode 100644 index 64e0cec50056..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/QueryEmbeddingsRequest.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Net.Http; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -internal sealed class QueryEmbeddingsRequest -{ - [JsonIgnore] - public string CollectionId { get; set; } - - [JsonPropertyName("query_embeddings")] - public ReadOnlyMemory[] QueryEmbeddings { get; set; } - - [JsonPropertyName("n_results")] - public int NResults { get; set; } - - [JsonPropertyName("include")] - public string[]? Include { get; set; } - - public static QueryEmbeddingsRequest Create(string collectionId, ReadOnlyMemory[] queryEmbeddings, int nResults, string[]? include = null) - { - return new QueryEmbeddingsRequest(collectionId, queryEmbeddings, nResults, include); - } - - public HttpRequestMessage Build() - { - return HttpRequest.CreatePostRequest($"collections/{this.CollectionId}/query", this); - } - - #region private ================================================================================ - - private QueryEmbeddingsRequest(string collectionId, ReadOnlyMemory[] queryEmbeddings, int nResults, string[]? include = null) - { - this.CollectionId = collectionId; - this.QueryEmbeddings = queryEmbeddings; - this.NResults = nResults; - this.Include = include; - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/UpsertEmbeddingsRequest.cs b/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/UpsertEmbeddingsRequest.cs deleted file mode 100644 index cba49003d7af..000000000000 --- a/dotnet/src/VectorData/Chroma/Http/ApiSchema/Internal/UpsertEmbeddingsRequest.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Net.Http; -using System.Text.Json.Serialization; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -internal sealed class UpsertEmbeddingsRequest -{ - [JsonIgnore] - public string CollectionId { get; set; } - - [JsonPropertyName("ids")] - public string[] Ids { get; set; } - - [JsonPropertyName("embeddings")] - public ReadOnlyMemory[] Embeddings { get; set; } - - [JsonPropertyName("metadatas")] - public object[]? Metadatas { get; set; } - - public static UpsertEmbeddingsRequest Create(string collectionId, string[] ids, ReadOnlyMemory[] embeddings, object[]? metadatas = null) - { - return new UpsertEmbeddingsRequest(collectionId, ids, embeddings, metadatas); - } - - public HttpRequestMessage Build() - { - return HttpRequest.CreatePostRequest($"collections/{this.CollectionId}/upsert", this); - } - - #region private ================================================================================ - - private UpsertEmbeddingsRequest(string collectionId, string[] ids, ReadOnlyMemory[] embeddings, object[]? metadatas = null) - { - this.CollectionId = collectionId; - this.Ids = ids; - this.Embeddings = embeddings; - this.Metadatas = metadatas; - } - - #endregion -} diff --git a/dotnet/src/VectorData/Chroma/IChromaClient.cs b/dotnet/src/VectorData/Chroma/IChromaClient.cs deleted file mode 100644 index fbecb112acf2..000000000000 --- a/dotnet/src/VectorData/Chroma/IChromaClient.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.SemanticKernel.Connectors.Chroma; - -/// -/// Interface for client to make requests to Chroma API. -/// -public interface IChromaClient -{ - /// - /// Creates Chroma collection. - /// - /// Collection name. - /// The to monitor for cancellation requests. The default is . - Task CreateCollectionAsync(string collectionName, CancellationToken cancellationToken = default); - - /// - /// Returns collection model instance by name. - /// - /// Collection name. - /// The to monitor for cancellation requests. The default is . - /// Instance of model. - Task GetCollectionAsync(string collectionName, CancellationToken cancellationToken = default); - - /// - /// Removes collection by name. - /// - /// Collection name. - /// The to monitor for cancellation requests. The default is . - Task DeleteCollectionAsync(string collectionName, CancellationToken cancellationToken = default); - - /// - /// Returns all collection names. - /// - /// The to monitor for cancellation requests. The default is . - /// An asynchronous list of collection names. - IAsyncEnumerable ListCollectionsAsync(CancellationToken cancellationToken = default); - - /// - /// Upserts embedding to specified collection. - /// - /// Collection identifier generated by Chroma. - /// Array of embedding identifiers. - /// Array of embedding vectors. - /// Array of embedding metadatas. - /// The to monitor for cancellation requests. The default is . - Task UpsertEmbeddingsAsync(string collectionId, string[] ids, ReadOnlyMemory[] embeddings, object[]? metadatas = null, CancellationToken cancellationToken = default); - - /// - /// Returns embeddings from specified collection. - /// - /// Collection identifier generated by Chroma. - /// Array of embedding identifiers. - /// Array of entities to include in response (e.g. "embeddings", "metadatas" "documents"). For more information see: https://github.com/chroma-core/chroma/blob/main/chromadb/api/types.py - /// The to monitor for cancellation requests. The default is . - /// Instance of model. - Task GetEmbeddingsAsync(string collectionId, string[] ids, string[]? include = null, CancellationToken cancellationToken = default); - - /// - /// Removes embeddings from specified collection. - /// - /// Collection identifier generated by Chroma. - /// Array of embedding identifiers. - /// The to monitor for cancellation requests. The default is . - Task DeleteEmbeddingsAsync(string collectionId, string[] ids, CancellationToken cancellationToken = default); - - /// - /// Searches nearest embeddings by distance in specified collection. - /// - /// Collection identifier generated by Chroma. - /// Embeddings to search for. - /// Number of results to return. - /// Array of entities to include in response (e.g. "embeddings", "metadatas" "documents"). For more information see: https://github.com/chroma-core/chroma/blob/main/chromadb/api/types.py - /// The to monitor for cancellation requests. The default is . - /// Instance of model. - Task QueryEmbeddingsAsync(string collectionId, ReadOnlyMemory[] queryEmbeddings, int nResults, string[]? include = null, CancellationToken cancellationToken = default); -} diff --git a/dotnet/src/VectorData/Chroma/README.md b/dotnet/src/VectorData/Chroma/README.md index 47b9037200c6..f67abcc1ee5b 100644 --- a/dotnet/src/VectorData/Chroma/README.md +++ b/dotnet/src/VectorData/Chroma/README.md @@ -1,34 +1,3 @@ -# Microsoft.SemanticKernel.Connectors.Chroma +# The Chroma connector has moved -This assembly contains implementation of Semantic Kernel Memory Store using [Chroma](https://www.trychroma.com/), open-source embedding database. - -**Note:** Chroma connector is verified using Chroma version **0.4.10**. Any higher versions may introduce incompatibility. - -## Quickstart using local Chroma server - -1. Clone Chroma: - -```bash -git clone https://github.com/chroma-core/chroma.git -cd chroma -``` - -2. Run local Chroma server with Docker within Chroma repository root: - -```bash -docker-compose up -d --build -``` - -3. Use Semantic Kernel with Chroma, using server local endpoint `http://localhost:8000`: - -```csharp -const string endpoint = "http://localhost:8000"; - -var memoryWithChroma = new MemoryBuilder() - .WithChromaMemoryStore(endpoint) - .WithLoggerFactory(loggerFactory) - .WithOpenAITextEmbeddingGeneration("text-embedding-ada-002", apiKey) - .Build(); - -var memoryPlugin = kernel.ImportPluginFromObject(new TextMemoryPlugin(memoryWithChroma)); -``` +The code for the Chroma connector can now be found in the [CommunityToolkit/AI repository](https://github.com/CommunityToolkit/AI/tree/main/MEVD/src/Chroma). diff --git a/dotnet/test/VectorData/Chroma.UnitTests/Chroma.UnitTests.csproj b/dotnet/test/VectorData/Chroma.UnitTests/Chroma.UnitTests.csproj deleted file mode 100644 index c1458d3f560d..000000000000 --- a/dotnet/test/VectorData/Chroma.UnitTests/Chroma.UnitTests.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - SemanticKernel.Connectors.Chroma.UnitTests - SemanticKernel.Connectors.Chroma.UnitTests - net10.0 - true - enable - disable - false - $(NoWarn);CA2007,CA1806,CA1869,CA1861,IDE0300,VSTHRD111,SKEXP0001,SKEXP0010,SKEXP0050 - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - - - - diff --git a/dotnet/test/VectorData/Chroma.UnitTests/ChromaMemoryStoreTests.cs b/dotnet/test/VectorData/Chroma.UnitTests/ChromaMemoryStoreTests.cs deleted file mode 100644 index bf2da33d900c..000000000000 --- a/dotnet/test/VectorData/Chroma.UnitTests/ChromaMemoryStoreTests.cs +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Net.Http; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.SemanticKernel; -using Microsoft.SemanticKernel.Connectors.Chroma; -using Microsoft.SemanticKernel.Memory; -using Moq; -using Xunit; - -namespace SemanticKernel.Connectors.UnitTests.Chroma; - -/// -/// Unit tests for class. -/// -[Experimental("SKEXP0020")] -public sealed class ChromaMemoryStoreTests : IDisposable -{ - private const string CollectionId = "fake-collection-id"; - private const string CollectionName = "fake-collection-name"; - - private readonly HttpMessageHandlerStub _messageHandlerStub; - private readonly HttpClient _httpClient; - private readonly Mock _chromaClientMock; - - public ChromaMemoryStoreTests() - { - this._messageHandlerStub = new HttpMessageHandlerStub(); - this._httpClient = this.GetHttpClientStub(); - this._chromaClientMock = new Mock(); - - this._chromaClientMock - .Setup(client => client.GetCollectionAsync(CollectionName, CancellationToken.None)) - .ReturnsAsync(new ChromaCollectionModel { Id = CollectionId, Name = CollectionName }); - } - - [Fact] - public async Task ItUsesProvidedEndpointFromConstructorAsync() - { - // Arrange - const string Endpoint = "https://fake-random-test-host/fake-path/"; - var store = new ChromaMemoryStore(this._httpClient, Endpoint); - - // Act - await store.GetAsync("fake-collection", "fake-key"); - - // Assert - Assert.StartsWith(Endpoint, this._messageHandlerStub.RequestUri?.AbsoluteUri, StringComparison.OrdinalIgnoreCase); - } - - [Fact] - public async Task ItUsesBaseAddressFromHttpClientAsync() - { - // Arrange - const string BaseAddress = "https://fake-random-test-host/fake-path/"; - - using var httpClient = this.GetHttpClientStub(); - httpClient.BaseAddress = new Uri(BaseAddress); - - var store = new ChromaMemoryStore(httpClient); - - // Act - await store.GetAsync("fake-collection", "fake-key"); - - // Assert - Assert.StartsWith(BaseAddress, this._messageHandlerStub.RequestUri?.AbsoluteUri, StringComparison.OrdinalIgnoreCase); - } - - [Fact] - public async Task ItCanCreateCollectionAsync() - { - // Arrange - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - await store.CreateCollectionAsync(CollectionName); - - // Assert - this._chromaClientMock.Verify(client => client.CreateCollectionAsync(CollectionName, CancellationToken.None), Times.Once()); - } - - [Fact] - public async Task ItCanDeleteCollectionAsync() - { - // Arrange - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - await store.DeleteCollectionAsync(CollectionName); - - // Assert - this._chromaClientMock.Verify(client => client.DeleteCollectionAsync(CollectionName, CancellationToken.None), Times.Once()); - } - - [Fact] - public async Task ItThrowsExceptionOnNonExistentCollectionDeletionAsync() - { - // Arrange - const string CollectionName = "non-existent-collection"; - const string CollectionDoesNotExistErrorMessage = $"Collection {CollectionName} does not exist"; - const string ExpectedExceptionMessage = $"Cannot delete non-existent collection {CollectionName}"; - - this._chromaClientMock - .Setup(client => client.DeleteCollectionAsync(CollectionName, CancellationToken.None)) - .Throws(new HttpOperationException { ResponseContent = CollectionDoesNotExistErrorMessage }); - - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - var exception = await Record.ExceptionAsync(() => store.DeleteCollectionAsync(CollectionName)); - - // Assert - Assert.IsType(exception); - Assert.Equal(ExpectedExceptionMessage, exception.Message); - } - - [Fact] - public async Task ItReturnsTrueWhenCollectionExistsAsync() - { - // Arrange - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - var doesCollectionExist = await store.DoesCollectionExistAsync(CollectionName); - - // Assert - Assert.True(doesCollectionExist); - } - - [Fact] - public async Task ItReturnsFalseWhenCollectionDoesNotExistAsync() - { - // Arrange - const string CollectionName = "non-existent-collection"; - const string CollectionDoesNotExistErrorMessage = $"Collection {CollectionName} does not exist"; - - this._chromaClientMock - .Setup(client => client.GetCollectionAsync(CollectionName, CancellationToken.None)) - .Throws(new HttpOperationException { ResponseContent = CollectionDoesNotExistErrorMessage }); - - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - var doesCollectionExist = await store.DoesCollectionExistAsync(CollectionName); - - // Assert - Assert.False(doesCollectionExist); - } - - [Fact] - public async Task ItCanGetMemoryRecordFromCollectionAsync() - { - // Arrange - var expectedMemoryRecord = this.GetRandomMemoryRecord(); - var embeddingsModel = this.GetEmbeddingsModelFromMemoryRecord(expectedMemoryRecord); - - this._chromaClientMock - .Setup(client => client.GetEmbeddingsAsync(CollectionId, new[] { expectedMemoryRecord.Key }, It.IsAny(), CancellationToken.None)) - .ReturnsAsync(embeddingsModel); - - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - var actualMemoryRecord = await store.GetAsync(CollectionName, expectedMemoryRecord.Key, withEmbedding: true); - - // Assert - Assert.NotNull(actualMemoryRecord); - this.AssertMemoryRecordEqual(expectedMemoryRecord, actualMemoryRecord); - } - - [Fact] - public async Task ItReturnsNullWhenMemoryRecordDoesNotExistAsync() - { - // Arrange - const string MemoryRecordKey = "fake-record-key"; - - this._chromaClientMock - .Setup(client => client.GetEmbeddingsAsync(CollectionId, new[] { MemoryRecordKey }, It.IsAny(), CancellationToken.None)) - .ReturnsAsync(new ChromaEmbeddingsModel()); - - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - var actualMemoryRecord = await store.GetAsync(CollectionName, MemoryRecordKey, withEmbedding: true); - - // Assert - Assert.Null(actualMemoryRecord); - } - - [Fact] - public async Task ItThrowsExceptionOnGettingMemoryRecordFromNonExistingCollectionAsync() - { - // Arrange - const string CollectionName = "non-existent-collection"; - const string MemoryRecordKey = "fake-record-key"; - const string CollectionDoesNotExistErrorMessage = $"Collection {CollectionName} does not exist"; - - this._chromaClientMock - .Setup(client => client.GetCollectionAsync(CollectionName, CancellationToken.None)) - .Throws(new KernelException(CollectionDoesNotExistErrorMessage)); - - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - var exception = await Record.ExceptionAsync(() => store.GetAsync(CollectionName, MemoryRecordKey, withEmbedding: true)); - - // Assert - Assert.IsType(exception); - Assert.Equal(CollectionDoesNotExistErrorMessage, exception.Message); - } - - [Fact] - public async Task ItCanGetMemoryRecordBatchFromCollectionAsync() - { - // Arrange - var memoryRecord1 = this.GetRandomMemoryRecord(); - var memoryRecord2 = this.GetRandomMemoryRecord(); - var memoryRecord3 = this.GetRandomMemoryRecord(); - - MemoryRecord[] expectedMemoryRecords = [memoryRecord1, memoryRecord2, memoryRecord3]; - var memoryRecordKeys = expectedMemoryRecords.Select(l => l.Key).ToArray(); - - var embeddingsModel = this.GetEmbeddingsModelFromMemoryRecords(expectedMemoryRecords); - - this._chromaClientMock - .Setup(client => client.GetEmbeddingsAsync(CollectionId, memoryRecordKeys, It.IsAny(), CancellationToken.None)) - .ReturnsAsync(embeddingsModel); - - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - var actualMemoryRecords = await store.GetBatchAsync(CollectionName, memoryRecordKeys, withEmbeddings: true).ToListAsync(); - - // Assert - Assert.Equal(expectedMemoryRecords.Length, actualMemoryRecords.Count); - - for (var i = 0; i < expectedMemoryRecords.Length; i++) - { - this.AssertMemoryRecordEqual(expectedMemoryRecords[i], actualMemoryRecords[i]); - } - } - - [Fact] - public async Task ItCanReturnCollectionsAsync() - { - // Arrange - var expectedCollections = new List { "fake-collection-1", "fake-collection-2", "fake-collection-3" }; - - this._chromaClientMock - .Setup(client => client.ListCollectionsAsync(CancellationToken.None)) - .Returns(expectedCollections.ToAsyncEnumerable()); - - var store = new ChromaMemoryStore(this._chromaClientMock.Object); - - // Act - var actualCollections = await store.GetCollectionsAsync().ToListAsync(); - - // Assert - Assert.Equal(expectedCollections.Count, actualCollections.Count); - - for (var i = 0; i < expectedCollections.Count; i++) - { - Assert.Equal(expectedCollections[i], actualCollections[i]); - } - } - - public void Dispose() - { - this._httpClient.Dispose(); - this._messageHandlerStub.Dispose(); - } - - #region private ================================================================================ - - private void AssertMemoryRecordEqual(MemoryRecord expectedRecord, MemoryRecord actualRecord) - { - Assert.Equal(expectedRecord.Key, actualRecord.Key); - Assert.True(expectedRecord.Embedding.Span.SequenceEqual(actualRecord.Embedding.Span)); - Assert.Equal(expectedRecord.Metadata.Id, actualRecord.Metadata.Id); - Assert.Equal(expectedRecord.Metadata.Text, actualRecord.Metadata.Text); - Assert.Equal(expectedRecord.Metadata.Description, actualRecord.Metadata.Description); - Assert.Equal(expectedRecord.Metadata.AdditionalMetadata, actualRecord.Metadata.AdditionalMetadata); - Assert.Equal(expectedRecord.Metadata.IsReference, actualRecord.Metadata.IsReference); - Assert.Equal(expectedRecord.Metadata.ExternalSourceName, actualRecord.Metadata.ExternalSourceName); - } - - private HttpClient GetHttpClientStub() - { - return new HttpClient(this._messageHandlerStub, false); - } - - private MemoryRecord GetRandomMemoryRecord(ReadOnlyMemory? embedding = null) - { - var id = Guid.NewGuid().ToString(); - var memoryEmbedding = embedding ?? new[] { 1f, 3f, 5f }; - - return MemoryRecord.LocalRecord( - id: id, - text: "text-" + Guid.NewGuid().ToString(), - description: "description-" + Guid.NewGuid().ToString(), - embedding: memoryEmbedding, - additionalMetadata: "metadata-" + Guid.NewGuid().ToString(), - key: id); - } - - private Dictionary GetEmbeddingMetadataFromMemoryRecord(MemoryRecord memoryRecord) - { - var serialized = JsonSerializer.Serialize(memoryRecord.Metadata); - return JsonSerializer.Deserialize>(serialized)!; - } - - private ChromaEmbeddingsModel GetEmbeddingsModelFromMemoryRecords(MemoryRecord[] memoryRecords) - { - var embeddingsModel = new ChromaEmbeddingsModel(); - - embeddingsModel.Ids.AddRange(memoryRecords.Select(l => l.Key)); - embeddingsModel.Embeddings.AddRange(memoryRecords.Select(l => l.Embedding.ToArray())); - embeddingsModel.Metadatas.AddRange(memoryRecords.Select(this.GetEmbeddingMetadataFromMemoryRecord)); - - return embeddingsModel; - } - - private ChromaEmbeddingsModel GetEmbeddingsModelFromMemoryRecord(MemoryRecord memoryRecord) - { - return this.GetEmbeddingsModelFromMemoryRecords([memoryRecord]); - } - - #endregion -} From 69ac1a3b89430c5530cc2766d00ee4614084316d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Oct 2026 08:31:55 +0000 Subject: [PATCH 2/4] Match Chroma migration notice style Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- dotnet/src/VectorData/Chroma/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dotnet/src/VectorData/Chroma/README.md b/dotnet/src/VectorData/Chroma/README.md index f67abcc1ee5b..4f456e46c3d6 100644 --- a/dotnet/src/VectorData/Chroma/README.md +++ b/dotnet/src/VectorData/Chroma/README.md @@ -1,3 +1 @@ -# The Chroma connector has moved - -The code for the Chroma connector can now be found in the [CommunityToolkit/AI repository](https://github.com/CommunityToolkit/AI/tree/main/MEVD/src/Chroma). +The code for Microsoft.SemanticKernel.Connectors.Chroma can now be found in the [CommunityToolkit/AI repository](https://github.com/CommunityToolkit/AI/tree/main/MEVD/src/Chroma) From c6fbffd79a8309c23e67790b3959246adb6dae74 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Oct 2026 08:48:25 +0000 Subject: [PATCH 3/4] Remove Chroma from experimental feature tracking Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- dotnet/docs/EXPERIMENTS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/dotnet/docs/EXPERIMENTS.md b/dotnet/docs/EXPERIMENTS.md index e51bfd54b04a..34f61029ee10 100644 --- a/dotnet/docs/EXPERIMENTS.md +++ b/dotnet/docs/EXPERIMENTS.md @@ -47,7 +47,6 @@ You can use the following diagnostic IDs to ignore warnings or errors for a part | SKEXP0010 | OpenAI file service | | | | | | | | | | SKEXP0020 | Azure AI Search memory connector | -| SKEXP0020 | Chroma memory connector | | SKEXP0020 | DuckDB memory connector | | SKEXP0020 | Kusto memory connector | | SKEXP0020 | Milvus memory connector | From 64c9921f9397d068e38d725b770d37ae6b4ba565 Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Tue, 13 Oct 2026 13:29:23 +0200 Subject: [PATCH 4/4] Apply suggestion from @westey-m Co-authored-by: westey <164392973+westey-m@users.noreply.github.com> --- dotnet/src/VectorData/Chroma/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/src/VectorData/Chroma/README.md b/dotnet/src/VectorData/Chroma/README.md index 4f456e46c3d6..2e862fa4adff 100644 --- a/dotnet/src/VectorData/Chroma/README.md +++ b/dotnet/src/VectorData/Chroma/README.md @@ -1 +1 @@ -The code for Microsoft.SemanticKernel.Connectors.Chroma can now be found in the [CommunityToolkit/AI repository](https://github.com/CommunityToolkit/AI/tree/main/MEVD/src/Chroma) +Microsoft.SemanticKernel.Connectors.Chroma and the MemoryStore abstraction has been deprecated. Please use the new Microsoft.Extensions.VectorData abstractions with the Chroma implementation that can be found in the [CommunityToolkit/AI repository](https://github.com/CommunityToolkit/AI/tree/main/MEVD/src/Chroma)