diff --git a/aspnet-core/LINGYUN.MicroService.Aspire.slnx b/aspnet-core/LINGYUN.MicroService.Aspire.slnx index 5a0522e3b..cdfeaa94b 100644 --- a/aspnet-core/LINGYUN.MicroService.Aspire.slnx +++ b/aspnet-core/LINGYUN.MicroService.Aspire.slnx @@ -285,6 +285,8 @@ + + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs index 6d3dc38f6..b190eadcb 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/AIServiceModule.cs @@ -1,5 +1,6 @@ using LINGYUN.Abp.AI.Ollama; using LINGYUN.Abp.AI.Tools; +using LINGYUN.Abp.AI.Tools.Http; using LINGYUN.Abp.AIManagement; using LINGYUN.Abp.AspNetCore.HttpOverrides; using LINGYUN.Abp.AspNetCore.Mvc.Localization; @@ -23,6 +24,7 @@ using Volo.Abp.AspNetCore.Serilog; using Volo.Abp.Autofac; using Volo.Abp.Caching.StackExchangeRedis; using Volo.Abp.Http.Client; +using Volo.Abp.Http.Client.IdentityModel.Web; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.Identity; using Volo.Abp.PermissionManagement.OpenIddict; @@ -50,13 +52,14 @@ namespace LINGYUN.Abp.MicroService.AIService; typeof(AbpAIOllamaModule), typeof(AbpAIToolsModule), + typeof(AbpAIToolsHttpModule), typeof(AbpAIManagementApplicationModule), typeof(AbpAIManagementHttpApiModule), typeof(AIServiceMigrationsEntityFrameworkCoreModule), typeof(AbpDataDbMigratorModule), typeof(AbpAspNetCoreAuthenticationJwtBearerModule), typeof(AbpEmailingExceptionHandlingModule), - typeof(AbpHttpClientModule), + typeof(AbpHttpClientIdentityModelWebModule), typeof(AbpSmsPlatformModule), typeof(AbpEmailingPlatformModule), typeof(AbpCachingStackExchangeRedisModule), 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 994165395..5457d9300 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 @@ -31,6 +31,7 @@ + @@ -58,6 +59,7 @@ + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json index c9a9d8327..3d9679901 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService/appsettings.Development.json @@ -7,6 +7,14 @@ "Auditing": { "AllEntitiesSelector": true }, + "AITools": { + "Http": { + "ApplicationConfiguration": { + "IsEnabled": true, + "EndPoint": "http://localhost:30010/api/abp/application-configuration" + } + } + }, "DistributedCache": { "HideErrors": true, "KeyPrefix": "LINGYUN.Abp.Application", diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/FodyWeavers.xml b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/FodyWeavers.xsd b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/FodyWeavers.xsd new file mode 100644 index 000000000..3f3946e28 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/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.Http/LINGYUN.Abp.AI.Tools.Http.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN.Abp.AI.Tools.Http.csproj new file mode 100644 index 000000000..42e278639 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN.Abp.AI.Tools.Http.csproj @@ -0,0 +1,31 @@ + + + + + + + net8.0;net9.0;net10.0 + LINGYUN.Abp.AI.Tools.Http + LINGYUN.Abp.AI.Tools.Http + false + false + false + enable + + + + + + + + + + + + + + + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/AbpAIToolsHttpModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/AbpAIToolsHttpModule.cs new file mode 100644 index 000000000..7d7f713c9 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/AbpAIToolsHttpModule.cs @@ -0,0 +1,42 @@ +using LINGYUN.Abp.AI.Localization; +using LINGYUN.Abp.AI.Tools.Http.ApplicationConfigurations; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Http.Client; +using Volo.Abp.Localization; +using Volo.Abp.Modularity; +using Volo.Abp.VirtualFileSystem; + +namespace LINGYUN.Abp.AI.Tools.Http; + +[DependsOn( + typeof(AbpAIToolsModule), + typeof(AbpHttpClientModule), + typeof(AbpAspNetCoreMvcContractsModule))] +public class AbpAIToolsHttpModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(context.Configuration.GetSection("AITools:Http:ApplicationConfiguration")); + + Configure(options => + { + options.FileSets.AddEmbedded(); + }); + + Configure(options => + { + // Http工具 + options.AIToolProviders.Add(); + }); + + Configure(options => + { + options.Resources + .Get() + .AddVirtualJson("/LINGYUN/Abp/AI/Tools/Http/Localization/Resources"); + }); + + context.Services.AddHttpAIToolClient(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/AbpAIToolsHttpOptiions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/AbpAIToolsHttpOptiions.cs new file mode 100644 index 000000000..55b447b8c --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/AbpAIToolsHttpOptiions.cs @@ -0,0 +1,31 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; + +namespace LINGYUN.Abp.AI.Tools.Http; +public class AbpAIToolsHttpOptiions +{ + public List> ClientBuildActions { get; } + + public List> ClientActions { get; } + + public List> ClientHandlerActions { get; } + /// + /// 自定义Http请求响应处理 + /// + /// + /// 参数1: HttpAITool工具名称 + /// 参数2: IServiceProvider + /// 参数3: 请求响应 + /// + public IDictionary>> HttpResponseActions { get; } + public AbpAIToolsHttpOptiions() + { + ClientBuildActions = new List>(); + ClientActions = new List>(); + ClientHandlerActions = new List>(); + HttpResponseActions = new Dictionary>>(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/ApplicationConfigurations/AbpAIToolsApplicationConfigurationOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/ApplicationConfigurations/AbpAIToolsApplicationConfigurationOptions.cs new file mode 100644 index 000000000..3a3bda315 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/ApplicationConfigurations/AbpAIToolsApplicationConfigurationOptions.cs @@ -0,0 +1,11 @@ +namespace LINGYUN.Abp.AI.Tools.Http.ApplicationConfigurations; +public class AbpAIToolsApplicationConfigurationOptions +{ + public bool IsEnabled { get; set; } + public string EndPoint { get; set; } + public AbpAIToolsApplicationConfigurationOptions() + { + IsEnabled = false; + EndPoint = "http://localhost:8080/api/abp/application-configuration"; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/GlobalHttpAIToolDefinitionProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/GlobalHttpAIToolDefinitionProvider.cs new file mode 100644 index 000000000..baab188cd --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/GlobalHttpAIToolDefinitionProvider.cs @@ -0,0 +1,36 @@ +using LINGYUN.Abp.AI.Localization; +using LINGYUN.Abp.AI.Tools.Http.ApplicationConfigurations; +using Microsoft.Extensions.Options; +using System.Net.Http; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.AI.Tools.Http; +public class GlobalHttpAIToolDefinitionProvider : AIToolDefinitionProvider +{ + protected AbpAIToolsApplicationConfigurationOptions Options { get; } + public GlobalHttpAIToolDefinitionProvider(IOptions options) + { + Options = options.Value; + } + + public override void Define(IAIToolDefinitionContext context) + { + if (Options.IsEnabled) + { + context.Add( + new AIToolDefinition( + "GetApplicationConfiguration", + HttpAIToolProvider.ProviderName, + L("Tools:GetApplicationConfiguration")) + .WithHttpEndpoint(Options.EndPoint) + .WithHttpMethod(HttpMethod.Get) + .WithHttpHeaders("_AbpDontWrapResult", "true")// 无需包装结果 + .UseHttpCurrentAccessToken()); + } + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAITool.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAITool.cs new file mode 100644 index 000000000..c265ef9a9 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAITool.cs @@ -0,0 +1,136 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Globalization; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading.Tasks; +using Volo.Abp.Http.Client.Authentication; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Timing; +using Volo.Abp.Tracing; + +namespace LINGYUN.Abp.AI.Tools.Http; +public class HttpAITool +{ + protected HttpAIToolInvokeContext Context { get; } + + public HttpAITool(HttpAIToolInvokeContext context) + { + Context = context; + } + + public async virtual Task InvokeAsync() + { + // Abp远程服务适配 + //var remoteService = Context.ToolDefinition.GetRemoteServiceOrNull(); + //var remoteMethod = Context.ToolDefinition.GetRemoteMethodOrNull(); + //if (!remoteService.IsNullOrWhiteSpace() && !remoteMethod.IsNullOrWhiteSpace()) + //{ + // var abpHttpClientOptions = Context.ServiceProvider.GetRequiredService>().Value; + // HttpClientProxyConfig? clientProxyConfig = null; + + // foreach (var httpClientProxyConfig in abpHttpClientOptions.HttpClientProxies.Values) + // { + // if (httpClientProxyConfig.RemoteServiceName.Equals(remoteService)) + // { + // clientProxyConfig = httpClientProxyConfig; + // continue; + // } + // } + + // if (clientProxyConfig != null) + // { + // // var serviceType = clientProxyConfig.Type.GetProperty(nameof(IHttpClientProxy.Service)); + // var serviceMethod = clientProxyConfig.Type.GetMethod(remoteMethod); + // if (serviceMethod != null) + // { + // var httpClientProxyType = typeof(IHttpClientProxy<>).MakeGenericType(clientProxyConfig.Type); + // var httpClientProxy = Context.ServiceProvider.GetRequiredService(httpClientProxyType); + // var service = httpClientProxyType.GetProperty(nameof(IHttpClientProxy.Service))!.GetValue(httpClientProxy); + // // TODO: 暂不支持有参远程服务调用 + // var task = (Task)serviceMethod.Invoke(service, null)!; + // await task; + // // TODO: 必须为Task返回结构 + // return typeof(Task<>).MakeGenericType(serviceMethod.ReturnType.GenericTypeArguments[0]) + // .GetProperty(nameof(Task.Result), BindingFlags.Public | BindingFlags.Instance)! + // .GetValue(task); + // } + // } + //} + + var options = Context.ServiceProvider.GetRequiredService>().Value; + var httpClientFactory = Context.ServiceProvider.GetRequiredService(); + var httpClient = httpClientFactory.GetHttpAIToolClient(); + + var httpRequestMessage = new HttpRequestMessage( + Context.ToolDefinition.GetHttpMethod(), + Context.ToolDefinition.GetHttpEndpoint()); + + var headers = Context.ToolDefinition.GetHttpHeaders(); + foreach (var header in headers) + { + httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + + if (Context.ToolDefinition.IsUseHttpCurrentAccessToken()) + { + var accessTokenProvider = Context.ServiceProvider.GetRequiredService(); + + var token = await accessTokenProvider.GetTokenAsync(); + if (!token.IsNullOrWhiteSpace()) + { + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + } + } + + AddDefaultHeaders(httpRequestMessage); + + var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage); + + if (options.HttpResponseActions.TryGetValue(Context.ToolDefinition.Name, out var customHandler)) + { + return await customHandler(Context.ServiceProvider, httpResponseMessage); + } + + return await httpResponseMessage.Content.ReadAsStringAsync(); + } + + protected virtual void AddDefaultHeaders(HttpRequestMessage requestMessage) + { + //CorrelationId + var correlationIdProvider = Context.ServiceProvider.GetRequiredService(); + var correlationId = correlationIdProvider.Get(); + if (correlationId != null) + { + var correlationIdOptions = Context.ServiceProvider.GetRequiredService>(); + requestMessage.Headers.Add(correlationIdOptions.Value.HttpHeaderName, correlationId); + } + + //TenantId + var currentTenant = Context.ServiceProvider.GetRequiredService(); + if (currentTenant.Id.HasValue) + { + //TODO: Use AbpAspNetCoreMultiTenancyOptions to get the key + requestMessage.Headers.Add(TenantResolverConsts.DefaultTenantKey, currentTenant.Id.Value.ToString()); + } + + //Culture + //TODO: Is that the way we want? Couldn't send the culture (not ui culture) + var currentCulture = CultureInfo.CurrentUICulture.Name ?? CultureInfo.CurrentCulture.Name; + if (!currentCulture.IsNullOrEmpty()) + { + requestMessage.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue(currentCulture)); + } + + //X-Requested-With + requestMessage.Headers.Add("X-Requested-With", "XMLHttpRequest"); + + //Timezone + var currentTimezoneProvider = Context.ServiceProvider.GetRequiredService(); + if (!currentTimezoneProvider.TimeZone.IsNullOrWhiteSpace()) + { + requestMessage.Headers.Add(TimeZoneConsts.DefaultTimeZoneKey, currentTimezoneProvider.TimeZone); + } + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolDefinitionExtenssions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolDefinitionExtenssions.cs new file mode 100644 index 000000000..7e6af037b --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolDefinitionExtenssions.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; +using System.Net.Http; +using Volo.Abp; + +namespace LINGYUN.Abp.AI.Tools.Http; +public static class HttpAIToolDefinitionExtenssions +{ + private const string RemoteService = "RemoteService"; + private const string RemoteController = "RemoteController"; + private const string RemoteMethod = "RemoteMethod"; + + private const string Endpoint = "HttpEndpoint"; + private const string Method = "HttpMethod"; + private const string Headers = "HttpHeaders"; + + private const string CurrentAccessToken = "UseHttpCurrentAccessToken"; + + public static AIToolDefinition UseHttpCurrentAccessToken(this AIToolDefinition definition, bool useCurrentAccessToken = true) + { + definition.WithProperty(CurrentAccessToken, useCurrentAccessToken); + + return definition; + } + + public static AIToolDefinition WithRemoteService(this AIToolDefinition definition, string service, string controllerName, string uniqueMethodName) + { + Check.NotNullOrWhiteSpace(service, nameof(service)); + Check.NotNullOrWhiteSpace(controllerName, nameof(controllerName)); + Check.NotNullOrWhiteSpace(uniqueMethodName, nameof(uniqueMethodName)); + + definition.WithProperty(RemoteService, service); + definition.WithProperty(RemoteController, controllerName); + definition.WithProperty(RemoteMethod, uniqueMethodName); + + return definition; + } + + public static AIToolDefinition WithHttpEndpoint(this AIToolDefinition definition, string endPoint) + { + Check.NotNullOrWhiteSpace(endPoint, nameof(endPoint)); + + definition.WithProperty(Endpoint, endPoint); + + return definition; + } + + public static AIToolDefinition WithHttpMethod(this AIToolDefinition definition, HttpMethod method) + { + Check.NotNull(method, nameof(method)); + + definition.WithProperty(Method, method.Method); + + return definition; + } + + public static AIToolDefinition WithHttpHeaders(this AIToolDefinition definition, IDictionary headers) + { + Check.NotNullOrEmpty(headers, nameof(headers)); + + var currentHeaders = definition.GetHttpHeaders(); + + currentHeaders.AddIfNotContains(headers); + + definition.WithProperty(Headers, currentHeaders); + + return definition; + } + + public static AIToolDefinition WithHttpHeaders(this AIToolDefinition definition, string key, string value) + { + Check.NotNullOrEmpty(key, nameof(key)); + Check.NotNullOrEmpty(value, nameof(value)); + + var currentHeaders = definition.GetHttpHeaders(); + + currentHeaders.TryAdd(key, value); + + definition.WithProperty(Headers, currentHeaders); + + return definition; + } + + public static bool IsUseHttpCurrentAccessToken(this AIToolDefinition definition) + { + if (definition.Properties.TryGetValue(CurrentAccessToken, out var useCurrentAccessTokenObj) && useCurrentAccessTokenObj != null + && bool.TryParse(useCurrentAccessTokenObj.ToString(), out var useCurrentAccessToken)) + { + return useCurrentAccessToken; + } + + return false; + } + + public static string? GetRemoteServiceOrNull(this AIToolDefinition definition) + { + definition.Properties.TryGetValue(RemoteService, out var remoteServiceObj); + + return remoteServiceObj?.ToString(); + } + + public static string? GetRemoteControllerOrNull(this AIToolDefinition definition) + { + definition.Properties.TryGetValue(RemoteController, out var remoteControllerObj); + + return remoteControllerObj?.ToString(); + } + + public static string? GetRemoteMethodOrNull(this AIToolDefinition definition) + { + definition.Properties.TryGetValue(RemoteMethod, out var remoteMethodObj); + + return remoteMethodObj?.ToString(); + } + + public static string GetHttpEndpoint(this AIToolDefinition definition) + { + definition.Properties.TryGetValue(Endpoint, out var endpointObj); + + Check.NotNull(endpointObj, nameof(Endpoint)); + + return endpointObj.ToString()!; + } + + public static HttpMethod GetHttpMethod(this AIToolDefinition definition) + { + if (definition.Properties.TryGetValue(Method, out var methodObj) && methodObj != null) + { + try + { + return HttpMethod.Parse(methodObj.ToString()); + } + catch { } + } + + return HttpMethod.Get; + } + + public static IDictionary GetHttpHeaders(this AIToolDefinition definition) + { + if (definition.Properties.TryGetValue(Headers, out var headersObj) && headersObj != null) + { + if (headersObj is IDictionary h) + { + return h; + } + } + + return new Dictionary(); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolInvokeContext.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolInvokeContext.cs new file mode 100644 index 000000000..41e2bdbf7 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolInvokeContext.cs @@ -0,0 +1,15 @@ +using System; + +namespace LINGYUN.Abp.AI.Tools.Http; +public class HttpAIToolInvokeContext +{ + public IServiceProvider ServiceProvider { get; } + public AIToolDefinition ToolDefinition { get; } + public HttpAIToolInvokeContext( + IServiceProvider serviceProvider, + AIToolDefinition toolDefinition) + { + ServiceProvider = serviceProvider; + ToolDefinition = toolDefinition; + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolProvider.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolProvider.cs new file mode 100644 index 000000000..32022ffa2 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolProvider.cs @@ -0,0 +1,47 @@ +using Microsoft.Extensions.AI; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace LINGYUN.Abp.AI.Tools.Http; +public class HttpAIToolProvider : IAIToolProvider, ITransientDependency +{ + public const string ProviderName = "Http"; + public string Name => ProviderName; + protected IServiceProvider ServiceProvider { get; } + public HttpAIToolProvider(IServiceProvider serviceProvider) + { + ServiceProvider = serviceProvider; + } + + public virtual Task CreateToolsAsync(AIToolDefinition definition) + { + string? description = null; + if (definition.Description != null) + { + var localizerFactory = ServiceProvider.GetRequiredService(); + description = definition.Description.Localize(localizerFactory)?.Value; + } + + var httpAITool = AIFunctionFactory.Create( + method: typeof(HttpAITool).GetMethod(nameof(HttpAITool.InvokeAsync))!, + createInstanceFunc: (AIFunctionArguments args) => + { + var context = new HttpAIToolInvokeContext( + args.Services ?? ServiceProvider, + definition); + + return new HttpAITool(context); + }, + options: new AIFunctionFactoryOptions + { + Name = definition.Name, + Description = description, + AdditionalProperties = definition.Properties, + }); + + return Task.FromResult([httpAITool]); + } +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/en.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/en.json new file mode 100644 index 000000000..088f61324 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/en.json @@ -0,0 +1,6 @@ +{ + "culture": "en", + "texts": { + "Tools:GetApplicationConfiguration": "Get Application Configuration" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/zh-Hans.json b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/zh-Hans.json new file mode 100644 index 000000000..d8439fa2d --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "culture": "zh-Hans", + "texts": { + "Tools:GetApplicationConfiguration": "获取应用程序配置" + } +} \ No newline at end of file diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/Microsoft/Extensions/DependencyInjection/HttpClientHttpAIToolExtenssions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/Microsoft/Extensions/DependencyInjection/HttpClientHttpAIToolExtenssions.cs new file mode 100644 index 000000000..49445cb11 --- /dev/null +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/Microsoft/Extensions/DependencyInjection/HttpClientHttpAIToolExtenssions.cs @@ -0,0 +1,48 @@ +using LINGYUN.Abp.AI.Tools.Http; +using System.Net.Http; +using System.Runtime.InteropServices; + +namespace Microsoft.Extensions.DependencyInjection; +internal static class HttpClientHttpAIToolExtenssions +{ + private const string HttpAIToolClient = "__AbpAIHttpToolClient"; + public static IServiceCollection AddHttpAIToolClient(this IServiceCollection services) + { + var preOptions = services.ExecutePreConfiguredActions(); + + var clientBuilder = services.AddHttpClient(HttpAIToolClient, (provider, client) => + { + foreach (var clientBuildAction in preOptions.ClientActions) + { + clientBuildAction(provider, client); + } + }).ConfigurePrimaryHttpMessageHandler(provider => + { + var handler = new HttpClientHandler(); + + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER"))) + { + handler.UseCookies = false; + } + + foreach (var handlerAction in preOptions.ClientHandlerActions) + { + handlerAction(provider, handler); + } + + return handler; + }); + + foreach (var clientBuildAction in preOptions.ClientBuildActions) + { + clientBuildAction(clientBuilder); + } + + return services; + } + + public static HttpClient GetHttpAIToolClient(this IHttpClientFactory httpClientFactory) + { + return httpClientFactory.CreateClient(HttpAIToolClient); + } +}