Browse Source

Refactor AI options to use workspace-based configuration

Replaces separate ChatClient and Kernel configuration dictionaries with a unified WorkspaceConfigurationDictionary. Removes related collection classes and extensions, updates service registration logic, and renames IKernel to IKernelAccessor. This change simplifies configuration and improves extensibility for managing multiple AI workspaces.
pull/23533/head
enisn 1 year ago
parent
commit
82f53dec94
No known key found for this signature in database GPG Key ID: A052619F04155D1C
  1. 56
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/Delegates/ChatClientWithTemperature.cs
  2. 4
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IKernelAccessor.cs
  3. 42
      framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIModule.cs
  4. 32
      framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIOptions.cs
  5. 37
      framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientConfigurationCollection.cs
  6. 16
      framework/src/Volo.Abp.AI/Volo/Abp/AI/Extensions/ChatClientWithTemperatureExtensions.cs
  7. 38
      framework/src/Volo.Abp.AI/Volo/Abp/AI/KernelConfigurationCollection.cs
  8. 8
      framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedKernel.cs

56
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/Delegates/ChatClientWithTemperature.cs

@ -1,56 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
namespace Volo.Abp.AI.Delegates;
public class ChatClientWithTemperature : DelegatingChatClient
{
private readonly float _temperature;
public ChatClientWithTemperature(IChatClient innerClient, float temperature)
: base(innerClient)
{
_temperature = temperature;
}
public override Task<ChatResponse> GetResponseAsync(
IEnumerable<ChatMessage> messages,
ChatOptions? options = null,
CancellationToken cancellationToken = default)
{
return base.GetResponseAsync(
messages,
BuildChatOptions(options),
cancellationToken
);
}
public override IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(
IEnumerable<ChatMessage> messages,
ChatOptions? options = null,
CancellationToken cancellationToken = default)
{
return base.GetStreamingResponseAsync(
messages,
BuildChatOptions(options),
cancellationToken
);
}
private ChatOptions BuildChatOptions(ChatOptions? options)
{
if (options?.Temperature != null)
{
return options;
}
options ??= new ChatOptions();
options.Temperature ??= _temperature;
return options;
}
}

4
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IKernel.cs → framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IKernelAccessor.cs

@ -2,8 +2,8 @@ using Microsoft.SemanticKernel;
namespace Volo.Abp.AI; namespace Volo.Abp.AI;
public interface IKernel<TWorkSpace> public interface IKernelAccessor<TWorkSpace>
where TWorkSpace : class where TWorkSpace : class
{ {
Kernel Kernel { get; } Kernel? Kernel { get; }
} }

42
framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIModule.cs

