From 83ca085c3cb0fdfbfa71ff81472ac9577d6295a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 30 Apr 2025 11:04:22 +0300 Subject: [PATCH] Introduce typed chat client services --- .../Volo/Abp/AI/AbpAIChatClientOptions.cs | 7 ++++ .../Volo.Abp.AI/Volo/Abp/AI/AbpAIModule.cs | 10 ++++- .../AI/ChatClientConfigurationCollection.cs | 17 ++++++++- .../Volo/Abp/AI/ChatClientNameAttribute.cs | 37 +++++++++++++++++++ .../Volo.Abp.AI/Volo/Abp/AI/IChatClient.cs | 9 +++++ .../Volo/Abp/AI/TypedChatClient.cs | 18 +++++++++ 6 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientNameAttribute.cs create mode 100644 framework/src/Volo.Abp.AI/Volo/Abp/AI/IChatClient.cs create mode 100644 framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs diff --git a/framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIChatClientOptions.cs b/framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIChatClientOptions.cs index 054f4ac8e2..9369da6a0f 100644 --- a/framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIChatClientOptions.cs +++ b/framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIChatClientOptions.cs @@ -2,5 +2,12 @@ namespace Volo.Abp.AI; public class AbpAIChatClientOptions { + public const string ChatClientServiceKeyNamePrefix = "Abp.AI.ChatClient_"; + public ChatClientConfigurationDictionary ChatClients { get; } = new(); + + public static string GetChatClientServiceKeyName(string name) + { + return $"{ChatClientServiceKeyNamePrefix}{name}"; + } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIModule.cs b/framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIModule.cs index fed1291379..6ac2dd8453 100644 --- a/framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIModule.cs +++ b/framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIModule.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Volo.Abp.Modularity; namespace Volo.Abp.AI; @@ -28,8 +29,13 @@ public class AbpAIModule : AbpModule { builderConfigurer.Action(chatClientConfig.Builder); } - - context.Services.AddKeyedChatClient(chatClientConfig.Name, provider => chatClientConfig.Builder.Build(provider)); + + context.Services.AddKeyedChatClient( + AbpAIChatClientOptions.GetChatClientServiceKeyName(chatClientConfig.Name), + provider => chatClientConfig.Builder.Build(provider) + ); } + + context.Services.TryAddTransient(typeof(IChatClient<>), typeof(TypedChatClient<>)); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientConfigurationCollection.cs b/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientConfigurationCollection.cs index 2e62cd5833..78eff2b080 100644 --- a/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientConfigurationCollection.cs +++ b/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientConfigurationCollection.cs @@ -5,9 +5,24 @@ namespace Volo.Abp.AI; public class ChatClientConfigurationDictionary : Dictionary { + public static string DefaultName => "Default"; + public void ConfigureDefault(Action configureAction) => - Configure("Default", configureAction); + Configure(DefaultName, configureAction); + public void Configure(Action configureAction) + { + Configure(typeof(T), configureAction); + } + + public void Configure(Type chatClientType, Action configureAction) + { + Configure( + ChatClientNameAttribute.GetChatClientName(chatClientType), + configureAction + ); + } + public void Configure(string name, Action configureAction) { if (!this.TryGetValue(name, out var configuration)) diff --git a/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientNameAttribute.cs b/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientNameAttribute.cs new file mode 100644 index 0000000000..85c9d66de4 --- /dev/null +++ b/framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientNameAttribute.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq; + +namespace Volo.Abp.AI; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct)] +public class ChatClientNameAttribute : Attribute +{ + public string Name { get; } + + public ChatClientNameAttribute(string name) + { + Check.NotNull(name, nameof(name)); + + Name = name; + } + + public static string GetChatClientName() + { + return GetChatClientName(typeof(TChatClient)); + } + + public static string GetChatClientName(Type chatClientType) + { + var chatClientNameAttribute = chatClientType + .GetCustomAttributes(true) + .OfType() + .FirstOrDefault(); + + if (chatClientNameAttribute != null) + { + return chatClientNameAttribute.Name; + } + + return chatClientType.FullName!; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AI/Volo/Abp/AI/IChatClient.cs b/framework/src/Volo.Abp.AI/Volo/Abp/AI/IChatClient.cs new file mode 100644 index 0000000000..645cb60f87 --- /dev/null +++ b/framework/src/Volo.Abp.AI/Volo/Abp/AI/IChatClient.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.AI; + +namespace Volo.Abp.AI; + +public interface IChatClient : IChatClient + where T: class +{ + +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs b/framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs new file mode 100644 index 0000000000..b77e803cb9 --- /dev/null +++ b/framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.DependencyInjection; + +namespace Volo.Abp.AI; + +public class TypedChatClient : DelegatingChatClient, IChatClient + where T : class +{ + public TypedChatClient(IServiceProvider serviceProvider) + : base( + serviceProvider.GetRequiredKeyedService( + AbpAIChatClientOptions.GetChatClientServiceKeyName( + ChatClientNameAttribute.GetChatClientName())) + ) + { + } +} \ No newline at end of file