Elastic.SemanticKernel.Connectors.Elasticsearch/ElasticsearchServiceCollectionExtensions.cs (83 lines of code) (raw):
// Licensed to Elasticsearch B.V under one or more agreements.
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
using Elastic.Clients.Elasticsearch;
using Microsoft.Extensions.DependencyInjection;
using Elastic.SemanticKernel.Connectors.Elasticsearch;
using Microsoft.Extensions.VectorData;
namespace Microsoft.SemanticKernel;
/// <summary>
/// Extension methods to register Elasticsearch <see cref="IVectorStore"/> instances on an <see cref="IServiceCollection"/>.
/// </summary>
public static class ElasticsearchServiceCollectionExtensions
{
/// <summary>
/// Register an Elasticsearch <see cref="IVectorStore"/> with the specified service ID and where <see cref="ElasticsearchClient"/> is retrieved from the dependency injection container.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to register the <see cref="IVectorStore"/> on.</param>
/// <param name="options">Optional options to further configure the <see cref="IVectorStore"/>.</param>
/// <param name="serviceId">An optional service id to use as the service key.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddElasticsearchVectorStore(this IServiceCollection services, ElasticsearchVectorStoreOptions? options = default, string? serviceId = default)
{
// If we are not constructing the ElasticsearchClient, add the IVectorStore as transient, since we
// cannot make assumptions about how ElasticsearchClient is being managed.
services.AddKeyedTransient<IVectorStore>(
serviceId,
(sp, _) =>
{
var elasticsearchClient = sp.GetRequiredService<ElasticsearchClient>();
var selectedOptions = options ?? sp.GetService<ElasticsearchVectorStoreOptions>();
return new ElasticsearchVectorStore(
elasticsearchClient,
selectedOptions);
});
return services;
}
/// <summary>
/// Register an Elasticsearch <see cref="IVectorStore"/> with the specified service ID and where <see cref="ElasticsearchClient"/> is constructed using the provided client settings.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to register the <see cref="IVectorStore"/> on.</param>
/// <param name="clientSettings">The Elasticsearch client settings.</param>
/// <param name="options">Optional options to further configure the <see cref="IVectorStore"/>.</param>
/// <param name="serviceId">An optional service id to use as the service key.</param>
/// <returns>The service collection.</returns>
public static IServiceCollection AddElasticsearchVectorStore(this IServiceCollection services, IElasticsearchClientSettings clientSettings, ElasticsearchVectorStoreOptions? options = default, string? serviceId = default)
{
services.AddKeyedSingleton<IVectorStore>(
serviceId,
(sp, _) =>
{
var elasticsearchClient = new ElasticsearchClient(clientSettings);
var selectedOptions = options ?? sp.GetService<ElasticsearchVectorStoreOptions>();
return new ElasticsearchVectorStore(
elasticsearchClient,
selectedOptions);
});
return services;
}
/// <summary>
/// Register an Elasticsearch <see cref="IVectorStoreRecordCollection{TKey, TRecord}"/> and <see cref="IVectorizedSearch{TRecord}"/> with the specified service ID
/// and where the <see cref="ElasticsearchClient"/> is retrieved from the dependency injection container.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TRecord">The type of the record.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to register the <see cref="IVectorStoreRecordCollection{TKey, TRecord}"/> on.</param>
/// <param name="collectionName">The name of the collection.</param>
/// <param name="options">Optional options to further configure the <see cref="IVectorStoreRecordCollection{TKey, TRecord}"/>.</param>
/// <param name="serviceId">An optional service id to use as the service key.</param>
/// <returns>Service collection.</returns>
public static IServiceCollection AddElasticsearchVectorStoreRecordCollection<TKey, TRecord>(
this IServiceCollection services,
string collectionName,
ElasticsearchVectorStoreRecordCollectionOptions<TRecord>? options = default,
string? serviceId = default)
where TKey : notnull
{
services.AddKeyedTransient<IVectorStoreRecordCollection<TKey, TRecord>>(
serviceId,
(sp, _) =>
{
var elasticsearchClient = sp.GetRequiredService<ElasticsearchClient>();
var selectedOptions = options ?? sp.GetService<ElasticsearchVectorStoreRecordCollectionOptions<TRecord>>();
return (new ElasticsearchVectorStoreRecordCollection<TRecord>(elasticsearchClient, collectionName, selectedOptions) as IVectorStoreRecordCollection<TKey, TRecord>)!;
});
AddVectorizedSearch<TKey, TRecord>(services, serviceId);
return services;
}
/// <summary>
/// Register an Elasticsearch <see cref="IVectorStoreRecordCollection{TKey, TRecord}"/> and <see cref="IVectorizedSearch{TRecord}"/> with the specified service ID
/// and where the <see cref="ElasticsearchClient"/> is constructed using the provided client settings.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TRecord">The type of the record.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to register the <see cref="IVectorStoreRecordCollection{TKey, TRecord}"/> on.</param>
/// <param name="collectionName">The name of the collection.</param>
/// <param name="clientSettings">The Elasticsearch client settings.</param>
/// <param name="options">Optional options to further configure the <see cref="IVectorStoreRecordCollection{TKey, TRecord}"/>.</param>
/// <param name="serviceId">An optional service id to use as the service key.</param>
/// <returns>Service collection.</returns>
public static IServiceCollection AddElasticsearchVectorStoreRecordCollection<TKey, TRecord>(
this IServiceCollection services,
string collectionName,
IElasticsearchClientSettings clientSettings,
ElasticsearchVectorStoreRecordCollectionOptions<TRecord>? options = default,
string? serviceId = default)
where TKey : notnull
{
services.AddKeyedSingleton<IVectorStoreRecordCollection<TKey, TRecord>>(
serviceId,
(sp, _) =>
{
var elasticsearchClient = new ElasticsearchClient(clientSettings);
var selectedOptions = options ?? sp.GetService<ElasticsearchVectorStoreRecordCollectionOptions<TRecord>>();
return (new ElasticsearchVectorStoreRecordCollection<TRecord>(elasticsearchClient, collectionName, selectedOptions) as IVectorStoreRecordCollection<TKey, TRecord>)!;
});
AddVectorizedSearch<TKey, TRecord>(services, serviceId);
return services;
}
/// <summary>
/// Also register the <see cref="IVectorStoreRecordCollection{TKey, TRecord}"/> with the given <paramref name="serviceId"/> as a <see cref="IVectorizedSearch{TRecord}"/>.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TRecord">The type of the data model that the collection should contain.</typeparam>
/// <param name="services">The service collection to register on.</param>
/// <param name="serviceId">The service id that the registrations should use.</param>
private static void AddVectorizedSearch<TKey, TRecord>(IServiceCollection services, string? serviceId)
where TKey : notnull
{
services.AddKeyedTransient<IVectorizedSearch<TRecord>>(
serviceId,
(sp, _) =>
{
return sp.GetRequiredKeyedService<IVectorStoreRecordCollection<TKey, TRecord>>(serviceId);
});
}
}