feat: 添加swarms团队编排功能并优化agent委派系统

- 引入AgentTeamOrchestrator支持多agent协同任务执行
- 增加第三方swarms库依赖并配置git协议替换以改善包管理
- 扩展DelegationManager支持团队任务调度和进度跟踪
- 实现中文bigram分词算法提升中文任务检索准确性
- 调整A2AClient和DelegationManager超时时间从30秒增至600秒
- 优化AgentRunResult状态判断逻辑增加有意义摘要检测
- 修改Dockerfile配置npm仓库镜像地址和git协议映射
- 更新CLI命令行接口支持网关端口配置传递
- 调整提供者超时配置机制增强请求稳定性
- 移除过时的support_group字段简化agent描述符结构
- 增强错误处理和进度事件报告机制改进用户体验
This commit is contained in:
2026-04-14 14:34:23 +08:00
parent fee9007da6
commit cdfc222c9f
85 changed files with 5443 additions and 1392 deletions

View File

@ -31,6 +31,7 @@ import type {
UiMcpServerDescriptor,
WsEvent,
} from '@/types';
import { getCurrentAppLocale, pickAppText } from '@/lib/i18n/core';
const API_URL = process.env.NEXT_PUBLIC_API_URL?.trim();
const WS_URL = process.env.NEXT_PUBLIC_WS_URL?.trim();
@ -90,9 +91,10 @@ function withTimeout(
signal?: AbortSignal,
timeoutMs: number = REQUEST_TIMEOUT_MS
): { signal: AbortSignal; cleanup: () => void } {
const locale = getCurrentAppLocale();
const controller = new AbortController();
const timeoutId = globalThis.setTimeout(() => {
controller.abort(new DOMException('请求超时', 'AbortError'));
controller.abort(new DOMException(pickAppText(locale, '请求超时', 'Request timed out'), 'AbortError'));
}, timeoutMs);
const forwardAbort = () => controller.abort(signal?.reason);
@ -154,6 +156,7 @@ function authHeaders(includeJsonContentType: boolean = true): Record<string, str
}
async function fetchJSON<T>(path: string, options?: FetchJsonOptions): Promise<T> {
const locale = getCurrentAppLocale();
const mergedHeaders = {
...authHeaders(),
...(options?.headers as Record<string, string> | undefined),
@ -170,7 +173,7 @@ async function fetchJSON<T>(path: string, options?: FetchJsonOptions): Promise<T
} catch (error) {
cleanup();
if (error instanceof DOMException && error.name === 'AbortError') {
throw new Error('请求超时');
throw new Error(pickAppText(locale, '请求超时', 'Request timed out'));
}
throw error;
}
@ -191,7 +194,7 @@ async function fetchJSON<T>(path: string, options?: FetchJsonOptions): Promise<T
} catch {
// keep raw text
}
throw new Error(`接口错误 ${res.status}: ${detail}`);
throw new Error(`${pickAppText(locale, '接口错误', 'API error')} ${res.status}: ${detail}`);
}
return res.json();
}
@ -262,6 +265,7 @@ export function streamMessage(
onError: (error: string) => void
): () => void {
const controller = new AbortController();
const locale = getCurrentAppLocale();
(async () => {
try {
@ -273,7 +277,7 @@ export function streamMessage(
});
if (!res.ok || !res.body) {
onError(`HTTP 错误 ${res.status}`);
onError(`${pickAppText(locale, 'HTTP 错误', 'HTTP error')} ${res.status}`);
return;
}
@ -308,7 +312,7 @@ export function streamMessage(
}
} catch (err: any) {
if (err.name !== 'AbortError') {
onError(err.message || '流式请求失败');
onError(err.message || pickAppText(locale, '流式请求失败', 'Streaming request failed'));
}
}
})();
@ -1059,8 +1063,9 @@ export async function uploadFile(
sessionId: string = 'web:default',
onProgress?: (percent: number) => void
): Promise<FileAttachment> {
const locale = getCurrentAppLocale();
if (file.size > MAX_FILE_SIZE) {
throw new Error('文件过大(最大 50MB');
throw new Error(pickAppText(locale, '文件过大(最大 50MB', 'File is too large (max 50MB)'));
}
const formData = new FormData();
@ -1086,11 +1091,11 @@ export async function uploadFile(
const data = JSON.parse(xhr.responseText);
resolve(data);
} else {
reject(new Error(`上传失败:${xhr.status}`));
reject(new Error(`${pickAppText(locale, '上传失败', 'Upload failed')}: ${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error('上传失败'));
xhr.onerror = () => reject(new Error(pickAppText(locale, '上传失败', 'Upload failed')));
xhr.send(formData);
});
@ -1142,8 +1147,9 @@ export async function uploadToWorkspace(
dirPath: string = '',
onProgress?: (percent: number) => void
): Promise<WorkspaceItem> {
const locale = getCurrentAppLocale();
if (file.size > MAX_FILE_SIZE) {
throw new Error('文件过大(最大 50MB');
throw new Error(pickAppText(locale, '文件过大(最大 50MB', 'File is too large (max 50MB)'));
}
const formData = new FormData();
@ -1168,11 +1174,11 @@ export async function uploadToWorkspace(
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.responseText));
} else {
reject(new Error(`上传失败:${xhr.status}`));
reject(new Error(`${pickAppText(locale, '上传失败', 'Upload failed')}: ${xhr.status}`));
}
};
xhr.onerror = () => reject(new Error('上传失败'));
xhr.onerror = () => reject(new Error(pickAppText(locale, '上传失败', 'Upload failed')));
xhr.send(formData);
});
}