Browse Source

Merge pull request #1457 from colinin/ai

AI Tool Management
pull/1467/head
yx lin 6 months ago
committed by GitHub
parent
commit
ce8b2c4adb
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 3
      apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json
  2. 3
      apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json
  3. 15
      apps/vben5/apps/app-antd/src/views/ai-management/tools/index.vue
  4. 2
      apps/vben5/packages/@abp/ai-management/package.json
  5. 1
      apps/vben5/packages/@abp/ai-management/src/api/index.ts
  6. 114
      apps/vben5/packages/@abp/ai-management/src/api/useAIToolsApi.ts
  7. 2
      apps/vben5/packages/@abp/ai-management/src/api/useWorkspaceDefinitionsApi.ts
  8. 1
      apps/vben5/packages/@abp/ai-management/src/components/index.ts
  9. 321
      apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionModal.vue
  10. 236
      apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionTable.vue
  11. 54
      apps/vben5/packages/@abp/ai-management/src/components/workspaces/WorkspaceDefinitionModal.vue
  12. 7
      apps/vben5/packages/@abp/ai-management/src/constants/permissions.ts
  13. 1
      apps/vben5/packages/@abp/ai-management/src/types/index.ts
  14. 61
      apps/vben5/packages/@abp/ai-management/src/types/tools.ts
  15. 2
      apps/vben5/packages/@abp/ai-management/src/types/workspaces.ts
  16. 8
      apps/vben5/packages/@abp/ui/src/components/properties/PropertyTable.vue
  17. 3
      apps/vben5/packages/@abp/ui/src/components/properties/types.ts
  18. 2
      apps/vben5/packages/@abp/webhooks/src/components/subscriptions/WebhookSubscriptionTable.vue
  19. 9
      apps/vben5/packages/@abp/webhooks/src/types/subscriptions.ts
  20. 2
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs
  21. 376
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327051206_Add-AI-Tool-Definition-Record.Designer.cs
  22. 51
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327051206_Add-AI-Tool-Definition-Record.cs
  23. 380
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327065840_Add-Tools-To-Workspace-Record.Designer.cs
  24. 29
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327065840_Add-Tools-To-Workspace-Record.cs
  25. 383
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327071051_Add-IsGlobal-To-AITool-Record.Designer.cs
  26. 29
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327071051_Add-IsGlobal-To-AITool-Record.cs
  27. 72
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs
  28. 10
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs
  29. 14
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolDefinitionExtenssions.cs
  30. 27
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolProvider.cs
  31. 6
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/en.json
  32. 6
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/zh-Hans.json
  33. 5
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN.Abp.AI.Tools.Mcp.csproj
  34. 17
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/AbpAIToolsMcpModule.cs
  35. 14
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/Localization/Resources/en.json
  36. 14
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/Localization/Resources/zh-Hans.json
  37. 12
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/McpAIToolDefinitionExtenssions.cs
  38. 36
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/McpAIToolProvider.cs
  39. 9
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinition.cs
  40. 10
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolFactory.cs
  41. 120
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolPropertyDescriptor.cs
  42. 32
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs
  43. 5
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs
  44. 13
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolDefinitionExtenssions.cs
  45. 18
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolProvider.cs
  46. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolProvider.cs
  47. 6
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/en.json
  48. 6
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/zh-Hans.json
  49. 25
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs
  50. 9
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionNames.cs
  51. 10
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordCreateDto.cs
  52. 21
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordCreateOrUpdateDto.cs
  53. 25
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordDto.cs
  54. 14
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordGetPagedListInput.cs
  55. 10
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordUpdateDto.cs
  56. 13
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolPropertyDescriptorDto.cs
  57. 11
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolProviderDto.cs
  58. 17
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/IAIToolDefinitionAppService.cs
  59. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateOrUpdateDto.cs
  60. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs
  61. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordGetPagedListInput.cs
  62. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs
  63. 22
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs
  64. 168
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionAppService.cs
  65. 35
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs
  66. 14
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs
  67. 13
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json
  68. 13
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json
  69. 16
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Tools/AIToolAlreadyExistsException.cs
  70. 8
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionRecordConsts.cs
  71. 1
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecordConsts.cs
  72. 1
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj
  73. 6
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs
  74. 12
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs
  75. 134
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionRecord.cs
  76. 68
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionSerializer.cs
  77. 138
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDynamicInitializer.cs
  78. 160
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/DynamicAIToolDefinitionStore.cs
  79. 57
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/DynamicAIToolDefinitionStoreCacheInvalidator.cs
  80. 81
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/DynamicAIToolDefinitionStoreInMemoryCache.cs
  81. 12
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/IAIToolDefinitionRecordRepository.cs
  82. 11
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/IAIToolDefinitionSerializer.cs
  83. 21
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/IDynamicAIToolDefinitionStoreInMemoryCache.cs
  84. 7
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/IStaticAIToolSaver.cs
  85. 241
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/StaticAIToolSaver.cs
  86. 4
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs
  87. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs
  88. 14
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs
  89. 1
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs
  90. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs
  91. 24
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs
  92. 3
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs
  93. 27
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreAIToolDefinitionRecordRepository.cs
  94. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceDefinitionRecordRepository.cs
  95. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs
  96. 64
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionController.cs
  97. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs

3
apps/vben5/apps/app-antd/src/locales/langs/en-US/abp.json

@ -153,6 +153,7 @@
"ai": { "ai": {
"title": "Artificial Intelligence", "title": "Artificial Intelligence",
"workspaces": "Workspaces", "workspaces": "Workspaces",
"conversations": "Conversations" "conversations": "Conversations",
"tools": "AI Tools"
} }
} }

3
apps/vben5/apps/app-antd/src/locales/langs/zh-CN/abp.json

@ -153,6 +153,7 @@
"ai": { "ai": {
"title": "人工智能", "title": "人工智能",
"workspaces": "工作区管理", "workspaces": "工作区管理",
"conversations": "会话管理" "conversations": "会话管理",
"tools": "工具管理"
} }
} }

15
apps/vben5/apps/app-antd/src/views/ai-management/tools/index.vue

@ -0,0 +1,15 @@
<script lang="ts" setup>
import { Page } from '@vben/common-ui';
import { AIToolDefinitionTable } from '@abp/ai-management';
defineOptions({
name: 'Vben5AIToolDefinitions',
});
</script>
<template>
<Page>
<AIToolDefinitionTable />
</Page>
</template>

2
apps/vben5/packages/@abp/ai-management/package.json

@ -37,10 +37,12 @@
"ant-design-vue": "catalog:", "ant-design-vue": "catalog:",
"ant-design-x-vue": "catalog:", "ant-design-x-vue": "catalog:",
"dayjs": "catalog:", "dayjs": "catalog:",
"lodash.debounce": "catalog:",
"markdown-it": "catalog:", "markdown-it": "catalog:",
"vue": "catalog:" "vue": "catalog:"
}, },
"devDependencies": { "devDependencies": {
"@types/lodash.debounce": "catalog:",
"@types/markdown-it": "catalog:" "@types/markdown-it": "catalog:"
} }
} }

1
apps/vben5/packages/@abp/ai-management/src/api/index.ts

@ -1,3 +1,4 @@
export { useAIToolsApi } from './useAIToolsApi';
export { useChatsApi } from './useChatsApi'; export { useChatsApi } from './useChatsApi';
export { useConversationsApi } from './useConversationsApi'; export { useConversationsApi } from './useConversationsApi';
export { useWorkspaceDefinitionsApi } from './useWorkspaceDefinitionsApi'; export { useWorkspaceDefinitionsApi } from './useWorkspaceDefinitionsApi';

114
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<AIToolDefinitionRecordDto> {
return request<AIToolDefinitionRecordDto>(
`/api/ai-management/tools/${id}`,
{
method: 'GET',
},
);
}
/**
* 获取AI工具分页列表
* @param input 查询过滤参数
* @returns AI工具分页列表
*/
function getPagedListApi(
input?: AIToolDefinitionRecordGetPagedListInput,
): Promise<PagedResultDto<AIToolDefinitionRecordDto>> {
return request<PagedResultDto<AIToolDefinitionRecordDto>>(
'/api/ai-management/tools',
{
method: 'GET',
params: input,
},
);
}
/**
* 获取可用AI工具提供者列表
* @returns AI工具提供者列表
*/
function getAvailableProviderListApi(): Promise<
ListResultDto<AIToolProviderDto>
> {
return request<ListResultDto<AIToolProviderDto>>(
'/api/ai-management/tools/available-providers',
{
method: 'GET',
},
);
}
/**
* 删除AI工具
* @param id AI工具id
*/
function deleteApi(id: string): Promise<void> {
return request(`/api/ai-management/tools/${id}`, {
method: 'DELETE',
});
}
/**
* 新增AI工具
* @param input 参数
* @returns AI工具详情
*/
function createApi(
input: AIToolDefinitionRecordCreateDto,
): Promise<AIToolDefinitionRecordDto> {
return request<AIToolDefinitionRecordDto>(`/api/ai-management/tools`, {
data: input,
method: 'POST',
});
}
/**
* 编辑AI工具
* @param id AI工具id
* @param input 参数
* @returns AI工具详情
*/
function updateApi(
id: string,
input: AIToolDefinitionRecordUpdateDto,
): Promise<AIToolDefinitionRecordDto> {
return request<AIToolDefinitionRecordDto>(
`/api/ai-management/tools/${id}`,
{
data: input,
method: 'PUT',
},
);
}
return {
cancel,
createApi,
deleteApi,
getApi,
getAvailableProviderListApi,
getPagedListApi,
updateApi,
};
}

2
apps/vben5/packages/@abp/ai-management/src/api/useWorkspaceDefinitionsApi.ts

@ -87,7 +87,7 @@ export function useWorkspaceDefinitionsApi() {
} }
/** /**
* 新增工作区 * 编辑工作区
* @param id 工作区id * @param id 工作区id
* @param input 参数 * @param input 参数
* @returns 工作区详情 * @returns 工作区详情

1
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 Conversations } from './conversations/index.vue';
export { default as AIToolDefinitionTable } from './tools/AIToolDefinitionTable.vue';
export { default as WorkspaceDefinitionTable } from './workspaces/WorkspaceDefinitionTable.vue'; export { default as WorkspaceDefinitionTable } from './workspaces/WorkspaceDefinitionTable.vue';

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

@ -0,0 +1,321 @@
<script setup lang="ts">
import type { PropertyInfo } from '@abp/ui';
import type { FormInstance } from 'ant-design-vue/lib/form';
import type {
DefaultOptionType,
SelectProps,
SelectValue,
} from 'ant-design-vue/lib/select';
import type {
AIToolDefinitionRecordDto,
AIToolProviderDto,
} from '../../types/tools';
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import { useAuthorization } from '@abp/core';
import { LocalizableInput, PropertyTable, useMessage } from '@abp/ui';
import {
Checkbox,
Form,
FormItemRest,
Input,
InputNumber,
Select,
Tabs,
} from 'ant-design-vue';
import { useAIToolsApi } from '../../api/useAIToolsApi';
import { AIToolDefinitionPermissions } from '../../constants/permissions';
const emit = defineEmits<{
(event: 'change', data: AIToolDefinitionVto): void;
}>();
const FormItem = Form.Item;
const TabPane = Tabs.TabPane;
type AIToolDefinitionVto = Omit<AIToolDefinitionRecordDto, 'creationTime'> & {
apiKey?: string;
};
// 默认表单数据
const defaultModel: AIToolDefinitionVto = {
id: '',
name: '',
isEnabled: true,
isGlobal: false,
isSystem: false,
provider: '',
extraProperties: {},
};
const message = useMessage();
const { isGranted } = useAuthorization();
const { createApi, updateApi, getApi, getAvailableProviderListApi } =
useAIToolsApi();
// 表单
const form = useTemplateRef<FormInstance>('formEl');
// 表单数据
const formModel = ref<AIToolDefinitionVto>({ ...defaultModel });
// 工具提供者选项
const aiToolProviders = ref<AIToolProviderDto[]>([]);
const currentAIToolProvider = ref<AIToolProviderDto>();
const getAIToolOptions = computed<NonNullable<SelectProps['options']>>(() => {
return aiToolProviders.value.map((provider) => {
return {
label: provider.name,
value: provider.name,
properties: provider.properties,
};
});
});
// 表单允许编辑
const getIsAllowUpdate = computed<boolean>(() => {
if (formModel.value.id) {
if (formModel.value.isSystem) {
return false;
}
return isGranted(AIToolDefinitionPermissions.Update);
}
return isGranted(AIToolDefinitionPermissions.Create);
});
watch(
() => getIsAllowUpdate.value,
(allowUpdate) => {
nextTick(() => {
modalApi.setState({ showConfirmButton: allowUpdate });
});
},
);
// 工作区编辑模态框
const [Modal, modalApi] = useVbenModal({
title: $t('AIManagement.Tools:New'),
class: 'w-1/2',
closeOnClickModal: false,
closeOnPressEscape: false,
fullscreenButton: false,
onConfirm: onSubmit,
async onOpenChange(isOpen) {
isOpen && (await onInit());
},
});
/** 初始化工作区数据 */
async function onInit() {
try {
modalApi.setState({
loading: true,
title: $t('AIManagement.Tools:New'),
});
await onInitProviderOptions();
const { id } = modalApi.getData<AIToolDefinitionVto>();
if (!id) {
formModel.value = { ...defaultModel };
currentAIToolProvider.value = undefined;
return;
}
const dto = await getApi(id);
formModel.value = dto;
currentAIToolProvider.value = aiToolProviders.value.find(
(p) => p.name === dto.provider,
);
modalApi.setState({
title: $t('AIManagement.Tools:Edit'),
});
} finally {
modalApi.setState({
loading: false,
});
}
}
/** 初始化模型选项 */
async function onInitProviderOptions() {
const { items } = await getAvailableProviderListApi();
aiToolProviders.value = items;
}
function onProviderChange(_: SelectValue, option: DefaultOptionType) {
const provider = option as AIToolProviderDto;
currentAIToolProvider.value = provider;
if (provider) {
const propertites: Record<string, any> = {};
provider.properties.forEach((prop) => {
propertites[prop.name] = '';
});
formModel.value.extraProperties ??= {};
formModel.value.extraProperties = propertites;
}
}
function onPropChange(key: string, prop: PropertyInfo) {
const propertites = formModel.value.extraProperties ?? {};
propertites[key] = {};
propertites[key][prop.key] = prop.value;
formModel.value.extraProperties = propertites;
}
function onPropDelete(key: string, prop: PropertyInfo) {
const propertites = formModel.value.extraProperties ?? {};
propertites[key] = {};
delete propertites[key][prop.key];
formModel.value.extraProperties = propertites;
}
/** 提交表单 */
async function onSubmit() {
await form.value?.validate();
try {
modalApi.setState({ submitting: true });
const vto = await (formModel.value.id
? updateApi(formModel.value.id, formModel.value)
: createApi(formModel.value));
formModel.value = vto;
message.success($t('AbpUi.SavedSuccessfully'));
emit('change', vto);
modalApi.close();
} finally {
modalApi.setState({ submitting: false });
}
}
</script>
<template>
<Modal>
<Form
:model="formModel"
ref="formEl"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
:disabled="!getIsAllowUpdate"
scroll-to-first-error
>
<Tabs>
<TabPane key="basic" :tab="$t('AIManagement.BasicInfo')" force-render>
<FormItem
:label="$t('AIManagement.DisplayName:IsEnabled')"
name="isEnabled"
>
<Checkbox v-model:checked="formModel.isEnabled">
{{ $t('AIManagement.DisplayName:IsEnabled') }}
</Checkbox>
</FormItem>
<FormItem
:label="$t('AIManagement.DisplayName:IsGlobal')"
name="isEnabled"
>
<Checkbox v-model:checked="formModel.isGlobal">
{{ $t('AIManagement.DisplayName:IsGlobal') }}
</Checkbox>
</FormItem>
<FormItem
:label="$t('AIManagement.DisplayName:ToolProvider')"
name="provider"
required
>
<Select
:options="getAIToolOptions"
v-model:value="formModel.provider"
@change="onProviderChange"
/>
</FormItem>
<FormItem
:label="$t('AIManagement.DisplayName:Name')"
name="name"
required
>
<Input v-model:value="formModel.name" />
</FormItem>
<FormItem
:label="$t('AIManagement.DisplayName:Description')"
name="description"
>
<LocalizableInput v-model:value="formModel.description" />
</FormItem>
</TabPane>
<TabPane
v-if="currentAIToolProvider"
key="propertites"
:tab="$t('AIManagement.Propertites')"
force-render
>
<div class="h-[500px] overflow-y-auto rounded bg-gray-50 p-4">
<template
v-for="prop in currentAIToolProvider.properties"
:key="prop.name"
>
<FormItem
v-if="prop.valueType === 'String'"
:extra="prop.description"
:label="prop.displayName"
:name="['extraProperties', prop.name]"
:required="prop.required"
>
<Input v-model:value="formModel.extraProperties[prop.name]" />
</FormItem>
<FormItem
v-if="prop.valueType === 'Number'"
:extra="prop.description"
:label="prop.displayName"
:name="['extraProperties', prop.name]"
:required="prop.required"
>
<InputNumber
class="w-full"
:min="0"
v-model:value="formModel.extraProperties[prop.name]"
/>
</FormItem>
<FormItem
v-else-if="prop.valueType === 'Boolean'"
:extra="prop.description"
:label="prop.displayName"
:name="['extraProperties', prop.name]"
:required="prop.required"
>
<Checkbox
v-model:checked="formModel.extraProperties[prop.name]"
>
{{ prop.displayName }}
</Checkbox>
</FormItem>
<FormItem
v-else-if="prop.valueType === 'Select'"
:extra="prop.description"
:label="prop.displayName"
:name="['extraProperties', prop.name]"
:required="prop.required"
>
<Select
class="w-full"
:options="prop.options"
:field-names="{ label: 'name', value: 'value' }"
v-model:value="formModel.extraProperties[prop.name]"
/>
</FormItem>
<FormItem
v-else-if="prop.valueType === 'Dictionary'"
:extra="prop.description"
:label="prop.displayName"
:name="['extraProperties', prop.name]"
>
<FormItemRest>
<PropertyTable
:data="formModel.extraProperties[prop.name]"
@change="(data) => onPropChange(prop.name, data)"
@delete="(data) => onPropDelete(prop.name, data)"
/>
</FormItemRest>
</FormItem>
</template>
</div>
</TabPane>
</Tabs>
</Form>
</Modal>
</template>
<style scoped></style>

