diff --git a/dotnet/src/Connectors/Connectors.AzureAISearch.UnitTests/AzureAISearchKernelBuilderExtensionsTests.cs b/dotnet/src/Connectors/Connectors.AzureAISearch.UnitTests/AzureAISearchKernelBuilderExtensionsTests.cs index 740c3898ce03..1f1308a227d1 100644 --- a/dotnet/src/Connectors/Connectors.AzureAISearch.UnitTests/AzureAISearchKernelBuilderExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.AzureAISearch.UnitTests/AzureAISearchKernelBuilderExtensionsTests.cs @@ -58,6 +58,39 @@ public void AddVectorStoreWithUriAndTokenCredsRegistersClass() this.AssertVectorStoreCreated(); } + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + this._kernelBuilder.Services.AddSingleton(Mock.Of()); + + // Act. + this._kernelBuilder.AddAzureAISearchVectorStoreRecordCollection("testcollection"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithUriAndCredsRegistersClass() + { + // Act. + this._kernelBuilder.AddAzureAISearchVectorStoreRecordCollection("testcollection", new Uri("https://localhost"), new AzureKeyCredential("fakeKey")); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithUriAndTokenCredsRegistersClass() + { + // Act. + this._kernelBuilder.AddAzureAISearchVectorStoreRecordCollection("testcollection", new Uri("https://localhost"), Mock.Of()); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + private void AssertVectorStoreCreated() { var kernel = this._kernelBuilder.Build(); @@ -65,4 +98,29 @@ private void AssertVectorStoreCreated() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + private void AssertVectorStoreRecordCollectionCreated() + { + var kernel = this._kernelBuilder.Build(); + + var collection = kernel.Services.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = kernel.Services.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + + var vectorizableSearch = kernel.Services.GetRequiredService>(); + Assert.NotNull(vectorizableSearch); + Assert.IsType>(vectorizableSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + } } diff --git a/dotnet/src/Connectors/Connectors.AzureAISearch.UnitTests/AzureAISearchServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.AzureAISearch.UnitTests/AzureAISearchServiceCollectionExtensionsTests.cs index e021d62c8159..5739b307a9ac 100644 --- a/dotnet/src/Connectors/Connectors.AzureAISearch.UnitTests/AzureAISearchServiceCollectionExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.AzureAISearch.UnitTests/AzureAISearchServiceCollectionExtensionsTests.cs @@ -58,6 +58,39 @@ public void AddVectorStoreWithUriAndTokenCredsRegistersClass() this.AssertVectorStoreCreated(); } + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + this._serviceCollection.AddSingleton(Mock.Of()); + + // Act. + this._serviceCollection.AddAzureAISearchVectorStoreRecordCollection("testcollection"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithUriAndCredsRegistersClass() + { + // Act. + this._serviceCollection.AddAzureAISearchVectorStoreRecordCollection("testcollection", new Uri("https://localhost"), new AzureKeyCredential("fakeKey")); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithUriAndTokenCredsRegistersClass() + { + // Act. + this._serviceCollection.AddAzureAISearchVectorStoreRecordCollection("testcollection", new Uri("https://localhost"), Mock.Of()); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + private void AssertVectorStoreCreated() { var serviceProvider = this._serviceCollection.BuildServiceProvider(); @@ -65,4 +98,29 @@ private void AssertVectorStoreCreated() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + private void AssertVectorStoreRecordCollectionCreated() + { + var serviceProvider = this._serviceCollection.BuildServiceProvider(); + + var collection = serviceProvider.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = serviceProvider.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + + var vectorizableSearch = serviceProvider.GetRequiredService>(); + Assert.NotNull(vectorizableSearch); + Assert.IsType>(vectorizableSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + } } diff --git a/dotnet/src/Connectors/Connectors.AzureCosmosDBMongoDB.UnitTests/AzureCosmosDBMongoDBKernelBuilderExtensionsTests.cs b/dotnet/src/Connectors/Connectors.AzureCosmosDBMongoDB.UnitTests/AzureCosmosDBMongoDBKernelBuilderExtensionsTests.cs index 5bdac2ee460b..06a57a280a99 100644 --- a/dotnet/src/Connectors/Connectors.AzureCosmosDBMongoDB.UnitTests/AzureCosmosDBMongoDBKernelBuilderExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.AzureCosmosDBMongoDB.UnitTests/AzureCosmosDBMongoDBKernelBuilderExtensionsTests.cs @@ -52,4 +52,48 @@ public void AddVectorStoreWithConnectionStringRegistersClass() var database = (IMongoDatabase)vectorStore.GetType().GetField("_mongoDatabase", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(vectorStore)!; Assert.Equal(HttpHeaderConstant.Values.UserAgent, database.Client.Settings.ApplicationName); } + + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange + this._kernelBuilder.Services.AddSingleton(Mock.Of()); + + // Act + this._kernelBuilder.AddAzureCosmosDBMongoDBVectorStoreRecordCollection("testcollection"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithConnectionStringRegistersClass() + { + // Act + this._kernelBuilder.AddAzureCosmosDBMongoDBVectorStoreRecordCollection("testcollection", "mongodb://localhost:27017", "mydb"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + private void AssertVectorStoreRecordCollectionCreated() + { + var kernel = this._kernelBuilder.Build(); + + var collection = kernel.Services.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = kernel.Services.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + } } diff --git a/dotnet/src/Connectors/Connectors.AzureCosmosDBMongoDB.UnitTests/AzureCosmosDBMongoDBServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.AzureCosmosDBMongoDB.UnitTests/AzureCosmosDBMongoDBServiceCollectionExtensionsTests.cs index 2a365d44e650..062f2c7565a1 100644 --- a/dotnet/src/Connectors/Connectors.AzureCosmosDBMongoDB.UnitTests/AzureCosmosDBMongoDBServiceCollectionExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.AzureCosmosDBMongoDB.UnitTests/AzureCosmosDBMongoDBServiceCollectionExtensionsTests.cs @@ -52,4 +52,48 @@ public void AddVectorStoreWithConnectionStringRegistersClass() var database = (IMongoDatabase)vectorStore.GetType().GetField("_mongoDatabase", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(vectorStore)!; Assert.Equal(HttpHeaderConstant.Values.UserAgent, database.Client.Settings.ApplicationName); } + + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange + this._serviceCollection.AddSingleton(Mock.Of()); + + // Act + this._serviceCollection.AddAzureCosmosDBMongoDBVectorStoreRecordCollection("testcollection"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithConnectionStringRegistersClass() + { + // Act + this._serviceCollection.AddAzureCosmosDBMongoDBVectorStoreRecordCollection("testcollection", "mongodb://localhost:27017", "mydb"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + private void AssertVectorStoreRecordCollectionCreated() + { + var serviceProvider = this._serviceCollection.BuildServiceProvider(); + + var collection = serviceProvider.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = serviceProvider.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + } } diff --git a/dotnet/src/Connectors/Connectors.AzureCosmosDBNoSQL.UnitTests/AzureCosmosDBNoSQLKernelBuilderExtensionsTests.cs b/dotnet/src/Connectors/Connectors.AzureCosmosDBNoSQL.UnitTests/AzureCosmosDBNoSQLKernelBuilderExtensionsTests.cs index e348fe136d58..947f962a4607 100644 --- a/dotnet/src/Connectors/Connectors.AzureCosmosDBNoSQL.UnitTests/AzureCosmosDBNoSQLKernelBuilderExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.AzureCosmosDBNoSQL.UnitTests/AzureCosmosDBNoSQLKernelBuilderExtensionsTests.cs @@ -51,4 +51,47 @@ public void AddVectorStoreWithConnectionStringRegistersClass() var database = (Database)vectorStore.GetType().GetField("_database", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(vectorStore)!; Assert.Equal(HttpHeaderConstant.Values.UserAgent, database.Client.ClientOptions.ApplicationName); } + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange + this._kernelBuilder.Services.AddSingleton(Mock.Of()); + + // Act + this._kernelBuilder.AddAzureCosmosDBNoSQLVectorStoreRecordCollection("testcollection"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithConnectionStringRegistersClass() + { + // Act + this._kernelBuilder.AddAzureCosmosDBNoSQLVectorStoreRecordCollection("testcollection", "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=mock;", "mydb"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + private void AssertVectorStoreRecordCollectionCreated() + { + var kernel = this._kernelBuilder.Build(); + + var collection = kernel.Services.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = kernel.Services.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + } } diff --git a/dotnet/src/Connectors/Connectors.AzureCosmosDBNoSQL.UnitTests/AzureCosmosDBNoSQLServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.AzureCosmosDBNoSQL.UnitTests/AzureCosmosDBNoSQLServiceCollectionExtensionsTests.cs index fd2f822bd934..8785f8a81955 100644 --- a/dotnet/src/Connectors/Connectors.AzureCosmosDBNoSQL.UnitTests/AzureCosmosDBNoSQLServiceCollectionExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.AzureCosmosDBNoSQL.UnitTests/AzureCosmosDBNoSQLServiceCollectionExtensionsTests.cs @@ -51,4 +51,48 @@ public void AddVectorStoreWithConnectionStringRegistersClass() var database = (Database)vectorStore.GetType().GetField("_database", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(vectorStore)!; Assert.Equal(HttpHeaderConstant.Values.UserAgent, database.Client.ClientOptions.ApplicationName); } + + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange + this._serviceCollection.AddSingleton(Mock.Of()); + + // Act + this._serviceCollection.AddAzureCosmosDBNoSQLVectorStoreRecordCollection("testcollection"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithConnectionStringRegistersClass() + { + // Act + this._serviceCollection.AddAzureCosmosDBNoSQLVectorStoreRecordCollection("testcollection", "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=mock;", "mydb"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + private void AssertVectorStoreRecordCollectionCreated() + { + var serviceProvider = this._serviceCollection.BuildServiceProvider(); + + var collection = serviceProvider.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = serviceProvider.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchKernelBuilderExtensions.cs index f9b04e3e53da..8fc1e370c371 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchKernelBuilderExtensions.cs @@ -56,4 +56,75 @@ public static IKernelBuilder AddAzureAISearchVectorStore(this IKernelBuilder bui builder.Services.AddAzureAISearchVectorStore(endpoint, credential, options, serviceId); return builder; } + + /// + /// Register an Azure AI Search , and with the + /// specified service ID and where is retrieved from the dependency injection container. + /// + /// The type of the data model that the collection should contain. + /// The builder to register the on. + /// The name of the collection that this will access. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddAzureAISearchVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + AzureAISearchVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddAzureAISearchVectorStoreRecordCollection(collectionName, options, serviceId); + return builder; + } + + /// + /// Register an Azure AI Search , and with the + /// provided and and the specified service ID. + /// + /// The type of the data model that the collection should contain. + /// The builder to register the on. + /// The name of the collection that this will access. + /// The service endpoint for Azure AI Search. + /// The credential to authenticate to Azure AI Search with. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddAzureAISearchVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + Uri endpoint, + TokenCredential tokenCredential, + AzureAISearchVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddAzureAISearchVectorStoreRecordCollection(collectionName, endpoint, tokenCredential, options, serviceId); + return builder; + } + + /// + /// Register an Azure AI Search , and with the + /// provided and and the specified service ID. + /// + /// The type of the data model that the collection should contain. + /// The builder to register the on. + /// The name of the collection that this will access. + /// The service endpoint for Azure AI Search. + /// The credential to authenticate to Azure AI Search with. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddAzureAISearchVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + Uri endpoint, + AzureKeyCredential credential, + AzureAISearchVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddAzureAISearchVectorStoreRecordCollection(collectionName, endpoint, credential, options, serviceId); + return builder; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchServiceCollectionExtensions.cs index ba518ddf6724..f5d418317fd9 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureAISearch/AzureAISearchServiceCollectionExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. using System; +using System.Text.Json; using Azure; using Azure.Core; using Azure.Core.Serialization; @@ -63,15 +64,7 @@ public static IServiceCollection AddAzureAISearchVectorStore(this IServiceCollec (sp, obj) => { var selectedOptions = options ?? sp.GetService(); - - // Build options for the Azure AI Search client and construct it. - var searchClientOptions = new SearchClientOptions(); - searchClientOptions.Diagnostics.ApplicationId = HttpHeaderConstant.Values.UserAgent; - if (selectedOptions?.JsonSerializerOptions != null) - { - searchClientOptions.Serializer = new JsonObjectSerializer(selectedOptions.JsonSerializerOptions); - } - + var searchClientOptions = BuildSearchClientOptions(selectedOptions?.JsonSerializerOptions); var searchIndexClient = new SearchIndexClient(endpoint, tokenCredential, searchClientOptions); // Construct the vector store. @@ -102,23 +95,194 @@ public static IServiceCollection AddAzureAISearchVectorStore(this IServiceCollec (sp, obj) => { var selectedOptions = options ?? sp.GetService(); + var searchClientOptions = BuildSearchClientOptions(selectedOptions?.JsonSerializerOptions); + var searchIndexClient = new SearchIndexClient(endpoint, credential, searchClientOptions); + + // Construct the vector store. + return new AzureAISearchVectorStore( + searchIndexClient, + selectedOptions); + }); + + return services; + } + + /// + /// Register an Azure AI Search , and with the + /// specified service ID and where is retrieved from the dependency injection container. + /// + /// The type of the data model that the collection should contain. + /// The to register the on. + /// The name of the collection that this will access. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The service collection. + public static IServiceCollection AddAzureAISearchVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + AzureAISearchVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + // If we are not constructing the SearchIndexClient, add the IVectorStore as transient, since we + // cannot make assumptions about how SearchIndexClient is being managed. + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + var searchIndexClient = sp.GetRequiredService(); + var selectedOptions = options ?? sp.GetService>(); + + return new AzureAISearchVectorStoreRecordCollection( + searchIndexClient, + collectionName, + selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + AddVectorizableTextSearch(services, serviceId); + + return services; + } + + /// + /// Register an Azure AI Search , and with the + /// provided and and the specified service ID. + /// + /// The type of the data model that the collection should contain. + /// The to register the on. + /// The name of the collection that this will access. + /// The service endpoint for Azure AI Search. + /// The credential to authenticate to Azure AI Search with. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The service collection. + public static IServiceCollection AddAzureAISearchVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + Uri endpoint, + TokenCredential tokenCredential, + AzureAISearchVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + Verify.NotNull(endpoint); + Verify.NotNull(tokenCredential); + + services.AddKeyedSingleton>( + serviceId, + (sp, obj) => + { + var selectedOptions = options ?? sp.GetService>(); + var searchClientOptions = BuildSearchClientOptions(selectedOptions?.JsonSerializerOptions); + var searchIndexClient = new SearchIndexClient(endpoint, tokenCredential, searchClientOptions); - // Build options for the Azure AI Search client and construct it. - var searchClientOptions = new SearchClientOptions(); - searchClientOptions.Diagnostics.ApplicationId = HttpHeaderConstant.Values.UserAgent; - if (selectedOptions?.JsonSerializerOptions != null) - { - searchClientOptions.Serializer = new JsonObjectSerializer(selectedOptions.JsonSerializerOptions); - } + // Construct the vector store. + return new AzureAISearchVectorStoreRecordCollection( + searchIndexClient, + collectionName, + selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + AddVectorizableTextSearch(services, serviceId); + + return services; + } + + /// + /// Register an Azure AI Search , and with the + /// provided and and the specified service ID. + /// + /// The type of the data model that the collection should contain. + /// The to register the on. + /// The name of the collection that this will access. + /// The service endpoint for Azure AI Search. + /// The credential to authenticate to Azure AI Search with. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The service collection. + public static IServiceCollection AddAzureAISearchVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + Uri endpoint, + AzureKeyCredential credential, + AzureAISearchVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + Verify.NotNull(endpoint); + Verify.NotNull(credential); + services.AddKeyedSingleton>( + serviceId, + (sp, obj) => + { + var selectedOptions = options ?? sp.GetService>(); + var searchClientOptions = BuildSearchClientOptions(selectedOptions?.JsonSerializerOptions); var searchIndexClient = new SearchIndexClient(endpoint, credential, searchClientOptions); // Construct the vector store. - return new AzureAISearchVectorStore( + return new AzureAISearchVectorStoreRecordCollection( searchIndexClient, + collectionName, selectedOptions); }); + AddVectorizedSearch(services, serviceId); + AddVectorizableTextSearch(services, serviceId); + return services; } + + /// + /// Also register the with the given as a . + /// + /// The type of the data model that the collection should contain. + /// The service collection to register on. + /// The service id that the registrations should use. + private static void AddVectorizedSearch(IServiceCollection services, string? serviceId) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + return sp.GetRequiredKeyedService>(serviceId); + }); + } + + /// + /// Also register the with the given as a . + /// + /// The type of the data model that the collection should contain. + /// The service collection to register on. + /// The service id that the registrations should use. + private static void AddVectorizableTextSearch(IServiceCollection services, string? serviceId) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + return (sp.GetRequiredKeyedService>(serviceId) as IVectorizableTextSearch)!; + }); + } + + /// + /// Build a instance, using the provided if it's not null and add the SK user agent string. + /// + /// Optional to add to the options if provided. + /// The . + private static SearchClientOptions BuildSearchClientOptions(JsonSerializerOptions? jsonSerializerOptions) + { + var searchClientOptions = new SearchClientOptions(); + searchClientOptions.Diagnostics.ApplicationId = HttpHeaderConstant.Values.UserAgent; + if (jsonSerializerOptions != null) + { + searchClientOptions.Serializer = new JsonObjectSerializer(jsonSerializerOptions); + } + + return searchClientOptions; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBMongoDB/AzureCosmosDBMongoDBKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBMongoDB/AzureCosmosDBMongoDBKernelBuilderExtensions.cs index 807bb030dcfc..f00ef9b97079 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBMongoDB/AzureCosmosDBMongoDBKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBMongoDB/AzureCosmosDBMongoDBKernelBuilderExtensions.cs @@ -48,4 +48,50 @@ public static IKernelBuilder AddAzureCosmosDBMongoDBVectorStore( builder.Services.AddAzureCosmosDBMongoDBVectorStore(connectionString, databaseName, options, serviceId); return builder; } + + /// + /// Register an Azure CosmosDB MongoDB and with the specified service ID + /// and where the Azure CosmosDB MongoDB is retrieved from the dependency injection container. + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddAzureCosmosDBMongoDBVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + AzureCosmosDBMongoDBVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddAzureCosmosDBMongoDBVectorStoreRecordCollection(collectionName, options, serviceId); + return builder; + } + + /// + /// Register an Azure CosmosDB MongoDB and with the specified service ID + /// and where the Azure CosmosDB MongoDB is constructed using the provided and . + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// Connection string required to connect to Azure CosmosDB MongoDB. + /// Database name for Azure CosmosDB MongoDB. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddAzureCosmosDBMongoDBVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + string connectionString, + string databaseName, + AzureCosmosDBMongoDBVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddAzureCosmosDBMongoDBVectorStoreRecordCollection(collectionName, connectionString, databaseName, options, serviceId); + return builder; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBMongoDB/AzureCosmosDBMongoDBServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBMongoDB/AzureCosmosDBMongoDBServiceCollectionExtensions.cs index c335a2c0ce60..2eba9b22ec37 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBMongoDB/AzureCosmosDBMongoDBServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBMongoDB/AzureCosmosDBMongoDBServiceCollectionExtensions.cs @@ -77,4 +77,94 @@ public static IServiceCollection AddAzureCosmosDBMongoDBVectorStore( return services; } + + /// + /// Register an Azure CosmosDB MongoDB and with the specified service ID + /// and where the Azure CosmosDB MongoDB is retrieved from the dependency injection container. + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddAzureCosmosDBMongoDBVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + AzureCosmosDBMongoDBVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + var database = sp.GetRequiredService(); + var selectedOptions = options ?? sp.GetService>(); + + return new AzureCosmosDBMongoDBVectorStoreRecordCollection(database, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Register an Azure CosmosDB MongoDB and with the specified service ID + /// and where the Azure CosmosDB MongoDB is constructed using the provided and . + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// Connection string required to connect to Azure CosmosDB MongoDB. + /// Database name for Azure CosmosDB MongoDB. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddAzureCosmosDBMongoDBVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + string connectionString, + string databaseName, + AzureCosmosDBMongoDBVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedSingleton>( + serviceId, + (sp, obj) => + { + var settings = MongoClientSettings.FromConnectionString(connectionString); + settings.ApplicationName = HttpHeaderConstant.Values.UserAgent; + + var mongoClient = new MongoClient(settings); + var database = mongoClient.GetDatabase(databaseName); + + var selectedOptions = options ?? sp.GetService>(); + + return new AzureCosmosDBMongoDBVectorStoreRecordCollection(database, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Also register the with the given as a . + /// + /// The type of the data model that the collection should contain. + /// The service collection to register on. + /// The service id that the registrations should use. + private static void AddVectorizedSearch(IServiceCollection services, string? serviceId) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + return sp.GetRequiredKeyedService>(serviceId); + }); + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBNoSQL/AzureCosmosDBNoSQLKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBNoSQL/AzureCosmosDBNoSQLKernelBuilderExtensions.cs index 0f1e3744f36c..91778c5e52de 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBNoSQL/AzureCosmosDBNoSQLKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBNoSQL/AzureCosmosDBNoSQLKernelBuilderExtensions.cs @@ -53,4 +53,50 @@ public static IKernelBuilder AddAzureCosmosDBNoSQLVectorStore( return builder; } + + /// + /// Register an Azure CosmosDB NoSQL and with the specified service ID + /// and where the Azure CosmosDB NoSQL is retrieved from the dependency injection container. + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddAzureCosmosDBNoSQLVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + AzureCosmosDBNoSQLVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddAzureCosmosDBNoSQLVectorStoreRecordCollection(collectionName, options, serviceId); + return builder; + } + + /// + /// Register an Azure CosmosDB NoSQL and with the specified service ID + /// and where the Azure CosmosDB NoSQL is constructed using the provided and . + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// Connection string required to connect to Azure CosmosDB NoSQL. + /// Database name for Azure CosmosDB NoSQL. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddAzureCosmosDBNoSQLVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + string connectionString, + string databaseName, + AzureCosmosDBNoSQLVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddAzureCosmosDBNoSQLVectorStoreRecordCollection(collectionName, connectionString, databaseName, options, serviceId); + return builder; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBNoSQL/AzureCosmosDBNoSQLServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBNoSQL/AzureCosmosDBNoSQLServiceCollectionExtensions.cs index 51f8cb41b333..0510970f51d9 100644 --- a/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBNoSQL/AzureCosmosDBNoSQLServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.AzureCosmosDBNoSQL/AzureCosmosDBNoSQLServiceCollectionExtensions.cs @@ -78,4 +78,95 @@ public static IServiceCollection AddAzureCosmosDBNoSQLVectorStore( return services; } + + /// + /// Register an Azure CosmosDB NoSQL and with the specified service ID + /// and where the Azure CosmosDB NoSQL is retrieved from the dependency injection container. + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddAzureCosmosDBNoSQLVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + AzureCosmosDBNoSQLVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + var database = sp.GetRequiredService(); + var selectedOptions = options ?? sp.GetService>(); + + return new AzureCosmosDBNoSQLVectorStoreRecordCollection(database, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Register an Azure CosmosDB NoSQL and with the specified service ID + /// and where the Azure CosmosDB NoSQL is constructed using the provided and . + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// Connection string required to connect to Azure CosmosDB NoSQL. + /// Database name for Azure CosmosDB NoSQL. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddAzureCosmosDBNoSQLVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + string connectionString, + string databaseName, + AzureCosmosDBNoSQLVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedSingleton>( + serviceId, + (sp, obj) => + { + var cosmosClient = new CosmosClient(connectionString, new() + { + ApplicationName = HttpHeaderConstant.Values.UserAgent, + UseSystemTextJsonSerializerWithOptions = options?.JsonSerializerOptions ?? JsonSerializerOptions.Default, + }); + + var database = cosmosClient.GetDatabase(databaseName); + var selectedOptions = options ?? sp.GetService>(); + + return new AzureCosmosDBNoSQLVectorStoreRecordCollection(database, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Also register the with the given as a . + /// + /// The type of the data model that the collection should contain. + /// The service collection to register on. + /// The service id that the registrations should use. + private static void AddVectorizedSearch(IServiceCollection services, string? serviceId) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + return sp.GetRequiredKeyedService>(serviceId); + }); + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeKernelBuilderExtensions.cs index 0c992f789d2a..d532878c8ec6 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeKernelBuilderExtensions.cs @@ -37,4 +37,48 @@ public static IKernelBuilder AddPineconeVectorStore(this IKernelBuilder builder, builder.Services.AddPineconeVectorStore(apiKey, options, serviceId); return builder; } + + /// + /// Register a Pinecone and with the + /// specified service ID and where is retrieved from the dependency injection container. + /// + /// The type of the data model that the collection should contain. + /// The builder to register the on. + /// The name of the collection that this will access. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddPineconeVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + PineconeVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddPineconeVectorStoreRecordCollection(collectionName, options, serviceId); + return builder; + } + + /// + /// Register a Pinecone and with the + /// provided and the specified service ID. + /// + /// The type of the data model that the collection should contain. + /// The builder to register the on. + /// The name of the collection that this will access. + /// The api key for Pinecone. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddPineconeVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + string apiKey, + PineconeVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddPineconeVectorStoreRecordCollection(collectionName, apiKey, options, serviceId); + return builder; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeServiceCollectionExtensions.cs index d6f76f9beaa7..6d8cf36d658b 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Pinecone/PineconeServiceCollectionExtensions.cs @@ -62,4 +62,95 @@ public static IServiceCollection AddPineconeVectorStore(this IServiceCollection return services; } + + /// + /// Register a Pinecone and with the + /// specified service ID and where is retrieved from the dependency injection container. + /// + /// The type of the data model that the collection should contain. + /// The to register the on. + /// The name of the collection that this will access. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The service collection. + public static IServiceCollection AddPineconeVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + PineconeVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + // If we are not constructing the PineconeClient, add the IVectorStore as transient, since we + // cannot make assumptions about how PineconeClient is being managed. + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + var pineconeClient = sp.GetRequiredService(); + var selectedOptions = options ?? sp.GetService>(); + + return new PineconeVectorStoreRecordCollection( + pineconeClient, + collectionName, + selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Register a Pinecone and with the + /// provided and the specified service ID. + /// + /// The type of the data model that the collection should contain. + /// The to register the on. + /// The name of the collection that this will access. + /// The api key for Pinecone. + /// Optional configuration options to pass to the . + /// An optional service id to use as the service key. + /// The service collection. + public static IServiceCollection AddPineconeVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + string apiKey, + PineconeVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedSingleton>( + serviceId, + (sp, obj) => + { + var pineconeClient = new Sdk.PineconeClient(apiKey); + var selectedOptions = options ?? sp.GetService>(); + + return new PineconeVectorStoreRecordCollection( + pineconeClient, + collectionName, + selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Also register the with the given as a . + /// + /// The type of the data model that the collection should contain. + /// The service collection to register on. + /// The service id that the registrations should use. + private static void AddVectorizedSearch(IServiceCollection services, string? serviceId) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + return sp.GetRequiredKeyedService>(serviceId); + }); + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantKernelBuilderExtensions.cs index 0f32f044832f..70bb93a3648b 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantKernelBuilderExtensions.cs @@ -39,4 +39,58 @@ public static IKernelBuilder AddQdrantVectorStore(this IKernelBuilder builder, s builder.Services.AddQdrantVectorStore(host, port, https, apiKey, options, serviceId); return builder; } + + /// + /// Register a Qdrant and with the specified service ID + /// and where the Qdrant is retrieved from the dependency injection container. + /// + /// The type of the key. + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddQdrantVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + QdrantVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TKey : notnull + where TRecord : class + { + builder.Services.AddQdrantVectorStoreRecordCollection(collectionName, options, serviceId); + return builder; + } + + /// + /// Register a Qdrant and with the specified service ID + /// and where the Qdrant is constructed using the provided parameters. + /// + /// The type of the key. + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// The Qdrant service host name. + /// The Qdrant service port. + /// A value indicating whether to use HTTPS for communicating with Qdrant. + /// The Qdrant service API key. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddQdrantVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + string host, + int port = 6334, + bool https = false, + string? apiKey = default, + QdrantVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TKey : notnull + where TRecord : class + { + builder.Services.AddQdrantVectorStoreRecordCollection(collectionName, host, port, https, apiKey, options, serviceId); + return builder; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantServiceCollectionExtensions.cs index 5fd908153e9a..065b32002de2 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Qdrant/QdrantServiceCollectionExtensions.cs @@ -64,4 +64,99 @@ public static IServiceCollection AddQdrantVectorStore(this IServiceCollection se return services; } + + /// + /// Register a Qdrant and with the specified service ID + /// and where the Qdrant is retrieved from the dependency injection container. + /// + /// The type of the key. + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddQdrantVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + QdrantVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TKey : notnull + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + var qdrantClient = sp.GetRequiredService(); + var selectedOptions = options ?? sp.GetService>(); + + return (new QdrantVectorStoreRecordCollection(qdrantClient, collectionName, selectedOptions) as IVectorStoreRecordCollection)!; + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Register a Qdrant and with the specified service ID + /// and where the Qdrant is constructed using the provided parameters. + /// + /// The type of the key. + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// The Qdrant service host name. + /// The Qdrant service port. + /// A value indicating whether to use HTTPS for communicating with Qdrant. + /// The Qdrant service API key. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddQdrantVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + string host, + int port = 6334, + bool https = false, + string? apiKey = default, + QdrantVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TKey : notnull + where TRecord : class + { + services.AddKeyedSingleton>( + serviceId, + (sp, obj) => + { + var qdrantClient = new QdrantClient(host, port, https, apiKey); + var selectedOptions = options ?? sp.GetService>(); + + return (new QdrantVectorStoreRecordCollection(qdrantClient, collectionName, selectedOptions) as IVectorStoreRecordCollection)!; + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Also register the with the given as a . + /// + /// The type of the key. + /// The type of the data model that the collection should contain. + /// The service collection to register on. + /// The service id that the registrations should use. + private static void AddVectorizedSearch(IServiceCollection services, string? serviceId) + where TKey : notnull + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + return sp.GetRequiredKeyedService>(serviceId); + }); + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Redis/RedisKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Redis/RedisKernelBuilderExtensions.cs index d338f0589e16..8cb902f0dc12 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Redis/RedisKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Redis/RedisKernelBuilderExtensions.cs @@ -37,4 +37,92 @@ public static IKernelBuilder AddRedisVectorStore(this IKernelBuilder builder, st builder.Services.AddRedisVectorStore(redisConnectionConfiguration, options, serviceId); return builder; } + + /// + /// Register a Redis with the specified service ID + /// and where the Redis is retrieved from the dependency injection container. + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddRedisHashSetVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + RedisHashSetVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddRedisHashSetVectorStoreRecordCollection(collectionName, options, serviceId); + return builder; + } + + /// + /// Register a Redis with the specified service ID + /// and where the Redis is constructed using the provided . + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// The Redis connection configuration string. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddRedisHashSetVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + string redisConnectionConfiguration, + RedisHashSetVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddRedisHashSetVectorStoreRecordCollection(collectionName, redisConnectionConfiguration, options, serviceId); + return builder; + } + + /// + /// Register a Redis with the specified service ID + /// and where the Redis is retrieved from the dependency injection container. + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddRedisJsonVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + RedisJsonVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddRedisJsonVectorStoreRecordCollection(collectionName, options, serviceId); + return builder; + } + + /// + /// Register a Redis with the specified service ID + /// and where the Redis is constructed using the provided . + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// The Redis connection configuration string. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddRedisJsonVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + string redisConnectionConfiguration, + RedisJsonVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddRedisJsonVectorStoreRecordCollection(collectionName, redisConnectionConfiguration, options, serviceId); + return builder; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Redis/RedisServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Redis/RedisServiceCollectionExtensions.cs index 3666d05871c4..339cbe4059f5 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Redis/RedisServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Redis/RedisServiceCollectionExtensions.cs @@ -64,4 +64,153 @@ public static IServiceCollection AddRedisVectorStore(this IServiceCollection ser return services; } + + /// + /// Register a Redis with the specified service ID + /// and where the Redis is retrieved from the dependency injection container. + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddRedisHashSetVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + RedisHashSetVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + var database = sp.GetRequiredService(); + var selectedOptions = options ?? sp.GetService>(); + + return new RedisHashSetVectorStoreRecordCollection(database, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Register a Redis with the specified service ID + /// and where the Redis is constructed using the provided . + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// The Redis connection configuration string. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddRedisHashSetVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + string redisConnectionConfiguration, + RedisHashSetVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedSingleton>( + serviceId, + (sp, obj) => + { + var database = ConnectionMultiplexer.Connect(redisConnectionConfiguration).GetDatabase(); + var selectedOptions = options ?? sp.GetService>(); + + return new RedisHashSetVectorStoreRecordCollection(database, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Register a Redis with the specified service ID + /// and where the Redis is retrieved from the dependency injection container. + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddRedisJsonVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + RedisJsonVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + var database = sp.GetRequiredService(); + var selectedOptions = options ?? sp.GetService>(); + + return new RedisJsonVectorStoreRecordCollection(database, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Register a Redis with the specified service ID + /// and where the Redis is constructed using the provided . + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// The Redis connection configuration string. + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddRedisJsonVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + string redisConnectionConfiguration, + RedisJsonVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedSingleton>( + serviceId, + (sp, obj) => + { + var database = ConnectionMultiplexer.Connect(redisConnectionConfiguration).GetDatabase(); + var selectedOptions = options ?? sp.GetService>(); + + return new RedisJsonVectorStoreRecordCollection(database, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Also register the with the given as a . + /// + /// The type of the data model that the collection should contain. + /// The service collection to register on. + /// The service id that the registrations should use. + private static void AddVectorizedSearch(IServiceCollection services, string? serviceId) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + return sp.GetRequiredKeyedService>(serviceId); + }); + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateKernelBuilderExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateKernelBuilderExtensions.cs index 57d10183d7de..bd27d66953f1 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateKernelBuilderExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateKernelBuilderExtensions.cs @@ -32,4 +32,30 @@ public static IKernelBuilder AddWeaviateVectorStore( builder.Services.AddWeaviateVectorStore(httpClient, options, serviceId); return builder; } + + /// + /// Register a Weaviate and with the specified service ID. + /// + /// The type of the record. + /// The builder to register the on. + /// The name of the collection. + /// + /// that is used to interact with Weaviate API. + /// should point to remote or local cluster and API key can be configured via . + /// It's also possible to provide these parameters via . + /// + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// The kernel builder. + public static IKernelBuilder AddWeaviateVectorStoreRecordCollection( + this IKernelBuilder builder, + string collectionName, + HttpClient? httpClient = default, + WeaviateVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + builder.Services.AddWeaviateVectorStoreRecordCollection(collectionName, httpClient, options, serviceId); + return builder; + } } diff --git a/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateServiceCollectionExtensions.cs b/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateServiceCollectionExtensions.cs index cdaff1ddd070..4a2e264cd70c 100644 --- a/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateServiceCollectionExtensions.cs +++ b/dotnet/src/Connectors/Connectors.Memory.Weaviate/WeaviateServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Net.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel.Connectors.Weaviate; @@ -42,4 +43,58 @@ public static IServiceCollection AddWeaviateVectorStore( return services; } + + /// + /// Register a Weaviate and with the specified service ID. + /// + /// The type of the record. + /// The to register the on. + /// The name of the collection. + /// + /// that is used to interact with Weaviate API. + /// should point to remote or local cluster and API key can be configured via . + /// It's also possible to provide these parameters via . + /// + /// Optional options to further configure the . + /// An optional service id to use as the service key. + /// Service collection. + public static IServiceCollection AddWeaviateVectorStoreRecordCollection( + this IServiceCollection services, + string collectionName, + HttpClient? httpClient = default, + WeaviateVectorStoreRecordCollectionOptions? options = default, + string? serviceId = default) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + var selectedHttpClient = HttpClientProvider.GetHttpClient(httpClient, sp); + var selectedOptions = options ?? sp.GetService>(); + + return new WeaviateVectorStoreRecordCollection(selectedHttpClient, collectionName, selectedOptions); + }); + + AddVectorizedSearch(services, serviceId); + + return services; + } + + /// + /// Also register the with the given as a . + /// + /// The type of the data model that the collection should contain. + /// The service collection to register on. + /// The service id that the registrations should use. + private static void AddVectorizedSearch(IServiceCollection services, string? serviceId) + where TRecord : class + { + services.AddKeyedTransient>( + serviceId, + (sp, obj) => + { + return sp.GetRequiredKeyedService>(serviceId); + }); + } } diff --git a/dotnet/src/Connectors/Connectors.Qdrant.UnitTests/QdrantKernelBuilderExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Qdrant.UnitTests/QdrantKernelBuilderExtensionsTests.cs index f0b4f327c0f0..6b45d530cc43 100644 --- a/dotnet/src/Connectors/Connectors.Qdrant.UnitTests/QdrantKernelBuilderExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Qdrant.UnitTests/QdrantKernelBuilderExtensionsTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.Qdrant; @@ -62,4 +63,62 @@ private void AssertVectorStoreCreated() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + using var qdrantClient = new QdrantClient("localhost"); + this._kernelBuilder.Services.AddSingleton(qdrantClient); + + // Act. + this._kernelBuilder.AddQdrantVectorStoreRecordCollection("testcollection"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithHostAndPortAndCredsRegistersClass() + { + // Act. + this._kernelBuilder.AddQdrantVectorStoreRecordCollection("testcollection", "localhost", 8080, true, "apikey"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithHostRegistersClass() + { + // Act. + this._kernelBuilder.AddQdrantVectorStoreRecordCollection("testcollection", "localhost"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + private void AssertVectorStoreRecordCollectionCreated() + { + var kernel = this._kernelBuilder.Build(); + + var collection = kernel.Services.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = kernel.Services.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public ulong Id { get; set; } + + [VectorStoreRecordVector(4)] + public ReadOnlyMemory Vector { get; set; } + } } diff --git a/dotnet/src/Connectors/Connectors.Qdrant.UnitTests/QdrantServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Qdrant.UnitTests/QdrantServiceCollectionExtensionsTests.cs index d2219e395a79..dae5002bed81 100644 --- a/dotnet/src/Connectors/Connectors.Qdrant.UnitTests/QdrantServiceCollectionExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Qdrant.UnitTests/QdrantServiceCollectionExtensionsTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.Qdrant; @@ -55,6 +56,40 @@ public void AddVectorStoreWithHostRegistersClass() this.AssertVectorStoreCreated(); } + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + using var qdrantClient = new QdrantClient("localhost"); + this._serviceCollection.AddSingleton(qdrantClient); + + // Act. + this._serviceCollection.AddQdrantVectorStoreRecordCollection("testcollection"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithHostAndPortAndCredsRegistersClass() + { + // Act. + this._serviceCollection.AddQdrantVectorStoreRecordCollection("testcollection", "localhost", 8080, true, "apikey"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithHostRegistersClass() + { + // Act. + this._serviceCollection.AddQdrantVectorStoreRecordCollection("testcollection", "localhost"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + private void AssertVectorStoreCreated() { var serviceProvider = this._serviceCollection.BuildServiceProvider(); @@ -62,4 +97,28 @@ private void AssertVectorStoreCreated() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + private void AssertVectorStoreRecordCollectionCreated() + { + var serviceProvider = this._serviceCollection.BuildServiceProvider(); + + var collection = serviceProvider.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = serviceProvider.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public ulong Id { get; set; } + + [VectorStoreRecordVector(4)] + public ReadOnlyMemory Vector { get; set; } + } } diff --git a/dotnet/src/Connectors/Connectors.Redis.UnitTests/RedisKernelBuilderExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Redis.UnitTests/RedisKernelBuilderExtensionsTests.cs index dcb8383b1525..7b56cfe6dba5 100644 --- a/dotnet/src/Connectors/Connectors.Redis.UnitTests/RedisKernelBuilderExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Redis.UnitTests/RedisKernelBuilderExtensionsTests.cs @@ -35,6 +35,32 @@ public void AddVectorStoreRegistersClass() this.AssertVectorStoreCreated(); } + [Fact] + public void AddRedisHashSetVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + this._kernelBuilder.Services.AddSingleton(Mock.Of()); + + // Act. + this._kernelBuilder.AddRedisHashSetVectorStoreRecordCollection("testCollection"); + + // Assert. + this.AssertHashSetVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddRedisJsonVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + this._kernelBuilder.Services.AddSingleton(Mock.Of()); + + // Act. + this._kernelBuilder.AddRedisJsonVectorStoreRecordCollection("testCollection"); + + // Assert. + this.AssertJsonVectorStoreRecordCollectionCreated(); + } + private void AssertVectorStoreCreated() { var kernel = this._kernelBuilder.Build(); @@ -42,4 +68,28 @@ private void AssertVectorStoreCreated() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + private void AssertHashSetVectorStoreRecordCollectionCreated() where TRecord : class + { + var kernel = this._kernelBuilder.Build(); + var collection = kernel.Services.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + } + + private void AssertJsonVectorStoreRecordCollectionCreated() where TRecord : class + { + var kernel = this._kernelBuilder.Build(); + var collection = kernel.Services.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + } } diff --git a/dotnet/src/Connectors/Connectors.Redis.UnitTests/RedisServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Redis.UnitTests/RedisServiceCollectionExtensionsTests.cs index 9bbe566f9c66..b835fff87c4e 100644 --- a/dotnet/src/Connectors/Connectors.Redis.UnitTests/RedisServiceCollectionExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Redis.UnitTests/RedisServiceCollectionExtensionsTests.cs @@ -35,6 +35,32 @@ public void AddVectorStoreRegistersClass() this.AssertVectorStoreCreated(); } + [Fact] + public void AddRedisHashSetVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + this._serviceCollection.AddSingleton(Mock.Of()); + + // Act. + this._serviceCollection.AddRedisHashSetVectorStoreRecordCollection("testCollection"); + + // Assert. + this.AssertHashSetVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddRedisJsonVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + this._serviceCollection.AddSingleton(Mock.Of()); + + // Act. + this._serviceCollection.AddRedisJsonVectorStoreRecordCollection("testCollection"); + + // Assert. + this.AssertJsonVectorStoreRecordCollectionCreated(); + } + private void AssertVectorStoreCreated() { var serviceProvider = this._serviceCollection.BuildServiceProvider(); @@ -42,4 +68,28 @@ private void AssertVectorStoreCreated() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + private void AssertHashSetVectorStoreRecordCollectionCreated() where TRecord : class + { + var serviceProvider = this._serviceCollection.BuildServiceProvider(); + var collection = serviceProvider.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + } + + private void AssertJsonVectorStoreRecordCollectionCreated() where TRecord : class + { + var serviceProvider = this._serviceCollection.BuildServiceProvider(); + var collection = serviceProvider.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + } } diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeKernelBuilderExtensionsTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeKernelBuilderExtensionsTests.cs index 67cd1588e0dd..7d4060d9a670 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeKernelBuilderExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeKernelBuilderExtensionsTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.Pinecone; @@ -45,6 +46,30 @@ public void AddVectorStoreWithApiKeyRegistersClass() this.AssertVectorStoreCreated(); } + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + using var client = new Sdk.PineconeClient("fake api key"); + this._kernelBuilder.Services.AddSingleton(client); + + // Act. + this._kernelBuilder.AddPineconeVectorStoreRecordCollection("testcollection"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithApiKeyRegistersClass() + { + // Act. + this._kernelBuilder.AddPineconeVectorStoreRecordCollection("testcollection", "fake api key"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + private void AssertVectorStoreCreated() { var kernel = this._kernelBuilder.Build(); @@ -52,4 +77,28 @@ private void AssertVectorStoreCreated() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + private void AssertVectorStoreRecordCollectionCreated() + { + var kernel = this._kernelBuilder.Build(); + + var collection = kernel.Services.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = kernel.Services.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + + [VectorStoreRecordVector(4)] + public ReadOnlyMemory Vector { get; set; } + } } diff --git a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeServiceCollectionExtensionsTests.cs index c58659302b46..63ba2d38c1fd 100644 --- a/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeServiceCollectionExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.UnitTests/Memory/Pinecone/PineconeServiceCollectionExtensionsTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.Connectors.Pinecone; @@ -44,6 +45,29 @@ public void AddVectorStoreWithApiKeyRegistersClass() // Assert. this.AssertVectorStoreCreated(); } + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange. + using var client = new Sdk.PineconeClient("fake api key"); + this._serviceCollection.AddSingleton(client); + + // Act. + this._serviceCollection.AddPineconeVectorStoreRecordCollection("testcollection"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } + + [Fact] + public void AddVectorStoreRecordCollectionWithApiKeyRegistersClass() + { + // Act. + this._serviceCollection.AddPineconeVectorStoreRecordCollection("testcollection", "fake api key"); + + // Assert. + this.AssertVectorStoreRecordCollectionCreated(); + } private void AssertVectorStoreCreated() { @@ -52,4 +76,28 @@ private void AssertVectorStoreCreated() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + private void AssertVectorStoreRecordCollectionCreated() + { + var serviceProvider = this._serviceCollection.BuildServiceProvider(); + + var collection = serviceProvider.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = serviceProvider.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public string Id { get; set; } = string.Empty; + + [VectorStoreRecordVector(4)] + public ReadOnlyMemory Vector { get; set; } + } } diff --git a/dotnet/src/Connectors/Connectors.Weaviate.UnitTests/WeaviateKernelBuilderExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Weaviate.UnitTests/WeaviateKernelBuilderExtensionsTests.cs index 60c6525b797b..d730deece2bd 100644 --- a/dotnet/src/Connectors/Connectors.Weaviate.UnitTests/WeaviateKernelBuilderExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Weaviate.UnitTests/WeaviateKernelBuilderExtensionsTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Net.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; @@ -33,4 +34,34 @@ public void AddVectorStoreRegistersClass() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + [Fact] + public void AddWeaviateVectorStoreRecordCollectionRegistersClass() + { + // Arrange + using var httpClient = new HttpClient() { BaseAddress = new Uri("http://localhost") }; + this._kernelBuilder.Services.AddSingleton(httpClient); + + // Act + this._kernelBuilder.AddWeaviateVectorStoreRecordCollection("testcollection"); + + var kernel = this._kernelBuilder.Build(); + + // Assert + var collection = kernel.Services.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = kernel.Services.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public Guid Id { get; set; } + } } diff --git a/dotnet/src/Connectors/Connectors.Weaviate.UnitTests/WeaviateServiceCollectionExtensionsTests.cs b/dotnet/src/Connectors/Connectors.Weaviate.UnitTests/WeaviateServiceCollectionExtensionsTests.cs index 74ed9f185485..cb36bcf9bb6d 100644 --- a/dotnet/src/Connectors/Connectors.Weaviate.UnitTests/WeaviateServiceCollectionExtensionsTests.cs +++ b/dotnet/src/Connectors/Connectors.Weaviate.UnitTests/WeaviateServiceCollectionExtensionsTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Net.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.SemanticKernel; @@ -33,4 +34,39 @@ public void AddVectorStoreRegistersClass() Assert.NotNull(vectorStore); Assert.IsType(vectorStore); } + + [Fact] + public void AddVectorStoreRecordCollectionRegistersClass() + { + // Arrange + using var httpClient = new HttpClient() { BaseAddress = new Uri("http://localhost") }; + this._serviceCollection.AddSingleton(httpClient); + + // Act + this._serviceCollection.AddWeaviateVectorStoreRecordCollection("testcollection"); + + // Assert + this.AssertVectorStoreRecordCollectionCreated(); + } + + private void AssertVectorStoreRecordCollectionCreated() + { + var serviceProvider = this._serviceCollection.BuildServiceProvider(); + + var collection = serviceProvider.GetRequiredService>(); + Assert.NotNull(collection); + Assert.IsType>(collection); + + var vectorizedSearch = serviceProvider.GetRequiredService>(); + Assert.NotNull(vectorizedSearch); + Assert.IsType>(vectorizedSearch); + } + +#pragma warning disable CA1812 // Avoid uninstantiated internal classes + private sealed class TestRecord +#pragma warning restore CA1812 // Avoid uninstantiated internal classes + { + [VectorStoreRecordKey] + public Guid Id { get; set; } + } }