committed by
GitHub
49 changed files with 1152 additions and 25 deletions
@ -0,0 +1,16 @@ |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.Agents.AI; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.AI.Agent; |
|||
public class AbpAIAgentOptions |
|||
{ |
|||
public List<Action<WorkspaceDefinition?, AIAgentBuilder>> AgentBuildActions { get; } |
|||
public List<Action<WorkspaceDefinition?, ChatClientAgentOptions>> AgentOptionActions { get; } |
|||
public AbpAIAgentOptions() |
|||
{ |
|||
AgentBuildActions = new List<Action<WorkspaceDefinition?, AIAgentBuilder>>(); |
|||
AgentOptionActions = new List<Action<WorkspaceDefinition?, ChatClientAgentOptions>>(); |
|||
} |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
using Microsoft.Extensions.AI; |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public static class GlobalFunctions |
|||
{ |
|||
public static AITool Now => AIFunctionFactory.Create( |
|||
() => DateTime.Now, |
|||
nameof(Now), |
|||
"Get now time"); |
|||
} |
|||
@ -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,27 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0</TargetFrameworks> |
|||
<AssemblyName>LINGYUN.Abp.AI.Ollama</AssemblyName> |
|||
<PackageId>LINGYUN.Abp.AI.Ollama</PackageId> |
|||
<NoWarn>$(NoWarn);SKEXP0001,SKEXP0010,SKEXP0070</NoWarn> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<Nullable>enable</Nullable> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="OllamaSharp" /> |
|||
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Ollama" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.AI.Core\LINGYUN.Abp.AI.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,17 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.AI.Ollama; |
|||
|
|||
[DependsOn(typeof(AbpAICoreModule))] |
|||
public class AbpAIOllamaModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpAICoreOptions>(options => |
|||
{ |
|||
options.ChatClientProviders.Add<OllamaChatClientProvider>(); |
|||
|
|||
options.KernelProviders.Add<OllamaKernelProvider>(); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
using LINGYUN.Abp.AI.Models; |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using OllamaSharp; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.AI.Ollama; |
|||
public class OllamaChatClientProvider : ChatClientProvider |
|||
{ |
|||
public const string DefaultEndpoint = "http://localhost:11434"; |
|||
|
|||
public const string ProviderName = "Ollama"; |
|||
public override string Name => ProviderName; |
|||
|
|||
public OllamaChatClientProvider( |
|||
IServiceProvider serviceProvider) : base(serviceProvider) |
|||
{ |
|||
} |
|||
|
|||
public async override Task<IChatClient> CreateAsync(WorkspaceDefinition workspace) |
|||
{ |
|||
Check.NotNull(workspace, nameof(workspace)); |
|||
|
|||
var options = ServiceProvider.GetRequiredService<IOptions<AbpAICoreOptions>>().Value; |
|||
|
|||
var ollamaApiClient = new OllamaApiClient(workspace.ApiBaseUrl ?? DefaultEndpoint); |
|||
|
|||
var chatClientBuilder = ChatClientBuilderChatClientExtensions.AsBuilder(ollamaApiClient); |
|||
|
|||
foreach (var handlerAction in options.ChatClientBuildActions) |
|||
{ |
|||
await handlerAction(workspace, ServiceProvider, chatClientBuilder); |
|||
} |
|||
|
|||
return chatClientBuilder |
|||
.UseLogging() |
|||
.UseOpenTelemetry() |
|||
.UseFunctionInvocation() |
|||
.UseDistributedCache() |
|||
.Build(ServiceProvider); |
|||
} |
|||
|
|||
public override ChatModel[] GetModels() |
|||
{ |
|||
return []; |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using LINGYUN.Abp.AI.Workspaces; |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using Microsoft.SemanticKernel; |
|||
using OllamaSharp; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.AI.Ollama; |
|||
public class OllamaKernelProvider : KernelProvider |
|||
{ |
|||
public const string ProviderName = "Ollama"; |
|||
public override string Name => ProviderName; |
|||
|
|||
public OllamaKernelProvider(IServiceProvider serviceProvider) : base(serviceProvider) |
|||
{ |
|||
} |
|||
|
|||
public override Task<Kernel> CreateAsync(WorkspaceDefinition workspace) |
|||
{ |
|||
Check.NotNull(workspace, nameof(workspace)); |
|||
|
|||
var options = ServiceProvider.GetRequiredService<IOptions<AbpAICoreOptions>>().Value; |
|||
|
|||
var ollamaApiClient = new OllamaApiClient(workspace.ApiBaseUrl ?? OllamaChatClientProvider.DefaultEndpoint); |
|||
|
|||
var kernelBuilder = Kernel.CreateBuilder() |
|||
.AddOllamaChatClient(ollamaApiClient); |
|||
|
|||
foreach (var handlerAction in options.KernelBuildActions) |
|||
{ |
|||
handlerAction(workspace, kernelBuilder); |
|||
} |
|||
|
|||
return Task.FromResult(kernelBuilder.Build()); |
|||
} |
|||
} |
|||
@ -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,26 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0</TargetFrameworks> |
|||
<AssemblyName>LINGYUN.Abp.AI.Tools</AssemblyName> |
|||
<PackageId>LINGYUN.Abp.AI.Tools</PackageId> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<Nullable>enable</Nullable> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\AI\Tools\Localization\Resources\*.json" /> |
|||
<EmbeddedResource Include="LINGYUN\Abp\AI\Tools\Localization\Resources\*.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.AI.Core\LINGYUN.Abp.AI.Core.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,55 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.SimpleStateChecking; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
|
|||
public class AIToolDefinition : IHasSimpleStateCheckers<AIToolDefinition> |
|||
{ |
|||
/// <summary>
|
|||
/// 名称
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public string Name { get; } |
|||
/// <summary>
|
|||
/// 提供者
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public string Provider { get; } |
|||
/// <summary>
|
|||
/// 描述
|
|||
/// </summary>
|
|||
public ILocalizableString? Description { get; set; } |
|||
/// <summary>
|
|||
/// 属性
|
|||
/// </summary>
|
|||
[NotNull] |
|||
public Dictionary<string, object?> Properties { get; } |
|||
|
|||
public List<ISimpleStateChecker<AIToolDefinition>> StateCheckers { get; } |
|||
|
|||
public AIToolDefinition( |
|||
[NotNull] string name, |
|||
[NotNull] string provider, |
|||
ILocalizableString? description = null) |
|||
{ |
|||
Name = name; |
|||
Provider = provider; |
|||
Description = description; |
|||
|
|||
Properties = new Dictionary<string, object?>(); |
|||
StateCheckers = new List<ISimpleStateChecker<AIToolDefinition>>(); |
|||
} |
|||
|
|||
public virtual AIToolDefinition WithProperty(string key, object? value) |
|||
{ |
|||
Properties[key] = value; |
|||
return this; |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return $"[{nameof(AIToolDefinition)} {Name}]"; |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public class AIToolDefinitionContext : IAIToolDefinitionContext |
|||
{ |
|||
protected Dictionary<string, AIToolDefinition> Tools { get; } |
|||
|
|||
public AIToolDefinitionContext(Dictionary<string, AIToolDefinition> tools) |
|||
{ |
|||
Tools = tools; |
|||
} |
|||
|
|||
public virtual AIToolDefinition? GetOrNull(string name) |
|||
{ |
|||
return Tools.GetOrDefault(name); |
|||
} |
|||
|
|||
public virtual IReadOnlyList<AIToolDefinition> GetAll() |
|||
{ |
|||
return Tools.Values.ToImmutableList(); |
|||
} |
|||
|
|||
public virtual void Add(params AIToolDefinition[] definitions) |
|||
{ |
|||
if (definitions.IsNullOrEmpty()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
foreach (var definition in definitions) |
|||
{ |
|||
Tools[definition.Name] = definition; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public class AIToolDefinitionManager : IAIToolDefinitionManager, ISingletonDependency |
|||
{ |
|||
protected readonly IStaticAIToolDefinitionStore StaticStore; |
|||
protected readonly IDynamicAIToolDefinitionStore DynamicStore; |
|||
|
|||
public AIToolDefinitionManager( |
|||
IStaticAIToolDefinitionStore staticStore, |
|||
IDynamicAIToolDefinitionStore dynamicStore) |
|||
{ |
|||
StaticStore = staticStore; |
|||
DynamicStore = dynamicStore; |
|||
} |
|||
|
|||
public virtual async Task<AIToolDefinition> GetAsync(string name) |
|||
{ |
|||
var workspace = await GetOrNullAsync(name); |
|||
if (workspace == null) |
|||
{ |
|||
throw new AbpException("Undefined AITool: " + name); |
|||
} |
|||
|
|||
return workspace; |
|||
} |
|||
|
|||
public virtual async Task<AIToolDefinition?> GetOrNullAsync(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
return await StaticStore.GetOrNullAsync(name) ?? await DynamicStore.GetOrNullAsync(name); |
|||
} |
|||
|
|||
public virtual async Task<IReadOnlyList<AIToolDefinition>> GetAllAsync() |
|||
{ |
|||
var staticAITools = await StaticStore.GetAllAsync(); |
|||
var staticAIToolNames = staticAITools |
|||
.Select(p => p.Name) |
|||
.ToImmutableHashSet(); |
|||
|
|||
var dynamicAITools = await DynamicStore.GetAllAsync(); |
|||
|
|||
return staticAITools.Concat(dynamicAITools.Where(d => !staticAIToolNames.Contains(d.Name))) |
|||
.ToImmutableList(); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public abstract class AIToolDefinitionProvider : IAIToolDefinitionProvider, ITransientDependency |
|||
{ |
|||
public abstract void Define(IAIToolDefinitionContext context); |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
using Microsoft.Extensions.AI; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.SimpleStateChecking; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public class AIToolFactory : IAIToolFactory, ITransientDependency |
|||
{ |
|||
protected ISimpleStateCheckerManager<AIToolDefinition> StateCheckerManager { get; } |
|||
protected IAIToolDefinitionManager AIToolDefinitionManager { get; } |
|||
protected IAIToolProviderManager AIToolProviderManager { get; } |
|||
|
|||
public AIToolFactory( |
|||
ISimpleStateCheckerManager<AIToolDefinition> stateCheckerManager, |
|||
IAIToolDefinitionManager aIToolDefinitionManager, |
|||
IAIToolProviderManager aIToolProviderManager) |
|||
{ |
|||
StateCheckerManager = stateCheckerManager; |
|||
AIToolDefinitionManager = aIToolDefinitionManager; |
|||
AIToolProviderManager = aIToolProviderManager; |
|||
} |
|||
|
|||
public virtual AITool CreateTool(AIToolDefinition definition) |
|||
{ |
|||
foreach (var provider in AIToolProviderManager.Providers) |
|||
{ |
|||
if (!string.Equals(provider.Name, definition.Provider)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
return provider.CreateTool(definition); |
|||
} |
|||
|
|||
throw new AbpException($"The AITool provider implementation named {definition.Provider} was not found!"); |
|||
} |
|||
|
|||
public async virtual Task<AITool[]> CreateAllTools() |
|||
{ |
|||
var aiTools = new List<AITool>(); |
|||
var toolDefines = await AIToolDefinitionManager.GetAllAsync(); |
|||
|
|||
foreach (var toolDefine in toolDefines) |
|||
{ |
|||
if (await StateCheckerManager.IsEnabledAsync(toolDefine)) |
|||
{ |
|||
aiTools.Add(CreateTool(toolDefine)); |
|||
} |
|||
} |
|||
|
|||
return aiTools.ToArray(); |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public class AIToolProviderManager : IAIToolProviderManager, ISingletonDependency |
|||
{ |
|||
public List<IAIToolProvider> Providers => _lazyProviders.Value; |
|||
|
|||
protected AbpAIToolsOptions Options { get; } |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
|
|||
private readonly Lazy<List<IAIToolProvider>> _lazyProviders; |
|||
|
|||
public AIToolProviderManager( |
|||
IServiceProvider serviceProvider, |
|||
IOptions<AbpAIToolsOptions> options) |
|||
{ |
|||
|
|||
Options = options.Value; |
|||
ServiceProvider = serviceProvider; |
|||
|
|||
_lazyProviders = new Lazy<List<IAIToolProvider>>(GetProviders, true); |
|||
} |
|||
|
|||
protected virtual List<IAIToolProvider> GetProviders() |
|||
{ |
|||
var providers = Options |
|||
.AIToolProviders |
|||
.Select(type => (ServiceProvider.GetRequiredService(type) as IAIToolProvider)!) |
|||
.ToList(); |
|||
|
|||
var multipleProviders = providers.GroupBy(p => p.Name).FirstOrDefault(x => x.Count() > 1); |
|||
if (multipleProviders != null) |
|||
{ |
|||
throw new AbpException($"Duplicate AITool provider name detected: {multipleProviders.Key}. Providers:{Environment.NewLine}{multipleProviders.Select(p => p.GetType().FullName!).JoinAsString(Environment.NewLine)}"); |
|||
} |
|||
|
|||
return providers; |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using LINGYUN.Abp.AI.Localization; |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
|
|||
[DependsOn(typeof(AbpAICoreModule))] |
|||
public class AbpAIToolsModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
AutoAddDefinitionProviders(context.Services); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAIToolsModule>(); |
|||
}); |
|||
|
|||
Configure<AbpAIToolsOptions>(options => |
|||
{ |
|||
// 自定义函数工具
|
|||
options.AIToolProviders.Add<FunctionAIToolProvider>(); |
|||
}); |
|||
|
|||
Configure<AbpAICoreOptions>(options => |
|||
{ |
|||
options.ChatClientBuildActions.Add(async (_, sp, builder) => |
|||
{ |
|||
var aiToolFactory = sp.GetRequiredService<IAIToolFactory>(); |
|||
var aiTools = await aiToolFactory.CreateAllTools(); |
|||
|
|||
builder.ConfigureOptions(ai => |
|||
{ |
|||
ai.ToolMode = ChatToolMode.Auto; |
|||
ai.AllowMultipleToolCalls = true; |
|||
|
|||
ai.Tools ??= []; |
|||
|
|||
foreach (var aiTool in aiTools) |
|||
{ |
|||
ai.Tools.Add(aiTool); |
|||
} |
|||
}); |
|||
}); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Get<AbpAIResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/AI/Tools/Localization/Resources"); |
|||
}); |
|||
} |
|||
|
|||
private static void AutoAddDefinitionProviders(IServiceCollection services) |
|||
{ |
|||
var definitionProviders = new List<Type>(); |
|||
|
|||
services.OnRegistered(context => |
|||
{ |
|||
if (typeof(IAIToolDefinitionProvider).IsAssignableFrom(context.ImplementationType)) |
|||
{ |
|||
definitionProviders.Add(context.ImplementationType); |
|||
} |
|||
}); |
|||
|
|||
services.Configure<AbpAIToolsOptions>(options => |
|||
{ |
|||
options.DefinitionProviders.AddIfNotContains(definitionProviders); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public class AbpAIToolsOptions |
|||
{ |
|||
public ITypeList<IAIToolDefinitionProvider> DefinitionProviders { get; } |
|||
public ITypeList<IAIToolProvider> AIToolProviders { get; } |
|||
public AbpAIToolsOptions() |
|||
{ |
|||
DefinitionProviders = new TypeList<IAIToolDefinitionProvider>(); |
|||
AIToolProviders = new TypeList<IAIToolProvider>(); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public static class FunctionAIToolDefinitionExtenssions |
|||
{ |
|||
private const string FunctionType = "Function"; |
|||
public static AIToolDefinition WithFunction<TFunc>(this AIToolDefinition definition) |
|||
{ |
|||
return definition.WithFunction(typeof(TFunc)); |
|||
} |
|||
|
|||
public static AIToolDefinition WithFunction(this AIToolDefinition definition, Type funcType) |
|||
{ |
|||
Check.NotNull(funcType, nameof(funcType)); |
|||
|
|||
definition.WithProperty(FunctionType, funcType); |
|||
|
|||
return definition; |
|||
} |
|||
|
|||
public static Type GetFunction(this AIToolDefinition definition) |
|||
{ |
|||
var funcTypeSet = definition.Properties.GetOrDefault(FunctionType); |
|||
Check.NotNull(funcTypeSet, nameof(FunctionType)); |
|||
|
|||
var funcType = Type.GetType(funcTypeSet.ToString()!); |
|||
|
|||
Check.NotNull(funcType, nameof(funcType)); |
|||
|
|||
return funcType; |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
using Microsoft.Extensions.AI; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using System; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public class FunctionAIToolProvider : IAIToolProvider, ITransientDependency |
|||
{ |
|||
public const string ProviderName = "Function"; |
|||
public string Name => ProviderName; |
|||
|
|||
protected IServiceProvider ServiceProvider { get; } |
|||
public FunctionAIToolProvider(IServiceProvider serviceProvider) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
} |
|||
|
|||
public virtual AITool CreateTool(AIToolDefinition definition) |
|||
{ |
|||
var aiToolType = definition.GetFunction(); |
|||
// 框架约定, 自定义Tool只需要定义同步方法(Invoke)或异步方法(InvokeAsync)即可
|
|||
var aiToolMethodInfo = aiToolType.GetMethod("Invoke") ?? aiToolType.GetMethod("InvokeAsync"); |
|||
|
|||
Check.NotNull(aiToolMethodInfo, nameof(aiToolMethodInfo)); |
|||
|
|||
string? description = null; |
|||
if (definition.Description != null) |
|||
{ |
|||
var localizerFactory = ServiceProvider.GetRequiredService<IStringLocalizerFactory>(); |
|||
description = definition.Description.Localize(localizerFactory)?.Value; |
|||
} |
|||
|
|||
return AIFunctionFactory.Create( |
|||
method: aiToolMethodInfo, |
|||
createInstanceFunc: (AIFunctionArguments args) => |
|||
{ |
|||
return ActivatorUtilities.CreateInstance(args.Services ?? ServiceProvider, aiToolType); |
|||
}, |
|||
options: new AIFunctionFactoryOptions |
|||
{ |
|||
Name = definition.Name, |
|||
Description = description, |
|||
AdditionalProperties = definition.Properties, |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using LINGYUN.Abp.AI.Localization; |
|||
using LINGYUN.Abp.AI.Tools.Settings; |
|||
using LINGYUN.Abp.AI.Tools.Timing; |
|||
using LINGYUN.Abp.AI.Tools.Users; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public class GlobalAIToolDefinitionProvider : AIToolDefinitionProvider |
|||
{ |
|||
public override void Define(IAIToolDefinitionContext context) |
|||
{ |
|||
context.Add( |
|||
new AIToolDefinition( |
|||
NowTimeTool.Name, |
|||
FunctionAIToolProvider.ProviderName, |
|||
L("Tools:GetNowTime")) |
|||
.WithFunction<NowTimeTool>(), |
|||
|
|||
new AIToolDefinition( |
|||
CurrentUserTool.Name, |
|||
FunctionAIToolProvider.ProviderName, |
|||
L("Tools:GetUserInfo")) |
|||
.WithFunction<CurrentUserTool>(), |
|||
|
|||
new AIToolDefinition( |
|||
GetSettingTool.Name, |
|||
FunctionAIToolProvider.ProviderName, |
|||
L("Tools:GetSetting")) |
|||
.WithFunction<GetSettingTool>(), |
|||
new AIToolDefinition( |
|||
GetSettingsTool.Name, |
|||
FunctionAIToolProvider.ProviderName, |
|||
L("Tools:GetSettings")) |
|||
.WithFunction<GetSettingsTool>()); |
|||
} |
|||
|
|||
private static LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<AbpAIResource>(name); |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public interface IAIToolDefinitionContext |
|||
{ |
|||
AIToolDefinition? GetOrNull(string name); |
|||
|
|||
IReadOnlyList<AIToolDefinition> GetAll(); |
|||
|
|||
void Add(params AIToolDefinition[] definitions); |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public interface IAIToolDefinitionManager |
|||
{ |
|||
[NotNull] |
|||
Task<AIToolDefinition> GetAsync([NotNull] string name); |
|||
|
|||
[ItemNotNull] |
|||
Task<IReadOnlyList<AIToolDefinition>> GetAllAsync(); |
|||
|
|||
[CanBeNull] |
|||
Task<AIToolDefinition?> GetOrNullAsync([NotNull] string name); |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public interface IAIToolDefinitionProvider |
|||
{ |
|||
void Define(IAIToolDefinitionContext context); |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Microsoft.Extensions.AI; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public interface IAIToolFactory |
|||
{ |
|||
AITool CreateTool(AIToolDefinition definition); |
|||
|
|||
Task<AITool[]> CreateAllTools(); |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Microsoft.Extensions.AI; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public interface IAIToolProvider |
|||
{ |
|||
string Name { get; } |
|||
|
|||
AITool CreateTool(AIToolDefinition definition); |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public interface IAIToolProviderManager |
|||
{ |
|||
List<IAIToolProvider> Providers { get; } |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public interface IDynamicAIToolDefinitionStore |
|||
{ |
|||
Task<AIToolDefinition> GetAsync([NotNull] string name); |
|||
|
|||
Task<IReadOnlyList<AIToolDefinition>> GetAllAsync(); |
|||
|
|||
Task<AIToolDefinition?> GetOrNullAsync([NotNull] string name); |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public interface IStaticAIToolDefinitionStore |
|||
{ |
|||
Task<AIToolDefinition> GetAsync([NotNull] string name); |
|||
|
|||
Task<IReadOnlyList<AIToolDefinition>> GetAllAsync(); |
|||
|
|||
Task<AIToolDefinition?> GetOrNullAsync([NotNull] string name); |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Tools": "Tools", |
|||
"Tools:GetNowTime": "Get now time", |
|||
"Tools:GetUserInfo": "Get the current user information", |
|||
"Tools:GetSetting": "Get setting value", |
|||
"Tools:GetSettings": "Get all settings" |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Tools": "工具列表", |
|||
"Tools:GetNowTime": "获取当前时间", |
|||
"Tools:GetUserInfo": "获取当前用户信息", |
|||
"Tools:GetSetting": "获取设置值", |
|||
"Tools:GetSettings": "获取所有设置" |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
|
|||
[Dependency(TryRegister = true)] |
|||
public class NullDynamicAIToolDefinitionStore : IDynamicAIToolDefinitionStore, ISingletonDependency |
|||
{ |
|||
private readonly static Task<AIToolDefinition?> CachedNullableAIToolResult = Task.FromResult((AIToolDefinition?)null); |
|||
private readonly static Task<AIToolDefinition> CachedAIToolResult = Task.FromResult((AIToolDefinition)null!); |
|||
|
|||
private readonly static Task<IReadOnlyList<AIToolDefinition>> CachedAIToolsResult = Task.FromResult( |
|||
(IReadOnlyList<AIToolDefinition>)Array.Empty<AIToolDefinition>().ToImmutableList()); |
|||
|
|||
public Task<AIToolDefinition> GetAsync(string name) |
|||
{ |
|||
return CachedAIToolResult; |
|||
} |
|||
|
|||
public Task<IReadOnlyList<AIToolDefinition>> GetAllAsync() |
|||
{ |
|||
return CachedAIToolsResult; |
|||
} |
|||
|
|||
public Task<AIToolDefinition?> GetOrNullAsync(string name) |
|||
{ |
|||
return CachedNullableAIToolResult; |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools.Settings; |
|||
public class GetSettingTool : ITransientDependency |
|||
{ |
|||
public const string Name = "GetSetting"; |
|||
|
|||
private readonly ISettingProvider _settingProvider; |
|||
public GetSettingTool(ISettingProvider settingProvider) |
|||
{ |
|||
_settingProvider = settingProvider; |
|||
} |
|||
|
|||
public async Task<object?> InvokeAsync(string name) |
|||
{ |
|||
return await _settingProvider.GetOrNullAsync(name); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools.Settings; |
|||
public class GetSettingsTool : ITransientDependency |
|||
{ |
|||
public const string Name = "GetSettings"; |
|||
|
|||
private readonly ISettingProvider _settingProvider; |
|||
public GetSettingsTool(ISettingProvider settingProvider) |
|||
{ |
|||
_settingProvider = settingProvider; |
|||
} |
|||
|
|||
public async Task<object?> InvokeAsync() |
|||
{ |
|||
return await _settingProvider.GetAllAsync(); |
|||
} |
|||
} |
|||
@ -0,0 +1,80 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Collections.Immutable; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.StaticDefinitions; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools; |
|||
public class StaticAIToolDefinitionStore : IStaticAIToolDefinitionStore, ISingletonDependency |
|||
{ |
|||
protected IServiceProvider ServiceProvider { get; } |
|||
protected AbpAIToolsOptions Options { get; } |
|||
protected IStaticDefinitionCache<AIToolDefinition, Dictionary<string, AIToolDefinition>> DefinitionCache { get; } |
|||
|
|||
public StaticAIToolDefinitionStore( |
|||
IServiceProvider serviceProvider, |
|||
IOptions<AbpAIToolsOptions> options, |
|||
IStaticDefinitionCache<AIToolDefinition, Dictionary<string, AIToolDefinition>> definitionCache) |
|||
{ |
|||
ServiceProvider = serviceProvider; |
|||
Options = options.Value; |
|||
DefinitionCache = definitionCache; |
|||
} |
|||
|
|||
public virtual async Task<AIToolDefinition> GetAsync(string name) |
|||
{ |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
var workspace = await GetOrNullAsync(name); |
|||
|
|||
if (workspace == null) |
|||
{ |
|||
throw new AbpException("Undefined AITool: " + name); |
|||
} |
|||
|
|||
return workspace; |
|||
} |
|||
|
|||
public virtual async Task<IReadOnlyList<AIToolDefinition>> GetAllAsync() |
|||
{ |
|||
var defs = await GetAIToolDefinitionsAsync(); |
|||
return defs.Values.ToImmutableList(); |
|||
} |
|||
|
|||
public virtual async Task<AIToolDefinition?> GetOrNullAsync(string name) |
|||
{ |
|||
var defs = await GetAIToolDefinitionsAsync(); |
|||
return defs.GetOrDefault(name); |
|||
} |
|||
|
|||
protected virtual async Task<Dictionary<string, AIToolDefinition>> GetAIToolDefinitionsAsync() |
|||
{ |
|||
return await DefinitionCache.GetOrCreateAsync(CreateAIToolDefinitionsAsync); |
|||
} |
|||
|
|||
protected virtual Task<Dictionary<string, AIToolDefinition>> CreateAIToolDefinitionsAsync() |
|||
{ |
|||
var workspaces = new Dictionary<string, AIToolDefinition>(); |
|||
|
|||
using (var scope = ServiceProvider.CreateScope()) |
|||
{ |
|||
var providers = Options |
|||
.DefinitionProviders |
|||
.Select(p => scope.ServiceProvider.GetRequiredService(p) as IAIToolDefinitionProvider) |
|||
.ToList(); |
|||
|
|||
foreach (var provider in providers) |
|||
{ |
|||
provider?.Define(new AIToolDefinitionContext(workspaces)); |
|||
} |
|||
} |
|||
|
|||
return Task.FromResult(workspaces); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,19 @@ |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools.Timing; |
|||
public class NowTimeTool : ITransientDependency |
|||
{ |
|||
public const string Name = "GetNowTime"; |
|||
|
|||
private readonly IClock _clock; |
|||
public NowTimeTool(IClock clock) |
|||
{ |
|||
_clock = clock; |
|||
} |
|||
|
|||
public object? Invoke() |
|||
{ |
|||
return _clock.Now; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace LINGYUN.Abp.AI.Tools.Users; |
|||
|
|||
public class CurrentUserTool : ITransientDependency |
|||
{ |
|||
public const string Name = "GetUserInfo"; |
|||
|
|||
private readonly ICurrentUser _currentUser; |
|||
|
|||
public CurrentUserTool(ICurrentUser currentUser) |
|||
{ |
|||
_currentUser = currentUser; |
|||
} |
|||
|
|||
public object? Invoke() |
|||
{ |
|||
return _currentUser; |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
# LINGYUN.Abp.AI.Tools |
|||
|
|||
AI 工具模块. |
|||
|
|||
参考: [AI 工具调用](https://learn.microsoft.com/zh-cn/dotnet/ai/conceptual/ai-tools) |
|||
|
|||
## 功能特性 |
|||
|
|||
|
|||
## 模块引用 |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpAIToolsModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
// other |
|||
} |
|||
``` |
|||
|
|||
## 依赖模块 |
|||
|
|||
* [AbpAICoreModule](../LINGYUN.Abp.AI.Core) |
|||
|
|||
## 自定义函数 |
|||
|
|||
> 注意: 自定义函数遵循一个约定: 同步工具需要定义一个名为 `Invoke` 的函数; 异步工具需要定义一个名为 `InvokeAsync` 的函数, 如需使用DI容器, 需要将工具注入到容器 |
|||
|
|||
```csharp |
|||
|
|||
public class NowTimeTool : ITransientDependency |
|||
{ |
|||
private readonly IClock _clock; |
|||
public NowTimeTool(IClock clock) |
|||
{ |
|||
_clock = clock; |
|||
} |
|||
|
|||
public object? Invoke() |
|||
{ |
|||
return _clock.Now; |
|||
} |
|||
} |
|||
|
|||
public class NowTimeAIToolDefinitionProvider : AIToolDefinitionProvider |
|||
{ |
|||
context.Add( |
|||
new AIToolDefinition( |
|||
"GetNowTime", |
|||
FunctionAIToolProvider.ProviderName) |
|||
.WithFunction<NowTimeTool>()); |
|||
} |
|||
``` |
|||
|
|||
## 扩展 |
|||
|
|||
实现 `IAIToolProvider` 接口, 以增加其他工具调用 |
|||
实现 `IDynamicAIToolDefinitionStore` 接口, 以增加动态AI工具管理 |
|||
|
|||
```csharp |
|||
public class McpAIToolProvider : IAIToolProvider, ITransientDependency |
|||
{ |
|||
public virtual AITool CreateTool(AIToolDefinition definition) |
|||
{ |
|||
// 你的具体实现 |
|||
} |
|||
} |
|||
|
|||
[DependsOn(typeof(AbpAIToolsModule))] |
|||
public class YouProjectModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpAIToolsOptions>(options => |
|||
{ |
|||
options.AIToolProviders.Add<McpAIToolProvider>(); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
Loading…
Reference in new issue