236
apps/vben5/packages/@abp/ai-management/src/components/tools/AIToolDefinitionTable.vue

@ -0,0 +1,236 @@
<script setup lang="ts">
import type { VxeGridProps } from '@abp/ui';
import type { VbenFormProps } from '@vben/common-ui';
import type { AIToolDefinitionRecordDto } from '../../types/tools';
import { defineAsyncComponent, h } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
import {
useAuthorization,
useLocalization,
useLocalizationSerializer,
} from '@abp/core';
import { useVbenVxeGrid } from '@abp/ui';
import {
CheckOutlined,
CloseOutlined,
DeleteOutlined,
EditOutlined,
PlusOutlined,
} from '@ant-design/icons-vue';
import { Button, message, Modal } from 'ant-design-vue';
import { useAIToolsApi } from '../../api/useAIToolsApi';
import { AIToolDefinitionPermissions } from '../../constants/permissions';
defineOptions({
name: 'WorkspaceDefinitionTable',
});
const { deleteApi, getPagedListApi } = useAIToolsApi();
const { isGranted } = useAuthorization();
const { Lr } = useLocalization();
const { deserialize: deserializeLocalizableString } =
useLocalizationSerializer();
const formOptions: VbenFormProps = {
// 默认展开
collapsed: true,
collapsedRows: 2,
schema: [
{
component: 'Input',
fieldName: 'filter',
formItemClass: 'col-span-2 items-baseline',
label: $t('AbpUi.Search'),
},
],
// 控制表单是否显示折叠按钮
showCollapseButton: true,
// 按下回车时是否提交表单
submitOnEnter: true,
wrapperClass: 'grid-cols-4',
};
const gridOptions: VxeGridProps<AIToolDefinitionRecordDto> = {
columns: [
{
align: 'left',
field: 'name',
sortable: true,
title: $t('AIManagement.DisplayName:Name'),
},
{
align: 'left',
field: 'provider',
sortable: true,
title: $t('AIManagement.DisplayName:ToolProvider'),
width: 150,
},
{
align: 'left',
field: 'description',
formatter: ({ row }) => {
if (!row.description) {
return '';
}
const localizableString = deserializeLocalizableString(row.description);
return Lr(localizableString.resourceName, localizableString.name);
},
sortable: true,
title: $t('AIManagement.DisplayName:Description'),
width: 180,
},
{
align: 'left',
field: 'isEnabled',
slots: { default: 'isEnabled' },
sortable: true,
title: $t('AIManagement.DisplayName:IsEnabled'),
width: 150,
},
{
align: 'left',
field: 'isGlobal',
slots: { default: 'isGlobal' },
sortable: true,
title: $t('AIManagement.DisplayName:IsGlobal'),
width: 150,
},
{
field: 'actions',
fixed: 'right',
slots: { default: 'actions' },
title: $t('AbpUi.Actions'),
width: 220,
visible: isGranted([
AIToolDefinitionPermissions.Default,
AIToolDefinitionPermissions.Delete,
]),
},
],
exportConfig: {},
keepSource: true,
proxyConfig: {
ajax: {
query: async ({ page, sort }, formValues) => {
const sorting = sort.order ? `${sort.field} ${sort.order}` : undefined;
return await getPagedListApi({
sorting,
maxResultCount: page.pageSize,
skipCount: (page.currentPage - 1) * page.pageSize,
...formValues,
});
},
},
response: {
total: 'totalCount',
list: 'items',
},
},
sortConfig: {
remote: true,
allowBtn: true,
},
toolbarConfig: {
custom: true,
export: true,
// import: true,
refresh: {
code: 'query',
},
zoom: true,
},
};
const [Grid, gridApi] = useVbenVxeGrid<AIToolDefinitionRecordDto>({
formOptions,
gridOptions,
});
const [AIToolDefinitionModal, modalApi] = useVbenModal({
connectedComponent: defineAsyncComponent(
() => import('./AIToolDefinitionModal.vue'),
),
});
function onCreate() {
modalApi.setData({});
modalApi.open();
}
function onUpdate(row: AIToolDefinitionRecordDto) {
modalApi.setData(row);
modalApi.open();
}
async function onDelete(row: AIToolDefinitionRecordDto) {
Modal.confirm({
centered: true,
content: $t('AbpUi.ItemWillBeDeletedMessage'),
onOk: async () => {
await deleteApi(row.id);
message.success($t('AbpUi.DeletedSuccessfully'));
gridApi.query();
},
title: $t('AbpUi.AreYouSure'),
});
}
</script>
<template>
<Grid :table-title="$t('AIManagement.Tools')">
<template #toolbar-tools>
<Button
v-access:code="[AIToolDefinitionPermissions.Create]"
:icon="h(PlusOutlined)"
type="primary"
@click="onCreate"
>
{{ $t('AIManagement.Tools:New') }}
</Button>
</template>
<template #isEnabled="{ row }">
<div class="flex flex-row justify-center">
<CheckOutlined v-if="row.isEnabled" class="text-green-500" />
<CloseOutlined v-else class="text-red-500" />
</div>
</template>
<template #isGlobal="{ row }">
<div class="flex flex-row justify-center">
<CheckOutlined v-if="row.isGlobal" class="text-green-500" />
<CloseOutlined v-else class="text-red-500" />
</div>
</template>
<template #actions="{ row }">
<div class="flex flex-row">
<Button
v-access:code="[AIToolDefinitionPermissions.Default]"
:icon="h(EditOutlined)"
block
type="link"
@click="onUpdate(row)"
>
{{ $t('AbpUi.Edit') }}
</Button>
<Button
v-access:code="[AIToolDefinitionPermissions.Delete]"
:icon="h(DeleteOutlined)"
block
danger
type="link"
@click="onDelete(row)"
>
{{ $t('AbpUi.Delete') }}
</Button>
</div>
</template>
</Grid>
<AIToolDefinitionModal @change="() => gridApi.query()" />
</template>
<style lang="scss" scoped></style>

