Browse Source

Merge pull request #23782 from abpframework/ai-code-review-3

AI Package Refactor & Review Changes
pull/23788/head
Enis Necipoglu 11 months ago
committed by GitHub
parent
commit
82cb05f0c3
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 4
      Directory.Packages.props
  2. 5
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/AbpAIAbstractionsModule.cs
  3. 1
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IChatClient.cs
  4. 14
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IChatClientAccessor.cs
  5. 2
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IKernelAccessor.cs
  6. 19
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/NullChatClientAccessor.cs
  7. 20
      framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/NullKernelAccessor.cs
  8. 129
      framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIModule.cs
  9. 17
      framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIOptions.cs
  10. 22
      framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIWorkspaceOptions.cs
  11. 35
      framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientAccessor.cs
  12. 2
      framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientConfiguration.cs
  13. 6
      framework/src/Volo.Abp.AI/Volo/Abp/AI/KernelAccessor.cs
  14. 2
      framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs

4
Directory.Packages.props

@ -88,9 +88,7 @@
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0-rc.*" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0-rc.*" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.*" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.*" /> <PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.Extensions.AI" Version="9.7.1" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="9.7.1" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.61.0" /> <PackageVersion Include="Microsoft.SemanticKernel" Version="1.61.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Abstractions" Version="1.61.0" /> <PackageVersion Include="Microsoft.SemanticKernel.Abstractions" Version="1.61.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.9.0" /> <PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.9.0" />

5
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/AbpAIAbstractionsModule.cs

@ -1,7 +1,4 @@
using Microsoft.Extensions.AI; using Volo.Abp.Modularity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Modularity;
namespace Volo.Abp.AI; namespace Volo.Abp.AI;

1
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IChatClient.cs

@ -5,4 +5,5 @@ namespace Volo.Abp.AI;
public interface IChatClient<TWorkSpace> : IChatClient public interface IChatClient<TWorkSpace> : IChatClient
where TWorkSpace : class where TWorkSpace : class
{ {
} }

14
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IChatClientAccessor.cs

@ -0,0 +1,14 @@
using Microsoft.Extensions.AI;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AI;
public interface IChatClientAccessor
{
IChatClient? ChatClient { get; }
}
public interface IChatClientAccessor<TWorkSpace> : IChatClientAccessor
where TWorkSpace : class
{
}

2
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/IKernelAccessor.cs

@ -11,4 +11,4 @@ public interface IKernelAccessor
public interface IKernelAccessor<TWorkSpace> : IKernelAccessor public interface IKernelAccessor<TWorkSpace> : IKernelAccessor
where TWorkSpace : class where TWorkSpace : class
{ {
} }

19
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/NullChatClientAccessor.cs

@ -0,0 +1,19 @@
using Microsoft.Extensions.AI;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AI;
[Dependency(TryRegister = true)]
[ExposeServices(typeof(IChatClientAccessor))]
public class NullChatClientAccessor : IChatClientAccessor
{
public IChatClient? ChatClient => null;
}
[Dependency(TryRegister = true)]
[ExposeServices(typeof(IChatClientAccessor<>))]
public class NullChatClientAccessor<TWorkSpace> : IChatClientAccessor<TWorkSpace>
where TWorkSpace : class
{
public IChatClient? ChatClient => null;
}

20
framework/src/Volo.Abp.AI.Abstractions/Volo/Abp/AI/NullKernelAccessor.cs

@ -0,0 +1,20 @@
using Microsoft.SemanticKernel;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AI;
[Dependency(TryRegister = true)]
[ExposeServices(typeof(IKernelAccessor))]
public class NullKernelAccessor : IKernelAccessor
{
public Kernel? Kernel => null;
}
[Dependency(TryRegister = true)]
[ExposeServices(typeof(IKernelAccessor<>))]
public class NullKernelAccessor<TWorkSpace> : IKernelAccessor<TWorkSpace>
where TWorkSpace : class
{
public Kernel? Kernel => null;
}

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

