diff --git a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json index bf17c63ce..3f2651440 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json @@ -153,6 +153,7 @@ "ai": { "title": "Artificial Intelligence", "workspaces": "Workspaces", - "conversations": "Conversations" + "conversations": "Conversations", + "tools": "AI Tools" } } diff --git a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json index 3e5fe5888..cd4ef5771 100644 --- a/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json +++ b/apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json @@ -153,6 +153,7 @@ "ai": { "title": "人工智能", "workspaces": "工作区管理", - "conversations": "会话管理" + "conversations": "会话管理", + "tools": "工具管理" } } diff --git a/apps/vben5/apps/app-antd/src/views/ai-management/tools/index.vue b/apps/vben5/apps/app-antd/src/views/ai-management/tools/index.vue new file mode 100644 index 000000000..ad9286716 --- /dev/null +++ b/apps/vben5/apps/app-antd/src/views/ai-management/tools/index.vue @@ -0,0 +1,15 @@ + + + diff --git a/apps/vben5/packages/@abp/ai-management/package.json b/apps/vben5/packages/@abp/ai-management/package.json index ae1b893e5..74d3a525e 100644 --- a/apps/vben5/packages/@abp/ai-management/package.json +++ b/apps/vben5/packages/@abp/ai-management/package.json @@ -37,10 +37,12 @@ "ant-design-vue": "catalog:", "ant-design-x-vue": "catalog:", "dayjs": "catalog:", + "lodash.debounce": "catalog:", "markdown-it": "catalog:", "vue": "catalog:" }, "devDependencies": { + "@types/lodash.debounce": "catalog:", "@types/markdown-it": "catalog:" } } diff --git a/apps/vben5/packages/@abp/ai-management/src/api/index.ts b/apps/vben5/packages/@abp/ai-management/src/api/index.ts index f25a7a2c2..b25425b54 100644 --- a/apps/vben5/packages/@abp/ai-management/src/api/index.ts +++ b/apps/vben5/packages/@abp/ai-management/src/api/index.ts @@ -1,3 +1,4 @@ +export { useAIToolsApi } from './useAIToolsApi'; export { useChatsApi } from './useChatsApi'; export { useConversationsApi } from './useConversationsApi'; export { useWorkspaceDefinitionsApi } from './useWorkspaceDefinitionsApi'; diff --git a/apps/vben5/packages/@abp/ai-management/src/api/useAIToolsApi.ts b/apps/vben5/packages/@abp/ai-management/src/api/useAIToolsApi.ts new file mode 100644 index 000000000..c433657ac --- /dev/null +++ b/apps/vben5/packages/@abp/ai-management/src/api/useAIToolsApi.ts @@ -0,0 +1,114 @@ +import type { ListResultDto, PagedResultDto } from '@abp/core'; + +import type { + AIToolDefinitionRecordCreateDto, + AIToolDefinitionRecordDto, + AIToolDefinitionRecordGetPagedListInput, + AIToolDefinitionRecordUpdateDto, + AIToolProviderDto, +} from '../types/tools'; + +import { useRequest } from '@abp/request'; + +export function useAIToolsApi() { + const { cancel, request } = useRequest(); + + /** + * 获取AI工具详情 + * @param id AI工具id + * @returns AI工具详情 + */ + function getApi(id: string): Promise { + return request( + `/api/ai-management/tools/${id}`, + { + method: 'GET', + }, + ); + } + + /** + * 获取AI工具分页列表 + * @param input 查询过滤参数 + * @returns AI工具分页列表 + */ + function getPagedListApi( + input?: AIToolDefinitionRecordGetPagedListInput, + ): Promise> { + return request>( + '/api/ai-management/tools', + { + method: 'GET', + params: input, + }, + ); + } + + /** + * 获取可用AI工具提供者列表 + * @returns AI工具提供者列表 + */ + function getAvailableProviderListApi(): Promise< + ListResultDto + > { + return request>( + '/api/ai-management/tools/available-providers', + { + method: 'GET', + }, + ); + } + + /** + * 删除AI工具 + * @param id AI工具id + */ + function deleteApi(id: string): Promise { + return request(`/api/ai-management/tools/${id}`, { + method: 'DELETE', + }); + } + + /** + * 新增AI工具 + * @param input 参数 + * @returns AI工具详情 + */ + function createApi( + input: AIToolDefinitionRecordCreateDto, + ): Promise { + return request(`/api/ai-management/tools`, { + data: input, + method: 'POST', + }); + } + + /** + * 编辑AI工具 + * @param id AI工具id + * @param input 参数 + * @returns AI工具详情 + */ + function updateApi( + id: string, + input: AIToolDefinitionRecordUpdateDto, + ): Promise { + return request( + `/api/ai-management/tools/${id}`, + { + data: input, + method: 'PUT', + }, + ); + } + + return { + cancel, + createApi, + deleteApi, + getApi, + getAvailableProviderListApi, + getPagedListApi, + updateApi, + }; +} diff --git a/apps/vben5/packages/@abp/ai-management/src/api/useWorkspaceDefinitionsApi.ts b/apps/vben5/packages/@abp/ai-management/src/api/useWorkspaceDefinitionsApi.ts index fec6e8d68..64695d656 100644 --- a/apps/vben5/packages/@abp/ai-management/src/api/useWorkspaceDefinitionsApi.ts +++ b/apps/vben5/packages/@abp/ai-management/src/api/useWorkspaceDefinitionsApi.ts @@ -87,7 +87,7 @@ export function useWorkspaceDefinitionsApi() { } /** - * 新增工作区 + * 编辑工作区 * @param id 工作区id * @param input 参数 * @returns 工作区详情 diff --git a/apps/vben5/packages/@abp/ai-management/src/components/index.ts b/apps/vben5/packages/@abp/ai-management/src/components/index.ts index 1e4ac33f0..cf5a5b9a3 100644 --- a/apps/vben5/packages/@abp/ai-management/src/components/index.ts +++ b/apps/vben5/packages/@abp/ai-management/src/components/index.ts @@ -1,2 +1,3 @@ export { default as Conversations } from './conversations/index.vue'; +export { default as AIToolDefinitionTable } from './tools/AIToolDefinitionTable.vue'; export { default as WorkspaceDefinitionTable } from './workspaces/WorkspaceDefinitionTable.vue'; diff --git a/apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionModal.vue b/apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionModal.vue new file mode 100644 index 000000000..c57c89f80 --- /dev/null +++ b/apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionModal.vue @@ -0,0 +1,321 @@ + + + + + diff --git a/apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionTable.vue b/apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionTable.vue new file mode 100644 index 000000000..fe687793c --- /dev/null +++ b/apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionTable.vue @@ -0,0 +1,236 @@ + + + + + diff --git a/apps/vben5/packages/@abp/ai-management/src/components/workspaces/WorkspaceDefinitionModal.vue b/apps/vben5/packages/@abp/ai-management/src/components/workspaces/WorkspaceDefinitionModal.vue index 9a0a344ea..2019f58d0 100644 --- a/apps/vben5/packages/@abp/ai-management/src/components/workspaces/WorkspaceDefinitionModal.vue +++ b/apps/vben5/packages/@abp/ai-management/src/components/workspaces/WorkspaceDefinitionModal.vue @@ -13,7 +13,11 @@ import { ref, useTemplateRef } from 'vue'; import { useVbenModal } from '@vben/common-ui'; import { $t } from '@vben/locales'; -import { useAuthorization } from '@abp/core'; +import { + useAuthorization, + useLocalization, + useLocalizationSerializer, +} from '@abp/core'; import { useMessage } from '@abp/ui'; import { AutoComplete, @@ -25,7 +29,9 @@ import { Tabs, Textarea, } from 'ant-design-vue'; +import debounce from 'lodash.debounce'; +import { useAIToolsApi } from '../../api/useAIToolsApi'; import { useWorkspaceDefinitionsApi } from '../../api/useWorkspaceDefinitionsApi'; import { WorkspaceDefinitionPermissions } from '../../constants/permissions'; @@ -53,6 +59,10 @@ const defaultModel: WorkspaceDefinitionVto = { const message = useMessage(); const { isGranted } = useAuthorization(); +const { Lr } = useLocalization(); +const { deserialize: deserializeLocalizableString } = + useLocalizationSerializer(); +const { getPagedListApi: getAIToolPagedListApi } = useAIToolsApi(); const { createApi, updateApi, getApi, getAvailableProviderListApi } = useWorkspaceDefinitionsApi(); // 表单允许编辑 @@ -65,6 +75,8 @@ const formModel = ref({ ...defaultModel }); const providerOptions = ref>([]); // 模型选项 const modelOptions = ref>([]); +// AI工具 +const aiToolOptions = ref>([]); // 工作区编辑模态框 const [Modal, modalApi] = useVbenModal({ @@ -85,7 +97,7 @@ async function onInit() { loading: true, title: $t('AIManagement.Workspaces:New'), }); - await onInitProviderOptions(); + await Promise.all([onSearchAIToolOptions(), onInitProviderOptions()]); const { id } = modalApi.getData(); if (!id) { isAllowUpdate.value = isGranted(WorkspaceDefinitionPermissions.Create); @@ -123,6 +135,27 @@ async function onInitProviderOptions() { }); } +/** 初始化AI工具选项 */ +const onSearchAIToolOptions = debounce((filter?: string) => { + getAIToolPagedListApi({ isEnabled: true, filter }).then((res) => { + aiToolOptions.value = res.items.map((item) => { + let description = item.description; + if (description) { + const localizableString = deserializeLocalizableString(description); + description = Lr( + localizableString.resourceName, + localizableString.name, + ); + } + return { + label: item.name, + value: item.name, + description, + }; + }); + }); +}, 500); + function onProviderChange(_: SelectValue, option: DefaultOptionType) { modelOptions.value = option.models; } @@ -188,6 +221,21 @@ async function onSubmit() { :auto-size="{ minRows: 3 }" /> + + +
@@ -254,7 +302,7 @@ async function onSubmit() { > , IHasConcurrencyStamp { + description?: string; + isEnabled: boolean; + isGlobal: boolean; + isSystem: boolean; + name: string; + provider: string; + stateCheckers?: string; +} + +interface AIToolDefinitionRecordCreateOrUpdateDto extends ExtensibleObject { + description?: string; + isEnabled: boolean; + isGlobal: boolean; + provider: string; + stateCheckers?: string; +} + +interface AIToolDefinitionRecordCreateDto extends AIToolDefinitionRecordCreateOrUpdateDto { + name: string; +} + +interface AIToolDefinitionRecordUpdateDto + extends AIToolDefinitionRecordCreateOrUpdateDto, IHasConcurrencyStamp {} + +interface AIToolDefinitionRecordGetPagedListInput extends PagedAndSortedResultRequestDto { + filter?: string; + isEnabled?: boolean; + provider?: string; +} + +interface AIToolPropertyDescriptorDto { + description?: string; + displayName: string; + name: string; + options: NameValue[]; + required: boolean; + valueType: string; +} + +interface AIToolProviderDto { + name: string; + properties: AIToolPropertyDescriptorDto[]; +} + +export type { + AIToolDefinitionRecordCreateDto, + AIToolDefinitionRecordDto, + AIToolDefinitionRecordGetPagedListInput, + AIToolDefinitionRecordUpdateDto, + AIToolProviderDto, +}; diff --git a/apps/vben5/packages/@abp/ai-management/src/types/workspaces.ts b/apps/vben5/packages/@abp/ai-management/src/types/workspaces.ts index acfedc2e4..f88093257 100644 --- a/apps/vben5/packages/@abp/ai-management/src/types/workspaces.ts +++ b/apps/vben5/packages/@abp/ai-management/src/types/workspaces.ts @@ -21,6 +21,7 @@ interface WorkspaceDefinitionRecordDto stateCheckers?: string; systemPrompt?: string; temperature?: number; + tools?: string[]; } interface WorkspaceDefinitionRecordCreateOrUpdateDto extends ExtensibleObject { @@ -38,6 +39,7 @@ interface WorkspaceDefinitionRecordCreateOrUpdateDto extends ExtensibleObject { stateCheckers?: string; systemPrompt?: string; temperature?: number; + tools?: string[]; } interface WorkspaceDefinitionRecordCreateDto extends WorkspaceDefinitionRecordCreateOrUpdateDto { diff --git a/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue b/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue index 1c10b5c35..6a377f570 100644 --- a/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue +++ b/apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue @@ -8,6 +8,7 @@ import { computed, defineAsyncComponent } from 'vue'; import { useVbenModal } from '@vben/common-ui'; import { createIconifyIcon } from '@vben/icons'; import { $t } from '@vben/locales'; +import { isFunction } from '@vben/utils'; import { Button, Popconfirm, Table } from 'ant-design-vue'; @@ -46,8 +47,13 @@ const getTableColumns = computed((): TableColumnsType => { align: 'left', dataIndex: 'key', fixed: 'left', - minWidth: 100, + width: 100, title: $t('component.extra_property_dictionary.key'), + customRender: (opt) => { + if (props.renderKey && isFunction(props.renderKey)) { + return props.renderKey(opt.record.key); + } + }, }, { align: 'left', diff --git a/apps/vben5/packages/@abp/ui/src/components/properties/types.ts b/apps/vben5/packages/@abp/ui/src/components/properties/types.ts index 45dadb6d1..7fd5d9136 100644 --- a/apps/vben5/packages/@abp/ui/src/components/properties/types.ts +++ b/apps/vben5/packages/@abp/ui/src/components/properties/types.ts @@ -1,5 +1,7 @@ import type { Dictionary } from '@abp/core'; +import type { VNode } from 'vue'; + interface PropertyInfo { key: string; value: string; @@ -9,6 +11,7 @@ interface PropertyProps { allowEdit?: boolean; data?: Dictionary; disabled?: boolean; + renderKey?: (key: string) => string | VNode; } export type { PropertyInfo, PropertyProps }; diff --git a/apps/vben5/packages/@abp/webhooks/src/components/subscriptions/WebhookSubscriptionTable.vue b/apps/vben5/packages/@abp/webhooks/src/components/subscriptions/WebhookSubscriptionTable.vue index 554f68efa..0634ecaff 100644 --- a/apps/vben5/packages/@abp/webhooks/src/components/subscriptions/WebhookSubscriptionTable.vue +++ b/apps/vben5/packages/@abp/webhooks/src/components/subscriptions/WebhookSubscriptionTable.vue @@ -273,7 +273,7 @@ function onUpdate(row: WebhookSubscriptionDto) { function onDelete(row: WebhookSubscriptionDto) { Modal.confirm({ centered: true, - content: $t('AbpUi.ItemWillBeDeletedMessageWithFormat', [row.title]), + content: $t('AbpUi.ItemWillBeDeletedMessageWithFormat', [row.description]), onCancel: () => { cancel(); }, diff --git a/apps/vben5/packages/@abp/webhooks/src/types/subscriptions.ts b/apps/vben5/packages/@abp/webhooks/src/types/subscriptions.ts index e88459dfb..bd3203f56 100644 --- a/apps/vben5/packages/@abp/webhooks/src/types/subscriptions.ts +++ b/apps/vben5/packages/@abp/webhooks/src/types/subscriptions.ts @@ -5,8 +5,7 @@ import type { } from '@abp/core'; interface WebhookSubscriptionDto - extends CreationAuditedEntityDto, - IHasConcurrencyStamp { + extends CreationAuditedEntityDto, IHasConcurrencyStamp { description?: string; headers?: Record; isActive: boolean; @@ -31,15 +30,13 @@ interface WebhookSubscriptionCreateOrUpdateDto { type WebhookSubscriptionCreateDto = WebhookSubscriptionCreateOrUpdateDto; interface WebhookSubscriptionUpdateDto - extends IHasConcurrencyStamp, - WebhookSubscriptionCreateOrUpdateDto {} + extends IHasConcurrencyStamp, WebhookSubscriptionCreateOrUpdateDto {} interface WebhookSubscriptionDeleteManyInput { recordIds: string[]; } -interface WebhookSubscriptionGetListInput - extends PagedAndSortedResultRequestDto { +interface WebhookSubscriptionGetListInput extends PagedAndSortedResultRequestDto { beginCreationTime?: Date; endCreationTime?: Date; filter?: string;