54
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 { useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { useAuthorization } from '@abp/core'; import {
useAuthorization,
useLocalization,
useLocalizationSerializer,
} from '@abp/core';
import { useMessage } from '@abp/ui'; import { useMessage } from '@abp/ui';
import { import {
AutoComplete, AutoComplete,
@ -25,7 +29,9 @@ import {
Tabs, Tabs,
Textarea, Textarea,
} from 'ant-design-vue'; } from 'ant-design-vue';
import debounce from 'lodash.debounce';
import { useAIToolsApi } from '../../api/useAIToolsApi';
import { useWorkspaceDefinitionsApi } from '../../api/useWorkspaceDefinitionsApi'; import { useWorkspaceDefinitionsApi } from '../../api/useWorkspaceDefinitionsApi';
import { WorkspaceDefinitionPermissions } from '../../constants/permissions'; import { WorkspaceDefinitionPermissions } from '../../constants/permissions';
@ -53,6 +59,10 @@ const defaultModel: WorkspaceDefinitionVto = {
const message = useMessage(); const message = useMessage();
const { isGranted } = useAuthorization(); const { isGranted } = useAuthorization();
const { Lr } = useLocalization();
const { deserialize: deserializeLocalizableString } =
useLocalizationSerializer();
const { getPagedListApi: getAIToolPagedListApi } = useAIToolsApi();
const { createApi, updateApi, getApi, getAvailableProviderListApi } = const { createApi, updateApi, getApi, getAvailableProviderListApi } =
useWorkspaceDefinitionsApi(); useWorkspaceDefinitionsApi();
// 表单允许编辑 // 表单允许编辑
@ -65,6 +75,8 @@ const formModel = ref<WorkspaceDefinitionVto>({ ...defaultModel });
const providerOptions = ref<NonNullable<SelectProps['options']>>([]); const providerOptions = ref<NonNullable<SelectProps['options']>>([]);
// 模型选项 // 模型选项
const modelOptions = ref<NonNullable<SelectProps['options']>>([]); const modelOptions = ref<NonNullable<SelectProps['options']>>([]);
// AI工具
const aiToolOptions = ref<NonNullable<SelectProps['options']>>([]);
// 工作区编辑模态框 // 工作区编辑模态框
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
@ -85,7 +97,7 @@ async function onInit() {
loading: true, loading: true,
title: $t('AIManagement.Workspaces:New'), title: $t('AIManagement.Workspaces:New'),
}); });
await onInitProviderOptions(); await Promise.all([onSearchAIToolOptions(), onInitProviderOptions()]);
const { id } = modalApi.getData<WorkspaceDefinitionVto>(); const { id } = modalApi.getData<WorkspaceDefinitionVto>();
if (!id) { if (!id) {
isAllowUpdate.value = isGranted(WorkspaceDefinitionPermissions.Create); 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) { function onProviderChange(_: SelectValue, option: DefaultOptionType) {
modelOptions.value = option.models; modelOptions.value = option.models;
} }
@ -188,6 +221,21 @@ async function onSubmit() {
:auto-size="{ minRows: 3 }" :auto-size="{ minRows: 3 }"
/> />
</FormItem> </FormItem>
<FormItem :label="$t('AIManagement.DisplayName:Tools')" name="tools">
<Select
:allow-clear="true"
:show-search="true"
:filter-option="false"
:options="aiToolOptions"
v-model:value="formModel.tools"
mode="multiple"
@search="onSearchAIToolOptions"
>
<template #option="{ label, description }">
{{ label }} ({{ description }})
</template>
</Select>
</FormItem>
</TabPane> </TabPane>
<TabPane key="model" :tab="$t('AIManagement.ModelInfo')" force-render> <TabPane key="model" :tab="$t('AIManagement.ModelInfo')" force-render>
<div class="h-[500px] overflow-y-auto rounded bg-gray-50 p-4"> <div class="h-[500px] overflow-y-auto rounded bg-gray-50 p-4">
@ -254,7 +302,7 @@ async function onSubmit() {
> >
<InputNumber <InputNumber
v-model:value="formModel.temperature" v-model:value="formModel.temperature"
:max="1.0" :max="2.0"
:min="0.0" :min="0.0"
:step="0.1" :step="0.1"
class="w-full" class="w-full"

7
apps/vben5/packages/@abp/ai-management/src/constants/permissions.ts

@ -16,3 +16,10 @@ export const ChatPermissions = {
Default: 'AIManagement.Chats', Default: 'AIManagement.Chats',
SendMessage: 'AIManagement.Chats.SendMessage', SendMessage: 'AIManagement.Chats.SendMessage',
}; };
export const AIToolDefinitionPermissions = {
Default: 'AIManagement.AIToolDefinitions',
Create: 'AIManagement.AIToolDefinitions.Create',
Update: 'AIManagement.AIToolDefinitions.Update',
Delete: 'AIManagement.AIToolDefinitions.Delete',
};

1
apps/vben5/packages/@abp/ai-management/src/types/index.ts

@ -1,3 +1,4 @@
export * from './chats'; export * from './chats';
export * from './conversations'; export * from './conversations';
export * from './tools';
export * from './workspaces'; export * from './workspaces';

61
apps/vben5/packages/@abp/ai-management/src/types/tools.ts

@ -0,0 +1,61 @@
import type {
ExtensibleAuditedEntityDto,
ExtensibleObject,
IHasConcurrencyStamp,
NameValue,
PagedAndSortedResultRequestDto,
} from '@abp/core';
interface AIToolDefinitionRecordDto
extends ExtensibleAuditedEntityDto<string>, 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<any>[];
required: boolean;
valueType: string;
}
interface AIToolProviderDto {
name: string;
properties: AIToolPropertyDescriptorDto[];
}
export type {
AIToolDefinitionRecordCreateDto,
AIToolDefinitionRecordDto,
AIToolDefinitionRecordGetPagedListInput,
AIToolDefinitionRecordUpdateDto,
AIToolProviderDto,
};

2
apps/vben5/packages/@abp/ai-management/src/types/workspaces.ts

@ -21,6 +21,7 @@ interface WorkspaceDefinitionRecordDto
stateCheckers?: string; stateCheckers?: string;
systemPrompt?: string; systemPrompt?: string;
temperature?: number; temperature?: number;
tools?: string[];
} }
interface WorkspaceDefinitionRecordCreateOrUpdateDto extends ExtensibleObject { interface WorkspaceDefinitionRecordCreateOrUpdateDto extends ExtensibleObject {
@ -38,6 +39,7 @@ interface WorkspaceDefinitionRecordCreateOrUpdateDto extends ExtensibleObject {
stateCheckers?: string; stateCheckers?: string;
systemPrompt?: string; systemPrompt?: string;
temperature?: number; temperature?: number;
tools?: string[];
} }
interface WorkspaceDefinitionRecordCreateDto extends WorkspaceDefinitionRecordCreateOrUpdateDto { interface WorkspaceDefinitionRecordCreateDto extends WorkspaceDefinitionRecordCreateOrUpdateDto {

8
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 { useVbenModal } from '@vben/common-ui';
import { createIconifyIcon } from '@vben/icons'; import { createIconifyIcon } from '@vben/icons';
import { $t } from '@vben/locales'; import { $t } from '@vben/locales';
import { isFunction } from '@vben/utils';
import { Button, Popconfirm, Table } from 'ant-design-vue'; import { Button, Popconfirm, Table } from 'ant-design-vue';
@ -46,8 +47,13 @@ const getTableColumns = computed((): TableColumnsType<PropertyInfo> => {
align: 'left', align: 'left',
dataIndex: 'key', dataIndex: 'key',
fixed: 'left', fixed: 'left',
minWidth: 100, width: 100,
title: $t('component.extra_property_dictionary.key'), title: $t('component.extra_property_dictionary.key'),
customRender: (opt) => {
if (props.renderKey && isFunction(props.renderKey)) {
return props.renderKey(opt.record.key);
}
},
}, },
{ {
align: 'left', align: 'left',

3
apps/vben5/packages/@abp/ui/src/components/properties/types.ts

@ -1,5 +1,7 @@
import type { Dictionary } from '@abp/core'; import type { Dictionary } from '@abp/core';
import type { VNode } from 'vue';
interface PropertyInfo { interface PropertyInfo {
key: string; key: string;
value: string; value: string;
@ -9,6 +11,7 @@ interface PropertyProps {
allowEdit?: boolean; allowEdit?: boolean;
data?: Dictionary<string, string>; data?: Dictionary<string, string>;
disabled?: boolean; disabled?: boolean;
renderKey?: (key: string) => string | VNode;
} }
export type { PropertyInfo, PropertyProps }; export type { PropertyInfo, PropertyProps };

2
apps/vben5/packages/@abp/webhooks/src/components/subscriptions/WebhookSubscriptionTable.vue

@ -273,7 +273,7 @@ function onUpdate(row: WebhookSubscriptionDto) {
function onDelete(row: WebhookSubscriptionDto) { function onDelete(row: WebhookSubscriptionDto) {
Modal.confirm({ Modal.confirm({
centered: true, centered: true,
content: $t('AbpUi.ItemWillBeDeletedMessageWithFormat', [row.title]), content: $t('AbpUi.ItemWillBeDeletedMessageWithFormat', [row.description]),
onCancel: () => { onCancel: () => {
cancel(); cancel();
}, },

9
apps/vben5/packages/@abp/webhooks/src/types/subscriptions.ts

@ -5,8 +5,7 @@ import type {
} from '@abp/core'; } from '@abp/core';
interface WebhookSubscriptionDto interface WebhookSubscriptionDto
extends CreationAuditedEntityDto<string>, extends CreationAuditedEntityDto<string>, IHasConcurrencyStamp {
IHasConcurrencyStamp {
description?: string; description?: string;
headers?: Record<string, string>; headers?: Record<string, string>;
isActive: boolean; isActive: boolean;
@ -31,15 +30,13 @@ interface WebhookSubscriptionCreateOrUpdateDto {
type WebhookSubscriptionCreateDto = WebhookSubscriptionCreateOrUpdateDto; type WebhookSubscriptionCreateDto = WebhookSubscriptionCreateOrUpdateDto;
interface WebhookSubscriptionUpdateDto interface WebhookSubscriptionUpdateDto
extends IHasConcurrencyStamp, extends IHasConcurrencyStamp, WebhookSubscriptionCreateOrUpdateDto {}
WebhookSubscriptionCreateOrUpdateDto {}
interface WebhookSubscriptionDeleteManyInput { interface WebhookSubscriptionDeleteManyInput {
recordIds: string[]; recordIds: string[];
} }
interface WebhookSubscriptionGetListInput interface WebhookSubscriptionGetListInput extends PagedAndSortedResultRequestDto {
extends PagedAndSortedResultRequestDto {
beginCreationTime?: Date; beginCreationTime?: Date;
endCreationTime?: Date; endCreationTime?: Date;
filter?: string; filter?: string;

2
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/AIServiceMigrationsDbContext.cs

@ -1,6 +1,7 @@
using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Chats;
using LINGYUN.Abp.AIManagement.EntityFrameworkCore; using LINGYUN.Abp.AIManagement.EntityFrameworkCore;
using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Tokens;
using LINGYUN.Abp.AIManagement.Tools;
using LINGYUN.Abp.AIManagement.Workspaces; using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data; using Volo.Abp.Data;
@ -14,6 +15,7 @@ public class AIServiceMigrationsDbContext :
IAIManagementDbContext IAIManagementDbContext
{ {
public DbSet<WorkspaceDefinitionRecord> WorkspaceDefinitions { get; set; } public DbSet<WorkspaceDefinitionRecord> WorkspaceDefinitions { get; set; }
public DbSet<AIToolDefinitionRecord> AIToolDefinitions { get; set; }
public DbSet<TextChatMessageRecord> TextChatMessageRecords { get; set; } public DbSet<TextChatMessageRecord> TextChatMessageRecords { get; set; }
public DbSet<ConversationRecord> ConversationRecords { get; set; } public DbSet<ConversationRecord> ConversationRecords { get; set; }
public DbSet<TokenUsageRecord> TokenUsageRecords { get; set; } public DbSet<TokenUsageRecord> TokenUsageRecords { get; set; }

376
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327051206_Add-AI-Tool-Definition-Record.Designer.cs

@ -0,0 +1,376 @@
// <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("20260327051206_Add-AI-Tool-Definition-Record")]
partial class AddAIToolDefinitionRecord
{
/// <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(128)
.HasColumnType("character varying(128)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("text")
.HasColumnName("ExtraProperties");
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<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.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpAIWorkspaceDefinitions", (string)null);
});
#pragma warning restore 612, 618
}
}
}

51
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327051206_Add-AI-Tool-Definition-Record.cs

@ -0,0 +1,51 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LINGYUN.Abp.MicroService.AIService.Migrations
{
/// <inheritdoc />
public partial class AddAIToolDefinitionRecord : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AbpAIAIToolDefinitionRecords",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Name = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
Provider = table.Column<string>(type: "character varying(20)", maxLength: 20, nullable: false),
Description = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: true),
IsEnabled = table.Column<bool>(type: "boolean", nullable: false),
IsSystem = table.Column<bool>(type: "boolean", nullable: false),
StateCheckers = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: true),
ExtraProperties = table.Column<string>(type: "text", nullable: false),
ConcurrencyStamp = table.Column<string>(type: "character varying(40)", maxLength: 40, nullable: false),
CreationTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
CreatorId = table.Column<Guid>(type: "uuid", nullable: true),
LastModificationTime = table.Column<DateTime>(type: "timestamp with time zone", nullable: true),
LastModifierId = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AbpAIAIToolDefinitionRecords", x => x.Id);
});
migrationBuilder.CreateIndex(
name: "IX_AbpAIAIToolDefinitionRecords_Name",
table: "AbpAIAIToolDefinitionRecords",
column: "Name",
unique: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AbpAIAIToolDefinitionRecords");
}
}
}

380
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327065840_Add-Tools-To-Workspace-Record.Designer.cs

@ -0,0 +1,380 @@
// <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("20260327065840_Add-Tools-To-Workspace-Record")]
partial class AddToolsToWorkspaceRecord
{
/// <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(128)
.HasColumnType("character varying(128)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("text")
.HasColumnName("ExtraProperties");
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<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
}
}
}

29
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327065840_Add-Tools-To-Workspace-Record.cs

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LINGYUN.Abp.MicroService.AIService.Migrations
{
/// <inheritdoc />
public partial class AddToolsToWorkspaceRecord : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Tools",
table: "AbpAIWorkspaceDefinitions",
type: "character varying(128)",
maxLength: 128,
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Tools",
table: "AbpAIWorkspaceDefinitions");
}
}
}

383
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327071051_Add-IsGlobal-To-AITool-Record.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("20260327071051_Add-IsGlobal-To-AITool-Record")]
partial class AddIsGlobalToAIToolRecord
{
/// <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(128)
.HasColumnType("character varying(128)");
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
}
}
}

29
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327071051_Add-IsGlobal-To-AITool-Record.cs

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LINGYUN.Abp.MicroService.AIService.Migrations
{
/// <inheritdoc />
public partial class AddIsGlobalToAIToolRecord : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsGlobal",
table: "AbpAIAIToolDefinitionRecords",
type: "boolean",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsGlobal",
table: "AbpAIAIToolDefinitionRecords");
}
}
}

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

@ -200,6 +200,74 @@ namespace LINGYUN.Abp.MicroService.AIService.Migrations
b.ToTable("AbpAITokenUsages", (string)null); 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(128)
.HasColumnType("character varying(128)");
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 => modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b =>
{ {
b.Property<Guid>("Id") b.Property<Guid>("Id")
@ -295,6 +363,10 @@ namespace LINGYUN.Abp.MicroService.AIService.Migrations
b.Property<float?>("Temperature") b.Property<float?>("Temperature")
.HasColumnType("real"); .HasColumnType("real");
b.Property<string>("Tools")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("Name") b.HasIndex("Name")

10
aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs

@ -126,6 +126,9 @@ public class WorkspaceDefinition : IHasSimpleStateCheckers<WorkspaceDefinition>
[NotNull] [NotNull]
public Dictionary<string, object> Properties { get; } public Dictionary<string, object> Properties { get; }
[NotNull]
public List<string> Tools { get; }
public List<ISimpleStateChecker<WorkspaceDefinition>> StateCheckers { get; } public List<ISimpleStateChecker<WorkspaceDefinition>> StateCheckers { get; }
public WorkspaceDefinition( public WorkspaceDefinition(
@ -155,6 +158,7 @@ public class WorkspaceDefinition : IHasSimpleStateCheckers<WorkspaceDefinition>
PresencePenalty = presencePenalty; PresencePenalty = presencePenalty;
IsEnabled = true; IsEnabled = true;
Tools = new List<string>();
Properties = new Dictionary<string, object>(); Properties = new Dictionary<string, object>();
StateCheckers = new List<ISimpleStateChecker<WorkspaceDefinition>>(); StateCheckers = new List<ISimpleStateChecker<WorkspaceDefinition>>();
} }
@ -171,6 +175,12 @@ public class WorkspaceDefinition : IHasSimpleStateCheckers<WorkspaceDefinition>
return this; return this;
} }
public virtual WorkspaceDefinition WithTools(params string[] tools)
{
Tools.AddIfNotContains(tools);
return this;
}
public virtual WorkspaceDefinition WithProperty(string key, object value) public virtual WorkspaceDefinition WithProperty(string key, object value)
{ {
Properties[key] = value; Properties[key] = value;

14
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolDefinitionExtenssions.cs

@ -5,15 +5,15 @@ using Volo.Abp;
namespace LINGYUN.Abp.AI.Tools.Http; namespace LINGYUN.Abp.AI.Tools.Http;
public static class HttpAIToolDefinitionExtenssions public static class HttpAIToolDefinitionExtenssions
{ {
private const string RemoteService = "RemoteService"; public const string RemoteService = "RemoteService";
private const string RemoteController = "RemoteController"; public const string RemoteController = "RemoteController";
private const string RemoteMethod = "RemoteMethod"; public const string RemoteMethod = "RemoteMethod";
private const string Endpoint = "HttpEndpoint"; public const string Endpoint = "HttpEndpoint";
private const string Method = "HttpMethod"; public const string Method = "HttpMethod";
private const string Headers = "HttpHeaders"; public const string Headers = "HttpHeaders";
private const string CurrentAccessToken = "UseHttpCurrentAccessToken"; public const string CurrentAccessToken = "UseHttpCurrentAccessToken";
public static AIToolDefinition UseHttpCurrentAccessToken(this AIToolDefinition definition, bool useCurrentAccessToken = true) public static AIToolDefinition UseHttpCurrentAccessToken(this AIToolDefinition definition, bool useCurrentAccessToken = true)
{ {

27
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/HttpAIToolProvider.cs

@ -1,9 +1,12 @@
using Microsoft.Extensions.AI; using LINGYUN.Abp.AI.Localization;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using System; using System;
using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.AI.Tools.Http; namespace LINGYUN.Abp.AI.Tools.Http;
public class HttpAIToolProvider : IAIToolProvider, ITransientDependency public class HttpAIToolProvider : IAIToolProvider, ITransientDependency
@ -16,6 +19,28 @@ public class HttpAIToolProvider : IAIToolProvider, ITransientDependency
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
} }
public virtual AIToolPropertyDescriptor[] GetPropertites()
{
return [
AIToolPropertyDescriptor.CreateStringProperty(
HttpAIToolDefinitionExtenssions.Endpoint,
LocalizableString.Create<AbpAIResource>("HttpAITool:Endpoint"),
required: true),
AIToolPropertyDescriptor.CreateSelectProperty(
HttpAIToolDefinitionExtenssions.Method,
LocalizableString.Create<AbpAIResource>("HttpAITool:Method"),
[
new(HttpMethod.Get.Method, HttpMethod.Get.Method),
new(HttpMethod.Post.Method, HttpMethod.Post.Method),
]),
AIToolPropertyDescriptor.CreateDictionaryProperty(
HttpAIToolDefinitionExtenssions.Headers,
LocalizableString.Create<AbpAIResource>("HttpAITool:Headers")),
AIToolPropertyDescriptor.CreateBoolProperty(
HttpAIToolDefinitionExtenssions.CurrentAccessToken,
LocalizableString.Create<AbpAIResource>("HttpAITool:CurrentAccessToken"))];
}
public virtual Task<AITool[]> CreateToolsAsync(AIToolDefinition definition) public virtual Task<AITool[]> CreateToolsAsync(AIToolDefinition definition)
{ {
string? description = null; string? description = null;

6
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/en.json

@ -1,6 +1,10 @@
{ {
"culture": "en", "culture": "en",
"texts": { "texts": {
"Tools:GetApplicationConfiguration": "Get Application Configuration" "Tools:GetApplicationConfiguration": "Get Application Configuration",
"HttpAITool:Endpoint": "Endpoint",
"HttpAITool:Method": "Method",
"HttpAITool:Headers": "Headers",
"HttpAITool:CurrentAccessToken": "Use the current user token"
} }
} }

6
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Http/LINGYUN/Abp/AI/Tools/Http/Localization/Resources/zh-Hans.json

@ -1,6 +1,10 @@
{ {
"culture": "zh-Hans", "culture": "zh-Hans",
"texts": { "texts": {
"Tools:GetApplicationConfiguration": "获取应用程序配置" "Tools:GetApplicationConfiguration": "获取应用程序配置",
"HttpAITool:Endpoint": "请求地址",
"HttpAITool:Method": "请求方法",
"HttpAITool:Headers": "请求头",
"HttpAITool:CurrentAccessToken": "使用当前用户Token"
} }
} }

5
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN.Abp.AI.Tools.Mcp.csproj

@ -14,6 +14,11 @@
<RootNamespace /> <RootNamespace />
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Remove="LINGYUN\Abp\AI\Tools\Mcp\Localization\Resources\*.json" />
<EmbeddedResource Include="LINGYUN\Abp\AI\Tools\Mcp\Localization\Resources\*.json" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Volo.Abp.Http.Client" /> <PackageReference Include="Volo.Abp.Http.Client" />
<PackageReference Include="ModelContextProtocol" /> <PackageReference Include="ModelContextProtocol" />

17
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/AbpAIToolsMcpModule.cs

@ -1,6 +1,9 @@
using Microsoft.Extensions.DependencyInjection; using LINGYUN.Abp.AI.Localization;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Http.Client; using Volo.Abp.Http.Client;
using Volo.Abp.Localization;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
namespace LINGYUN.Abp.AI.Tools.Mcp; namespace LINGYUN.Abp.AI.Tools.Mcp;
@ -13,10 +16,22 @@ public class AbpAIToolsMcpModule : AbpModule
{ {
context.Services.AddMcpAIToolClient(); context.Services.AddMcpAIToolClient();
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<AbpAIToolsMcpModule>();
});
Configure<AbpAIToolsOptions>(options => Configure<AbpAIToolsOptions>(options =>
{ {
// Mcp工具 // Mcp工具
options.AIToolProviders.Add<McpAIToolProvider>(); options.AIToolProviders.Add<McpAIToolProvider>();
}); });
Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<AbpAIResource>()
.AddVirtualJson("/LINGYUN/Abp/AI/Tools/Mcp/Localization/Resources");
});
} }
} }

14
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/Localization/Resources/en.json

@ -0,0 +1,14 @@
{
"culture": "en",
"texts": {
"McpAITool:Endpoint": "Endpoint",
"McpAITool:Headers": "Headers",
"McpAITool:TransportMode": "Transport Mode",
"McpAITool:TransportModeDesc": "The transport mode to use for the connection. The default is: Auto Detect.",
"McpAITool:ConnectionTimeout": "Connection Timeout",
"McpAITool:ConnectionTimeoutDesc": "The timeout used to establish the initial connection to the SSE server. The default is 30 seconds.",
"McpAITool:MaxReconnectionAttempts": "Max Reconnection Attempts",
"McpAITool:MaxReconnectionAttemptsDesc": "The maximum number of reconnection attempts. The default is 5.",
"McpAITool:CurrentAccessToken": "Use the current user token"
}
}

14
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/Localization/Resources/zh-Hans.json

@ -0,0 +1,14 @@
{
"culture": "zh-Hans",
"texts": {
"McpAITool:Endpoint": "请求地址",
"McpAITool:Headers": "请求头",
"McpAITool:TransportMode": "传输方式",
"McpAITool:TransportModeDesc": "连接所采用的传输方式, 默认: Auto Detect.",
"McpAITool:ConnectionTimeout": "超时时间",
"McpAITool:ConnectionTimeoutDesc": "用于建立与 SSE 服务器初始连接的超时时间, 默认: 30秒.",
"McpAITool:MaxReconnectionAttempts": "最大重连次数",
"McpAITool:MaxReconnectionAttemptsDesc": "最大重连次数, 默认: 5次.",
"McpAITool:CurrentAccessToken": "使用当前用户Token"
}
}

12
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools.Mcp/LINGYUN/Abp/AI/Tools/Mcp/McpAIToolDefinitionExtenssions.cs

@ -6,12 +6,12 @@ using Volo.Abp;
namespace LINGYUN.Abp.AI.Tools.Mcp; namespace LINGYUN.Abp.AI.Tools.Mcp;
public static class McpAIToolDefinitionExtenssions public static class McpAIToolDefinitionExtenssions
{ {
private const string Endpoint = "McpEndpoint"; public const string Endpoint = "McpEndpoint";
private const string Headers = "McpHeaders"; public const string Headers = "McpHeaders";
private const string TransportMode = "McpTransportMode"; public const string TransportMode = "McpTransportMode";
private const string ConnectionTimeout = "McpConnectionTimeout"; public const string ConnectionTimeout = "McpConnectionTimeout";
private const string MaxReconnectionAttempts = "McpMaxReconnectionAttempts"; public const string MaxReconnectionAttempts = "McpMaxReconnectionAttempts";
private const string CurrentAccessToken = "UseMcpCurrentAccessToken"; public const string CurrentAccessToken = "UseMcpCurrentAccessToken";
public static AIToolDefinition UseMcpCurrentAccessToken(this AIToolDefinition definition, bool useCurrentAccessToken = true) public static AIToolDefinition UseMcpCurrentAccessToken(this AIToolDefinition definition, bool useCurrentAccessToken = true)
{ {

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

@ -1,4 +1,5 @@
using Microsoft.Extensions.AI; using LINGYUN.Abp.AI.Localization;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Client; using ModelContextProtocol.Client;
using System; using System;
@ -8,6 +9,7 @@ using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.Authentication; using Volo.Abp.Http.Client.Authentication;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.AI.Tools.Mcp; namespace LINGYUN.Abp.AI.Tools.Mcp;
public class McpAIToolProvider : IAIToolProvider, ITransientDependency public class McpAIToolProvider : IAIToolProvider, ITransientDependency
@ -25,6 +27,38 @@ public class McpAIToolProvider : IAIToolProvider, ITransientDependency
HttpClientFactory = httpClientFactory; HttpClientFactory = httpClientFactory;
} }
public virtual AIToolPropertyDescriptor[] GetPropertites()
{
return [
AIToolPropertyDescriptor.CreateStringProperty(
McpAIToolDefinitionExtenssions.Endpoint,
LocalizableString.Create<AbpAIResource>("McpAITool:Endpoint"),
required: true),
AIToolPropertyDescriptor.CreateDictionaryProperty(
McpAIToolDefinitionExtenssions.Headers,
LocalizableString.Create<AbpAIResource>("McpAITool:Headers")),
AIToolPropertyDescriptor.CreateSelectProperty(
McpAIToolDefinitionExtenssions.TransportMode,
LocalizableString.Create<AbpAIResource>("McpAITool:TransportMode"),
[
new("Auto Detect", HttpTransportMode.AutoDetect),
new("Streamable Http", HttpTransportMode.StreamableHttp),
new("Sse", HttpTransportMode.Sse),
],
LocalizableString.Create<AbpAIResource>("McpAITool:TransportModeDesc")),
AIToolPropertyDescriptor.CreateNumberProperty(
McpAIToolDefinitionExtenssions.ConnectionTimeout,
LocalizableString.Create<AbpAIResource>("McpAITool:ConnectionTimeout"),
LocalizableString.Create<AbpAIResource>("McpAITool:ConnectionTimeoutDesc")),
AIToolPropertyDescriptor.CreateNumberProperty(
McpAIToolDefinitionExtenssions.MaxReconnectionAttempts,
LocalizableString.Create<AbpAIResource>("McpAITool:MaxReconnectionAttempts"),
LocalizableString.Create<AbpAIResource>("McpAITool:MaxReconnectionAttemptsDesc")),
AIToolPropertyDescriptor.CreateBoolProperty(
McpAIToolDefinitionExtenssions.CurrentAccessToken,
LocalizableString.Create<AbpAIResource>("McpAITool:CurrentAccessToken"))];
}
public async virtual Task<AITool[]> CreateToolsAsync(AIToolDefinition definition) public async virtual Task<AITool[]> CreateToolsAsync(AIToolDefinition definition)
{ {
var httpClient = HttpClientFactory.CreateMcpAIToolClient(); var httpClient = HttpClientFactory.CreateMcpAIToolClient();

9
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinition.cs

@ -26,6 +26,14 @@ public class AIToolDefinition : IHasSimpleStateCheckers<AIToolDefinition>
/// </summary> /// </summary>
[NotNull] [NotNull]
public Dictionary<string, object?> Properties { get; } public Dictionary<string, object?> Properties { get; }
/// <summary>
/// 启用/禁用工具
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// 全局工具
/// </summary>
public bool IsGlobal { get; set; }
public List<ISimpleStateChecker<AIToolDefinition>> StateCheckers { get; } public List<ISimpleStateChecker<AIToolDefinition>> StateCheckers { get; }
@ -38,6 +46,7 @@ public class AIToolDefinition : IHasSimpleStateCheckers<AIToolDefinition>
Provider = provider; Provider = provider;
Description = description; Description = description;
IsEnabled = true;
Properties = new Dictionary<string, object?>(); Properties = new Dictionary<string, object?>();
StateCheckers = new List<ISimpleStateChecker<AIToolDefinition>>(); StateCheckers = new List<ISimpleStateChecker<AIToolDefinition>>();
} }

10
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolFactory.cs

@ -22,8 +22,12 @@ public class AIToolFactory : IAIToolFactory, IScopedDependency
AIToolProviderManager = aIToolProviderManager; AIToolProviderManager = aIToolProviderManager;
} }
public virtual Task<AITool[]> CreateTool(AIToolDefinition definition) public async virtual Task<AITool[]> CreateTool(AIToolDefinition definition)
{ {
if (!definition.IsEnabled)
{
return [];
}
foreach (var provider in AIToolProviderManager.Providers) foreach (var provider in AIToolProviderManager.Providers)
{ {
if (!string.Equals(provider.Name, definition.Provider)) if (!string.Equals(provider.Name, definition.Provider))
@ -31,7 +35,7 @@ public class AIToolFactory : IAIToolFactory, IScopedDependency
continue; continue;
} }
return provider.CreateToolsAsync(definition); return await provider.CreateToolsAsync(definition);
} }
throw new AbpException($"The AITool provider implementation named {definition.Provider} was not found!"); throw new AbpException($"The AITool provider implementation named {definition.Provider} was not found!");
@ -44,7 +48,7 @@ public class AIToolFactory : IAIToolFactory, IScopedDependency
foreach (var toolDefine in toolDefines) foreach (var toolDefine in toolDefines)
{ {
if (await StateCheckerManager.IsEnabledAsync(toolDefine)) if (toolDefine.IsEnabled && await StateCheckerManager.IsEnabledAsync(toolDefine))
{ {
aiTools.AddRange(await CreateTool(toolDefine)); aiTools.AddRange(await CreateTool(toolDefine));
} }

120
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolPropertyDescriptor.cs

@ -0,0 +1,120 @@
using System.Collections.Generic;
using Volo.Abp;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.AI.Tools;
public enum PropertyValueType
{
String = 0,
Number = 1,
Boolean = 2,
Dictionary = 3,
Select = 4,
}
public class AIToolPropertyDescriptor
{
public string Name { get; }
public bool Required { get; }
public PropertyValueType ValueType { get; }
public List<NameValue<object>> Options { get; }
public ILocalizableString DisplayName { get; }
public ILocalizableString? Description { get; }
private AIToolPropertyDescriptor(
string name,
PropertyValueType valueType,
ILocalizableString displayName,
ILocalizableString? description = null,
bool required = false)
{
Name = name;
ValueType = valueType;
DisplayName = displayName;
Description = description;
Required = required;
Options = new List<NameValue<object>>();
}
public static AIToolPropertyDescriptor CreateStringProperty(
string name,
ILocalizableString displayName,
ILocalizableString? description = null,
bool required = false)
{
return new AIToolPropertyDescriptor(
name,
PropertyValueType.String,
displayName,
description,
required);
}
public static AIToolPropertyDescriptor CreateNumberProperty(
string name,
ILocalizableString displayName,
ILocalizableString? description = null,
bool required = false)
{
return new AIToolPropertyDescriptor(
name,
PropertyValueType.Number,
displayName,
description,
required);
}
public static AIToolPropertyDescriptor CreateBoolProperty(
string name,
ILocalizableString displayName,
ILocalizableString? description = null,
bool required = false)
{
return new AIToolPropertyDescriptor(
name,
PropertyValueType.Boolean,
displayName,
description,
required);
}
public static AIToolPropertyDescriptor CreateDictionaryProperty(
string name,
ILocalizableString displayName,
ILocalizableString? description = null)
{
return new AIToolPropertyDescriptor(
name,
PropertyValueType.Dictionary,
displayName,
description);
}
public static AIToolPropertyDescriptor CreateSelectProperty(
string name,
ILocalizableString displayName,
List<NameValue<object>> options,
ILocalizableString? description = null,
bool required = false)
{
var propertyDescriptor = new AIToolPropertyDescriptor(
name,
PropertyValueType.Select,
displayName,
description,
required);
foreach (var option in options)
{
propertyDescriptor.WithOption(option.Name, option.Value);
}
return propertyDescriptor;
}
public void WithOption(string name, object value)
{
Options.Add(new NameValue<object>(name, value));
}
}

32
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs

@ -3,6 +3,7 @@ using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem; using Volo.Abp.VirtualFileSystem;
@ -32,10 +33,35 @@ public class AbpAIToolsModule : AbpModule
Configure<AbpAICoreOptions>(options => Configure<AbpAICoreOptions>(options =>
{ {
options.ChatClientBuildActions.Add(async (_, sp, builder) => options.ChatClientBuildActions.Add(async (workspace, sp, builder) =>
{ {
var useAITools = new List<AITool>();
var useAIToolDefinitions = new List<AIToolDefinition>();
var aiToolFactory = sp.GetRequiredService<IAIToolFactory>(); var aiToolFactory = sp.GetRequiredService<IAIToolFactory>();
var aiTools = await aiToolFactory.CreateAllTools(); var aiToolDefinitionManager = sp.GetRequiredService<IAIToolDefinitionManager>();
var aiToolDefinitions = await aiToolDefinitionManager.GetAllAsync();
if (workspace.Tools.Count > 0)
{
useAIToolDefinitions.AddRange(aiToolDefinitions.Where(aiTool => workspace.Tools.Contains(aiTool.Name)));
}
foreach (var globalAIToolDefinition in aiToolDefinitions.Where(aiTool => aiTool.IsGlobal))
{
if (!useAIToolDefinitions.Any(tool => tool.Name == globalAIToolDefinition.Name))
{
useAIToolDefinitions.Add(globalAIToolDefinition);
}
}
foreach (var aiToolDefinition in useAIToolDefinitions)
{
var aiTools = await aiToolFactory.CreateTool(aiToolDefinition);
if (aiTools.Length > 0)
{
useAITools.AddRange(aiTools);
}
}
builder.ConfigureOptions(ai => builder.ConfigureOptions(ai =>
{ {
@ -44,7 +70,7 @@ public class AbpAIToolsModule : AbpModule
ai.Tools ??= []; ai.Tools ??= [];
foreach (var aiTool in aiTools) foreach (var aiTool in useAITools)
{ {
ai.Tools.Add(aiTool); ai.Tools.Add(aiTool);
} }

5
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs

@ -1,13 +1,16 @@
using Volo.Abp.Collections; using System.Collections.Generic;
using Volo.Abp.Collections;
namespace LINGYUN.Abp.AI.Tools; namespace LINGYUN.Abp.AI.Tools;
public class AbpAIToolsOptions public class AbpAIToolsOptions
{ {
public ITypeList<IAIToolDefinitionProvider> DefinitionProviders { get; } public ITypeList<IAIToolDefinitionProvider> DefinitionProviders { get; }
public ITypeList<IAIToolProvider> AIToolProviders { get; } public ITypeList<IAIToolProvider> AIToolProviders { get; }
public HashSet<string> DeletedAITools { get; }
public AbpAIToolsOptions() public AbpAIToolsOptions()
{ {
DefinitionProviders = new TypeList<IAIToolDefinitionProvider>(); DefinitionProviders = new TypeList<IAIToolDefinitionProvider>();
AIToolProviders = new TypeList<IAIToolProvider>(); AIToolProviders = new TypeList<IAIToolProvider>();
DeletedAITools = new HashSet<string>();
} }
} }

13
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolDefinitionExtenssions.cs

@ -5,17 +5,22 @@ using Volo.Abp;
namespace LINGYUN.Abp.AI.Tools; namespace LINGYUN.Abp.AI.Tools;
public static class FunctionAIToolDefinitionExtenssions public static class FunctionAIToolDefinitionExtenssions
{ {
private const string FunctionType = "Function"; public const string FunctionType = "FunctionType";
public static AIToolDefinition WithFunction<TFunc>(this AIToolDefinition definition) public const string FunctionName = "FunctionName";
public static AIToolDefinition WithFunction<TFunc>(this AIToolDefinition definition, string? funcName = null)
{ {
return definition.WithFunction(typeof(TFunc)); return definition.WithFunction(typeof(TFunc), funcName);
} }
public static AIToolDefinition WithFunction(this AIToolDefinition definition, Type funcType) public static AIToolDefinition WithFunction(this AIToolDefinition definition, Type funcType, string? funcName = null)
{ {
Check.NotNull(funcType, nameof(funcType)); Check.NotNull(funcType, nameof(funcType));
definition.WithProperty(FunctionType, funcType.AssemblyQualifiedName); definition.WithProperty(FunctionType, funcType.AssemblyQualifiedName);
if (!funcName.IsNullOrWhiteSpace())
{
definition.WithProperty(FunctionName, funcName);
}
return definition; return definition;
} }

18
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/FunctionAIToolProvider.cs

@ -1,10 +1,12 @@
using Microsoft.Extensions.AI; using LINGYUN.Abp.AI.Localization;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.AI.Tools; namespace LINGYUN.Abp.AI.Tools;
public class FunctionAIToolProvider : IAIToolProvider, ITransientDependency public class FunctionAIToolProvider : IAIToolProvider, ITransientDependency
@ -18,6 +20,20 @@ public class FunctionAIToolProvider : IAIToolProvider, ITransientDependency
ServiceProvider = serviceProvider; ServiceProvider = serviceProvider;
} }
public virtual AIToolPropertyDescriptor[] GetPropertites()
{
return [
AIToolPropertyDescriptor.CreateStringProperty(
FunctionAIToolDefinitionExtenssions.FunctionType,
LocalizableString.Create<AbpAIResource>("FunctionAITool:FunctionType"),
LocalizableString.Create<AbpAIResource>("FunctionAITool:FunctionTypeDesc"),
required: true),
AIToolPropertyDescriptor.CreateStringProperty(
FunctionAIToolDefinitionExtenssions.FunctionName,
LocalizableString.Create<AbpAIResource>("FunctionAITool:FunctionName"),
LocalizableString.Create<AbpAIResource>("FunctionAITool:FunctionNameDesc"))];
}
public virtual Task<AITool[]> CreateToolsAsync(AIToolDefinition definition) public virtual Task<AITool[]> CreateToolsAsync(AIToolDefinition definition)
{ {
var aiToolType = definition.GetFunction(); var aiToolType = definition.GetFunction();

2
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/IAIToolProvider.cs

@ -6,5 +6,7 @@ public interface IAIToolProvider
{ {
string Name { get; } string Name { get; }
AIToolPropertyDescriptor[] GetPropertites();
Task<AITool[]> CreateToolsAsync(AIToolDefinition definition); Task<AITool[]> CreateToolsAsync(AIToolDefinition definition);
} }

6
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/en.json

@ -5,6 +5,10 @@
"Tools:GetNowTime": "Get now time", "Tools:GetNowTime": "Get now time",
"Tools:GetUserInfo": "Get the current user information", "Tools:GetUserInfo": "Get the current user information",
"Tools:GetSetting": "Get setting value", "Tools:GetSetting": "Get setting value",
"Tools:GetSettings": "Get all settings" "Tools:GetSettings": "Get all settings",
"FunctionAITool:FunctionType": "Custom function type",
"FunctionAITool:FunctionTypeDesc": "To use the full name of the function, use Type.AssemblyQualifiedName for retrieval.",
"FunctionAITool:FunctionName": "Custom function name",
"FunctionAITool:FunctionNameDesc": "The name of the called function, if not specified, will default to Invoke (synchronous) or InvokeAsync (asynchronous) function."
} }
} }

6
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/Localization/Resources/zh-Hans.json

@ -5,6 +5,10 @@
"Tools:GetNowTime": "获取当前时间", "Tools:GetNowTime": "获取当前时间",
"Tools:GetUserInfo": "获取当前用户信息", "Tools:GetUserInfo": "获取当前用户信息",
"Tools:GetSetting": "获取设置值", "Tools:GetSetting": "获取设置值",
"Tools:GetSettings": "获取所有设置" "Tools:GetSettings": "获取所有设置",
"FunctionAITool:FunctionType": "自定义函数类型",
"FunctionAITool:FunctionTypeDesc": "使用函数全名, 应使用Type.AssemblyQualifiedName获取.",
"FunctionAITool:FunctionName": "自定义函数名称",
"FunctionAITool:FunctionNameDesc": "调用的函数名称, 如未指定, 默认调用 Invoke(同步) 或 InvokeAsync(异步)函数."
} }
} }

25
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionDefinitionProvider.cs

@ -11,23 +11,40 @@ public class AIManagementPermissionDefinitionProvider : PermissionDefinitionProv
{ {
var group = context.AddGroup(AIManagementPermissionNames.GroupName, L("Permission:AIManagement")); var group = context.AddGroup(AIManagementPermissionNames.GroupName, L("Permission:AIManagement"));
var groupDefinition = group.AddPermission( var workspaceDefinition = group.AddPermission(
AIManagementPermissionNames.WorkspaceDefinition.Default, AIManagementPermissionNames.WorkspaceDefinition.Default,
L("Permission:WorkspaceDefinition"), L("Permission:WorkspaceDefinition"),
MultiTenancySides.Host); MultiTenancySides.Host);
groupDefinition.AddChild( workspaceDefinition.AddChild(
AIManagementPermissionNames.WorkspaceDefinition.Create, AIManagementPermissionNames.WorkspaceDefinition.Create,
L("Permission:Create"), L("Permission:Create"),
MultiTenancySides.Host); MultiTenancySides.Host);
groupDefinition.AddChild( workspaceDefinition.AddChild(
AIManagementPermissionNames.WorkspaceDefinition.Update, AIManagementPermissionNames.WorkspaceDefinition.Update,
L("Permission:Update"), L("Permission:Update"),
MultiTenancySides.Host); MultiTenancySides.Host);
groupDefinition.AddChild( workspaceDefinition.AddChild(
AIManagementPermissionNames.WorkspaceDefinition.Delete, AIManagementPermissionNames.WorkspaceDefinition.Delete,
L("Permission:Delete"), L("Permission:Delete"),
MultiTenancySides.Host); MultiTenancySides.Host);
var aiToolDefinition = group.AddPermission(
AIManagementPermissionNames.AIToolDefinition.Default,
L("Permission:AIToolDefinition"),
MultiTenancySides.Host);
aiToolDefinition.AddChild(
AIManagementPermissionNames.AIToolDefinition.Create,
L("Permission:Create"),
MultiTenancySides.Host);
aiToolDefinition.AddChild(
AIManagementPermissionNames.AIToolDefinition.Update,
L("Permission:Update"),
MultiTenancySides.Host);
aiToolDefinition.AddChild(
AIManagementPermissionNames.AIToolDefinition.Delete,
L("Permission:Delete"),
MultiTenancySides.Host);
var conversation = group.AddPermission( var conversation = group.AddPermission(
AIManagementPermissionNames.Conversation.Default, AIManagementPermissionNames.Conversation.Default,
L("Permission:Conversation")); L("Permission:Conversation"));

9
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Permissions/AIManagementPermissionNames.cs

@ -15,6 +15,15 @@ public static class AIManagementPermissionNames
public const string Delete = Default + ".Delete"; public const string Delete = Default + ".Delete";
} }
public static class AIToolDefinition
{
public const string Default = GroupName + ".AIToolDefinitions";
public const string Create = Default + ".Create";
public const string Update = Default + ".Update";
public const string Delete = Default + ".Delete";
}
public static class Conversation public static class Conversation
{ {
public const string Default = GroupName + ".Conversations"; public const string Default = GroupName + ".Conversations";

10
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordCreateDto.cs

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.AIManagement.Tools.Dtos;
public class AIToolDefinitionRecordCreateDto : AIToolDefinitionRecordCreateOrUpdateDto
{
[Required]
[DynamicStringLength(typeof(AIToolDefinitionRecordConsts), nameof(AIToolDefinitionRecordConsts.MaxNameLength))]
public string Name { get; set; }
}

21
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordCreateOrUpdateDto.cs

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.ObjectExtending;
using Volo.Abp.Validation;
namespace LINGYUN.Abp.AIManagement.Tools.Dtos;
public abstract class AIToolDefinitionRecordCreateOrUpdateDto : ExtensibleObject
{
[Required]
[DynamicStringLength(typeof(AIToolDefinitionRecordConsts), nameof(AIToolDefinitionRecordConsts.MaxProviderLength))]
public string Provider { get; set; }
[DynamicStringLength(typeof(AIToolDefinitionRecordConsts), nameof(AIToolDefinitionRecordConsts.MaxDescriptionLength))]
public string? Description { get; set; }
public bool IsEnabled { get; set; }
public bool IsGlobal { get; set; }
[DynamicStringLength(typeof(AIToolDefinitionRecordConsts), nameof(AIToolDefinitionRecordConsts.MaxStateCheckersLength))]
public string? StateCheckers { get; set; }
}

25
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordDto.cs

@ -0,0 +1,25 @@
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Domain.Entities;
namespace LINGYUN.Abp.AIManagement.Tools.Dtos;
[Serializable]
public class AIToolDefinitionRecordDto : ExtensibleAuditedEntityDto<Guid>, IHasConcurrencyStamp
{
public string Name { get; set; }
public string Provider { get; set; }
public string? Description { get; set; }
public bool IsEnabled { get; set; }
public bool IsSystem { get; set; }
public bool IsGlobal { get; set; }
public string? StateCheckers { get; set; }
public string ConcurrencyStamp { get; set; }
}

14
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordGetPagedListInput.cs

@ -0,0 +1,14 @@
using System;
using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.AIManagement.Tools.Dtos;
[Serializable]
public class AIToolDefinitionRecordGetPagedListInput : PagedAndSortedResultRequestDto
{
public string? Filter { get; set; }
public bool? IsEnabled { get; set; }
public string? Provider { get; set; }
}

10
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordUpdateDto.cs

@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Domain.Entities;
namespace LINGYUN.Abp.AIManagement.Tools.Dtos;
public class AIToolDefinitionRecordUpdateDto : AIToolDefinitionRecordCreateOrUpdateDto, IHasConcurrencyStamp
{
[Required]
[StringLength(40)]
public string ConcurrencyStamp { get; set; }
}

13
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolPropertyDescriptorDto.cs

@ -0,0 +1,13 @@
using System.Collections.Generic;
using Volo.Abp;
namespace LINGYUN.Abp.AIManagement.Tools.Dtos;
public class AIToolPropertyDescriptorDto
{
public string Name { get; set; }
public bool Required { get; set; }
public string ValueType { get; set; }
public List<NameValue<object>> Options { get; set; }
public string DisplayName { get; set; }
public string? Description { get; set; }
}

11
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolProviderDto.cs

@ -0,0 +1,11 @@
namespace LINGYUN.Abp.AIManagement.Tools.Dtos;
public class AIToolProviderDto
{
public string Name { get; }
public AIToolPropertyDescriptorDto[] Properties { get; }
public AIToolProviderDto(string name, AIToolPropertyDescriptorDto[] properties)
{
Name = name;
Properties = properties;
}
}

17
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/IAIToolDefinitionAppService.cs

@ -0,0 +1,17 @@
using LINGYUN.Abp.AIManagement.Tools.Dtos;
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace LINGYUN.Abp.AIManagement.Tools;
public interface IAIToolDefinitionAppService :
ICrudAppService<
AIToolDefinitionRecordDto,
Guid,
AIToolDefinitionRecordGetPagedListInput,
AIToolDefinitionRecordCreateDto,
AIToolDefinitionRecordUpdateDto>
{
Task<ListResultDto<AIToolProviderDto>> GetAvailableProvidersAsync();
}

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordCreateOrUpdateDto.cs

@ -42,6 +42,8 @@ public abstract class WorkspaceDefinitionRecordCreateOrUpdateDto : ExtensibleObj
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
public string[]? Tools { get; set; }
[DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength))] [DynamicStringLength(typeof(WorkspaceDefinitionRecordConsts), nameof(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength))]
public string? StateCheckers { get; set; } public string? StateCheckers { get; set; }
} }

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordDto.cs

@ -35,6 +35,8 @@ public class WorkspaceDefinitionRecordDto : ExtensibleAuditedEntityDto<Guid>, IH
public bool IsSystem { get; set; } public bool IsSystem { get; set; }
public string[] Tools { get; set; }
public string? StateCheckers { get; set; } public string? StateCheckers { get; set; }
public string ConcurrencyStamp { get; set; } public string ConcurrencyStamp { get; set; }

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordGetListInput.cs → aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/Dtos/WorkspaceDefinitionRecordGetPagedListInput.cs

@ -4,7 +4,7 @@ using Volo.Abp.Application.Dtos;
namespace LINGYUN.Abp.AIManagement.Workspaces.Dtos; namespace LINGYUN.Abp.AIManagement.Workspaces.Dtos;
[Serializable] [Serializable]
public class WorkspaceDefinitionRecordGetListInput : PagedAndSortedResultRequestDto public class WorkspaceDefinitionRecordGetPagedListInput : PagedAndSortedResultRequestDto
{ {
public string? Filter { get; set; } public string? Filter { get; set; }

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Workspaces/IWorkspaceDefinitionAppService.cs

@ -9,7 +9,7 @@ public interface IWorkspaceDefinitionAppService :
ICrudAppService< ICrudAppService<
WorkspaceDefinitionRecordDto, WorkspaceDefinitionRecordDto,
Guid, Guid,
WorkspaceDefinitionRecordGetListInput, WorkspaceDefinitionRecordGetPagedListInput,
WorkspaceDefinitionRecordCreateDto, WorkspaceDefinitionRecordCreateDto,
WorkspaceDefinitionRecordUpdateDto> WorkspaceDefinitionRecordUpdateDto>
{ {

22
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/AbpAIManagementApplicationMappers.cs

@ -1,8 +1,11 @@
using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Chats;
using LINGYUN.Abp.AIManagement.Chats.Dtos; using LINGYUN.Abp.AIManagement.Chats.Dtos;
using LINGYUN.Abp.AIManagement.Tools;
using LINGYUN.Abp.AIManagement.Tools.Dtos;
using LINGYUN.Abp.AIManagement.Workspaces; using LINGYUN.Abp.AIManagement.Workspaces;
using LINGYUN.Abp.AIManagement.Workspaces.Dtos; using LINGYUN.Abp.AIManagement.Workspaces.Dtos;
using Riok.Mapperly.Abstractions; using Riok.Mapperly.Abstractions;
using System;
using Volo.Abp.Mapperly; using Volo.Abp.Mapperly;
using Volo.Abp.ObjectExtending; using Volo.Abp.ObjectExtending;
@ -12,8 +15,17 @@ namespace LINGYUN.Abp.AIManagement;
[MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)] [MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)]
public partial class WorkspaceDefinitionRecordToWorkspaceDefinitionRecordDtoMapper : MapperBase<WorkspaceDefinitionRecord, WorkspaceDefinitionRecordDto> public partial class WorkspaceDefinitionRecordToWorkspaceDefinitionRecordDtoMapper : MapperBase<WorkspaceDefinitionRecord, WorkspaceDefinitionRecordDto>
{ {
[MapPropertyFromSource(nameof(WorkspaceDefinitionRecordDto.Tools), Use = nameof(ConvertTools))]
public override partial WorkspaceDefinitionRecordDto Map(WorkspaceDefinitionRecord source); public override partial WorkspaceDefinitionRecordDto Map(WorkspaceDefinitionRecord source);
[MapPropertyFromSource(nameof(WorkspaceDefinitionRecordDto.Tools), Use = nameof(ConvertTools))]
public override partial void Map(WorkspaceDefinitionRecord source, WorkspaceDefinitionRecordDto destination); public override partial void Map(WorkspaceDefinitionRecord source, WorkspaceDefinitionRecordDto destination);
[UserMapping(Default = false)]
private static string[] ConvertTools(WorkspaceDefinitionRecord record)
{
return !record.Tools.IsNullOrWhiteSpace() ? record.Tools.Split(",") : [];
}
} }
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)] [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
@ -31,4 +43,12 @@ public partial class TextChatMessageRecordToTextChatMessageDtoMapper : MapperBas
{ {
return record.Role.Value; return record.Role.Value;
} }
} }
[Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Target)]
[MapExtraProperties(DefinitionChecks = MappingPropertyDefinitionChecks.None)]
public partial class AIToolDefinitionRecordToAIToolDefinitionRecordDtoMapper : MapperBase<AIToolDefinitionRecord, AIToolDefinitionRecordDto>
{
public override partial AIToolDefinitionRecordDto Map(AIToolDefinitionRecord source);
public override partial void Map(AIToolDefinitionRecord source, AIToolDefinitionRecordDto destination);
}

168
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionAppService.cs

@ -0,0 +1,168 @@
using LINGYUN.Abp.AI.Tools;
using LINGYUN.Abp.AIManagement.Localization;
using LINGYUN.Abp.AIManagement.Permissions;
using LINGYUN.Abp.AIManagement.Tools.Dtos;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
namespace LINGYUN.Abp.AIManagement.Tools;
public class AIToolDefinitionAppService :
CrudAppService<
AIToolDefinitionRecord,
AIToolDefinitionRecordDto,
Guid,
AIToolDefinitionRecordGetPagedListInput,
AIToolDefinitionRecordCreateDto,
AIToolDefinitionRecordUpdateDto>,
IAIToolDefinitionAppService
{
protected AbpAIToolsOptions AIToolsOptions { get; }
protected IAIToolDefinitionRecordRepository AIToolDefinitionRecordRepository { get; }
public AIToolDefinitionAppService(
IOptions<AbpAIToolsOptions> aiToolsOptions,
IAIToolDefinitionRecordRepository repository)
: base(repository)
{
AIToolsOptions = aiToolsOptions.Value;
AIToolDefinitionRecordRepository = repository;
LocalizationResource = typeof(AIManagementResource);
ObjectMapperContext = typeof(AbpAIManagementApplicationModule);
CreatePolicyName = AIManagementPermissionNames.AIToolDefinition.Create;
UpdatePolicyName = AIManagementPermissionNames.AIToolDefinition.Update;
DeletePolicyName = AIManagementPermissionNames.AIToolDefinition.Delete;
GetListPolicyName = AIManagementPermissionNames.AIToolDefinition.Default;
GetPolicyName = AIManagementPermissionNames.AIToolDefinition.Default;
}
public virtual Task<ListResultDto<AIToolProviderDto>> GetAvailableProvidersAsync()
{
var providers = AIToolsOptions.AIToolProviders
.Select(LazyServiceProvider.GetRequiredService)
.OfType<IAIToolProvider>()
.Select(provider =>
{
var properties = provider.GetPropertites();
return new AIToolProviderDto(
provider.Name,
properties.Select(prop =>
{
var property = new AIToolPropertyDescriptorDto
{
Name = prop.Name,
Options = prop.Options,
Required = prop.Required,
ValueType = prop.ValueType.ToString(),
DisplayName = prop.DisplayName.Localize(StringLocalizerFactory),
};
if (prop.Description != null)
{
property.Description = prop.Description.Localize(StringLocalizerFactory);
}
return property;
}).ToArray());
});
return Task.FromResult(new ListResultDto<AIToolProviderDto>(providers.ToImmutableArray()));
}
protected async override Task DeleteByIdAsync(Guid id)
{
var aiTool = await Repository.GetAsync(id);
if (aiTool.IsSystem)
{
throw new BusinessException(
AIManagementErrorCodes.AITool.SystemAIToolNotAllowedToBeDeleted,
$"System AITool {aiTool.Name} is not allowed to be deleted!")
.WithData("AITool", aiTool.Name);
}
await Repository.DeleteAsync(aiTool);
}
protected async override Task<IQueryable<AIToolDefinitionRecord>> CreateFilteredQueryAsync(AIToolDefinitionRecordGetPagedListInput input)
{
var queryable = await base.CreateFilteredQueryAsync(input);
return queryable
.WhereIf(input.IsEnabled.HasValue, x => x.IsEnabled == input.IsEnabled)
.WhereIf(!input.Provider.IsNullOrWhiteSpace(), x => x.Provider == input.Provider)
.WhereIf(!input.Filter.IsNullOrWhiteSpace(), x => x.Provider.Contains(input.Filter!) ||
x.Name.Contains(input.Filter!) || x.Description!.Contains(input.Filter!));
}
protected async override Task<AIToolDefinitionRecord> MapToEntityAsync(AIToolDefinitionRecordCreateDto createInput)
{
if (await AIToolDefinitionRecordRepository.FindByNameAsync(createInput.Name) != null)
{
throw new AIToolAlreadyExistsException(createInput.Name);
}
var entity = new AIToolDefinitionRecord(
GuidGenerator.Create(),
createInput.Name,
createInput.Provider,
createInput.Description,
createInput.StateCheckers)
{
IsEnabled = createInput.IsEnabled,
IsGlobal = createInput.IsGlobal,
};
if (!entity.HasSameExtraProperties(createInput))
{
entity.ExtraProperties.Clear();
foreach (var property in createInput.ExtraProperties)
{
entity.ExtraProperties.Add(property.Key, property.Value);
}
}
return entity;
}
protected override void MapToEntity(AIToolDefinitionRecordUpdateDto updateInput, AIToolDefinitionRecord entity)
{
if (entity.Description != updateInput.Description)
{
entity.Description = updateInput.Description;
}
if (entity.IsEnabled != updateInput.IsEnabled)
{
entity.IsEnabled = updateInput.IsEnabled;
}
if (entity.IsGlobal != updateInput.IsGlobal)
{
entity.IsGlobal = updateInput.IsGlobal;
}
if (!entity.HasSameExtraProperties(updateInput))
{
entity.ExtraProperties.Clear();
foreach (var property in updateInput.ExtraProperties)
{
entity.ExtraProperties.Add(property.Key, property.Value);
}
}
entity.SetConcurrencyStampIfNotNull(updateInput.ConcurrencyStamp);
}
}

35
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionAppService.cs

@ -5,6 +5,7 @@ using LINGYUN.Abp.AIManagement.Workspaces.Dtos;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -12,6 +13,7 @@ using Volo.Abp;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Security.Encryption; using Volo.Abp.Security.Encryption;
namespace LINGYUN.Abp.AIManagement.Workspaces; namespace LINGYUN.Abp.AIManagement.Workspaces;
@ -20,7 +22,7 @@ public class WorkspaceDefinitionAppService :
WorkspaceDefinitionRecord, WorkspaceDefinitionRecord,
WorkspaceDefinitionRecordDto, WorkspaceDefinitionRecordDto,
Guid, Guid,
WorkspaceDefinitionRecordGetListInput, WorkspaceDefinitionRecordGetPagedListInput,
WorkspaceDefinitionRecordCreateDto, WorkspaceDefinitionRecordCreateDto,
WorkspaceDefinitionRecordUpdateDto>, WorkspaceDefinitionRecordUpdateDto>,
IWorkspaceDefinitionAppService IWorkspaceDefinitionAppService
@ -79,7 +81,7 @@ public class WorkspaceDefinitionAppService :
await Repository.DeleteAsync(workspace); await Repository.DeleteAsync(workspace);
} }
protected async override Task<IQueryable<WorkspaceDefinitionRecord>> CreateFilteredQueryAsync(WorkspaceDefinitionRecordGetListInput input) protected async override Task<IQueryable<WorkspaceDefinitionRecord>> CreateFilteredQueryAsync(WorkspaceDefinitionRecordGetPagedListInput input)
{ {
var queryable = await base.CreateFilteredQueryAsync(input); var queryable = await base.CreateFilteredQueryAsync(input);
@ -100,7 +102,7 @@ public class WorkspaceDefinitionAppService :
throw new WorkspaceAlreadyExistsException(createInput.Name); throw new WorkspaceAlreadyExistsException(createInput.Name);
} }
var record = new WorkspaceDefinitionRecord( var entity = new WorkspaceDefinitionRecord(
GuidGenerator.Create(), GuidGenerator.Create(),
createInput.Name, createInput.Name,
createInput.Provider, createInput.Provider,
@ -113,15 +115,29 @@ public class WorkspaceDefinitionAppService :
createInput.MaxOutputTokens, createInput.MaxOutputTokens,
createInput.FrequencyPenalty, createInput.FrequencyPenalty,
createInput.PresencePenalty, createInput.PresencePenalty,
createInput.StateCheckers); createInput.Tools?.JoinAsString(","),
createInput.StateCheckers)
{
IsEnabled = createInput.IsEnabled,
};
if (!createInput.ApiKey.IsNullOrWhiteSpace()) if (!createInput.ApiKey.IsNullOrWhiteSpace())
{ {
var encryptApiKey = StringEncryptionService.Encrypt(createInput.ApiKey); var encryptApiKey = StringEncryptionService.Encrypt(createInput.ApiKey);
record.SetApiKey(encryptApiKey, createInput.ApiBaseUrl); entity.SetApiKey(encryptApiKey, createInput.ApiBaseUrl);
} }
return record; if (!entity.HasSameExtraProperties(createInput))
{
entity.ExtraProperties.Clear();
foreach (var property in createInput.ExtraProperties)
{
entity.ExtraProperties.Add(property.Key, property.Value);
}
}
return entity;
} }
protected override void MapToEntity(WorkspaceDefinitionRecordUpdateDto updateInput, WorkspaceDefinitionRecord entity) protected override void MapToEntity(WorkspaceDefinitionRecordUpdateDto updateInput, WorkspaceDefinitionRecord entity)
@ -181,6 +197,11 @@ public class WorkspaceDefinitionAppService :
entity.StateCheckers = updateInput.StateCheckers; entity.StateCheckers = updateInput.StateCheckers;
} }
if (updateInput.Tools != null)
{
entity.Tools = updateInput.Tools.JoinAsString(",");
}
if (!updateInput.ApiKey.IsNullOrWhiteSpace()) if (!updateInput.ApiKey.IsNullOrWhiteSpace())
{ {
var encryptApiKey = StringEncryptionService.Encrypt(updateInput.ApiKey); var encryptApiKey = StringEncryptionService.Encrypt(updateInput.ApiKey);
@ -196,5 +217,7 @@ public class WorkspaceDefinitionAppService :
entity.ExtraProperties.Add(property.Key, property.Value); entity.ExtraProperties.Add(property.Key, property.Value);
} }
} }
entity.SetConcurrencyStampIfNotNull(updateInput.ConcurrencyStamp);
} }
} }