@ -5,7 +5,6 @@ using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.SemanticKernel; using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
namespace Volo.Abp.AI; namespace Volo.Abp.AI;
@ -17,80 +16,88 @@ public class AbpAIModule : AbpModule
{ {
public const string DefaultWorkspaceName = "Default"; public const string DefaultWorkspaceName = "Default";
public override void PostConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
var options = context.Services.ExecutePreConfiguredActions<AbpAIOptions>(); var options = context.Services.ExecutePreConfiguredActions<AbpAIWorkspaceOptions>();
context.Services.Configure<AbpAIWorkspaceOptions>(workspaceOptions => context.Services.Configure<AbpAIOptions>(workspaceOptions =>
{ {
workspaceOptions.ConfiguredWorkspaceNames.UnionWith(options.Workspaces.Select(x => x.Key).ToArray()); workspaceOptions.ConfiguredWorkspaceNames.AddIfNotContains(
options.Workspaces.Select(x => x.Key)
);
}); });
foreach (var workspaceConfig in options.Workspaces.Values) foreach (var workspaceConfig in options.Workspaces.Values)
{ {
if (workspaceConfig.ChatClient?.Builder is null) ConfigureChatClient(context, workspaceConfig);
{ ConfigureKernel(context, workspaceConfig);
continue; }
}
context.Services.TryAddTransient(typeof(IChatClient<>), typeof(TypedChatClient<>));
foreach (var builderConfigurer in workspaceConfig.ChatClient.BuilderConfigurers) context.Services.TryAddTransient(typeof(IKernelAccessor<>), typeof(KernelAccessor<>));
{ }
builderConfigurer.Action(workspaceConfig.ChatClient.Builder!);
} private static void ConfigureKernel(ServiceConfigurationContext context, WorkspaceConfiguration workspaceConfig)
{
context.Services.AddKeyedChatClient( if (workspaceConfig.Kernel.Builder is null)
AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.Name), {
provider => workspaceConfig.ChatClient.Builder!.Build(provider), return;
ServiceLifetime.Transient }
foreach (var builderConfigurer in workspaceConfig.Kernel.BuilderConfigurers)
{
builderConfigurer.Action(workspaceConfig.Kernel.Builder!);
}
// TODO: Check if we can use transient instead of singleton for Kernel
context.Services.AddKeyedTransient<Kernel>(
AbpAIWorkspaceOptions.GetKernelServiceKeyName(workspaceConfig.Name),
(provider, _) => workspaceConfig.Kernel.Builder!.Build());
if (workspaceConfig.Name == DefaultWorkspaceName)
{
context.Services.AddTransient<Kernel>(sp => sp.GetRequiredKeyedService<Kernel>(
AbpAIWorkspaceOptions.GetKernelServiceKeyName(workspaceConfig.Name)
)
); );
}
if (workspaceConfig.Name == DefaultWorkspaceName) if (workspaceConfig.ChatClient?.Builder is null)
{ {
context.Services.AddTransient<IChatClient>(sp => sp.GetRequiredKeyedService<IChatClient>( context.Services.AddKeyedTransient<IChatClient>(
AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.Name) AbpAIWorkspaceOptions.GetChatClientServiceKeyName(workspaceConfig.Name),
) (sp, _) => sp.GetKeyedService<Kernel>(AbpAIWorkspaceOptions.GetKernelServiceKeyName(workspaceConfig.Name))?
); .GetRequiredService<IChatClient>()
} ?? throw new InvalidOperationException("Kernel or IChatClient not found with workspace name: " + workspaceConfig.Name)
);
} }
}
context.Services.TryAddTransient(typeof(IChatClient<>), typeof(TypedChatClient<>)); private static void ConfigureChatClient(ServiceConfigurationContext context, WorkspaceConfiguration workspaceConfig)
{
if (workspaceConfig.ChatClient.Builder is null)
{
return;
}
foreach (var workspaceConfig in options.Workspaces.Values) foreach (var builderConfigurer in workspaceConfig.ChatClient.BuilderConfigurers)
{ {
if (workspaceConfig.Kernel?.Builder is null) builderConfigurer.Action(workspaceConfig.ChatClient.Builder);
{
continue;
}
foreach (var builderConfigurer in workspaceConfig.Kernel.BuilderConfigurers)
{
builderConfigurer.Action(workspaceConfig.Kernel.Builder!);
}
// TODO: Check if we can use transient instead of singleton for Kernel
context.Services.AddKeyedTransient<Kernel>(
AbpAIOptions.GetKernelServiceKeyName(workspaceConfig.Name),
(provider, _) => workspaceConfig.Kernel.Builder!.Build());
if (workspaceConfig.Name == DefaultWorkspaceName)
{
context.Services.AddTransient<Kernel>(sp => sp.GetRequiredKeyedService<Kernel>(
AbpAIOptions.GetKernelServiceKeyName(workspaceConfig.Name)
)
);
}
if (workspaceConfig.ChatClient?.Builder is null)
{
context.Services.AddKeyedTransient<IChatClient>(
AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.Name),
(sp, _) => sp.GetKeyedService<Kernel>(AbpAIOptions.GetKernelServiceKeyName(workspaceConfig.Name))?
.GetRequiredService<IChatClient>()
?? throw new InvalidOperationException("Kernel or IChatClient not found with workspace name: " + workspaceConfig.Name)
);
}
} }
context.Services.TryAddTransient(typeof(IKernelAccessor<>), typeof(TypedKernelAccessor<>)); var serviceName = AbpAIWorkspaceOptions.GetChatClientServiceKeyName(workspaceConfig.Name);
context.Services.AddKeyedChatClient(
serviceName,
provider => workspaceConfig.ChatClient.Builder.Build(provider),
ServiceLifetime.Transient
);
if (workspaceConfig.Name == DefaultWorkspaceName)
{
context.Services.AddTransient<IChatClient>(
sp => sp.GetRequiredKeyedService<IChatClient>(serviceName)
);
}
} }
} }

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