@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.SemanticKernel; using Microsoft.SemanticKernel;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Volo.Abp.AI; namespace Volo.Abp.AI;
@ -11,31 +12,34 @@ namespace Volo.Abp.AI;
)] )]
public class AbpAIModule : AbpModule public class AbpAIModule : AbpModule
{ {
public const string DefaultWorkspaceName = "Default";
public override void PostConfigureServices(ServiceConfigurationContext context) public override void PostConfigureServices(ServiceConfigurationContext context)
{ {
var options = context.Services.ExecutePreConfiguredActions<AbpAIOptions>(); var options = context.Services.ExecutePreConfiguredActions<AbpAIOptions>();
foreach (var chatClientConfig in options.ChatClients.Values) foreach (var workspaceConfig in options.Workspaces.Values)
{ {
if (chatClientConfig.Builder == null) if (workspaceConfig.ChatClient?.Builder is null)
{ {
throw new AbpException("ChatClientBuilder is not properly configured. Set the Builder property."); continue;
} }
foreach (var builderConfigurer in chatClientConfig.BuilderConfigurers) foreach (var builderConfigurer in workspaceConfig.ChatClient.BuilderConfigurers)
{ {
builderConfigurer.Action(chatClientConfig.Builder); builderConfigurer.Action(workspaceConfig.ChatClient.Builder!);
} }
context.Services.AddKeyedChatClient( context.Services.AddKeyedChatClient(
AbpAIOptions.GetChatClientServiceKeyName(chatClientConfig.Name), AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.ChatClient.Name),
provider => chatClientConfig.Builder.Build(provider) provider => workspaceConfig.ChatClient.Builder!.Build(provider)
); );
if (chatClientConfig.Name == ChatClientConfigurationDictionary.DefaultChatClientName) if (workspaceConfig.ChatClient.Name == DefaultWorkspaceName)
{ {
context.Services.AddTransient<IChatClient>(sp => sp.GetRequiredKeyedService<IChatClient>( context.Services.AddTransient<IChatClient>(sp => sp.GetRequiredKeyedService<IChatClient>(
AbpAIOptions.GetChatClientServiceKeyName(chatClientConfig.Name) AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.ChatClient.Name)
) )
); );
} }
@ -43,31 +47,31 @@ public class AbpAIModule : AbpModule
context.Services.TryAddTransient(typeof(IChatClient<>), typeof(TypedChatClient<>)); context.Services.TryAddTransient(typeof(IChatClient<>), typeof(TypedChatClient<>));
foreach (var kernelConfig in options.Kernels.Values) foreach (var workspaceConfig in options.Workspaces.Values)
{ {
if (kernelConfig.Builder == null) if (workspaceConfig.Kernel?.Builder is null)
{ {
throw new AbpException("KernelBuilder is not properly configured. Set the Builder property."); continue;
} }
foreach (var builderConfigurer in kernelConfig.BuilderConfigurers) foreach (var builderConfigurer in workspaceConfig.Kernel.BuilderConfigurers)
{ {
builderConfigurer.Action(kernelConfig.Builder); builderConfigurer.Action(workspaceConfig.Kernel.Builder!);
} }
context.Services.AddKeyedSingleton<Kernel>( context.Services.AddKeyedSingleton<Kernel>(
AbpAIOptions.GetKernelServiceKeyName(kernelConfig.Name), AbpAIOptions.GetKernelServiceKeyName(workspaceConfig.Kernel.Name),
(provider, _) => kernelConfig.Builder.Build()); (provider, _) => workspaceConfig.Kernel.Builder!.Build());
if (kernelConfig.Name == KernelConfigurationDictionary.DefaultKernelName) if (workspaceConfig.Kernel.Name == DefaultWorkspaceName)
{ {
context.Services.AddSingleton<Kernel>(sp => sp.GetRequiredKeyedService<Kernel>( context.Services.AddSingleton<Kernel>(sp => sp.GetRequiredKeyedService<Kernel>(
AbpAIOptions.GetKernelServiceKeyName(kernelConfig.Name) AbpAIOptions.GetKernelServiceKeyName(workspaceConfig.Kernel.Name)
) )
); );
} }
} }
context.Services.TryAddTransient(typeof(IKernel<>), typeof(TypedKernel<>)); context.Services.TryAddTransient(typeof(IKernelAccessor<>), typeof(TypedKernelAccessor<>));
} }
} }

32
framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIOptions.cs

@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AI; namespace Volo.Abp.AI;
public class AbpAIOptions public class AbpAIOptions
@ -5,8 +8,7 @@ public class AbpAIOptions
public const string ChatClientServiceKeyNamePrefix = "Abp.AI.ChatClient_"; public const string ChatClientServiceKeyNamePrefix = "Abp.AI.ChatClient_";
public const string KernelServiceKeyNamePrefix = "Abp.AI.Kernel_"; public const string KernelServiceKeyNamePrefix = "Abp.AI.Kernel_";
public ChatClientConfigurationDictionary ChatClients { get; } = new(); public WorkspaceConfigurationDictionary Workspaces { get; } = new();
public KernelConfigurationDictionary Kernels { get; } = new();
public static string GetChatClientServiceKeyName(string name) public static string GetChatClientServiceKeyName(string name)
{ {
@ -17,4 +19,30 @@ public class AbpAIOptions
{ {
return $"{KernelServiceKeyNamePrefix}{name}"; return $"{KernelServiceKeyNamePrefix}{name}";
} }
}
public class WorkspaceConfiguration
{
public ChatClientConfiguration? ChatClient { get; set; }
public KernelConfiguration? Kernel { get; set; }
}
public class WorkspaceConfigurationDictionary : Dictionary<string, WorkspaceConfiguration>
{
public void Configure<TWorkSpace>(Action<WorkspaceConfiguration> configureAction)
where TWorkSpace : class
{
Configure(WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>(), configureAction);
}
public void Configure(string name, Action<WorkspaceConfiguration> configureAction)
{
if (!TryGetValue(name, out var configuration))
{
configuration = new WorkspaceConfiguration();
this[name] = configuration;
}
configureAction(configuration);
}
} }