14
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/AIManagementErrorCodes.cs

@ -16,4 +16,18 @@ public static class AIManagementErrorCodes
/// </summary> /// </summary>
public const string SystemWorkspaceNotAllowedToBeDeleted = Prefix + "002"; public const string SystemWorkspaceNotAllowedToBeDeleted = Prefix + "002";
} }
public static class AITool
{
public const string Prefix = Namespace + ":112";
/// <summary>
/// AITool {AITool} already exists!
/// </summary>
public const string NameAlreadyExists = Prefix + "001";
/// <summary>
/// System AITool {AITool} is not allowed to be deleted!
/// </summary>
public const string SystemAIToolNotAllowedToBeDeleted = Prefix + "002";
}
} }

13
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/en.json

@ -3,6 +3,7 @@
"texts": { "texts": {
"Permission:AIManagement": "Artificia Intelligence", "Permission:AIManagement": "Artificia Intelligence",
"Permission:WorkspaceDefinition": "Workspaces", "Permission:WorkspaceDefinition": "Workspaces",
"Permission:AIToolDefinition": "AI Tools",
"Permission:Conversation": "Conversations", "Permission:Conversation": "Conversations",
"Permission:Chats": "Chats", "Permission:Chats": "Chats",
"Permission:SendMessage": "Send Message", "Permission:SendMessage": "Send Message",
@ -11,6 +12,8 @@
"Permission:Delete": "Delete", "Permission:Delete": "Delete",
"AIManagement:111001": "Workspace {Workspace} already exists!", "AIManagement:111001": "Workspace {Workspace} already exists!",
"AIManagement:111002": "System workspace {Workspace} is not allowed to be deleted!", "AIManagement:111002": "System workspace {Workspace} is not allowed to be deleted!",
"AIManagement:112001": "AITool {AITool} already exists!",
"AIManagement:112002": "System AITool {AITool} is not allowed to be deleted!",
"DisplayName:MaxLatestHistoryMessagesToKeep": "Carry the recent conversation records", "DisplayName:MaxLatestHistoryMessagesToKeep": "Carry the recent conversation records",
"Description:MaxLatestHistoryMessagesToKeep": "When a user initiates a conversation with a large model, the upper limit of the user's recent conversation history that is carried along.", "Description:MaxLatestHistoryMessagesToKeep": "When a user initiates a conversation with a large model, the upper limit of the user's recent conversation history that is carried along.",
"Conversations:Delete": "Delete Conversation", "Conversations:Delete": "Delete Conversation",
@ -48,6 +51,14 @@
"DisplayName:PresencePenalty": "Presence Penalty", "DisplayName:PresencePenalty": "Presence Penalty",
"Description:PresencePenalty": "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.", "Description:PresencePenalty": "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.",
"DisplayName:IsEnabled": "Is Enabled", "DisplayName:IsEnabled": "Is Enabled",
"DisplayName:Workspace": "Workspace" "DisplayName:Workspace": "Workspace",
"DisplayName:Tools": "Tools",
"DisplayName:ToolProvider": "Tool Provider",
"DisplayName:IsGlobal": "Is Global",
"Description:IsGlobal": "The global tool is available in all workspaces and may result in a higher token value. Please exercise caution when choosing!",
"Tools": "Tools",
"Propertites": "Propertites",
"Tools:New": "New Tool",
"Tools:Edit": "Edit Tool"
} }
} }