@ -1,19 +1,8 @@
using System.Collections.Generic;
namespace Volo.Abp.AI; namespace Volo.Abp.AI;
public class AbpAIOptions public class AbpAIOptions
{ {
public const string ChatClientServiceKeyNamePrefix = "Abp.AI.ChatClient_"; public HashSet<string> ConfiguredWorkspaceNames { get; } = new();
public const string KernelServiceKeyNamePrefix = "Abp.AI.Kernel_";
public WorkspaceConfigurationDictionary Workspaces { get; } = new();
public static string GetChatClientServiceKeyName(string name)
{
return $"{ChatClientServiceKeyNamePrefix}{name}";
}
public static string GetKernelServiceKeyName(string name)
{
return $"{KernelServiceKeyNamePrefix}{name}";
}
} }

22
framework/src/Volo.Abp.AI/Volo/Abp/AI/AbpAIWorkspaceOptions.cs

@ -1,8 +1,24 @@
using System.Collections.Generic;
namespace Volo.Abp.AI; namespace Volo.Abp.AI;
/// <summary>
/// Pre-configured options for the AI workspaces. Not used via Options pattern. Use it with 'PreConfigure' method in a Module class.
/// In example:
/// <code>PreConfigure&lt;AbpAIWorkspaceOptions&gt;(options => { });</code>
/// </summary>
public class AbpAIWorkspaceOptions public class AbpAIWorkspaceOptions
{ {
public HashSet<string> ConfiguredWorkspaceNames { get; } = new(); public const string ChatClientServiceKeyNamePrefix = "Abp.AI.ChatClient_";
public const string KernelServiceKeyNamePrefix = "Abp.AI.Kernel_";
public WorkspaceConfigurationDictionary Workspaces { get; } = new();
public static string GetChatClientServiceKeyName(string name)
{
return $"{ChatClientServiceKeyNamePrefix}{name}";
}
public static string GetKernelServiceKeyName(string name)
{
return $"{KernelServiceKeyNamePrefix}{name}";
}
} }

35
framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientAccessor.cs

@ -0,0 +1,35 @@
using System;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AI;
[Dependency(ReplaceServices = true, TryRegister = true)]
[ExposeServices(typeof(IChatClientAccessor))]
public class ChatClientAccessor : IChatClientAccessor
{
public IChatClient? ChatClient { get; }
public ChatClientAccessor(IServiceProvider serviceProvider)
{
ChatClient = serviceProvider.GetKeyedService<IChatClient>(
AbpAIWorkspaceOptions.GetChatClientServiceKeyName(
AbpAIModule.DefaultWorkspaceName));
}
}
[Dependency(ReplaceServices = true, TryRegister = true)]
[ExposeServices(typeof(IChatClientAccessor))]
public class ChatClientAccessor<TWorkSpace> : IChatClientAccessor<TWorkSpace>
where TWorkSpace : class
{
public IChatClient? ChatClient { get; }
public ChatClientAccessor(IServiceProvider serviceProvider)
{
ChatClient = serviceProvider.GetKeyedService<IChatClient>(
AbpAIWorkspaceOptions.GetChatClientServiceKeyName(
WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>()));
}
}

2
framework/src/Volo.Abp.AI/Volo/Abp/AI/ChatClientConfiguration.cs

@ -9,8 +9,6 @@ public class ChatClientConfiguration
public BuilderConfigurerList BuilderConfigurers { get; } = new(); public BuilderConfigurerList BuilderConfigurers { get; } = new();
// TODO: Base chat client (for inheriting a chat client configuration from some other one)
public void ConfigureBuilder(Action<ChatClientBuilder> configureAction) public void ConfigureBuilder(Action<ChatClientBuilder> configureAction)
{ {
BuilderConfigurers.Add(configureAction); BuilderConfigurers.Add(configureAction);

6
framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedKernelAccessor.cs → framework/src/Volo.Abp.AI/Volo/Abp/AI/KernelAccessor.cs

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

2
framework/src/Volo.Abp.AI/Volo/Abp/AI/TypedChatClient.cs

@ -10,7 +10,7 @@ public class TypedChatClient<TWorkSpace> : DelegatingChatClient, IChatClient<TWo
public TypedChatClient(IServiceProvider serviceProvider) public TypedChatClient(IServiceProvider serviceProvider)
: base( : base(
serviceProvider.GetRequiredKeyedService<IChatClient>( serviceProvider.GetRequiredKeyedService<IChatClient>(
AbpAIOptions.GetChatClientServiceKeyName( AbpAIWorkspaceOptions.GetChatClientServiceKeyName(
WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>())) WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>()))
) )
{ {

Loading…
Cancel
Save