37
framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientConfigurationCollection.cs

@ -1,37 +0,0 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AI;
public class ChatClientConfigurationDictionary : Dictionary<string, ChatClientConfiguration>
{
public static string DefaultChatClientName => "Default";
// TODO: Consider to rename to Configure only.
public void ConfigureDefault(Action<ChatClientConfiguration> configureAction) =>
Configure(DefaultChatClientName, configureAction);
public void Configure<T>(Action<ChatClientConfiguration> configureAction)
{
Configure(typeof(T), configureAction);
}
public void Configure(Type chatClientType, Action<ChatClientConfiguration> configureAction)
{
Configure(
WorkspaceNameAttribute.GetWorkspaceName(chatClientType),
configureAction
);
}
public void Configure(string name, Action<ChatClientConfiguration> configureAction)
{
if (!this.TryGetValue(name, out var configuration))
{
configuration = new ChatClientConfiguration(name);
this[name] = configuration;
}
configureAction(configuration);
}
}

16
framework/src/Volo.Abp.AI/Volo/Abp/AI/Extensions/ChatClientWithTemperatureExtensions.cs

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.AI;
using Volo.Abp.AI.Delegates;
namespace Volo.Abp.AI.Extensions;
public static class ChatClientWithTemperatureExtensions
{
public static ChatClientBuilder UseTemperature(this ChatClientBuilder builder, float temperature)
{
return builder.Use(chatClient => new ChatClientWithTemperature(chatClient, temperature));
}
}

38
framework/src/Volo.Abp.AI/Volo/Abp/AI/KernelConfigurationCollection.cs

@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.AI;
public class KernelConfigurationDictionary : Dictionary<string, KernelConfiguration>
{
public static string DefaultKernelName => "Default";
public void ConfigureDefault(Action<KernelConfiguration> configureAction) =>
Configure(DefaultKernelName, configureAction);
public void Configure<TWorkSpace>(Action<KernelConfiguration> configureAction)
{
Configure(typeof(TWorkSpace), configureAction);
}
public void Configure(Type workspaceType, Action<KernelConfiguration> configureAction)
{
Configure(
WorkspaceNameAttribute.GetWorkspaceName(workspaceType),
configureAction
);
}
public void Configure(string name, Action<KernelConfiguration> configureAction)
{
if (!TryGetValue(name, out var configuration))
{
configuration = new KernelConfiguration(name);
this[name] = configuration;
}
configureAction(configuration);
}
}

8
framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedKernel.cs

@ -4,14 +4,14 @@ using Microsoft.SemanticKernel;
namespace Volo.Abp.AI; namespace Volo.Abp.AI;
public class TypedKernel<TWorkSpace> : IKernel<TWorkSpace> public class TypedKernelAccessor<TWorkSpace> : IKernelAccessor<TWorkSpace>
where TWorkSpace : class where TWorkSpace : class
{ {
public Kernel Kernel { get; } public Kernel? Kernel { get; }
public TypedKernel(IServiceProvider serviceProvider) public TypedKernelAccessor(IServiceProvider serviceProvider)
{ {
Kernel = serviceProvider.GetRequiredKeyedService<Kernel>( Kernel = serviceProvider.GetKeyedService<Kernel>(
AbpAIOptions.GetKernelServiceKeyName( AbpAIOptions.GetKernelServiceKeyName(
WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>())); WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>()));
} }

Loading…
Cancel
Save