13
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Localization/Resources/zh-Hans.json

@ -3,6 +3,7 @@
"texts": { "texts": {
"Permission:AIManagement": "人工智能", "Permission:AIManagement": "人工智能",
"Permission:WorkspaceDefinition": "工作区管理", "Permission:WorkspaceDefinition": "工作区管理",
"Permission:AIToolDefinition": "AI工具管理",
"Permission:Conversation": "对话管理", "Permission:Conversation": "对话管理",
"Permission:Chats": "聊天管理", "Permission:Chats": "聊天管理",
"Permission:SendMessage": "发送消息", "Permission:SendMessage": "发送消息",
@ -11,6 +12,8 @@
"Permission:Delete": "删除", "Permission:Delete": "删除",
"AIManagement:111001": "工作区 {Workspace} 已存在!", "AIManagement:111001": "工作区 {Workspace} 已存在!",
"AIManagement:111002": "系统工作区 {Workspace} 不允许删除!", "AIManagement:111002": "系统工作区 {Workspace} 不允许删除!",
"AIManagement:112001": "工具 {AITool} 已存在!",
"AIManagement:112002": "系统工具 {AITool} 不允许删除!",
"DisplayName:MaxLatestHistoryMessagesToKeep": "携带最近对话记录", "DisplayName:MaxLatestHistoryMessagesToKeep": "携带最近对话记录",
"Description:MaxLatestHistoryMessagesToKeep": "用户发起与大模型对话时,携带用户最近对话记录的上限.", "Description:MaxLatestHistoryMessagesToKeep": "用户发起与大模型对话时,携带用户最近对话记录的上限.",
"Conversations:Delete": "删除对话", "Conversations:Delete": "删除对话",
@ -48,6 +51,14 @@
"DisplayName:PresencePenalty": "存在惩罚", "DisplayName:PresencePenalty": "存在惩罚",
"Description:PresencePenalty": "介于 -2.0 和 2.0 之间的数字.如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性.", "Description:PresencePenalty": "介于 -2.0 和 2.0 之间的数字.如果该值为正,那么新 token 会根据其是否已在已有文本中出现受到相应的惩罚,从而增加模型谈论新主题的可能性.",
"DisplayName:IsEnabled": "启用", "DisplayName:IsEnabled": "启用",
"DisplayName:Workspace": "工作区" "DisplayName:Workspace": "工作区",
"DisplayName:Tools": "工具列表",
"DisplayName:ToolProvider": "工具提供者",
"DisplayName:IsGlobal": "全局工具",
"Description:IsGlobal": "全局工具在所有工作区均可用, 可能会造成Token偏高, 请谨慎选择!",
"Tools": "工具列表",
"Propertites": "属性",
"Tools:New": "新工具",
"Tools:Edit": "编辑工具"
} }
} }

