Browse Source

Merge pull request #1460 from colinin/ai

Optimization ai tool
dev
yx lin 14 hours ago
committed by GitHub
parent
commit
c2829d7f3b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 15
      apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionModal.vue
  2. 17
      apps/vben5/packages/@abp/ui/src/components/localizable-input/LocalizableInput.vue
  3. 383
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327114905_Adjujst-AITool-Description-Length.Designer.cs
  4. 40
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327114905_Adjujst-AITool-Description-Length.cs
  5. 4
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs
  6. 61
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/McpAIToolProvider.cs
  7. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionRecordConsts.cs

15
apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionModal.vue

@ -1,5 +1,6 @@
<script setup lang="ts">
import type { PropertyInfo } from '@abp/ui';
import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface';
import type { FormInstance } from 'ant-design-vue/lib/form';
import type {
DefaultOptionType,
@ -152,6 +153,13 @@ function onProviderChange(_: SelectValue, option: DefaultOptionType) {
}
}
function onBoolPropChange(key: string, e: CheckboxChangeEvent) {
const propertites = formModel.value.extraProperties ?? {};
propertites[key] = {};
propertites[key] = e.target.checked;
formModel.value.extraProperties = propertites;
}
function onPropChange(key: string, prop: PropertyInfo) {
const propertites = formModel.value.extraProperties ?? {};
propertites[key] = {};
@ -277,7 +285,8 @@ async function onSubmit() {
:required="prop.required"
>
<Checkbox
v-model:checked="formModel.extraProperties[prop.name]"
:checked="formModel.extraProperties[prop.name] === true"
@change="onBoolPropChange(prop.name, $event)"
>
{{ prop.displayName }}
</Checkbox>
@ -305,8 +314,8 @@ async function onSubmit() {
<FormItemRest>
<PropertyTable
:data="formModel.extraProperties[prop.name]"
@change="(data) => onPropChange(prop.name, data)"
@delete="(data) => onPropDelete(prop.name, data)"
@change="onPropChange(prop.name, $event)"
@delete="onPropDelete(prop.name, $event)"
/>
</FormItemRest>
</FormItem>

17
apps/vben5/packages/@abp/ui/src/components/localizable-input/LocalizableInput.vue

@ -11,7 +11,7 @@ import {
useAbpStore,
useLocalizationSerializer,
} from '@abp/core';
import { Form, Input, Select } from 'ant-design-vue';
import { Form, Input, Select, Textarea } from 'ant-design-vue';
const props = defineProps<{
allowClear?: boolean;
@ -125,7 +125,7 @@ function triggerDisplayNameChange(value?: string) {
</script>
<template>
<div class="w-full">
<div class="localizable-input w-full">
<ItemReset>
<InputGroup>
<div class="flex flex-row gap-4">
@ -143,7 +143,7 @@ function triggerDisplayNameChange(value?: string) {
/>
</div>
<div class="basis-3/5">
<Input
<Textarea
v-if="getIsFixed"
:allow-clear="props.allowClear"
:disabled="props.disabled"
@ -151,7 +151,7 @@ function triggerDisplayNameChange(value?: string) {
$t('component.localizable_input.resources.fiexed.placeholder')
"
:value="state.displayName"
autocomplete="off"
:auto-size="{ maxRows: 3 }"
class="w-full"
@change="
(e: any) => handleDisplayNameChange(e.target.value?.toString())
@ -179,3 +179,12 @@ function triggerDisplayNameChange(value?: string) {
</ItemReset>
</div>
</template>
<style scoped lang="scss">
.localizable-input {
:deep(.ant-select-selector .ant-select-selection-item) {
word-break: break-all;
white-space: normal;
}
}
</style>

383
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327114905_Adjujst-AITool-Description-Length.Designer.cs

@ -0,0 +1,383 @@
// <auto-generated />
using System;
using LINGYUN.Abp.MicroService.AIService;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Volo.Abp.EntityFrameworkCore;
#nullable disable
namespace LINGYUN.Abp.MicroService.AIService.Migrations
{
[DbContext(typeof(AIServiceMigrationsDbContext))]
[Migration("20260327114905_Adjujst-AITool-Description-Length")]
partial class AdjujstAIToolDescriptionLength
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
.HasAnnotation("ProductVersion", "10.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<DateTime>("ExpiredAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<Guid?>("TenantId")
.HasColumnType("uuid")
.HasColumnName("TenantId");
b.Property<DateTime?>("UpdateAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Workspace")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.HasKey("Id");
b.ToTable("AbpAIConversations", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(1024)
.HasColumnType("character varying(1024)");
b.Property<Guid?>("ConversationId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("text")
.HasColumnName("ExtraProperties");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<DateTime?>("ReplyAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("ReplyMessage")
.HasColumnType("text");
b.Property<string>("Role")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<Guid?>("TenantId")
.HasColumnType("uuid")
.HasColumnName("TenantId");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.Property<string>("Workspace")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.HasKey("Id");
b.HasIndex("TenantId", "ConversationId");
b.ToTable("AbpAITextChatMessages", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<long?>("CachedInputTokenCount")
.HasColumnType("bigint");
b.Property<Guid?>("ConversationId")
.HasColumnType("uuid");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<long?>("InputTokenCount")
.HasColumnType("bigint");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<long?>("OutputTokenCount")
.HasColumnType("bigint");
b.Property<long?>("ReasoningTokenCount")
.HasColumnType("bigint");
b.Property<Guid?>("TenantId")
.HasColumnType("uuid")
.HasColumnName("TenantId");
b.Property<long?>("TotalTokenCount")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("TenantId", "ConversationId");
b.ToTable("AbpAITokenUsages", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tools.AIToolDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("character varying(1024)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("text")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsEnabled")
.HasColumnType("boolean");
b.Property<bool>("IsGlobal")
.HasColumnType("boolean");
b.Property<bool>("IsSystem")
.HasColumnType("boolean");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<string>("Provider")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("StateCheckers")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpAIAIToolDefinitionRecords", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ApiBaseUrl")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("ApiKey")
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<string>("Description")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("text")
.HasColumnName("ExtraProperties");
b.Property<float?>("FrequencyPenalty")
.HasColumnType("real");
b.Property<string>("Instructions")
.HasMaxLength(512)
.HasColumnType("character varying(512)");
b.Property<bool>("IsEnabled")
.HasColumnType("boolean");
b.Property<bool>("IsSystem")
.HasColumnType("boolean");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<int?>("MaxOutputTokens")
.HasColumnType("integer");
b.Property<string>("ModelName")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<float?>("PresencePenalty")
.HasColumnType("real");
b.Property<string>("Provider")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("StateCheckers")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("SystemPrompt")
.HasMaxLength(512)
.HasColumnType("character varying(512)");
b.Property<float?>("Temperature")
.HasColumnType("real");
b.Property<string>("Tools")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpAIWorkspaceDefinitions", (string)null);
});
#pragma warning restore 612, 618
}
}
}

40
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327114905_Adjujst-AITool-Description-Length.cs

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LINGYUN.Abp.MicroService.AIService.Migrations
{
/// <inheritdoc />
public partial class AdjujstAIToolDescriptionLength : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Description",
table: "AbpAIAIToolDefinitionRecords",
type: "character varying(1024)",
maxLength: 1024,
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(128)",
oldMaxLength: 128,
oldNullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "Description",
table: "AbpAIAIToolDefinitionRecords",
type: "character varying(128)",
maxLength: 128,
nullable: true,
oldClrType: typeof(string),
oldType: "character varying(1024)",
oldMaxLength: 1024,
oldNullable: true);
}
}
}

4
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs

@ -221,8 +221,8 @@ namespace LINGYUN.Abp.MicroService.AIService.Migrations
.HasColumnName("CreatorId");
b.Property<string>("Description")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
.HasMaxLength(1024)
.HasColumnType("character varying(1024)");
b.Property<string>("ExtraProperties")
.IsRequired()

61
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/McpAIToolProvider.cs

@ -1,6 +1,7 @@
using LINGYUN.Abp.AI.Localization;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Client;
using System;
using System.Collections.Generic;
@ -61,37 +62,47 @@ public class McpAIToolProvider : IAIToolProvider, ITransientDependency
public async virtual Task<AITool[]> CreateToolsAsync(AIToolDefinition definition)
{
var httpClient = HttpClientFactory.CreateMcpAIToolClient();
var httpClientTransportOptions = new HttpClientTransportOptions
try
{
Endpoint = new Uri(definition.GetMcpEndpoint()),
AdditionalHeaders = new Dictionary<string, string>(),
TransportMode = definition.GetMcpTransportMode(),
ConnectionTimeout = definition.GetMcpConnectionTimeout(),
MaxReconnectionAttempts = definition.GetMcpMaxReconnectionAttempts(),
};
var headers = definition.GetMcpHeaders();
foreach (var header in headers)
{
httpClientTransportOptions.AdditionalHeaders.TryAdd(header.Key, header.Value);
}
var httpClient = HttpClientFactory.CreateMcpAIToolClient();
var httpClientTransportOptions = new HttpClientTransportOptions
{
Endpoint = new Uri(definition.GetMcpEndpoint()),
AdditionalHeaders = new Dictionary<string, string>(),
TransportMode = definition.GetMcpTransportMode(),
ConnectionTimeout = definition.GetMcpConnectionTimeout(),
MaxReconnectionAttempts = definition.GetMcpMaxReconnectionAttempts(),
};
if (definition.IsUseMcpCurrentAccessToken())
{
var accessTokenProvider = ServiceProvider.GetRequiredService<IAbpAccessTokenProvider>();
var headers = definition.GetMcpHeaders();
foreach (var header in headers)
{
httpClientTransportOptions.AdditionalHeaders.TryAdd(header.Key, header.Value);
}
var token = await accessTokenProvider.GetTokenAsync();
if (!token.IsNullOrWhiteSpace())
if (definition.IsUseMcpCurrentAccessToken())
{
// TODO: 使用OAuth配置?
httpClientTransportOptions.AdditionalHeaders.TryAdd("Authorization", $"Bearer {token}");
var accessTokenProvider = ServiceProvider.GetRequiredService<IAbpAccessTokenProvider>();
var token = await accessTokenProvider.GetTokenAsync();
if (!token.IsNullOrWhiteSpace())
{
// TODO: 使用OAuth配置?
httpClientTransportOptions.AdditionalHeaders.TryAdd("Authorization", $"Bearer {token}");
}
}
}
var mcpClient = await McpClient.CreateAsync(
new HttpClientTransport(httpClientTransportOptions, httpClient));
var mcpClient = await McpClient.CreateAsync(
new HttpClientTransport(httpClientTransportOptions, httpClient));
return (await mcpClient.ListToolsAsync()).ToArray();
return (await mcpClient.ListToolsAsync()).ToArray();
}
catch (Exception ex)
{
ServiceProvider
.GetService<ILogger<McpAIToolProvider>>()
?.LogWarning(ex, "Mcp tool connection failed: {message}", ex.Message);
return [];
}
}
}

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionRecordConsts.cs

@ -3,6 +3,6 @@ public static class AIToolDefinitionRecordConsts
{
public static int MaxNameLength { get; set; } = 64;
public static int MaxProviderLength { get; set; } = 20;
public static int MaxDescriptionLength { get; set; } = 128;
public static int MaxDescriptionLength { get; set; } = 1024;
public static int MaxStateCheckersLength { get; set; } = 256;
}

Loading…
Cancel
Save