diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ChatClientBuilderServiceCollectionExtensions.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ChatClientBuilderServiceCollectionExtensions.cs
index c3d8ab88edb..68c7c6f2cea 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ChatClientBuilderServiceCollectionExtensions.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ChatClientBuilderServiceCollectionExtensions.cs
@@ -13,27 +13,31 @@ public static class ChatClientBuilderServiceCollectionExtensions
/// Registers a singleton in the .
/// The to which the client should be added.
/// The inner that represents the underlying backend.
+ /// The service lifetime for the client. Defaults to .
/// A that can be used to build a pipeline around the inner client.
/// The client is registered as a singleton service.
public static ChatClientBuilder AddChatClient(
this IServiceCollection serviceCollection,
- IChatClient innerClient)
- => AddChatClient(serviceCollection, _ => innerClient);
+ IChatClient innerClient,
+ ServiceLifetime lifetime = ServiceLifetime.Singleton)
+ => AddChatClient(serviceCollection, _ => innerClient, lifetime);
/// Registers a singleton in the .
/// The to which the client should be added.
/// A callback that produces the inner that represents the underlying backend.
+ /// The service lifetime for the client. Defaults to .
/// A that can be used to build a pipeline around the inner client.
/// The client is registered as a singleton service.
public static ChatClientBuilder AddChatClient(
this IServiceCollection serviceCollection,
- Func innerClientFactory)
+ Func innerClientFactory,
+ ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
_ = Throw.IfNull(serviceCollection);
_ = Throw.IfNull(innerClientFactory);
var builder = new ChatClientBuilder(innerClientFactory);
- _ = serviceCollection.AddSingleton(builder.Build);
+ serviceCollection.Add(new ServiceDescriptor(typeof(IChatClient), builder.Build, lifetime));
return builder;
}
@@ -41,31 +45,35 @@ public static ChatClientBuilder AddChatClient(
/// The to which the client should be added.
/// The key with which to associate the client.
/// The inner that represents the underlying backend.
+ /// The service lifetime for the client. Defaults to .
/// A that can be used to build a pipeline around the inner client.
/// The client is registered as a scoped service.
public static ChatClientBuilder AddKeyedChatClient(
this IServiceCollection serviceCollection,
object serviceKey,
- IChatClient innerClient)
- => AddKeyedChatClient(serviceCollection, serviceKey, _ => innerClient);
+ IChatClient innerClient,
+ ServiceLifetime lifetime = ServiceLifetime.Singleton)
+ => AddKeyedChatClient(serviceCollection, serviceKey, _ => innerClient, lifetime);
/// Registers a keyed singleton in the .
/// The to which the client should be added.
/// The key with which to associate the client.
/// A callback that produces the inner that represents the underlying backend.
+ /// The service lifetime for the client. Defaults to .
/// A that can be used to build a pipeline around the inner client.
/// The client is registered as a scoped service.
public static ChatClientBuilder AddKeyedChatClient(
this IServiceCollection serviceCollection,
object serviceKey,
- Func innerClientFactory)
+ Func innerClientFactory,
+ ServiceLifetime lifetime = ServiceLifetime.Singleton)
{
_ = Throw.IfNull(serviceCollection);
_ = Throw.IfNull(serviceKey);
_ = Throw.IfNull(innerClientFactory);
var builder = new ChatClientBuilder(innerClientFactory);
- _ = serviceCollection.AddKeyedSingleton(serviceKey, (services, _) => builder.Build(services));
+ serviceCollection.Add(new ServiceDescriptor(typeof(IChatClient), serviceKey, factory: (services, serviceKey) => builder.Build(services), lifetime));
return builder;
}
}
diff --git a/src/Libraries/Microsoft.Extensions.AI/Embeddings/EmbeddingGeneratorBuilderServiceCollectionExtensions.cs b/src/Libraries/Microsoft.Extensions.AI/Embeddings/EmbeddingGeneratorBuilderServiceCollectionExtensions.cs
index 1c57fb08215..b35fcd7f4ff 100644
--- a/src/Libraries/Microsoft.Extensions.AI/Embeddings/EmbeddingGeneratorBuilderServiceCollectionExtensions.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/Embeddings/EmbeddingGeneratorBuilderServiceCollectionExtensions.cs
@@ -15,31 +15,35 @@ public static class EmbeddingGeneratorBuilderServiceCollectionExtensions
/// The type of embeddings to generate.
/// The to which the generator should be added.
/// The inner that represents the underlying backend.
+ /// The service lifetime for the client. Defaults to .
/// An that can be used to build a pipeline around the inner generator.
/// The generator is registered as a singleton service.
public static EmbeddingGeneratorBuilder AddEmbeddingGenerator(
this IServiceCollection serviceCollection,
- IEmbeddingGenerator innerGenerator)
+ IEmbeddingGenerator innerGenerator,
+ ServiceLifetime lifetime = ServiceLifetime.Singleton)
where TEmbedding : Embedding
- => AddEmbeddingGenerator(serviceCollection, _ => innerGenerator);
+ => AddEmbeddingGenerator(serviceCollection, _ => innerGenerator, lifetime);
/// Registers a singleton embedding generator in the .
/// The type from which embeddings will be generated.
/// The type of embeddings to generate.
/// The to which the generator should be added.
/// A callback that produces the inner that represents the underlying backend.
+ /// The service lifetime for the client. Defaults to .
/// An that can be used to build a pipeline around the inner generator.
/// The generator is registered as a singleton service.
public static EmbeddingGeneratorBuilder AddEmbeddingGenerator(
this IServiceCollection serviceCollection,
- Func> innerGeneratorFactory)
+ Func> innerGeneratorFactory,
+ ServiceLifetime lifetime = ServiceLifetime.Singleton)
where TEmbedding : Embedding
{
_ = Throw.IfNull(serviceCollection);
_ = Throw.IfNull(innerGeneratorFactory);
var builder = new EmbeddingGeneratorBuilder(innerGeneratorFactory);
- _ = serviceCollection.AddSingleton(builder.Build);
+ serviceCollection.Add(new ServiceDescriptor(typeof(IEmbeddingGenerator), builder.Build, lifetime));
return builder;
}
@@ -49,14 +53,16 @@ public static EmbeddingGeneratorBuilder AddEmbeddingGenerato
/// The to which the generator should be added.
/// The key with which to associated the generator.
/// The inner that represents the underlying backend.
+ /// The service lifetime for the client. Defaults to .
/// An that can be used to build a pipeline around the inner generator.
/// The generator is registered as a singleton service.
public static EmbeddingGeneratorBuilder AddKeyedEmbeddingGenerator(
this IServiceCollection serviceCollection,
object serviceKey,
- IEmbeddingGenerator innerGenerator)
+ IEmbeddingGenerator innerGenerator,
+ ServiceLifetime lifetime = ServiceLifetime.Singleton)
where TEmbedding : Embedding
- => AddKeyedEmbeddingGenerator(serviceCollection, serviceKey, _ => innerGenerator);
+ => AddKeyedEmbeddingGenerator(serviceCollection, serviceKey, _ => innerGenerator, lifetime);
/// Registers a keyed singleton embedding generator in the .
/// The type from which embeddings will be generated.
@@ -64,12 +70,14 @@ public static EmbeddingGeneratorBuilder AddKeyedEmbeddingGen
/// The to which the generator should be added.
/// The key with which to associated the generator.
/// A callback that produces the inner that represents the underlying backend.
+ /// The service lifetime for the client. Defaults to .
/// An that can be used to build a pipeline around the inner generator.
/// The generator is registered as a singleton service.
public static EmbeddingGeneratorBuilder AddKeyedEmbeddingGenerator(
this IServiceCollection serviceCollection,
object serviceKey,
- Func> innerGeneratorFactory)
+ Func> innerGeneratorFactory,
+ ServiceLifetime lifetime = ServiceLifetime.Singleton)
where TEmbedding : Embedding
{
_ = Throw.IfNull(serviceCollection);
@@ -77,7 +85,7 @@ public static EmbeddingGeneratorBuilder AddKeyedEmbeddingGen
_ = Throw.IfNull(innerGeneratorFactory);
var builder = new EmbeddingGeneratorBuilder(innerGeneratorFactory);
- _ = serviceCollection.AddKeyedSingleton(serviceKey, (services, _) => builder.Build(services));
+ serviceCollection.Add(new ServiceDescriptor(typeof(IEmbeddingGenerator), serviceKey, factory: (services, serviceKey) => builder.Build(services), lifetime));
return builder;
}
}
diff --git a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/DependencyInjectionPatterns.cs b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/DependencyInjectionPatterns.cs
index c99d4511f75..c2f288165cb 100644
--- a/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/DependencyInjectionPatterns.cs
+++ b/test/Libraries/Microsoft.Extensions.AI.Tests/ChatCompletion/DependencyInjectionPatterns.cs
@@ -109,6 +109,96 @@ public void CanRegisterKeyedSingletonUsingSharedInstance()
Assert.IsType(instance.InnerClient);
}
+ [Theory]
+ [InlineData(null)]
+ [InlineData(ServiceLifetime.Singleton)]
+ [InlineData(ServiceLifetime.Scoped)]
+ [InlineData(ServiceLifetime.Transient)]
+ public void AddChatClient_RegistersExpectedLifetime(ServiceLifetime? lifetime)
+ {
+ ServiceCollection sc = new();
+ ServiceLifetime expectedLifetime = lifetime ?? ServiceLifetime.Singleton;
+ ChatClientBuilder builder = lifetime.HasValue
+ ? sc.AddChatClient(services => new TestChatClient(), lifetime.Value)
+ : sc.AddChatClient(services => new TestChatClient());
+
+ ServiceDescriptor sd = Assert.Single(sc);
+ Assert.Equal(typeof(IChatClient), sd.ServiceType);
+ Assert.False(sd.IsKeyedService);
+ Assert.Null(sd.ImplementationInstance);
+ Assert.NotNull(sd.ImplementationFactory);
+ Assert.IsType(sd.ImplementationFactory(null!));
+ Assert.Equal(expectedLifetime, sd.Lifetime);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData(ServiceLifetime.Singleton)]
+ [InlineData(ServiceLifetime.Scoped)]
+ [InlineData(ServiceLifetime.Transient)]
+ public void AddKeyedChatClient_RegistersExpectedLifetime(ServiceLifetime? lifetime)
+ {
+ ServiceCollection sc = new();
+ ServiceLifetime expectedLifetime = lifetime ?? ServiceLifetime.Singleton;
+ ChatClientBuilder builder = lifetime.HasValue
+ ? sc.AddKeyedChatClient("key", services => new TestChatClient(), lifetime.Value)
+ : sc.AddKeyedChatClient("key", services => new TestChatClient());
+
+ ServiceDescriptor sd = Assert.Single(sc);
+ Assert.Equal(typeof(IChatClient), sd.ServiceType);
+ Assert.True(sd.IsKeyedService);
+ Assert.Equal("key", sd.ServiceKey);
+ Assert.Null(sd.KeyedImplementationInstance);
+ Assert.NotNull(sd.KeyedImplementationFactory);
+ Assert.IsType(sd.KeyedImplementationFactory(null!, null!));
+ Assert.Equal(expectedLifetime, sd.Lifetime);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData(ServiceLifetime.Singleton)]
+ [InlineData(ServiceLifetime.Scoped)]
+ [InlineData(ServiceLifetime.Transient)]
+ public void AddEmbeddingGenerator_RegistersExpectedLifetime(ServiceLifetime? lifetime)
+ {
+ ServiceCollection sc = new();
+ ServiceLifetime expectedLifetime = lifetime ?? ServiceLifetime.Singleton;
+ var builder = lifetime.HasValue
+ ? sc.AddEmbeddingGenerator(services => new TestEmbeddingGenerator(), lifetime.Value)
+ : sc.AddEmbeddingGenerator(services => new TestEmbeddingGenerator());
+
+ ServiceDescriptor sd = Assert.Single(sc);
+ Assert.Equal(typeof(IEmbeddingGenerator>), sd.ServiceType);
+ Assert.False(sd.IsKeyedService);
+ Assert.Null(sd.ImplementationInstance);
+ Assert.NotNull(sd.ImplementationFactory);
+ Assert.IsType(sd.ImplementationFactory(null!));
+ Assert.Equal(expectedLifetime, sd.Lifetime);
+ }
+
+ [Theory]
+ [InlineData(null)]
+ [InlineData(ServiceLifetime.Singleton)]
+ [InlineData(ServiceLifetime.Scoped)]
+ [InlineData(ServiceLifetime.Transient)]
+ public void AddKeyedEmbeddingGenerator_RegistersExpectedLifetime(ServiceLifetime? lifetime)
+ {
+ ServiceCollection sc = new();
+ ServiceLifetime expectedLifetime = lifetime ?? ServiceLifetime.Singleton;
+ var builder = lifetime.HasValue
+ ? sc.AddKeyedEmbeddingGenerator("key", services => new TestEmbeddingGenerator(), lifetime.Value)
+ : sc.AddKeyedEmbeddingGenerator("key", services => new TestEmbeddingGenerator());
+
+ ServiceDescriptor sd = Assert.Single(sc);
+ Assert.Equal(typeof(IEmbeddingGenerator>), sd.ServiceType);
+ Assert.True(sd.IsKeyedService);
+ Assert.Equal("key", sd.ServiceKey);
+ Assert.Null(sd.KeyedImplementationInstance);
+ Assert.NotNull(sd.KeyedImplementationFactory);
+ Assert.IsType(sd.KeyedImplementationFactory(null!, null!));
+ Assert.Equal(expectedLifetime, sd.Lifetime);
+ }
+
public class SingletonMiddleware(IChatClient inner, IServiceProvider services) : DelegatingChatClient(inner)
{
public new IChatClient InnerClient => base.InnerClient;