16
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Tools/AIToolAlreadyExistsException.cs

@ -0,0 +1,16 @@
using Volo.Abp;
namespace LINGYUN.Abp.AIManagement.Tools;
public class AIToolAlreadyExistsException : BusinessException
{
public string AITool { get; }
public AIToolAlreadyExistsException(string aiTool)
: base(
AIManagementErrorCodes.AITool.NameAlreadyExists,
$"A AITool named {aiTool} already exists!")
{
AITool = aiTool;
WithData(nameof(AITool), aiTool);
}
}

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

@ -0,0 +1,8 @@
namespace LINGYUN.Abp.AIManagement.Tools;
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 MaxStateCheckersLength { get; set; } = 256;
}

1
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain.Shared/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecordConsts.cs

@ -10,5 +10,6 @@ public static class WorkspaceDefinitionRecordConsts
public static int MaxApiBaseUrlLength { get; set; } = 128; public static int MaxApiBaseUrlLength { get; set; } = 128;
public static int MaxSystemPromptLength { get; set; } = 512; public static int MaxSystemPromptLength { get; set; } = 512;
public static int MaxInstructionsLength { get; set; } = 512; public static int MaxInstructionsLength { get; set; } = 512;
public static int MaxToolsLength { get; set; } = 128;
public static int MaxStateCheckersLength { get; set; } = 256; public static int MaxStateCheckersLength { get; set; } = 256;
} }

1
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN.Abp.AIManagement.Domain.csproj

@ -24,6 +24,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.AI.Agent\LINGYUN.Abp.AI.Agent.csproj" /> <ProjectReference Include="..\LINGYUN.Abp.AI.Agent\LINGYUN.Abp.AI.Agent.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.AI.Tools\LINGYUN.Abp.AI.Tools.csproj" />
<ProjectReference Include="..\LINGYUN.Abp.AIManagement.Domain.Shared\LINGYUN.Abp.AIManagement.Domain.Shared.csproj" /> <ProjectReference Include="..\LINGYUN.Abp.AIManagement.Domain.Shared\LINGYUN.Abp.AIManagement.Domain.Shared.csproj" />
</ItemGroup> </ItemGroup>

6
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AIManagementOptions.cs

@ -1,10 +1,10 @@
using System; namespace LINGYUN.Abp.AIManagement;
namespace LINGYUN.Abp.AIManagement;
public class AIManagementOptions public class AIManagementOptions
{ {
public bool IsDynamicWorkspaceStoreEnabled { get; set; } public bool IsDynamicWorkspaceStoreEnabled { get; set; }
public bool SaveStaticWorkspacesToDatabase { get; set; } public bool SaveStaticWorkspacesToDatabase { get; set; }
public bool IsDynamicAIToolStoreEnabled { get; set; }
public bool SaveStaticAIToolsToDatabase { get; set; }
public AIManagementOptions() public AIManagementOptions()
{ {

12
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/AbpAIManagementDomainModule.cs

@ -1,6 +1,8 @@
using LINGYUN.Abp.AI.Agent; using LINGYUN.Abp.AI.Agent;
using LINGYUN.Abp.AI.Localization; using LINGYUN.Abp.AI.Localization;
using LINGYUN.Abp.AI.Tools;
using LINGYUN.Abp.AIManagement.Localization; using LINGYUN.Abp.AIManagement.Localization;
using LINGYUN.Abp.AIManagement.Tools;
using LINGYUN.Abp.AIManagement.Workspaces; using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Threading; using System.Threading;
@ -20,6 +22,7 @@ namespace LINGYUN.Abp.AIManagement;
[DependsOn( [DependsOn(
typeof(AbpAIManagementDomainSharedModule), typeof(AbpAIManagementDomainSharedModule),
typeof(AbpAIAgentModule), typeof(AbpAIAgentModule),
typeof(AbpAIToolsModule),
typeof(AbpCachingModule), typeof(AbpCachingModule),
typeof(AbpMapperlyModule), typeof(AbpMapperlyModule),
typeof(AbpDddDomainModule))] typeof(AbpDddDomainModule))]
@ -57,8 +60,13 @@ public class AbpAIManagementDomainModule : AbpModule
public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
{ {
var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>(); var rootServiceProvider = context.ServiceProvider.GetRequiredService<IRootServiceProvider>();
var initializer = rootServiceProvider.GetRequiredService<WorkspaceDynamicInitializer>(); var workspaceDynamicInitializer = rootServiceProvider.GetRequiredService<WorkspaceDynamicInitializer>();
await initializer.InitializeAsync(true, _cancellationTokenSource.Token); var aIToolDynamicInitializer = rootServiceProvider.GetRequiredService<AIToolDynamicInitializer>();
await Task.WhenAll([
workspaceDynamicInitializer.InitializeAsync(true, _cancellationTokenSource.Token),
aIToolDynamicInitializer.InitializeAsync(true, _cancellationTokenSource.Token)
]);
} }
public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context) public override Task OnApplicationShutdownAsync(ApplicationShutdownContext context)

134
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionRecord.cs

@ -0,0 +1,134 @@
using JetBrains.Annotations;
using System;
using Volo.Abp;
using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Auditing;
namespace LINGYUN.Abp.AIManagement.Tools;
public class AIToolDefinitionRecord : AuditedAggregateRoot<Guid>
{
public string Name { get; private set; }
public string Provider { get; private set; }
public string? Description { get; set; }
public bool IsEnabled { get; set; }
public bool IsSystem { get; set; }
public bool IsGlobal { get; set; }
public string? StateCheckers { get; set; }
protected AIToolDefinitionRecord()
{
}
public AIToolDefinitionRecord(
[NotNull] Guid id,
[NotNull] string name,
[NotNull] string provider,
[CanBeNull] string? description = null,
[CanBeNull] string? stateCheckers = null)
: base(id)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), AIToolDefinitionRecordConsts.MaxNameLength);
Provider = Check.NotNullOrWhiteSpace(provider, nameof(provider), AIToolDefinitionRecordConsts.MaxProviderLength);
Description = Check.Length(description, nameof(description), AIToolDefinitionRecordConsts.MaxDescriptionLength);
StateCheckers = Check.Length(stateCheckers, nameof(stateCheckers), AIToolDefinitionRecordConsts.MaxStateCheckersLength);
}
public bool HasSameData(AIToolDefinitionRecord otherAITool)
{
if (Name != otherAITool.Name)
{
return false;
}
if (Provider != otherAITool.Provider)
{
return false;
}
if (Description != otherAITool.Description)
{
return false;
}
if (IsEnabled != otherAITool.IsEnabled)
{
return false;
}
if (IsSystem != otherAITool.IsSystem)
{
return false;
}
if (IsGlobal != otherAITool.IsGlobal)
{
return false;
}
if (StateCheckers != otherAITool.StateCheckers)
{
return false;
}
if (!this.HasSameExtraProperties(otherAITool))
{
return false;
}
return true;
}
public void Patch(AIToolDefinitionRecord otherAITool)
{
if (Name != otherAITool.Name)
{
Name = otherAITool.Name;
}
if (Provider != otherAITool.Provider)
{
Provider = otherAITool.Provider;
}
if (Description != otherAITool.Description)
{
Description = otherAITool.Description;
}
if (IsEnabled != otherAITool.IsEnabled)
{
IsEnabled = otherAITool.IsEnabled;
}
if (IsSystem != otherAITool.IsSystem)
{
IsSystem = otherAITool.IsSystem;
}
if (IsGlobal != otherAITool.IsGlobal)
{
IsGlobal = otherAITool.IsGlobal;
}
if (StateCheckers != otherAITool.StateCheckers)
{
StateCheckers = otherAITool.StateCheckers;
}
if (!this.HasSameExtraProperties(otherAITool))
{
ExtraProperties.Clear();
foreach (var property in otherAITool.ExtraProperties)
{
ExtraProperties.Add(property.Key, property.Value);
}
}
}
}

68
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionSerializer.cs

@ -0,0 +1,68 @@
using LINGYUN.Abp.AI.Tools;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Localization;
using Volo.Abp.SimpleStateChecking;
namespace LINGYUN.Abp.AIManagement.Tools;
public class AIToolDefinitionSerializer : IAIToolDefinitionSerializer, ITransientDependency
{
protected IGuidGenerator GuidGenerator { get; }
protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
public AIToolDefinitionSerializer(
IGuidGenerator guidGenerator,
ISimpleStateCheckerSerializer stateCheckerSerializer,
ILocalizableStringSerializer localizableStringSerializer)
{
GuidGenerator = guidGenerator;
StateCheckerSerializer = stateCheckerSerializer;
LocalizableStringSerializer = localizableStringSerializer;
}
public async virtual Task<AIToolDefinitionRecord[]> SerializeAsync(IEnumerable<AIToolDefinition> definitions)
{
var records = new List<AIToolDefinitionRecord>();
foreach (var aiToolDef in definitions)
{
records.Add(await SerializeAsync(aiToolDef));
}
return records.ToArray();
}
public virtual Task<AIToolDefinitionRecord> SerializeAsync(AIToolDefinition definition)
{
using (CultureHelper.Use(CultureInfo.InvariantCulture))
{
var aiToolRecord = new AIToolDefinitionRecord(
GuidGenerator.Create(),
definition.Name,
definition.Provider,
definition.Description != null ? LocalizableStringSerializer.Serialize(definition.Description) : null,
SerializeStateCheckers(definition.StateCheckers))
{
IsGlobal = definition.IsGlobal,
IsEnabled = definition.IsEnabled,
IsSystem = true,
};
foreach (var property in definition.Properties)
{
aiToolRecord.SetProperty(property.Key, property.Value);
}
return Task.FromResult(aiToolRecord);
}
}
protected virtual string? SerializeStateCheckers(List<ISimpleStateChecker<AIToolDefinition>> stateCheckers)
{
return StateCheckerSerializer.Serialize(stateCheckers);
}
}

