Browse Source

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

AI Package Refactor & Review Changes
pull/23788/head
Enis Necipoglu 8 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.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.Extensions.AI" Version="9.7.1" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="9.7.1" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.*" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.61.0" />
<PackageVersion Include="Microsoft.SemanticKernel.Abstractions" Version="1.61.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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Modularity;
using Volo.Abp.Modularity;
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
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
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.Extensions;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Volo.Abp.Modularity;
namespace Volo.Abp.AI;
@ -17,80 +16,88 @@ public class AbpAIModule : AbpModule
{
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)
{
if (workspaceConfig.ChatClient?.Builder is null)
{
continue;
}
foreach (var builderConfigurer in workspaceConfig.ChatClient.BuilderConfigurers)
{
builderConfigurer.Action(workspaceConfig.ChatClient.Builder!);
}
context.Services.AddKeyedChatClient(
AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.Name),
provider => workspaceConfig.ChatClient.Builder!.Build(provider),
ServiceLifetime.Transient
ConfigureChatClient(context, workspaceConfig);
ConfigureKernel(context, workspaceConfig);
}
context.Services.TryAddTransient(typeof(IChatClient<>), typeof(TypedChatClient<>));
context.Services.TryAddTransient(typeof(IKernelAccessor<>), typeof(KernelAccessor<>));
}
private static void ConfigureKernel(ServiceConfigurationContext context, WorkspaceConfiguration workspaceConfig)
{
if (workspaceConfig.Kernel.Builder is null)
{
return;
}
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)
{
context.Services.AddTransient<IChatClient>(sp => sp.GetRequiredKeyedService<IChatClient>(
AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.Name)
)
);
}
if (workspaceConfig.ChatClient?.Builder is null)
{
context.Services.AddKeyedTransient<IChatClient>(
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)
{
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)
);
}
builderConfigurer.Action(workspaceConfig.ChatClient.Builder);
}
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;
public class AbpAIOptions
{
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}";
}
public HashSet<string> ConfiguredWorkspaceNames { get; } = new();
}

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

@ -1,8 +1,24 @@
using System.Collections.Generic;
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 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();
// TODO: Base chat client (for inheriting a chat client configuration from some other one)
public void ConfigureBuilder(Action<ChatClientBuilder> 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;
public class TypedKernelAccessor<TWorkSpace> : IKernelAccessor<TWorkSpace>
public class KernelAccessor<TWorkSpace> : IKernelAccessor<TWorkSpace>
where TWorkSpace : class
{
public Kernel? Kernel { get; }
public TypedKernelAccessor(IServiceProvider serviceProvider)
public KernelAccessor(IServiceProvider serviceProvider)
{
Kernel = serviceProvider.GetKeyedService<Kernel>(
AbpAIOptions.GetKernelServiceKeyName(
AbpAIWorkspaceOptions.GetKernelServiceKeyName(
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)
: base(
serviceProvider.GetRequiredKeyedService<IChatClient>(
AbpAIOptions.GetChatClientServiceKeyName(
AbpAIWorkspaceOptions.GetChatClientServiceKeyName(
WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>()))
)
{

Loading…
Cancel
Save