diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9964f7847..df0b5e2c1 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -348,6 +348,8 @@
+
+
diff --git a/aspnet-core/LINGYUN.MicroService.Aspire.slnx b/aspnet-core/LINGYUN.MicroService.Aspire.slnx
index ff2345acb..5a0522e3b 100644
--- a/aspnet-core/LINGYUN.MicroService.Aspire.slnx
+++ b/aspnet-core/LINGYUN.MicroService.Aspire.slnx
@@ -284,6 +284,7 @@
+
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs
index e18ca18d8..6d3dc38f6 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs
@@ -1,4 +1,6 @@
-using LINGYUN.Abp.AIManagement;
+using LINGYUN.Abp.AI.Ollama;
+using LINGYUN.Abp.AI.Tools;
+using LINGYUN.Abp.AIManagement;
using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Localization;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
@@ -46,6 +48,8 @@ namespace LINGYUN.Abp.MicroService.AIService;
typeof(AbpIdentitySessionAspNetCoreModule),
+ typeof(AbpAIOllamaModule),
+ typeof(AbpAIToolsModule),
typeof(AbpAIManagementApplicationModule),
typeof(AbpAIManagementHttpApiModule),
typeof(AIServiceMigrationsEntityFrameworkCoreModule),
diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj
index 828674405..994165395 100644
--- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj
+++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/LINGYUN.Abp.MicroService.AIService.csproj
@@ -56,6 +56,8 @@
+
+
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AbpAIAgentOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AbpAIAgentOptions.cs
new file mode 100644
index 000000000..78986a1dd
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AbpAIAgentOptions.cs
@@ -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> AgentBuildActions { get; }
+ public List> AgentOptionActions { get; }
+ public AbpAIAgentOptions()
+ {
+ AgentBuildActions = new List>();
+ AgentOptionActions = new List>();
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs
index 0a89027e3..110f5695f 100644
--- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Agent/LINGYUN/Abp/AI/Agent/AgentFactory.cs
@@ -2,6 +2,7 @@
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Localization;
+using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
using Volo.Abp.AI;
@@ -12,6 +13,7 @@ namespace LINGYUN.Abp.AI.Agent;
public class AgentFactory : IAgentFactory, IScopedDependency
{
protected IServiceProvider ServiceProvider { get; }
+ protected AbpAIAgentOptions AgentOptions { get; }
protected IChatClientFactory ChatClientFactory { get; }
protected IStringLocalizerFactory StringLocalizerFactory { get; }
protected IWorkspaceDefinitionManager WorkspaceDefinitionManager { get; }
@@ -19,10 +21,12 @@ public class AgentFactory : IAgentFactory, IScopedDependency
public AgentFactory(
IServiceProvider serviceProvider,
IChatClientFactory chatClientFactory,
+ IOptions agentOptions,
IStringLocalizerFactory stringLocalizerFactory,
IWorkspaceDefinitionManager workspaceDefinitionManager,
ISimpleStateCheckerManager stateCheckerManager)
{
+ AgentOptions = agentOptions.Value;
ServiceProvider = serviceProvider;
ChatClientFactory = chatClientFactory;
StringLocalizerFactory = stringLocalizerFactory;
@@ -75,8 +79,21 @@ public class AgentFactory : IAgentFactory, IScopedDependency
Description = description,
};
- var aiAgent = chatClient.CreateAIAgent(clientAgentOptions)
- .AsBuilder()
+ foreach (var handlerAction in AgentOptions.AgentOptionActions)
+ {
+ handlerAction(workspace, clientAgentOptions);
+ }
+
+ var aiAgentBuilder = chatClient
+ .CreateAIAgent(clientAgentOptions)
+ .AsBuilder();
+
+ foreach (var handlerAction in AgentOptions.AgentBuildActions)
+ {
+ handlerAction(workspace, aiAgentBuilder);
+ }
+
+ var aiAgent = aiAgentBuilder
.UseLogging()
.UseOpenTelemetry()
.Build(ServiceProvider);
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs
index 8297e1b58..93b597a09 100644
--- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs
@@ -1,5 +1,9 @@
using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Extensions.AI;
+using Microsoft.SemanticKernel;
+using System;
using System.Collections.Generic;
+using System.Threading.Tasks;
using Volo.Abp.Collections;
namespace LINGYUN.Abp.AI;
@@ -9,6 +13,9 @@ public class AbpAICoreOptions
public ITypeList ChatClientProviders { get; }
public ITypeList KernelProviders { get; }
+ public List> KernelBuildActions { get; }
+ public List> ChatClientBuildActions { get; }
+
public HashSet DeletedWorkspaces { get; }
public AbpAICoreOptions()
@@ -18,5 +25,7 @@ public class AbpAICoreOptions
KernelProviders = new TypeList();
DeletedWorkspaces = new HashSet();
+ KernelBuildActions = new List>();
+ ChatClientBuildActions = new List>();
}
}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs
index 7e3bd4047..3d413b26f 100644
--- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIChatClientProvider.cs
@@ -1,6 +1,8 @@
using LINGYUN.Abp.AI.Models;
using LINGYUN.Abp.AI.Workspaces;
using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using OpenAI;
using System;
using System.ClientModel;
@@ -31,7 +33,7 @@ public class OpenAIChatClientProvider : ChatClientProvider
];
}
- public override Task CreateAsync(WorkspaceDefinition workspace)
+ public override async Task CreateAsync(WorkspaceDefinition workspace)
{
Check.NotNull(workspace, nameof(workspace));
Check.NotNullOrWhiteSpace(workspace.ApiKey, nameof(WorkspaceDefinition.ApiKey));
@@ -43,16 +45,23 @@ public class OpenAIChatClientProvider : ChatClientProvider
Endpoint = new Uri(workspace.ApiBaseUrl ?? DefaultEndpoint),
});
- var chatClient = openAIClient
+ var options = ServiceProvider.GetRequiredService>().Value;
+
+ var chatClientBuilder = openAIClient
.GetChatClient(workspace.ModelName)
.AsIChatClient()
- .AsBuilder()
+ .AsBuilder();
+
+ foreach (var handlerAction in options.ChatClientBuildActions)
+ {
+ await handlerAction(workspace, ServiceProvider, chatClientBuilder);
+ }
+
+ return chatClientBuilder
.UseLogging()
.UseOpenTelemetry()
.UseFunctionInvocation()
.UseDistributedCache()
.Build(ServiceProvider);
-
- return Task.FromResult(chatClient);
}
}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIKernelProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIKernelProvider.cs
index 2eb16859e..56cd5245e 100644
--- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIKernelProvider.cs
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Internal/OpenAIKernelProvider.cs
@@ -1,4 +1,6 @@
using LINGYUN.Abp.AI.Workspaces;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using Microsoft.SemanticKernel;
using OpenAI;
using System;
@@ -23,6 +25,8 @@ public class OpenAIKernelProvider : KernelProvider
Check.NotNull(workspace, nameof(workspace));
Check.NotNullOrWhiteSpace(workspace.ApiKey, nameof(WorkspaceDefinition.ApiKey));
+ var options = ServiceProvider.GetRequiredService>().Value;
+
var openAIClient = new OpenAIClient(
new ApiKeyCredential(workspace.ApiKey),
new OpenAIClientOptions
@@ -30,10 +34,14 @@ public class OpenAIKernelProvider : KernelProvider
Endpoint = new Uri(workspace.ApiBaseUrl ?? DefaultEndpoint),
});
- var kernel = Kernel.CreateBuilder()
- .AddOpenAIChatClient(workspace.ModelName, openAIClient)
- .Build();
+ var kernelBuilder = Kernel.CreateBuilder()
+ .AddOpenAIChatClient(workspace.ModelName, openAIClient);
+
+ foreach (var handlerAction in options.KernelBuildActions)
+ {
+ handlerAction(workspace, kernelBuilder);
+ }
- return Task.FromResult(kernel);
+ return Task.FromResult(kernelBuilder.Build());
}
}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tools/GlobalFunctions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tools/GlobalFunctions.cs
deleted file mode 100644
index cc52e1fd7..000000000
--- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Tools/GlobalFunctions.cs
+++ /dev/null
@@ -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");
-}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionManager.cs
index 82bd1c224..2c1014072 100644
--- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionManager.cs
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/IWorkspaceDefinitionManager.cs
@@ -5,12 +5,12 @@ using System.Threading.Tasks;
namespace LINGYUN.Abp.AI.Workspaces;
public interface IWorkspaceDefinitionManager
{
- [ItemNotNull]
+ [NotNull]
Task GetAsync([NotNull] string name);
[ItemNotNull]
Task> GetAllAsync();
- [ItemCanBeNull]
+ [CanBeNull]
Task GetOrNullAsync([NotNull] string name);
}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/NullDynamicWorkspaceDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/NullDynamicWorkspaceDefinitionStore.cs
index dd6953ba7..16a9a41c6 100644
--- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/NullDynamicWorkspaceDefinitionStore.cs
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/NullDynamicWorkspaceDefinitionStore.cs
@@ -5,6 +5,8 @@ using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.AI.Workspaces;
+
+[Dependency(TryRegister = true)]
public class NullDynamicWorkspaceDefinitionStore : IDynamicWorkspaceDefinitionStore, ISingletonDependency
{
private readonly static Task CachedNullableWorkspaceResult = Task.FromResult((WorkspaceDefinition?)null);
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN.Abp.AI.Ollama.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN.Abp.AI.Ollama.csproj
new file mode 100644
index 000000000..60d093d0a
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN.Abp.AI.Ollama.csproj
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0
+ LINGYUN.Abp.AI.Ollama
+ LINGYUN.Abp.AI.Ollama
+ $(NoWarn);SKEXP0001,SKEXP0010,SKEXP0070
+ false
+ false
+ false
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/AbpAIOllamaModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/AbpAIOllamaModule.cs
new file mode 100644
index 000000000..7c061c42e
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/AbpAIOllamaModule.cs
@@ -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(options =>
+ {
+ options.ChatClientProviders.Add();
+
+ options.KernelProviders.Add();
+ });
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/OllamaChatClientProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/OllamaChatClientProvider.cs
new file mode 100644
index 000000000..7ce79dde8
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/OllamaChatClientProvider.cs
@@ -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 CreateAsync(WorkspaceDefinition workspace)
+ {
+ Check.NotNull(workspace, nameof(workspace));
+
+ var options = ServiceProvider.GetRequiredService>().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 [];
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/OllamaKernelProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/OllamaKernelProvider.cs
new file mode 100644
index 000000000..3cef63e1f
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Ollama/LINGYUN/Abp/AI/Ollama/OllamaKernelProvider.cs
@@ -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 CreateAsync(WorkspaceDefinition workspace)
+ {
+ Check.NotNull(workspace, nameof(workspace));
+
+ var options = ServiceProvider.GetRequiredService>().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());
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN.Abp.AI.Tools.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN.Abp.AI.Tools.csproj
new file mode 100644
index 000000000..d82d95f49
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN.Abp.AI.Tools.csproj
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0
+ LINGYUN.Abp.AI.Tools
+ LINGYUN.Abp.AI.Tools
+ false
+ false
+ false
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinition.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinition.cs
new file mode 100644
index 000000000..5e4bc9a7e
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinition.cs
@@ -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
+{
+ ///
+ /// 名称
+ ///
+ [NotNull]
+ public string Name { get; }
+ ///
+ /// 提供者
+ ///
+ [NotNull]
+ public string Provider { get; }
+ ///
+ /// 描述
+ ///
+ public ILocalizableString? Description { get; set; }
+ ///
+ /// 属性
+ ///
+ [NotNull]
+ public Dictionary Properties { get; }
+
+ public List> StateCheckers { get; }
+
+ public AIToolDefinition(
+ [NotNull] string name,
+ [NotNull] string provider,
+ ILocalizableString? description = null)
+ {
+ Name = name;
+ Provider = provider;
+ Description = description;
+
+ Properties = new Dictionary();
+ StateCheckers = new List>();
+ }
+
+ public virtual AIToolDefinition WithProperty(string key, object? value)
+ {
+ Properties[key] = value;
+ return this;
+ }
+
+ public override string ToString()
+ {
+ return $"[{nameof(AIToolDefinition)} {Name}]";
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionContext.cs
new file mode 100644
index 000000000..319fa6608
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionContext.cs
@@ -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 Tools { get; }
+
+ public AIToolDefinitionContext(Dictionary tools)
+ {
+ Tools = tools;
+ }
+
+ public virtual AIToolDefinition? GetOrNull(string name)
+ {
+ return Tools.GetOrDefault(name);
+ }
+
+ public virtual IReadOnlyList 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;
+ }
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs
new file mode 100644
index 000000000..2c33bc160
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs
@@ -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 GetAsync(string name)
+ {
+ var workspace = await GetOrNullAsync(name);
+ if (workspace == null)
+ {
+ throw new AbpException("Undefined AITool: " + name);
+ }
+
+ return workspace;
+ }
+
+ public virtual async Task GetOrNullAsync(string name)
+ {
+ Check.NotNull(name, nameof(name));
+
+ return await StaticStore.GetOrNullAsync(name) ?? await DynamicStore.GetOrNullAsync(name);
+ }
+
+ public virtual async Task> 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();
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionProvider.cs
new file mode 100644
index 000000000..ab3641d8f
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionProvider.cs
@@ -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);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolFactory.cs
new file mode 100644
index 000000000..5d9a34fc3
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolFactory.cs
@@ -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 StateCheckerManager { get; }
+ protected IAIToolDefinitionManager AIToolDefinitionManager { get; }
+ protected IAIToolProviderManager AIToolProviderManager { get; }
+
+ public AIToolFactory(
+ ISimpleStateCheckerManager 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 CreateAllTools()
+ {
+ var aiTools = new List();
+ var toolDefines = await AIToolDefinitionManager.GetAllAsync();
+
+ foreach (var toolDefine in toolDefines)
+ {
+ if (await StateCheckerManager.IsEnabledAsync(toolDefine))
+ {
+ aiTools.Add(CreateTool(toolDefine));
+ }
+ }
+
+ return aiTools.ToArray();
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolProviderManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolProviderManager.cs
new file mode 100644
index 000000000..e58c40a14
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolProviderManager.cs
@@ -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 Providers => _lazyProviders.Value;
+
+ protected AbpAIToolsOptions Options { get; }
+ protected IServiceProvider ServiceProvider { get; }
+
+ private readonly Lazy> _lazyProviders;
+
+ public AIToolProviderManager(
+ IServiceProvider serviceProvider,
+ IOptions options)
+ {
+
+ Options = options.Value;
+ ServiceProvider = serviceProvider;
+
+ _lazyProviders = new Lazy>(GetProviders, true);
+ }
+
+ protected virtual List 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;
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs
new file mode 100644
index 000000000..30029e5f5
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs
@@ -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(options =>
+ {
+ options.FileSets.AddEmbedded();
+ });
+
+ Configure(options =>
+ {
+ // 自定义函数工具
+ options.AIToolProviders.Add();
+ });
+
+ Configure(options =>
+ {
+ options.ChatClientBuildActions.Add(async (_, sp, builder) =>
+ {
+ var aiToolFactory = sp.GetRequiredService();
+ 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(options =>
+ {
+ options.Resources
+ .Get()
+ .AddVirtualJson("/LINGYUN/Abp/AI/Tools/Localization/Resources");
+ });
+ }
+
+ private static void AutoAddDefinitionProviders(IServiceCollection services)
+ {
+ var definitionProviders = new List();
+
+ services.OnRegistered(context =>
+ {
+ if (typeof(IAIToolDefinitionProvider).IsAssignableFrom(context.ImplementationType))
+ {
+ definitionProviders.Add(context.ImplementationType);
+ }
+ });
+
+ services.Configure(options =>
+ {
+ options.DefinitionProviders.AddIfNotContains(definitionProviders);
+ });
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs
new file mode 100644
index 000000000..c2d4d872c
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Collections;
+
+namespace LINGYUN.Abp.AI.Tools;
+public class AbpAIToolsOptions
+{
+ public ITypeList DefinitionProviders { get; }
+ public ITypeList AIToolProviders { get; }
+ public AbpAIToolsOptions()
+ {
+ DefinitionProviders = new TypeList();
+ AIToolProviders = new TypeList();
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolDefinitionExtenssions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolDefinitionExtenssions.cs
new file mode 100644
index 000000000..56f9e9e98
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolDefinitionExtenssions.cs
@@ -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(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;
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolProvider.cs
new file mode 100644
index 000000000..fea88cecb
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolProvider.cs
@@ -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();
+ 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,
+ });
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/GlobalAIToolDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/GlobalAIToolDefinitionProvider.cs
new file mode 100644
index 000000000..03cfbc3ae
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/GlobalAIToolDefinitionProvider.cs
@@ -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(),
+
+ new AIToolDefinition(
+ CurrentUserTool.Name,
+ FunctionAIToolProvider.ProviderName,
+ L("Tools:GetUserInfo"))
+ .WithFunction(),
+
+ new AIToolDefinition(
+ GetSettingTool.Name,
+ FunctionAIToolProvider.ProviderName,
+ L("Tools:GetSetting"))
+ .WithFunction(),
+ new AIToolDefinition(
+ GetSettingsTool.Name,
+ FunctionAIToolProvider.ProviderName,
+ L("Tools:GetSettings"))
+ .WithFunction());
+ }
+
+ private static LocalizableString L(string name)
+ {
+ return LocalizableString.Create(name);
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionContext.cs
new file mode 100644
index 000000000..20cc54b27
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionContext.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.AI.Tools;
+public interface IAIToolDefinitionContext
+{
+ AIToolDefinition? GetOrNull(string name);
+
+ IReadOnlyList GetAll();
+
+ void Add(params AIToolDefinition[] definitions);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionManager.cs
new file mode 100644
index 000000000..430763b06
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionManager.cs
@@ -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 GetAsync([NotNull] string name);
+
+ [ItemNotNull]
+ Task> GetAllAsync();
+
+ [CanBeNull]
+ Task GetOrNullAsync([NotNull] string name);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionProvider.cs
new file mode 100644
index 000000000..29cd49430
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolDefinitionProvider.cs
@@ -0,0 +1,5 @@
+namespace LINGYUN.Abp.AI.Tools;
+public interface IAIToolDefinitionProvider
+{
+ void Define(IAIToolDefinitionContext context);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolFactory.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolFactory.cs
new file mode 100644
index 000000000..929403ab3
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolFactory.cs
@@ -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 CreateAllTools();
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolProvider.cs
new file mode 100644
index 000000000..2d65d95eb
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolProvider.cs
@@ -0,0 +1,9 @@
+using Microsoft.Extensions.AI;
+
+namespace LINGYUN.Abp.AI.Tools;
+public interface IAIToolProvider
+{
+ string Name { get; }
+
+ AITool CreateTool(AIToolDefinition definition);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolProviderManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolProviderManager.cs
new file mode 100644
index 000000000..5a565483d
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolProviderManager.cs
@@ -0,0 +1,7 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.AI.Tools;
+public interface IAIToolProviderManager
+{
+ List Providers { get; }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IDynamicAIToolDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IDynamicAIToolDefinitionStore.cs
new file mode 100644
index 000000000..b980cc406
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IDynamicAIToolDefinitionStore.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI.Tools;
+public interface IDynamicAIToolDefinitionStore
+{
+ Task GetAsync([NotNull] string name);
+
+ Task> GetAllAsync();
+
+ Task GetOrNullAsync([NotNull] string name);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IStaticAIToolDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IStaticAIToolDefinitionStore.cs
new file mode 100644
index 000000000..d938ce26f
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IStaticAIToolDefinitionStore.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.AI.Tools;
+public interface IStaticAIToolDefinitionStore
+{
+ Task GetAsync([NotNull] string name);
+
+ Task> GetAllAsync();
+
+ Task GetOrNullAsync([NotNull] string name);
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/en.json
new file mode 100644
index 000000000..7a1b2b9f1
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/en.json
@@ -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"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/zh-Hans.json
new file mode 100644
index 000000000..0b5545756
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/zh-Hans.json
@@ -0,0 +1,10 @@
+{
+ "culture": "zh-Hans",
+ "texts": {
+ "Tools": "工具列表",
+ "Tools:GetNowTime": "获取当前时间",
+ "Tools:GetUserInfo": "获取当前用户信息",
+ "Tools:GetSetting": "获取设置值",
+ "Tools:GetSettings": "获取所有设置"
+ }
+}
\ No newline at end of file
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/NullDynamicAIToolDefinitionStore.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/NullDynamicAIToolDefinitionStore.cs
new file mode 100644
index 000000000..b108cf9d6
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/NullDynamicAIToolDefinitionStore.cs
@@ -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 CachedNullableAIToolResult = Task.FromResult((AIToolDefinition?)null);
+ private readonly static Task CachedAIToolResult = Task.FromResult((AIToolDefinition)null!);
+
+ private readonly static Task> CachedAIToolsResult = Task.FromResult(
+ (IReadOnlyList)Array.Empty().ToImmutableList());
+
+ public Task GetAsync(string name)
+ {
+ return CachedAIToolResult;
+ }
+
+ public Task> GetAllAsync()
+ {
+ return CachedAIToolsResult;
+ }
+
+ public Task GetOrNullAsync(string name)
+ {
+ return CachedNullableAIToolResult;
+ }
+}
diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Settings/GetSettingTool.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Settings/GetSettingTool.cs
new file mode 100644
index 000000000..b5ee97281
--- /dev/null
+++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Settings/GetSettingTool.cs
@@ -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