138
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDynamicInitializer.cs

@ -0,0 +1,138 @@
using LINGYUN.Abp.AI.Tools;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Polly;
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.AIManagement.Tools;
public class AIToolDynamicInitializer : ITransientDependency
{
public ILogger<AIToolDynamicInitializer> Logger { get; set; }
protected IServiceProvider ServiceProvider { get; }
public AIToolDynamicInitializer(IServiceProvider serviceProvider)
{
Logger = NullLogger<AIToolDynamicInitializer>.Instance;
ServiceProvider = serviceProvider;
}
public virtual Task InitializeAsync(bool runInBackground, CancellationToken cancellationToken = default)
{
var options = ServiceProvider.GetRequiredService<IOptions<AIManagementOptions>>().Value;
if (!options.SaveStaticAIToolsToDatabase && !options.IsDynamicAIToolStoreEnabled)
{
return Task.CompletedTask;
}
if (runInBackground)
{
var applicationLifetime = ServiceProvider.GetService<IHostApplicationLifetime>();
Task.Run(async () =>
{
if (cancellationToken == default && applicationLifetime?.ApplicationStopping != null)
{
cancellationToken = applicationLifetime.ApplicationStopping;
}
await ExecuteInitializationAsync(options, cancellationToken);
}, cancellationToken);
return Task.CompletedTask;
}
return ExecuteInitializationAsync(options, cancellationToken);
}
protected virtual async Task ExecuteInitializationAsync(AIManagementOptions options, CancellationToken cancellationToken)
{
try
{
var cancellationTokenProvider = ServiceProvider.GetRequiredService<ICancellationTokenProvider>();
using (cancellationTokenProvider.Use(cancellationToken))
{
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await SaveStaticAIToolsToDatabaseAsync(options, cancellationToken);
if (cancellationTokenProvider.Token.IsCancellationRequested)
{
return;
}
await PreCacheDynamicAIToolsAsync(options);
}
}
catch
{
// No need to log here since inner calls log
}
}
protected virtual async Task SaveStaticAIToolsToDatabaseAsync(
AIManagementOptions options,
CancellationToken cancellationToken)
{
if (!options.SaveStaticAIToolsToDatabase)
{
return;
}
var staticAIToolSaver = ServiceProvider.GetRequiredService<IStaticAIToolSaver>();
await Policy
.Handle<Exception>(ex => ex is not OperationCanceledException)
.WaitAndRetryAsync(
8,
retryAttempt => TimeSpan.FromSeconds(
Volo.Abp.RandomHelper.GetRandom(
(int)Math.Pow(2, retryAttempt) * 8,
(int)Math.Pow(2, retryAttempt) * 12)
)
)
.ExecuteAsync(async _ =>
{
try
{
await staticAIToolSaver.SaveAsync();
}
catch (Exception ex)
{
Logger.LogException(ex);
throw; // Polly will catch it
}
}, cancellationToken);
}
protected virtual async Task PreCacheDynamicAIToolsAsync(AIManagementOptions options)
{
if (!options.IsDynamicAIToolStoreEnabled)
{
return;
}
var dynamicAIToolDefinitionStore = ServiceProvider.GetRequiredService<IDynamicAIToolDefinitionStore>();
try
{
// Pre-cache AITools, so first request doesn't wait
await dynamicAIToolDefinitionStore.GetAllAsync();
}
catch (Exception ex)
{
Logger.LogException(ex);
throw; // It will be cached in Initialize()
}
}
}

160
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/DynamicAIToolDefinitionStore.cs

@ -0,0 +1,160 @@
using JetBrains.Annotations;
using LINGYUN.Abp.AI.Tools;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Threading;
namespace LINGYUN.Abp.AIManagement.Tools;
[Dependency(ReplaceServices = true)]
public class DynamicAIToolDefinitionStore : IDynamicAIToolDefinitionStore, ITransientDependency
{
protected IAIToolDefinitionRecordRepository AIToolDefinitionRecordRepository { get; }
protected IAIToolDefinitionSerializer AIToolDefinitionSerializer { get; }
protected IDynamicAIToolDefinitionStoreInMemoryCache StoreCache { get; }
protected IDistributedCache DistributedCache { get; }
protected IAbpDistributedLock DistributedLock { get; }
public AIManagementOptions AIManagementOptions { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
public DynamicAIToolDefinitionStore(
IAIToolDefinitionRecordRepository aiToolDefinitionRecordRepository,
IAIToolDefinitionSerializer aiToolDefinitionSerializer,
IDynamicAIToolDefinitionStoreInMemoryCache storeCache,
IDistributedCache distributedCache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IOptions<AIManagementOptions> aiManagementOptions,
IAbpDistributedLock distributedLock)
{
AIToolDefinitionRecordRepository = aiToolDefinitionRecordRepository;
AIToolDefinitionSerializer = aiToolDefinitionSerializer;
StoreCache = storeCache;
DistributedCache = distributedCache;
DistributedLock = distributedLock;
AIManagementOptions = aiManagementOptions.Value;
CacheOptions = cacheOptions.Value;
}
public async virtual Task<IReadOnlyList<AIToolDefinition>> GetAllAsync()
{
if (!AIManagementOptions.IsDynamicAIToolStoreEnabled)
{
return Array.Empty<AIToolDefinition>();
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetAITools();
}
}
public async virtual Task<AIToolDefinition> GetAsync([NotNull] string name)
{
Check.NotNull(name, nameof(name));
return await GetOrNullAsync(name) ?? throw new AbpException("Undefined AITool: " + name);
}
public async virtual Task<AIToolDefinition?> GetOrNullAsync([NotNull] string name)
{
Check.NotNull(name, nameof(name));
if (!AIManagementOptions.IsDynamicAIToolStoreEnabled)
{
return null;
}
using (await StoreCache.SyncSemaphore.LockAsync())
{
await EnsureCacheIsUptoDateAsync();
return StoreCache.GetAIToolOrNull(name);
}
}
protected virtual async Task EnsureCacheIsUptoDateAsync()
{
if (StoreCache.LastCheckTime.HasValue &&
DateTime.Now.Subtract(StoreCache.LastCheckTime.Value).TotalSeconds < 30)
{
return;
}
var stampInDistributedCache = await GetOrSetStampInDistributedCache();
if (stampInDistributedCache == StoreCache.CacheStamp)
{
StoreCache.LastCheckTime = DateTime.Now;
return;
}
await UpdateInMemoryStoreCache();
StoreCache.CacheStamp = stampInDistributedCache;
StoreCache.LastCheckTime = DateTime.Now;
}
protected virtual async Task UpdateInMemoryStoreCache()
{
var workspaces = await AIToolDefinitionRecordRepository.GetListAsync();
await StoreCache.FillAsync(workspaces);
}
protected virtual async Task<string> GetOrSetStampInDistributedCache()
{
var cacheKey = GetCommonStampCacheKey();
var stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
await using (var commonLockHandle = await DistributedLock
.TryAcquireAsync(GetCommonDistributedLockKey(), TimeSpan.FromMinutes(2)))
{
if (commonLockHandle == null)
{
throw new AbpException(
"Could not acquire distributed lock for AITool definition common stamp check!"
);
}
stampInDistributedCache = await DistributedCache.GetStringAsync(cacheKey);
if (stampInDistributedCache != null)
{
return stampInDistributedCache;
}
stampInDistributedCache = Guid.NewGuid().ToString();
await DistributedCache.SetStringAsync(
cacheKey,
stampInDistributedCache,
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(30)
}
);
}
return stampInDistributedCache;
}
protected virtual string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemoryAIToolCacheStamp";
}
protected virtual string GetCommonDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_Common_AbpAIToolUpdateLock";
}
}

57
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/DynamicAIToolDefinitionStoreCacheInvalidator.cs

@ -0,0 +1,57 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus;
using Volo.Abp.Threading;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.AIManagement.Tools;
public class DynamicAIToolDefinitionStoreCacheInvalidator :
ILocalEventHandler<EntityChangedEventData<AIToolDefinitionRecord>>,
ITransientDependency
{
private readonly IDynamicAIToolDefinitionStoreInMemoryCache _storeCache;
private readonly IClock _clock;
private readonly IDistributedCache _distributedCache;
private readonly AbpDistributedCacheOptions _cacheOptions;
public DynamicAIToolDefinitionStoreCacheInvalidator(
IClock clock,
IDistributedCache distributedCache,
IDynamicAIToolDefinitionStoreInMemoryCache storeCache,
IOptions<AbpDistributedCacheOptions> cacheOptions)
{
_storeCache = storeCache;
_clock = clock;
_distributedCache = distributedCache;
_cacheOptions = cacheOptions.Value;
}
public async virtual Task HandleEventAsync(EntityChangedEventData<AIToolDefinitionRecord> eventData)
{
await RemoveStampInDistributedCacheAsync();
}
protected async virtual Task RemoveStampInDistributedCacheAsync()
{
using (await _storeCache.SyncSemaphore.LockAsync())
{
var cacheKey = GetCommonStampCacheKey();
await _distributedCache.RemoveAsync(cacheKey);
_storeCache.CacheStamp = Guid.NewGuid().ToString();
_storeCache.LastCheckTime = _clock.Now.AddMinutes(-5);
}
}
protected virtual string GetCommonStampCacheKey()
{
return $"{_cacheOptions.KeyPrefix}_AbpInMemoryAIToolCacheStamp";
}
}

81
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/DynamicAIToolDefinitionStoreInMemoryCache.cs

@ -0,0 +1,81 @@
using LINGYUN.Abp.AI.Tools;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Localization;
using Volo.Abp.SimpleStateChecking;
namespace LINGYUN.Abp.AIManagement.Tools;
public class DynamicAIToolDefinitionStoreInMemoryCache : IDynamicAIToolDefinitionStoreInMemoryCache, ISingletonDependency
{
public string CacheStamp { get; set; }
protected IDictionary<string, AIToolDefinition> AIToolDefinitions { get; }
protected ISimpleStateCheckerSerializer StateCheckerSerializer { get; }
protected ILocalizableStringSerializer LocalizableStringSerializer { get; }
public SemaphoreSlim SyncSemaphore { get; } = new(1, 1);
public DateTime? LastCheckTime { get; set; }
public DynamicAIToolDefinitionStoreInMemoryCache(
ISimpleStateCheckerSerializer stateCheckerSerializer,
ILocalizableStringSerializer localizableStringSerializer)
{
StateCheckerSerializer = stateCheckerSerializer;
LocalizableStringSerializer = localizableStringSerializer;
AIToolDefinitions = new Dictionary<string, AIToolDefinition>();
}
public Task FillAsync(List<AIToolDefinitionRecord> tools)
{
AIToolDefinitions.Clear();
foreach (var tool in tools)
{
var toolDef = new AIToolDefinition(
tool.Name,
tool.Provider,
!tool.Description.IsNullOrWhiteSpace() ? LocalizableStringSerializer.Deserialize(tool.Description) : null)
{
IsEnabled = tool.IsEnabled,
IsGlobal = tool.IsGlobal,
};
if (!tool.StateCheckers.IsNullOrWhiteSpace())
{
var checkers = StateCheckerSerializer
.DeserializeArray(
tool.StateCheckers,
toolDef
);
toolDef.StateCheckers.AddRange(checkers);
}
foreach (var property in tool.ExtraProperties)
{
if (property.Value != null)
{
toolDef.WithProperty(property.Key, property.Value);
}
}
AIToolDefinitions[tool.Name] = toolDef;
}
return Task.CompletedTask;
}
public AIToolDefinition? GetAIToolOrNull(string name)
{
return AIToolDefinitions.GetOrDefault(name);
}
public IReadOnlyList<AIToolDefinition> GetAITools()
{
return AIToolDefinitions.Values.ToList();
}
}

12
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/IAIToolDefinitionRecordRepository.cs

@ -0,0 +1,12 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
namespace LINGYUN.Abp.AIManagement.Tools;
public interface IAIToolDefinitionRecordRepository : IRepository<AIToolDefinitionRecord, Guid>
{
Task<AIToolDefinitionRecord?> FindByNameAsync(
string name,
CancellationToken cancellationToken = default);
}

11
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/IAIToolDefinitionSerializer.cs

@ -0,0 +1,11 @@
using LINGYUN.Abp.AI.Tools;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.Abp.AIManagement.Tools;
public interface IAIToolDefinitionSerializer
{
Task<AIToolDefinitionRecord[]> SerializeAsync(IEnumerable<AIToolDefinition> definitions);
Task<AIToolDefinitionRecord> SerializeAsync(AIToolDefinition definition);
}

21
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/IDynamicAIToolDefinitionStoreInMemoryCache.cs

@ -0,0 +1,21 @@
using LINGYUN.Abp.AI.Tools;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace LINGYUN.Abp.AIManagement.Tools;
public interface IDynamicAIToolDefinitionStoreInMemoryCache
{
string CacheStamp { get; set; }
SemaphoreSlim SyncSemaphore { get; }
DateTime? LastCheckTime { get; set; }
Task FillAsync(List<AIToolDefinitionRecord> tools);
AIToolDefinition? GetAIToolOrNull(string name);
IReadOnlyList<AIToolDefinition> GetAITools();
}

7
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/IStaticAIToolSaver.cs

@ -0,0 +1,7 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.AIManagement.Tools;
public interface IStaticAIToolSaver
{
Task SaveAsync();
}

241
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/StaticAIToolSaver.cs

@ -0,0 +1,241 @@
using LINGYUN.Abp.AI.Tools;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DistributedLocking;
using Volo.Abp.Guids;
using Volo.Abp.Json.SystemTextJson.Modifiers;
using Volo.Abp.Threading;
using Volo.Abp.Uow;
namespace LINGYUN.Abp.AIManagement.Tools;
public class StaticAIToolSaver : IStaticAIToolSaver, ITransientDependency
{
protected IStaticAIToolDefinitionStore StaticStore { get; }
protected IAIToolDefinitionRecordRepository AIToolDefinitionRecordRepository { get; }
protected IAIToolDefinitionSerializer AIToolDefinitionSerializer { get; }
protected IDistributedCache Cache { get; }
protected IApplicationInfoAccessor ApplicationInfoAccessor { get; }
protected IAbpDistributedLock DistributedLock { get; }
protected AbpAIToolsOptions AIToolOptions { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected AbpDistributedCacheOptions CacheOptions { get; }
protected IUnitOfWorkManager UnitOfWorkManager { get; }
protected IGuidGenerator GuidGenerator { get; }
public StaticAIToolSaver(
IStaticAIToolDefinitionStore staticStore,
IAIToolDefinitionRecordRepository aiToolDefinitionRecordRepository,
IAIToolDefinitionSerializer aiToolDefinitionSerializer,
IDistributedCache cache,
IOptions<AbpDistributedCacheOptions> cacheOptions,
IApplicationInfoAccessor applicationInfoAccessor,
IAbpDistributedLock distributedLock,
IOptions<AbpAIToolsOptions> aiToolOptions,
ICancellationTokenProvider cancellationTokenProvider,
IUnitOfWorkManager unitOfWorkManager,
IGuidGenerator guidGenerator)
{
StaticStore = staticStore;
AIToolDefinitionRecordRepository = aiToolDefinitionRecordRepository;
AIToolDefinitionSerializer = aiToolDefinitionSerializer;
Cache = cache;
ApplicationInfoAccessor = applicationInfoAccessor;
DistributedLock = distributedLock;
CancellationTokenProvider = cancellationTokenProvider;
AIToolOptions = aiToolOptions.Value;
CacheOptions = cacheOptions.Value;
UnitOfWorkManager = unitOfWorkManager;
GuidGenerator = guidGenerator;
}
[UnitOfWork]
public async Task SaveAsync()
{
await using var applicationLockHandle = await DistributedLock.TryAcquireAsync(
GetApplicationDistributedLockKey()
);
if (applicationLockHandle == null)
{
return;
}
var cacheKey = GetApplicationHashCacheKey();
var cachedHash = await Cache.GetStringAsync(cacheKey, CancellationTokenProvider.Token);
var aiTools = await AIToolDefinitionSerializer.SerializeAsync(await StaticStore.GetAllAsync());
var currentHash = CalculateHash(aiTools, AIToolOptions.DeletedAITools);
if (cachedHash == currentHash)
{
return;
}
await using (var commonLockHandle = await DistributedLock.TryAcquireAsync(
GetCommonDistributedLockKey(),
TimeSpan.FromMinutes(5)))
{
if (commonLockHandle == null)
{
/* It will re-try */
throw new AbpException("Could not acquire distributed lock for saving static AITool!");
}
using (var unitOfWork = UnitOfWorkManager.Begin(requiresNew: true, isTransactional: true))
{
try
{
var hasChangesInAITools = await UpdateChangedAIToolsAsync(aiTools);
if (hasChangesInAITools)
{
await Cache.SetStringAsync(
GetCommonStampCacheKey(),
Guid.NewGuid().ToString(),
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(30)
},
CancellationTokenProvider.Token
);
}
}
catch
{
try
{
await unitOfWork.RollbackAsync();
}
catch
{
/* ignored */
}
throw;
}
await unitOfWork.CompleteAsync();
}
}
await Cache.SetStringAsync(
cacheKey,
currentHash,
new DistributedCacheEntryOptions
{
SlidingExpiration = TimeSpan.FromDays(30)
},
CancellationTokenProvider.Token
);
}
private async Task<bool> UpdateChangedAIToolsAsync(AIToolDefinitionRecord[] aiToolRecords)
{
var newRecords = new List<AIToolDefinitionRecord>();
var changedRecords = new List<AIToolDefinitionRecord>();
var aiToolRecordsInDatabase = (await AIToolDefinitionRecordRepository.GetListAsync()).ToDictionary(x => x.Name);
foreach (var record in aiToolRecords)
{
var aiToolRecordInDatabase = aiToolRecordsInDatabase.GetOrDefault(record.Name);
if (aiToolRecordInDatabase == null)
{
/* New group */
newRecords.Add(record);
continue;
}
if (record.HasSameData(aiToolRecordInDatabase))
{
/* Not changed */
continue;
}
/* Changed */
aiToolRecordInDatabase.Patch(record);
changedRecords.Add(aiToolRecordInDatabase);
}
/* Deleted */
var deletedRecords = new List<AIToolDefinitionRecord>();
if (AIToolOptions.DeletedAITools.Any())
{
deletedRecords.AddRange(aiToolRecordsInDatabase.Values.Where(x => AIToolOptions.DeletedAITools.Contains(x.Name)));
}
if (newRecords.Any())
{
await AIToolDefinitionRecordRepository.InsertManyAsync(newRecords);
}
if (changedRecords.Any())
{
await AIToolDefinitionRecordRepository.UpdateManyAsync(changedRecords);
}
if (deletedRecords.Any())
{
await AIToolDefinitionRecordRepository.DeleteManyAsync(deletedRecords);
}
return newRecords.Any() || changedRecords.Any() || deletedRecords.Any();
}
private string GetApplicationDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_AbpAIToolUpdateLock";
}
private string GetCommonDistributedLockKey()
{
return $"{CacheOptions.KeyPrefix}_Common_AbpAIToolUpdateLock";
}
private string GetApplicationHashCacheKey()
{
return $"{CacheOptions.KeyPrefix}_{ApplicationInfoAccessor.ApplicationName}_AbpAIToolsHash";
}
private string GetCommonStampCacheKey()
{
return $"{CacheOptions.KeyPrefix}_AbpInMemoryAIToolCacheStamp";
}
private string CalculateHash(AIToolDefinitionRecord[] aiToolRecords, IEnumerable<string> deletedAITool)
{
var jsonSerializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
new AbpIgnorePropertiesModifiers<AIToolDefinitionRecord, Guid>().CreateModifyAction(x => x.Id),
}
}
};
var stringBuilder = new StringBuilder();
stringBuilder.Append("AITools:");
stringBuilder.AppendLine(JsonSerializer.Serialize(aiToolRecords, jsonSerializerOptions));
stringBuilder.Append("DeletedAITool:");
stringBuilder.Append(deletedAITool.JoinAsString(","));
return stringBuilder
.ToString()
.ToMd5();
}
}

