mirror of https://github.com/abpframework/abp.git
committed by
GitHub
34 changed files with 2826 additions and 1 deletions
@ -0,0 +1,307 @@ |
|||
# Artificial Intelligence |
|||
|
|||
ABP provides a simple way to integrate AI capabilities into your applications by unifying two popular .NET AI stacks under a common concept called a "workspace": |
|||
|
|||
- Microsoft.Extensions.AI `IChatClient` |
|||
- Microsoft.SemanticKernel `Kernel` |
|||
|
|||
A workspace is just a named scope. You configure providers per workspace and then resolve either default services (for the "Default" workspace) or workspace-scoped services. |
|||
|
|||
## Installation |
|||
|
|||
> This package is not included by default. Install it to enable AI features. |
|||
|
|||
It is suggested to use the ABP CLI to install the package. Open a command line window in the folder of the project (.csproj file) and type the following command: |
|||
|
|||
```bash |
|||
abp add-package Volo.Abp.AI |
|||
``` |
|||
|
|||
### Manual Installation |
|||
|
|||
Add nuget package to your project: |
|||
|
|||
```bash |
|||
dotnet add package Volo.Abp.AI |
|||
``` |
|||
|
|||
Then add the module dependency to your module class: |
|||
|
|||
```csharp |
|||
using Volo.Abp.AI; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
[DependsOn(typeof(AbpAIModule))] |
|||
public class MyProjectModule : AbpModule |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
## Usage |
|||
|
|||
### Chat Client |
|||
|
|||
#### Default configuration (quick start) |
|||
|
|||
Configure the default workspace to inject `IChatClient` directly. |
|||
|
|||
```csharp |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.SemanticKernel; |
|||
using Volo.Abp.AI; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
public class MyProjectModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.PreConfigure<AbpAIOptions>(options => |
|||
{ |
|||
options.Workspaces.ConfigureDefault(configuration => |
|||
{ |
|||
configuration.ConfigureChatClient(chatClientConfiguration => |
|||
{ |
|||
chatClientConfiguration.Builder = new ChatClientBuilder( |
|||
sp => new OllamaApiClient("http://localhost:11434", "mistral") |
|||
); |
|||
}); |
|||
|
|||
// Chat client only in this quick start |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Once configured, inject the default chat client: |
|||
|
|||
```csharp |
|||
using Microsoft.Extensions.AI; |
|||
|
|||
public class MyService |
|||
{ |
|||
private readonly IChatClient _chatClient; // default chat client |
|||
|
|||
public MyService(IChatClient chatClient) |
|||
{ |
|||
_chatClient = chatClient; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### Workspace configuration |
|||
|
|||
Workspaces allow multiple, isolated AI configurations. Define workspace types (optionally decorated with `WorkspaceNameAttribute`). If omitted, the type’s full name is used. |
|||
|
|||
```csharp |
|||
using Volo.Abp.AI; |
|||
|
|||
[WorkspaceName("GreetingAssistant")] |
|||
public class GreetingAssistant // ChatClient-only workspace |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
Configure a ChatClient workspace: |
|||
|
|||
```csharp |
|||
public class MyProjectModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.PreConfigure<AbpAIOptions>(options => |
|||
{ |
|||
options.Workspaces.Configure<GreetingAssistant>(configuration => |
|||
{ |
|||
configuration.ConfigureChatClient(chatClientConfiguration => |
|||
{ |
|||
chatClientConfiguration.Builder = new ChatClientBuilder( |
|||
sp => new OllamaApiClient("http://localhost:11434", "mistral") |
|||
); |
|||
|
|||
chatClientConfiguration.BuilderConfigurers.Add(builder => |
|||
{ |
|||
// Anything you want to do with the builder: |
|||
// builder.UseFunctionInvocation().UseLogging(); // For example |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Semantic Kernel |
|||
|
|||
#### Default configuration |
|||
|
|||
|
|||
```csharp |
|||
public class MyProjectModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.PreConfigure<AbpAIOptions>(options => |
|||
{ |
|||
options.Workspaces.ConfigureDefault(configuration => |
|||
{ |
|||
configuration.ConfigureKernel(kernelConfiguration => |
|||
{ |
|||
kernelConfiguration.Builder = Kernel.CreateBuilder() |
|||
.AddAzureOpenAIChatClient("...", "..."); |
|||
}); |
|||
// Note: Chat client is not configured here |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Once configured, inject the default kernel: |
|||
|
|||
```csharp |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.AI; |
|||
|
|||
public class MyService |
|||
{ |
|||
private readonly IKernelAccessor _kernelAccessor; |
|||
public MyService(IKernelAccessor kernelAccessor) |
|||
{ |
|||
_kernelAccessor = kernelAccessor; |
|||
} |
|||
|
|||
public async Task DoSomethingAsync() |
|||
{ |
|||
var kernel = _kernelAccessor.Kernel; // Kernel might be null if no workspace is configured. |
|||
|
|||
var result = await kernel.InvokeAsync(/*... */); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### Workspace configuration |
|||
|
|||
```csharp |
|||
public class MyProjectModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.PreConfigure<AbpAIOptions>(options => |
|||
{ |
|||
options.Workspaces.Configure<ContentPlanner>(configuration => |
|||
{ |
|||
configuration.ConfigureKernel(kernelConfiguration => |
|||
{ |
|||
kernelConfiguration.Builder = Kernel.CreateBuilder() |
|||
.AddOpenAIChatCompletion("...", "..."); |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
#### Workspace usage |
|||
|
|||
```csharp |
|||
using Microsoft.Extensions.AI; |
|||
using Volo.Abp.AI; |
|||
using Microsoft.SemanticKernel; |
|||
|
|||
public class PlanningService |
|||
{ |
|||
private readonly IKernelAccessor<ContentPlanner> _kernelAccessor; |
|||
private readonly IChatClient<ContentPlanner> _chatClient; // available even if only Kernel is configured |
|||
|
|||
public PlanningService( |
|||
IKernelAccessor<ContentPlanner> kernelAccessor, |
|||
IChatClient<ContentPlanner> chatClient) |
|||
{ |
|||
_kernelAccessor = kernelAccessor; |
|||
_chatClient = chatClient; |
|||
} |
|||
|
|||
public async Task<string> PlanAsync(string topic) |
|||
{ |
|||
var kernel = _kernelAccessor.Kernel; // Microsoft.SemanticKernel.Kernel |
|||
// Use Semantic Kernel APIs if needed... |
|||
|
|||
var response = await _chatClient.GetResponseAsync( |
|||
[new ChatMessage(ChatRole.User, $"Create a content plan for: {topic}")] |
|||
); |
|||
return response?.Message?.Text ?? string.Empty; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
## Options |
|||
|
|||
`AbpAIOptions` configuration pattern offers `ConfigureChatClient(...)` and `ConfigureKernel(...)` methods for configuration. These methods are defined in the `WorkspaceConfiguration` class. They are used to configure the `ChatClient` and `Kernel` respectively. |
|||
|
|||
`Builder` is set once and is used to build the `ChatClient` or `Kernel` instance. `BuilderConfigurers` is a list of actions that are applied to the `Builder` instance for incremental changes. These actions are executed in the order they are added. |
|||
|
|||
If a workspace configures only the Kernel, a chat client may still be exposed for that workspace through the Kernel’s service provider (when available). |
|||
|
|||
|
|||
## Advanced Usage and Customizations |
|||
|
|||
### Addding Your Own DelegatingChatClient |
|||
|
|||
If you want to build your own decorator, implement a `DelegatingChatClient` derivative and provide an extension method that adds it to the `ChatClientBuilder` using `builder.Use(...)`. |
|||
|
|||
Example sketch: |
|||
|
|||
```csharp |
|||
using Microsoft.Extensions.AI; |
|||
|
|||
public class SystemMessageChatClient : DelegatingChatClient |
|||
{ |
|||
public SystemMessageChatClient(IChatClient inner, string systemMessage) : base(inner) |
|||
{ |
|||
SystemMessage = systemMessage; |
|||
} |
|||
|
|||
public string SystemMessage { get; set; } |
|||
|
|||
public override Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default) |
|||
{ |
|||
// Mutate messages/options as needed, then call base |
|||
return base.GetResponseAsync(messages, options, cancellationToken); |
|||
} |
|||
} |
|||
|
|||
public static class SystemMessageChatClientExtensions |
|||
{ |
|||
public static ChatClientBuilder UseSystemMessage(this ChatClientBuilder builder, string systemMessage) |
|||
{ |
|||
return builder.Use(client => new SystemMessageChatClient(client, systemMessage)); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
|
|||
```cs |
|||
chatClientConfiguration.BuilderConfigurers.Add(builder => |
|||
{ |
|||
builder.UseSystemMessage("You are a helpful assistant that greets users in a friendly manner with their names."); |
|||
}); |
|||
``` |
|||
|
|||
## Technical Anatomy |
|||
|
|||
- `AbpAIModule`: Wires up configured workspaces, registers keyed services and default services for the `"Default"` workspace. |
|||
- `AbpAIOptions`: Holds `Workspaces` and provides helper methods for internal keyed service naming. |
|||
- `WorkspaceConfigurationDictionary` and `WorkspaceConfiguration`: Configure per-workspace Chat Client and Kernel. |
|||
- `ChatClientConfiguration` and `KernelConfiguration`: Hold builders and a list of ordered builder configurers. |
|||
- `WorkspaceNameAttribute`: Names a workspace; falls back to the type’s full name if not specified. |
|||
- `IChatClient<TWorkspace>`: Typed chat client for a workspace. |
|||
- `IKernelAccessor<TWorkspace>`: Provides access to the workspace’s `Kernel` instance if configured. |
|||
- `AbpAIWorkspaceOptions`: Exposes `ConfiguredWorkspaceNames` for diagnostics. |
|||
|
|||
There are no database tables for this feature; it is a pure configuration and DI integration layer. |
|||
|
|||
## See Also |
|||
|
|||
- Microsoft.Extensions.AI (Chat Client) |
|||
- Microsoft Semantic Kernel |
|||
File diff suppressed because it is too large
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,3 @@ |
|||
{ |
|||
"role": "lib.framework" |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks> |
|||
<Nullable>enable</Nullable> |
|||
<WarningsAsErrors>Nullable</WarningsAsErrors> |
|||
<PackageId>Volo.Abp.AI.Abstractions</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.SemanticKernel.Abstractions" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,10 @@ |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class AbpAIAbstractionsModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Microsoft.Extensions.AI; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public interface IChatClient<TWorkSpace> : IChatClient |
|||
where TWorkSpace : class |
|||
{ |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
|
|||
using Microsoft.SemanticKernel; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public interface IKernelAccessor |
|||
{ |
|||
Kernel? Kernel { get; } |
|||
} |
|||
|
|||
public interface IKernelAccessor<TWorkSpace> : IKernelAccessor |
|||
where TWorkSpace : class |
|||
{ |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Collections.Concurrent; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
[AttributeUsage(AttributeTargets.Class)] |
|||
public class WorkspaceNameAttribute : Attribute |
|||
{ |
|||
public string Name { get; } |
|||
|
|||
public WorkspaceNameAttribute(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
Name = name; |
|||
} |
|||
|
|||
private static readonly ConcurrentDictionary<Type, string> _nameCache = new(); |
|||
|
|||
public static string GetWorkspaceName<TWorkspace>() |
|||
{ |
|||
return GetWorkspaceName(typeof(TWorkspace)); |
|||
} |
|||
|
|||
public static string GetWorkspaceName(Type workspaceType) |
|||
{ |
|||
return _nameCache.GetOrAdd(workspaceType, type => |
|||
{ |
|||
var workspaceNameAttribute = type |
|||
.GetCustomAttributes(true) |
|||
.OfType<WorkspaceNameAttribute>() |
|||
.FirstOrDefault(); |
|||
|
|||
return workspaceNameAttribute?.Name ?? type.FullName!; |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,3 @@ |
|||
{ |
|||
"role": "lib.framework" |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0</TargetFrameworks> |
|||
<Nullable>enable</Nullable> |
|||
<WarningsAsErrors>Nullable</WarningsAsErrors> |
|||
<PackageId>Volo.Abp.AI</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.AI.Abstractions\Volo.Abp.AI.Abstractions.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.SemanticKernel" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,96 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
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; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAIAbstractionsModule) |
|||
)] |
|||
public class AbpAIModule : AbpModule |
|||
{ |
|||
public const string DefaultWorkspaceName = "Default"; |
|||
|
|||
public override void PostConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var options = context.Services.ExecutePreConfiguredActions<AbpAIOptions>(); |
|||
|
|||
context.Services.Configure<AbpAIWorkspaceOptions>(workspaceOptions => |
|||
{ |
|||
workspaceOptions.ConfiguredWorkspaceNames.UnionWith(options.Workspaces.Select(x => x.Key).ToArray()); |
|||
}); |
|||
|
|||
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 |
|||
); |
|||
|
|||
if (workspaceConfig.Name == DefaultWorkspaceName) |
|||
{ |
|||
context.Services.AddTransient<IChatClient>(sp => sp.GetRequiredKeyedService<IChatClient>( |
|||
AbpAIOptions.GetChatClientServiceKeyName(workspaceConfig.Name) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
context.Services.TryAddTransient(typeof(IChatClient<>), typeof(TypedChatClient<>)); |
|||
|
|||
foreach (var workspaceConfig in options.Workspaces.Values) |
|||
{ |
|||
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) |
|||
); |
|||
} |
|||
} |
|||
|
|||
context.Services.TryAddTransient(typeof(IKernelAccessor<>), typeof(TypedKernelAccessor<>)); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
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}"; |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class AbpAIWorkspaceOptions |
|||
{ |
|||
public HashSet<string> ConfiguredWorkspaceNames { get; } = new(); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Microsoft.Extensions.AI; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class BuilderConfigurerList : NamedActionList<ChatClientBuilder> |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using Microsoft.Extensions.AI; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class ChatClientConfiguration |
|||
{ |
|||
public ChatClientBuilder? Builder { get; set; } |
|||
|
|||
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); |
|||
} |
|||
|
|||
public void ConfigureBuilder(string name, Action<ChatClientBuilder> configureAction) |
|||
{ |
|||
BuilderConfigurers.Add(name, configureAction); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.SemanticKernel; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
[ExposeServices(typeof(IKernelAccessor))] |
|||
public class DefaultKernelAccessor : IKernelAccessor, ITransientDependency |
|||
{ |
|||
public Kernel? Kernel { get; } |
|||
|
|||
public DefaultKernelAccessor(IServiceProvider serviceProvider) |
|||
{ |
|||
Kernel = serviceProvider.GetKeyedService<Kernel>( |
|||
AbpAIModule.DefaultWorkspaceName); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Microsoft.SemanticKernel; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class KernelBuilderConfigurerList : NamedActionList<IKernelBuilder> |
|||
{ |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using Microsoft.SemanticKernel; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class KernelConfiguration |
|||
{ |
|||
public IKernelBuilder? Builder { get; set; } |
|||
|
|||
public KernelBuilderConfigurerList BuilderConfigurers { get; } = new(); |
|||
|
|||
public void ConfigureBuilder(Action<IKernelBuilder> configureAction) |
|||
{ |
|||
BuilderConfigurers.Add(configureAction); |
|||
} |
|||
|
|||
public void ConfigureBuilder(string name, Action<IKernelBuilder> configureAction) |
|||
{ |
|||
BuilderConfigurers.Add(name, configureAction); |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class TypedChatClient<TWorkSpace> : DelegatingChatClient, IChatClient<TWorkSpace> |
|||
where TWorkSpace : class |
|||
{ |
|||
public TypedChatClient(IServiceProvider serviceProvider) |
|||
: base( |
|||
serviceProvider.GetRequiredKeyedService<IChatClient>( |
|||
AbpAIOptions.GetChatClientServiceKeyName( |
|||
WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>())) |
|||
) |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.SemanticKernel; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class TypedKernelAccessor<TWorkSpace> : IKernelAccessor<TWorkSpace> |
|||
where TWorkSpace : class |
|||
{ |
|||
public Kernel? Kernel { get; } |
|||
|
|||
public TypedKernelAccessor(IServiceProvider serviceProvider) |
|||
{ |
|||
Kernel = serviceProvider.GetKeyedService<Kernel>( |
|||
AbpAIOptions.GetKernelServiceKeyName( |
|||
WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>())); |
|||
} |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,28 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class WorkspaceConfiguration |
|||
{ |
|||
public string Name { get; } |
|||
public ChatClientConfiguration ChatClient { get; } = new(); |
|||
public KernelConfiguration Kernel { get; } = new(); |
|||
|
|||
public WorkspaceConfiguration(string name) |
|||
{ |
|||
Name = name; |
|||
} |
|||
|
|||
public WorkspaceConfiguration ConfigureChatClient(Action<ChatClientConfiguration> configureAction) |
|||
{ |
|||
configureAction(ChatClient); |
|||
return this; |
|||
} |
|||
|
|||
|
|||
public WorkspaceConfiguration ConfigureKernel(Action<KernelConfiguration> configureAction) |
|||
{ |
|||
configureAction(Kernel); |
|||
return this; |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class WorkspaceConfigurationDictionary : Dictionary<string, WorkspaceConfiguration> |
|||
{ |
|||
public void Configure<TWorkSpace>(Action<WorkspaceConfiguration>? configureAction = null) |
|||
where TWorkSpace : class |
|||
{ |
|||
Configure(WorkspaceNameAttribute.GetWorkspaceName<TWorkSpace>(), configureAction); |
|||
} |
|||
|
|||
public void Configure(string name, Action<WorkspaceConfiguration>? configureAction = null) |
|||
{ |
|||
if (!TryGetValue(name, out var configuration)) |
|||
{ |
|||
configuration = new WorkspaceConfiguration(name); |
|||
this[name] = configuration; |
|||
} |
|||
|
|||
configureAction?.Invoke(configuration); |
|||
} |
|||
|
|||
public void ConfigureDefault(Action<WorkspaceConfiguration>? configureAction = null) |
|||
{ |
|||
Configure(AbpAIModule.DefaultWorkspaceName, configureAction); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class NamedActionList<T> : NamedObjectList<NamedAction<T>> |
|||
{ |
|||
public void Add(Action<T> action) |
|||
{ |
|||
this.Add(Guid.NewGuid().ToString("N"), action); |
|||
} |
|||
|
|||
public void Add(string name, Action<T> action) |
|||
=> this.Add(new NamedAction<T>(name, action)); |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AI; |
|||
|
|||
public class NamedObjectList<T> : List<T> |
|||
where T : NamedObject |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp; |
|||
|
|||
public class NamedAction<T> : NamedObject |
|||
{ |
|||
public Action<T> Action { get; set; } |
|||
|
|||
public NamedAction(string name, Action<T> action) |
|||
: base(name) |
|||
{ |
|||
Action = Check.NotNull(action, nameof(action)); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace Volo.Abp; |
|||
|
|||
public class NamedObject |
|||
{ |
|||
public string Name { get; } |
|||
|
|||
public NamedObject(string name) |
|||
{ |
|||
Name = Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue