fix: 完成系统向智能体数据链路切换

- 切换工作台、聊天历史、资源候选与公共调用到 Agent

- 加固资源绑定、删除保护及发布运行并发控制

- 隔离旧 Bot 专属服务和组件并保留兼容入口
This commit is contained in:
2026-07-31 14:24:15 +08:00
parent f0aba1eddd
commit f872eac1f9
114 changed files with 5997 additions and 874 deletions

View File

@@ -78,7 +78,7 @@ export interface AgentChatCapabilityPayload {
}
export function getPublishedAgents() {
return api.get<RequestResult<AgentInfo[]>>('/api/v1/agent/list', {
return api.get<RequestResult<AgentInfo[]>>('/api/v1/agent/options', {
params: { publishedOnly: true },
});
}
@@ -95,10 +95,7 @@ export function getAgentSession(sessionId: number | string) {
export function getPublishedKnowledges() {
return api.get<RequestResult<AgentChatKnowledgeView[]>>(
'/api/v1/documentCollection/list',
{
params: { publishedOnly: true },
},
'/api/v1/agent/knowledgeOptions',
);
}

View File

@@ -23,10 +23,8 @@ import {
import {
getAgentDetail,
getAgentModels,
getMcpPage,
getMcpTools,
getPublishedKnowledgeList,
getAgentMcpToolOptions,
getAgentResourceOptions,
saveAgent,
submitAgentOfflineApproval,
submitAgentPublishApproval,
@@ -76,7 +74,7 @@ const workflows = ref<AgentOption[]>([]);
const pluginTools = ref<AgentOption[]>([]);
const mcps = ref<AgentOption[]>([]);
const fetchMcpToolResource = createMcpToolLoader(async (id) => {
const res = await getMcpTools(id);
const res = await getAgentMcpToolOptions(id);
return res.errorCode === 0 ? res.data : undefined;
});
@@ -128,7 +126,6 @@ const offlineDisabled = computed(() => {
});
onMounted(async () => {
void loadDeferredOptions();
try {
await Promise.all([loadCriticalOptions(), loadAgent()]);
} finally {
@@ -213,11 +210,11 @@ function syncNavTitle(title: string, options: { force?: boolean } = {}) {
}
async function loadCriticalOptions() {
const [categoryResult, modelResult] = await Promise.allSettled([
const [categoryResult, resourceResult] = await Promise.allSettled([
api.get('/api/v1/agentCategory/visibleList', {
params: { sortKey: 'sortNo', sortType: 'asc' },
}),
getAgentModels(),
getAgentResourceOptions(),
]);
if (categoryResult.status === 'fulfilled') {
@@ -227,71 +224,33 @@ async function loadCriticalOptions() {
raw: item,
}));
}
if (modelResult.status === 'fulfilled') {
models.value = (modelResult.value.data || []).map((item: any) => ({
if (resourceResult.status === 'fulfilled') {
const resources = resourceResult.value.data;
if (resourceResult.value.errorCode !== 0 || !resources) {
return;
}
models.value = (resources.models || []).map((item: any) => ({
label: item.title || item.name,
value: String(item.id),
raw: item,
}));
}
}
async function loadDeferredOptions() {
const [knowledgeResult, workflowResult, pluginResult, mcpResult] =
await Promise.allSettled([
getPublishedKnowledgeList(),
api.get('/api/v1/workflow/page', {
params: { pageNumber: 1, pageSize: 200 },
}),
api.get('/api/v1/plugin/pageByCategory', {
params: { pageNumber: 1, pageSize: 200, category: 0 },
}),
getMcpPage(),
]);
if (knowledgeResult.status === 'fulfilled') {
knowledges.value = (knowledgeResult.value.data || []).map((item: any) => ({
knowledges.value = (resources.knowledges || []).map((item: any) => ({
label: item.title || item.name,
value: String(item.id),
raw: item,
}));
}
if (workflowResult.status === 'fulfilled') {
workflows.value = (
(workflowResult.value.data?.records ||
workflowResult.value.data ||
[]) as any[]
).map((item) => ({
workflows.value = (resources.workflows || []).map((item: any) => ({
label: item.title || item.name,
value: String(item.id),
raw: item,
}));
pluginTools.value = (resources.pluginTools || []).map((item: any) => ({
label: item.name || item.title,
value: String(item.id),
raw: item,
}));
mcps.value = mapMcpOptions(resources.mcps || []);
}
if (pluginResult.status === 'fulfilled') {
pluginTools.value = flattenPluginTools(
pluginResult.value.data?.records || pluginResult.value.data || [],
);
}
if (mcpResult.status === 'fulfilled') {
mcps.value = mapMcpOptions(
mcpResult.value.data?.records || mcpResult.value.data || [],
);
}
}
function flattenPluginTools(list: any[]): AgentOption[] {
const result: AgentOption[] = [];
list.forEach((plugin) => {
const tools = Array.isArray(plugin.tools) ? plugin.tools : [];
tools.forEach((tool: any) => {
result.push({
label: tool.name || tool.title,
value: String(tool.id),
raw: { ...tool, pluginName: plugin.name || plugin.title },
});
});
});
return result;
}
function mapMcpOptions(list: any[]): AgentOption[] {
@@ -325,8 +284,7 @@ async function loadMcpToolsForOption(id: number | string) {
const currentOption = mcps.value.find((item) => String(item.value) === key);
const mergedResource = {
...currentOption?.raw,
...resource,
tools: Array.isArray(resource.tools) ? resource.tools : [],
tools: Array.isArray(resource) ? resource : [],
};
if (currentOption) {
mcps.value = mcps.value.map((item) =>
@@ -334,7 +292,7 @@ async function loadMcpToolsForOption(id: number | string) {
? {
...item,
label:
resource.title || resource.name || currentOption.label || 'MCP',
currentOption.label || 'MCP',
raw: mergedResource,
}
: item,

View File

@@ -12,7 +12,7 @@ import {Delete, Edit, Plus, Promotion} from '@element-plus/icons-vue';
import {ElMessage, ElMessageBox, ElTag} from 'element-plus';
import {tryit} from 'radash';
import defaultAvatar from '#/assets/ai/bot/defaultBotAvatar.png';
import defaultAgentAvatar from '#/assets/defaultUserAvatar.png';
import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
import PageData from '#/components/page/PageData.vue';
import PageSide from '#/components/page/PageSide.vue';
@@ -262,7 +262,7 @@ async function handleDeleteAction(row: AgentInfo) {
<CardList
title-field="name"
icon-field="avatar"
:default-icon="defaultAvatar"
:default-icon="defaultAgentAvatar"
:data="pageList"
:primary-action="primaryAction"
:actions="actions"

View File

@@ -1,6 +1,10 @@
import type {AgentInfo, AgentKnowledgeBinding, AgentToolBinding,} from './types';
import type {
AgentInfo,
AgentKnowledgeBinding,
AgentToolBinding,
} from './types';
import {api} from '#/api/request';
import { api } from '#/api/request';
export interface RequestResult<T = any> {
data: T;
@@ -22,10 +26,6 @@ export function updateAgent(agent: AgentInfo) {
return api.post<RequestResult<AgentInfo>>('/api/v1/agent/update', agent);
}
export function removeAgent(id: number | string) {
return api.post<RequestResult>('/api/v1/agent/remove', { id });
}
export function updateAgentToolBindings(
agentId: number | string,
bindings: AgentToolBinding[],
@@ -98,24 +98,22 @@ export function getAgentCategories() {
});
}
export function getAgentModels() {
return api.get<RequestResult<any[]>>('/api/v1/model/list', {
params: { modelType: 'chatModel', added: true },
});
export interface AgentResourceOptions {
knowledges: any[];
mcps: any[];
models: any[];
pluginTools: any[];
workflows: any[];
}
export function getPublishedKnowledgeList() {
return api.get<RequestResult<any[]>>('/api/v1/documentCollection/list', {
params: { publishedOnly: true },
});
export function getAgentResourceOptions() {
return api.get<RequestResult<AgentResourceOptions>>(
'/api/v1/agent/resourceOptions',
);
}
export function getMcpPage() {
return api.get<RequestResult<any>>('/api/v1/mcp/page', {
params: { pageNumber: 1, pageSize: 200, status: 1 },
export function getAgentMcpToolOptions(id: number | string) {
return api.get<RequestResult<any[]>>('/api/v1/agent/mcpToolOptions', {
params: { id },
});
}
export function getMcpTools(id: number | string) {
return api.post<RequestResult<any>>('/api/v1/mcp/getMcpTools', { id });
}

View File

@@ -6,7 +6,7 @@ import pageSource from './index.vue?raw';
describe('管理端智能体聊天历史契约', () => {
it('使用智能体候选接口并保留 assistantId 查询参数', () => {
expect(pageSource).toContain("'/api/v1/agent/list'");
expect(pageSource).toContain("'/api/v1/agent/options'");
expect(pageSource).not.toContain('/api/v1/bot/list');
expect(pageSource).toContain('label: item.name');
expect(pageSource).toContain('assistantId: query.value.assistantId');

View File

@@ -98,7 +98,7 @@ onMounted(async () => {
async function fetchAgents() {
agentLoading.value = true;
const [error, res] = await tryit(api.get)('/api/v1/agent/list');
const [error, res] = await tryit(api.get)('/api/v1/agent/options');
agentLoading.value = false;
if (error || res?.errorCode !== 0) {
agentOptions.value = [];

View File

@@ -360,11 +360,11 @@ const submitOfflineAction = async (item: any) => {
}
try {
await ElMessageBox.confirm(
impactRes.data?.hasBotBindings
impactRes.data?.hasAgentBindings
? buildOfflineImpactMessage(
$t('documentCollection.offlineImpactBoundBotsIntro'),
impactRes.data.botBindings,
$t('documentCollection.offlineImpactBoundBotsFooter'),
$t('documentCollection.offlineImpactBoundAgentsIntro'),
impactRes.data.agentBindings,
$t('documentCollection.offlineImpactBoundAgentsFooter'),
)
: $t('documentCollection.submitOfflineApprovalConfirm'),
$t('message.noticeTitle'),

View File

@@ -7,10 +7,10 @@ export interface OfflineImpactBinding {
export interface OfflineImpactCheck {
canProceed: boolean;
hasBotBindings: boolean;
hasAgentBindings: boolean;
hasPluginBindings: boolean;
hasWorkflowUsages: boolean;
botBindings: OfflineImpactBinding[];
agentBindings: OfflineImpactBinding[];
pluginBindings: OfflineImpactBinding[];
workflowUsages: OfflineImpactBinding[];
message?: string;
@@ -33,7 +33,13 @@ export function buildOfflineImpactMessage(
h('p', intro),
h(
'ul',
items.map((item) => h('li', { key: String(item.id || item.title || '') }, resolveTitle(item))),
items.map((item) =>
h(
'li',
{ key: String(item.id || item.title || '') },
resolveTitle(item),
),
),
),
footer ? h('p', footer) : null,
]);

View File

@@ -797,15 +797,15 @@ async function submitOfflineAction(row: any) {
}
try {
const sections = [];
let offlineImpactFooter = $t('aiWorkflow.offlineImpactBoundBotsFooter');
if (impactRes.data?.hasBotBindings) {
let offlineImpactFooter = $t('aiWorkflow.offlineImpactBoundAgentsFooter');
if (impactRes.data?.hasAgentBindings) {
sections.push(
buildOfflineImpactMessage(
$t('aiWorkflow.offlineImpactBoundBotsIntro'),
impactRes.data.botBindings,
$t('aiWorkflow.offlineImpactBoundAgentsIntro'),
impactRes.data.agentBindings,
impactRes.data?.hasPluginBindings
? undefined
: $t('aiWorkflow.offlineImpactBoundBotsFooter'),
: $t('aiWorkflow.offlineImpactBoundAgentsFooter'),
),
);
}
@@ -814,13 +814,13 @@ async function submitOfflineAction(row: any) {
buildOfflineImpactMessage(
$t('aiWorkflow.offlineImpactBoundPluginsIntro'),
impactRes.data.pluginBindings,
impactRes.data?.hasBotBindings
impactRes.data?.hasAgentBindings
? undefined
: $t('aiWorkflow.offlineImpactBoundPluginsFooter'),
),
);
}
if (impactRes.data?.hasBotBindings && impactRes.data?.hasPluginBindings) {
if (impactRes.data?.hasAgentBindings && impactRes.data?.hasPluginBindings) {
offlineImpactFooter = $t('aiWorkflow.offlineImpactBoundMixedFooter');
} else if (impactRes.data?.hasPluginBindings) {
offlineImpactFooter = $t('aiWorkflow.offlineImpactBoundPluginsFooter');