4
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/DynamicWorkspaceDefinitionStoreInMemoryCache.cs

@ -62,6 +62,10 @@ public class DynamicWorkspaceDefinitionStoreInMemoryCache : IDynamicWorkspaceDef
{ {
workspaceDef.WithApiBaseUrl(workspace.ApiBaseUrl); workspaceDef.WithApiBaseUrl(workspace.ApiBaseUrl);
} }
if (!workspace.Tools.IsNullOrWhiteSpace())
{
workspaceDef.WithTools(workspace.Tools.Split(","));
}
workspaceDef.IsEnabled = workspace.IsEnabled; workspaceDef.IsEnabled = workspace.IsEnabled;
if (!workspace.StateCheckers.IsNullOrWhiteSpace()) if (!workspace.StateCheckers.IsNullOrWhiteSpace())

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/IDynamicWorkspaceDefinitionStoreInMemoryCache.cs

@ -14,7 +14,7 @@ public interface IDynamicWorkspaceDefinitionStoreInMemoryCache
DateTime? LastCheckTime { get; set; } DateTime? LastCheckTime { get; set; }
Task FillAsync(List<WorkspaceDefinitionRecord> permissions); Task FillAsync(List<WorkspaceDefinitionRecord> workspaces);
WorkspaceDefinition? GetWorkspaceOrNull(string name); WorkspaceDefinition? GetWorkspaceOrNull(string name);

14
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionRecord.cs

@ -36,6 +36,8 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot<Guid>
public bool IsSystem { get; set; } public bool IsSystem { get; set; }
public string? Tools { get; set; }
public string? StateCheckers { get; set; } public string? StateCheckers { get; set; }
protected WorkspaceDefinitionRecord() protected WorkspaceDefinitionRecord()
@ -57,6 +59,7 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot<Guid>
int? maxOutputTokens = null, int? maxOutputTokens = null,
float? frequencyPenalty = null, float? frequencyPenalty = null,
float? presencePenalty = null, float? presencePenalty = null,
string? tools = null,
string? stateCheckers = null) string? stateCheckers = null)
: base(id) : base(id)
{ {
@ -67,6 +70,7 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot<Guid>
Description = Check.Length(description, nameof(description), WorkspaceDefinitionRecordConsts.MaxDescriptionLength); Description = Check.Length(description, nameof(description), WorkspaceDefinitionRecordConsts.MaxDescriptionLength);
SystemPrompt = Check.Length(systemPrompt, nameof(systemPrompt), WorkspaceDefinitionRecordConsts.MaxSystemPromptLength); SystemPrompt = Check.Length(systemPrompt, nameof(systemPrompt), WorkspaceDefinitionRecordConsts.MaxSystemPromptLength);
Instructions = Check.Length(instructions, nameof(instructions), WorkspaceDefinitionRecordConsts.MaxInstructionsLength); Instructions = Check.Length(instructions, nameof(instructions), WorkspaceDefinitionRecordConsts.MaxInstructionsLength);
Tools = Check.Length(tools, nameof(tools), WorkspaceDefinitionRecordConsts.MaxToolsLength);
StateCheckers = Check.Length(stateCheckers, nameof(stateCheckers), WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); StateCheckers = Check.Length(stateCheckers, nameof(stateCheckers), WorkspaceDefinitionRecordConsts.MaxStateCheckersLength);
Temperature = temperature; Temperature = temperature;
MaxOutputTokens = maxOutputTokens; MaxOutputTokens = maxOutputTokens;
@ -147,6 +151,11 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot<Guid>
return false; return false;
} }
if (IsSystem != otherWorkspace.IsSystem)
{
return false;
}
if (Temperature != otherWorkspace.Temperature) if (Temperature != otherWorkspace.Temperature)
{ {
return false; return false;
@ -232,6 +241,11 @@ public class WorkspaceDefinitionRecord : AuditedAggregateRoot<Guid>
IsEnabled = otherWorkspace.IsEnabled; IsEnabled = otherWorkspace.IsEnabled;
} }
if (IsSystem != otherWorkspace.IsSystem)
{
IsSystem = otherWorkspace.IsSystem;
}
if (Temperature != otherWorkspace.Temperature) if (Temperature != otherWorkspace.Temperature)
{ {
Temperature = otherWorkspace.Temperature; Temperature = otherWorkspace.Temperature;

1
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionSerializer.cs

@ -58,6 +58,7 @@ public class WorkspaceDefinitionSerializer : IWorkspaceDefinitionSerializer, ITr
definition.MaxOutputTokens, definition.MaxOutputTokens,
definition.FrequencyPenalty, definition.FrequencyPenalty,
definition.PresencePenalty, definition.PresencePenalty,
definition.Tools.JoinAsString(","),
SerializeStateCheckers(definition.StateCheckers)); SerializeStateCheckers(definition.StateCheckers));
if (!definition.ApiKey.IsNullOrWhiteSpace()) if (!definition.ApiKey.IsNullOrWhiteSpace())

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContext.cs

@ -1,5 +1,6 @@
using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Chats;
using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Tokens;
using LINGYUN.Abp.AIManagement.Tools;
using LINGYUN.Abp.AIManagement.Workspaces; using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data; using Volo.Abp.Data;
@ -11,6 +12,7 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore;
public class AIManagementDbContext : AbpDbContext<AIManagementDbContext>, IAIManagementDbContext public class AIManagementDbContext : AbpDbContext<AIManagementDbContext>, IAIManagementDbContext
{ {
public DbSet<WorkspaceDefinitionRecord> WorkspaceDefinitions { get; set; } public DbSet<WorkspaceDefinitionRecord> WorkspaceDefinitions { get; set; }
public DbSet<AIToolDefinitionRecord> AIToolDefinitions { get; set; }
public DbSet<TextChatMessageRecord> TextChatMessageRecords { get; set; } public DbSet<TextChatMessageRecord> TextChatMessageRecords { get; set; }
public DbSet<ConversationRecord> ConversationRecords { get; set; } public DbSet<ConversationRecord> ConversationRecords { get; set; }
public DbSet<TokenUsageRecord> TokenUsageRecords { get; set; } public DbSet<TokenUsageRecord> TokenUsageRecords { get; set; }

24
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AIManagementDbContextModelBuilderExtensions.cs

@ -2,6 +2,7 @@
using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Chats;
using LINGYUN.Abp.AIManagement.EntityFrameworkCore.ValueConversions; using LINGYUN.Abp.AIManagement.EntityFrameworkCore.ValueConversions;
using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Tokens;
using LINGYUN.Abp.AIManagement.Tools;
using LINGYUN.Abp.AIManagement.Workspaces; using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp; using Volo.Abp;
@ -90,6 +91,29 @@ public static class AIManagementDbContextModelBuilderExtensions
.HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength); .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxInstructionsLength);
b.Property(x => x.StateCheckers) b.Property(x => x.StateCheckers)
.HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength); .HasMaxLength(WorkspaceDefinitionRecordConsts.MaxStateCheckersLength);
b.Property(x => x.Tools)
.HasMaxLength(WorkspaceDefinitionRecordConsts.MaxToolsLength);
b.HasIndex(x => new { x.Name }).IsUnique();
b.ApplyObjectExtensionMappings();
});
builder.Entity<AIToolDefinitionRecord>(b =>
{
b.ToTable(AbpAIManagementDbProperties.DbTablePrefix + "AIToolDefinitionRecords", AbpAIManagementDbProperties.DbSchema);
b.ConfigureByConvention();
b.Property(x => x.Name)
.HasMaxLength(AIToolDefinitionRecordConsts.MaxNameLength)
.IsRequired();
b.Property(x => x.Provider)
.HasMaxLength(AIToolDefinitionRecordConsts.MaxProviderLength)
.IsRequired();
b.Property(x => x.Description)
.HasMaxLength(AIToolDefinitionRecordConsts.MaxDescriptionLength);
b.Property(x => x.StateCheckers)
.HasMaxLength(AIToolDefinitionRecordConsts.MaxStateCheckersLength);
b.HasIndex(x => new { x.Name }).IsUnique(); b.HasIndex(x => new { x.Name }).IsUnique();

3
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/AbpAIManagementEntityFrameworkCoreModule.cs

@ -1,5 +1,6 @@
using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Chats;
using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Tokens;
using LINGYUN.Abp.AIManagement.Tools;
using LINGYUN.Abp.AIManagement.Workspaces; using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore;
@ -23,6 +24,8 @@ public class AbpAIManagementEntityFrameworkCoreModule : AbpModule
options.AddRepository<TokenUsageRecord, EfCoreTokenUsageRecordRepository>(); options.AddRepository<TokenUsageRecord, EfCoreTokenUsageRecordRepository>();
options.AddRepository<WorkspaceDefinitionRecord, EfCoreWorkspaceDefinitionRecordRepository>(); options.AddRepository<WorkspaceDefinitionRecord, EfCoreWorkspaceDefinitionRecordRepository>();
options.AddRepository<AIToolDefinitionRecord, EfCoreAIToolDefinitionRecordRepository>();
}); });
} }
} }

27
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreAIToolDefinitionRecordRepository.cs

@ -0,0 +1,27 @@
using LINGYUN.Abp.AIManagement.Tools;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore;
public class EfCoreAIToolDefinitionRecordRepository :
EfCoreRepository<IAIManagementDbContext, AIToolDefinitionRecord, Guid>,
IAIToolDefinitionRecordRepository
{
public EfCoreAIToolDefinitionRecordRepository(
IDbContextProvider<IAIManagementDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async virtual Task<AIToolDefinitionRecord?> FindByNameAsync(string name, CancellationToken cancellationToken = default)
{
return await (await GetQueryableAsync())
.Where(x => x.Name == name)
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
}
}

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/EfCoreWorkspaceDefinitionRecordRepository.cs

@ -21,6 +21,6 @@ public class EfCoreWorkspaceDefinitionRecordRepository : EfCoreRepository<IAIMan
{ {
return await (await GetQueryableAsync()) return await (await GetQueryableAsync())
.Where(x => x.Name == name) .Where(x => x.Name == name)
.FirstOrDefaultAsync(GetCancellationToken(cancellationToken)); ; .FirstOrDefaultAsync(GetCancellationToken(cancellationToken));
} }
} }

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.EntityFrameworkCore/LINGYUN/Abp/AIManagement/EntityFrameworkCore/IAIManagementDbContext.cs

@ -1,5 +1,6 @@
using LINGYUN.Abp.AIManagement.Chats; using LINGYUN.Abp.AIManagement.Chats;
using LINGYUN.Abp.AIManagement.Tokens; using LINGYUN.Abp.AIManagement.Tokens;
using LINGYUN.Abp.AIManagement.Tools;
using LINGYUN.Abp.AIManagement.Workspaces; using LINGYUN.Abp.AIManagement.Workspaces;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data; using Volo.Abp.Data;
@ -11,6 +12,7 @@ namespace LINGYUN.Abp.AIManagement.EntityFrameworkCore;
public interface IAIManagementDbContext : IEfCoreDbContext public interface IAIManagementDbContext : IEfCoreDbContext
{ {
DbSet<WorkspaceDefinitionRecord> WorkspaceDefinitions { get; } DbSet<WorkspaceDefinitionRecord> WorkspaceDefinitions { get; }
DbSet<AIToolDefinitionRecord> AIToolDefinitions { get; }
DbSet<TextChatMessageRecord> TextChatMessageRecords { get; } DbSet<TextChatMessageRecord> TextChatMessageRecords { get; }
DbSet<ConversationRecord> ConversationRecords { get; } DbSet<ConversationRecord> ConversationRecords { get; }
DbSet<TokenUsageRecord> TokenUsageRecords { get; } DbSet<TokenUsageRecord> TokenUsageRecords { get; }

64
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionController.cs

@ -0,0 +1,64 @@
using LINGYUN.Abp.AIManagement.Permissions;
using LINGYUN.Abp.AIManagement.Tools.Dtos;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.AspNetCore.Mvc;
namespace LINGYUN.Abp.AIManagement.Tools;
[Controller]
[RemoteService(Name = AIManagementRemoteServiceConsts.RemoteServiceName)]
[Area(AIManagementRemoteServiceConsts.ModuleName)]
[Route($"api/{AIManagementRemoteServiceConsts.ModuleName}/tools")]
[Authorize(AIManagementPermissionNames.AIToolDefinition.Default)]
public class AIToolDefinitionController : AbpControllerBase, IAIToolDefinitionAppService
{
private readonly IAIToolDefinitionAppService _service;
public AIToolDefinitionController(IAIToolDefinitionAppService service)
{
_service = service;
}
[HttpPost]
[Authorize(AIManagementPermissionNames.AIToolDefinition.Create)]
public virtual Task<AIToolDefinitionRecordDto> CreateAsync(AIToolDefinitionRecordCreateDto input)
{
return _service.CreateAsync(input);
}
[HttpDelete("{id}")]
[Authorize(AIManagementPermissionNames.AIToolDefinition.Delete)]
public virtual Task DeleteAsync(Guid id)
{
return _service.DeleteAsync(id);
}
[HttpGet("{id}")]
public virtual Task<AIToolDefinitionRecordDto> GetAsync(Guid id)
{
return _service.GetAsync(id);
}
[HttpGet("available-providers")]
public virtual Task<ListResultDto<AIToolProviderDto>> GetAvailableProvidersAsync()
{
return _service.GetAvailableProvidersAsync();
}
[HttpGet]
public virtual Task<PagedResultDto<AIToolDefinitionRecordDto>> GetListAsync(AIToolDefinitionRecordGetPagedListInput input)
{
return _service.GetListAsync(input);
}
[HttpPut("{id}")]
[Authorize(AIManagementPermissionNames.AIToolDefinition.Update)]
public virtual Task<AIToolDefinitionRecordDto> UpdateAsync(Guid id, AIToolDefinitionRecordUpdateDto input)
{
return _service.UpdateAsync(id, input);
}
}

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.HttpApi/LINGYUN/Abp/AIManagement/Workspaces/WorkspaceDefinitionController.cs

@ -50,7 +50,7 @@ public class WorkspaceDefinitionController : AbpControllerBase, IWorkspaceDefini
} }
[HttpGet] [HttpGet]
public virtual Task<PagedResultDto<WorkspaceDefinitionRecordDto>> GetListAsync(WorkspaceDefinitionRecordGetListInput input) public virtual Task<PagedResultDto<WorkspaceDefinitionRecordDto>> GetListAsync(WorkspaceDefinitionRecordGetPagedListInput input)
{ {
return _service.GetListAsync(input); return _service.GetListAsync(input);
} }

Loading…
Cancel
Save