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;
public interface IKernel<TWorkSpace>
public interface IKernelAccessor<TWorkSpace>
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.SemanticKernel;
using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Volo.Abp.AI;
@ -11,31 +12,34 @@ namespace Volo.Abp.AI;
)]
public class AbpAIModule : AbpModule
{
public const string DefaultWorkspaceName = "Default";
public override void PostConfigureServices(ServiceConfigurationContext context)
{
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(
AbpAIOptions.GetChatClientServiceKeyName(chatClientConfig.Name),
provider => chatClientConfig.Builder.Build(provider)
AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.ChatClient.Name),
provider => workspaceConfig.ChatClient.Builder!.Build(provider)
);
if (chatClientConfig.Name == ChatClientConfigurationDictionary.DefaultChatClientName)
if (workspaceConfig.ChatClient.Name == DefaultWorkspaceName)
{
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<>));
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>(
AbpAIOptions.GetKernelServiceKeyName(kernelConfig.Name),
(provider, _) => kernelConfig.Builder.Build());
AbpAIOptions.GetKernelServiceKeyName(workspaceConfig.Kernel.Name),
(provider, _) => workspaceConfig.Kernel.Builder!.Build());
if (kernelConfig.Name == KernelConfigurationDictionary.DefaultKernelName)
if (workspaceConfig.Kernel.Name == DefaultWorkspaceName)
{
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;
public class AbpAIOptions
@ -5,8 +8,7 @@ public class AbpAIOptions
public const string ChatClientServiceKeyNamePrefix = "Abp.AI.ChatClient_";
public const string KernelServiceKeyNamePrefix = "Abp.AI.Kernel_";
public ChatClientConfigurationDictionary ChatClients { get; } = new();
public KernelConfigurationDictionary Kernels { get; } = new();
public WorkspaceConfigurationDictionary Workspaces { get; } = new();
public static string GetChatClientServiceKeyName(string name)
{
@ -17,4 +19,30 @@ public class AbpAIOptions
{
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;
public class TypedKernel<TWorkSpace> : IKernel<TWorkSpace>
public class TypedKernelAccessor<TWorkSpace> : IKernelAccessor<TWorkSpace>
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(
WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>()));
}

Loading…
Cancel
Save