diff --git a/extensions/SQLServer/SQLServer/DependencyInjection.cs b/extensions/SQLServer/SQLServer/DependencyInjection.cs index 926b5d7b1..f0843678e 100644 --- a/extensions/SQLServer/SQLServer/DependencyInjection.cs +++ b/extensions/SQLServer/SQLServer/DependencyInjection.cs @@ -30,14 +30,20 @@ public static IKernelMemoryBuilder WithSqlServerMemoryDb( /// Kernel Memory Builder extension method to add SQL Server memory connector. /// /// KM builder instance - /// SQL Server connection string - /// Whether to use native vector search or not + /// SQL Server connection string + /// Whether to use native vector search or not. + /// When is , it is the vector size used by the VECTOR data type. + /// + /// Currently, the native Vector search is available on Azure SQL Database only. + /// See Overview of vectors in the SQL Database Engine for more information about native Vectors support. + /// public static IKernelMemoryBuilder WithSqlServerMemoryDb( this IKernelMemoryBuilder builder, - string connString, - bool useNativeVectorSearch = false) + string connectionString, + bool useNativeVectorSearch = false, + int vectorSize = SqlServerConfig.DefaultVectorSize) { - builder.Services.AddSqlServerAsMemoryDb(connString, useNativeVectorSearch); + builder.Services.AddSqlServerAsMemoryDb(connectionString, useNativeVectorSearch, vectorSize); return builder; } } @@ -56,6 +62,8 @@ public static IServiceCollection AddSqlServerAsMemoryDb( this IServiceCollection services, SqlServerConfig config) { + config.Validate(); + return services .AddSingleton(config) .AddSingleton(); @@ -65,14 +73,22 @@ public static IServiceCollection AddSqlServerAsMemoryDb( /// Inject SQL Server as the default implementation of IMemoryDb /// /// Service collection - /// SQL Server connection string - /// Whether to use native vector search or not + /// SQL Server connection string + /// Whether to use native vector search or not. Currently, the native Vector search is in Early Access Preview (EAP) and is available on Azure SQL Database and Managed Instance only. + /// When is , it is the vector size used by the VECTOR SQL Server type. public static IServiceCollection AddSqlServerAsMemoryDb( this IServiceCollection services, - string connString, - bool useNativeVectorSearch = false) + string connectionString, + bool useNativeVectorSearch = false, + int vectorSize = SqlServerConfig.DefaultVectorSize) { - var config = new SqlServerConfig { ConnectionString = connString, UseNativeVectorSearch = useNativeVectorSearch }; + var config = new SqlServerConfig + { + ConnectionString = connectionString, + UseNativeVectorSearch = useNativeVectorSearch, + VectorSize = vectorSize + }; + return services.AddSqlServerAsMemoryDb(config); } } diff --git a/extensions/SQLServer/SQLServer/QueryProviders/VectorQueryProvider.cs b/extensions/SQLServer/SQLServer/QueryProviders/VectorQueryProvider.cs index 46ee434eb..3523ef8fa 100644 --- a/extensions/SQLServer/SQLServer/QueryProviders/VectorQueryProvider.cs +++ b/extensions/SQLServer/SQLServer/QueryProviders/VectorQueryProvider.cs @@ -90,7 +90,7 @@ public string PrepareGetRecordsListQuery(string index, SqlParameterCollection parameters) { var queryColumns = "[key], [payload], [tags]"; - if (withEmbeddings) { queryColumns += ", VECTOR_TO_JSON_ARRAY([embedding]) AS [embedding]"; } + if (withEmbeddings) { queryColumns += ", CAST([embedding] AS NVARCHAR(MAX)) AS [embedding]"; } var sql = $""" WITH [filters] AS @@ -126,7 +126,7 @@ public string PrepareGetSimilarRecordsListQuery(string index, if (withEmbedding) { queryColumns += $"," + - $"VECTOR_TO_JSON_ARRAY({this.GetFullTableName(this._config.MemoryTableName)}.[embedding]) AS [embedding]"; + $"CAST({this.GetFullTableName(this._config.MemoryTableName)}.[embedding] AS NVARCHAR(MAX)) AS [embedding]"; } var generatedFilters = this.GenerateFilters(index, parameters, filters); @@ -134,11 +134,11 @@ public string PrepareGetSimilarRecordsListQuery(string index, var sql = $""" SELECT TOP (@limit) {queryColumns}, - VECTOR_DISTANCE('cosine', JSON_ARRAY_TO_VECTOR(@vector), Embedding) AS [distance] + VECTOR_DISTANCE('cosine', CAST(@vector AS VECTOR({this._config.VectorSize})), Embedding) AS [distance] FROM {this.GetFullTableName(this._config.MemoryTableName)} WHERE - VECTOR_DISTANCE('cosine', JSON_ARRAY_TO_VECTOR(@vector), Embedding) <= @max_distance + VECTOR_DISTANCE('cosine', CAST(@vector AS VECTOR({this._config.VectorSize})), Embedding) <= @max_distance {generatedFilters} ORDER BY [distance] ASC """; @@ -156,10 +156,10 @@ public string PrepareUpsertRecordsBatchQuery(string index) USING (SELECT @key) as [src]([key]) ON {this.GetFullTableName(this._config.MemoryTableName)}.[key] = [src].[key] WHEN MATCHED THEN - UPDATE SET payload=@payload, embedding=JSON_ARRAY_TO_VECTOR(@embedding), tags=@tags + UPDATE SET payload=@payload, embedding=CAST(@embedding AS VECTOR({this._config.VectorSize})), tags=@tags WHEN NOT MATCHED THEN - INSERT ([id], [key], [collection], [payload], [tags], [embedding]) - VALUES (NEWID(), @key, @index, @payload, @tags, JSON_ARRAY_TO_VECTOR(@embedding)); + INSERT ([key], [collection], [payload], [tags], [embedding]) + VALUES (@key, @index, @payload, @tags, CAST(@embedding AS VECTOR({this._config.VectorSize}))); DELETE FROM [tgt] FROM {this.GetFullTableName($"{this._config.TagsTableName}_{index}")} AS [tgt] @@ -211,12 +211,12 @@ PRIMARY KEY ([id]) IF OBJECT_ID(N'{this.GetFullTableName(this._config.MemoryTableName)}', N'U') IS NULL CREATE TABLE {this.GetFullTableName(this._config.MemoryTableName)} - ( [id] UNIQUEIDENTIFIER NOT NULL, + ( [id] UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID(), [key] NVARCHAR(256) NOT NULL, [collection] NVARCHAR(256) NOT NULL, [payload] NVARCHAR(MAX), [tags] NVARCHAR(MAX), - [embedding] VARBINARY(8000), + [embedding] VECTOR({this._config.VectorSize}), PRIMARY KEY ([id]), FOREIGN KEY ([collection]) REFERENCES {this.GetFullTableName(this._config.MemoryCollectionTableName)}([id]) ON DELETE CASCADE, CONSTRAINT UK_{this._config.MemoryTableName} UNIQUE([collection], [key]) diff --git a/extensions/SQLServer/SQLServer/SqlServerConfig.cs b/extensions/SQLServer/SQLServer/SqlServerConfig.cs index fca14fe0c..b66af7d2d 100644 --- a/extensions/SQLServer/SQLServer/SqlServerConfig.cs +++ b/extensions/SQLServer/SQLServer/SqlServerConfig.cs @@ -30,7 +30,12 @@ public class SqlServerConfig /// /// The default schema used by the SQL Server memory store. /// - public const string DefaultSchema = "dbo"; + internal const string DefaultSchema = "dbo"; + + /// + /// The default vector size when using the native VECTOR type. + /// + internal const int DefaultVectorSize = 1536; /// /// The connection string to the SQL Server database. @@ -66,8 +71,38 @@ public class SqlServerConfig /// Whether to use native vector search or not. /// /// - /// Currently, Vector Search supports only Azure SQL Database and can handle vectors up to 1998 dimensions. - /// See https://devblogs.microsoft.com/azure-sql/announcing-eap-native-vector-support-in-azure-sql-database for more information. + /// Currently, the native Vector search is available on Azure SQL Database only. + /// See Overview of vectors in the SQL Database Engine for more information. /// + /// public bool UseNativeVectorSearch { get; set; } = false; + + /// + /// The vector size when using the native vector search. + /// + /// + /// Currently, the maximum supported vector size is 1998. + /// See Overview of vectors in the SQL Database Engine for more information. + /// + /// + public int VectorSize { get; set; } = DefaultVectorSize; + + /// + /// Verify that the current state is valid. + /// + public void Validate() + { + if (this.UseNativeVectorSearch) + { + if (this.VectorSize < 0) + { + throw new ConfigurationException("The vector size must be greater than 0"); + } + + if (this.VectorSize > 1998) + { + throw new ConfigurationException("The vector size must be less than or equal to 1998"); + } + } + } } diff --git a/extensions/SQLServer/SQLServer/SqlServerMemory.cs b/extensions/SQLServer/SQLServer/SqlServerMemory.cs index 80300a16c..f00a60c97 100644 --- a/extensions/SQLServer/SQLServer/SqlServerMemory.cs +++ b/extensions/SQLServer/SQLServer/SqlServerMemory.cs @@ -290,7 +290,7 @@ public async IAsyncEnumerable GetListAsync( command.Parameters.AddWithValue("@min_relevance_score", minRelevance); command.Parameters.AddWithValue("@max_distance", 1 - minRelevance); - command.Parameters.AddWithValue("@vector", JsonSerializer.Serialize(embedding.Data.ToArray())); + command.Parameters.AddWithValue("@vector", JsonSerializer.Serialize(embedding.Data)); command.Parameters.AddWithValue("@index", index); command.Parameters.AddWithValue("@limit", limit); @@ -326,7 +326,7 @@ public async Task UpsertAsync(string index, MemoryRecord record, Cancell { if (!this._isReady) { await this.InitAsync(cancellationToken).ConfigureAwait(false); } - await foreach (var item in this.UpsertBatchAsync(index, new[] { record }, cancellationToken).ConfigureAwait(false)) + await foreach (var item in this.UpsertBatchAsync(index, [record], cancellationToken).ConfigureAwait(false)) { return item; } @@ -363,7 +363,7 @@ public async IAsyncEnumerable UpsertBatchAsync(string index, IEnumerable command.Parameters.AddWithValue("@key", record.Id); command.Parameters.AddWithValue("@payload", JsonSerializer.Serialize(record.Payload) ?? (object)DBNull.Value); command.Parameters.AddWithValue("@tags", JsonSerializer.Serialize(record.Tags) ?? (object)DBNull.Value); - command.Parameters.AddWithValue("@embedding", JsonSerializer.Serialize(record.Vector.Data.ToArray())); + command.Parameters.AddWithValue("@embedding", JsonSerializer.Serialize(record.Vector.Data)); await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); command.Dispose(); diff --git a/service/Service/appsettings.json b/service/Service/appsettings.json index d132ed4b6..49da0a3fb 100644 --- a/service/Service/appsettings.json +++ b/service/Service/appsettings.json @@ -633,8 +633,9 @@ "MemoryTableName": "KMMemories", "EmbeddingsTableName": "KMEmbeddings", "TagsTableName": "KMMemoriesTags", - // See https://devblogs.microsoft.com/azure-sql/announcing-eap-native-vector-support-in-azure-sql-database - "UseNativeVectorSearch": false + // See https://learn.microsoft.com/sql/relational-databases/vectors/vectors-sql-server?view=azuresqldb-current + "UseNativeVectorSearch": false, + "VectorSize": 1536 } } }