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:
78
app-instance/frontend/lib/i18n/common.ts
Normal file
78
app-instance/frontend/lib/i18n/common.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import type { OfficeTaskStatus } from '@/lib/office';
|
||||
import type { ProcessArtifact, ProcessRun } from '@/types';
|
||||
import { getCurrentAppLocale, pickAppText, type AppLocale } from '@/lib/i18n/core';
|
||||
import type { WsStatus } from '@/lib/api';
|
||||
|
||||
export function appStatusLabel(
|
||||
status: ProcessRun['status'] | OfficeTaskStatus | string,
|
||||
locale: AppLocale = getCurrentAppLocale()
|
||||
): string {
|
||||
if (status === 'queued') return pickAppText(locale, '排队中', 'Queued');
|
||||
if (status === 'running') return pickAppText(locale, '运行中', 'Running');
|
||||
if (status === 'waiting') return pickAppText(locale, '等待中', 'Waiting');
|
||||
if (status === 'blocked') return pickAppText(locale, '阻塞', 'Blocked');
|
||||
if (status === 'done') return pickAppText(locale, '已完成', 'Done');
|
||||
if (status === 'error') return pickAppText(locale, '失败', 'Error');
|
||||
if (status === 'cancelled') return pickAppText(locale, '已取消', 'Cancelled');
|
||||
return status;
|
||||
}
|
||||
|
||||
export function appActorTypeLabel(actorType: string, locale: AppLocale = getCurrentAppLocale()): string {
|
||||
if (actorType === 'mcp') return 'MCP';
|
||||
if (actorType === 'system') return pickAppText(locale, '系统', 'System');
|
||||
if (actorType === 'agent') return pickAppText(locale, '智能体', 'Agent');
|
||||
return actorType;
|
||||
}
|
||||
|
||||
export function appEventKindLabel(kind: string, locale: AppLocale = getCurrentAppLocale()): string {
|
||||
if (kind === 'run_started') return pickAppText(locale, '已启动', 'Started');
|
||||
if (kind === 'run_progress') return pickAppText(locale, '进行中', 'In Progress');
|
||||
if (kind === 'run_status') return pickAppText(locale, '状态更新', 'Status');
|
||||
if (kind === 'run_message') return pickAppText(locale, '消息', 'Message');
|
||||
if (kind === 'run_artifact') return pickAppText(locale, '产物', 'Artifact');
|
||||
if (kind === 'run_finished') return pickAppText(locale, '已结束', 'Finished');
|
||||
if (kind === 'run_cancelled') return pickAppText(locale, '已取消', 'Cancelled');
|
||||
return kind;
|
||||
}
|
||||
|
||||
export function appFeedRoleLabel(
|
||||
role: 'user' | 'assistant' | 'system' | 'tool',
|
||||
locale: AppLocale = getCurrentAppLocale()
|
||||
): string {
|
||||
if (role === 'user') return pickAppText(locale, '主 agent', 'Lead agent');
|
||||
if (role === 'tool') return pickAppText(locale, '工具输出', 'Tool output');
|
||||
if (role === 'system') return pickAppText(locale, '状态', 'Status');
|
||||
return pickAppText(locale, '子 agent', 'Sub-agent');
|
||||
}
|
||||
|
||||
export function appArtifactPreview(artifact: ProcessArtifact, locale: AppLocale = getCurrentAppLocale()): string {
|
||||
if (artifact.artifact_type === 'link' && artifact.url) {
|
||||
return `${artifact.title}\n${artifact.url}`;
|
||||
}
|
||||
if ((artifact.artifact_type === 'text' || artifact.artifact_type === 'markdown') && artifact.content) {
|
||||
return `${artifact.title}\n${artifact.content}`;
|
||||
}
|
||||
if (artifact.artifact_type === 'json') {
|
||||
return `${artifact.title}\n${pickAppText(locale, '已生成结构化结果', 'Structured output generated')}`;
|
||||
}
|
||||
if (artifact.file_id) {
|
||||
return `${artifact.title}\n${pickAppText(locale, '已生成文件输出', 'File output generated')}`;
|
||||
}
|
||||
return artifact.title;
|
||||
}
|
||||
|
||||
export function appConnectionStatusLabel(
|
||||
wsStatus: WsStatus,
|
||||
nanobotReady: boolean | null,
|
||||
locale: AppLocale = getCurrentAppLocale()
|
||||
): string {
|
||||
const isOnline = wsStatus === 'connected' && nanobotReady === true;
|
||||
const isChecking = wsStatus === 'connected' && nanobotReady === null;
|
||||
const isOffline = wsStatus === 'disconnected' || (wsStatus === 'connected' && nanobotReady === false);
|
||||
|
||||
if (isOnline) return pickAppText(locale, '已连接', 'Connected');
|
||||
if (isChecking) return pickAppText(locale, '检查中', 'Checking');
|
||||
if (wsStatus === 'connecting') return pickAppText(locale, '连接中', 'Connecting');
|
||||
if (isOffline && wsStatus === 'connected') return pickAppText(locale, '服务离线', 'Service offline');
|
||||
return pickAppText(locale, '未连接', 'Disconnected');
|
||||
}
|
||||
76
app-instance/frontend/lib/i18n/core.ts
Normal file
76
app-instance/frontend/lib/i18n/core.ts
Normal file
@ -0,0 +1,76 @@
|
||||
export const APP_LOCALE_COOKIE = 'nanobot_locale';
|
||||
export const APP_LOCALE_STORAGE_KEY = 'nanobot_locale';
|
||||
|
||||
export const APP_LOCALES = ['zh-CN', 'en-US'] as const;
|
||||
|
||||
export type AppLocale = (typeof APP_LOCALES)[number];
|
||||
|
||||
export function isAppLocale(value: string | null | undefined): value is AppLocale {
|
||||
return value === 'zh-CN' || value === 'en-US';
|
||||
}
|
||||
|
||||
export function normalizeAppLocale(value?: string | null): AppLocale {
|
||||
const probe = value?.trim().toLowerCase() || '';
|
||||
if (probe.startsWith('en')) {
|
||||
return 'en-US';
|
||||
}
|
||||
return 'zh-CN';
|
||||
}
|
||||
|
||||
function readCookieLocale(): string | null {
|
||||
if (typeof document === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = document.cookie
|
||||
.split('; ')
|
||||
.find((item) => item.startsWith(`${APP_LOCALE_COOKIE}=`));
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return decodeURIComponent(match.slice(APP_LOCALE_COOKIE.length + 1));
|
||||
}
|
||||
|
||||
export function readBrowserAppLocale(): AppLocale {
|
||||
if (typeof window === 'undefined') {
|
||||
return 'zh-CN';
|
||||
}
|
||||
|
||||
const fromDocument = document.documentElement.lang;
|
||||
if (fromDocument) {
|
||||
return normalizeAppLocale(fromDocument);
|
||||
}
|
||||
|
||||
const fromStorage = window.localStorage.getItem(APP_LOCALE_STORAGE_KEY);
|
||||
if (fromStorage) {
|
||||
return normalizeAppLocale(fromStorage);
|
||||
}
|
||||
|
||||
const fromCookie = readCookieLocale();
|
||||
if (fromCookie) {
|
||||
return normalizeAppLocale(fromCookie);
|
||||
}
|
||||
|
||||
return normalizeAppLocale(window.navigator.language);
|
||||
}
|
||||
|
||||
export function persistAppLocale(locale: AppLocale): void {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
document.documentElement.lang = locale;
|
||||
window.localStorage.setItem(APP_LOCALE_STORAGE_KEY, locale);
|
||||
document.cookie = `${APP_LOCALE_COOKIE}=${encodeURIComponent(locale)}; path=/; max-age=31536000; samesite=lax`;
|
||||
}
|
||||
|
||||
export function getCurrentAppLocale(): AppLocale {
|
||||
if (typeof window === 'undefined') {
|
||||
return 'zh-CN';
|
||||
}
|
||||
return readBrowserAppLocale();
|
||||
}
|
||||
|
||||
export function pickAppText<T>(locale: AppLocale, zhValue: T, enValue: T): T {
|
||||
return locale === 'en-US' ? enValue : zhValue;
|
||||
}
|
||||
57
app-instance/frontend/lib/i18n/provider.tsx
Normal file
57
app-instance/frontend/lib/i18n/provider.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
type AppLocale,
|
||||
persistAppLocale,
|
||||
readBrowserAppLocale,
|
||||
} from '@/lib/i18n/core';
|
||||
|
||||
type AppI18nContextValue = {
|
||||
locale: AppLocale;
|
||||
setLocale: (locale: AppLocale) => void;
|
||||
};
|
||||
|
||||
const AppI18nContext = React.createContext<AppI18nContextValue | null>(null);
|
||||
|
||||
export function AppI18nProvider({
|
||||
initialLocale,
|
||||
children,
|
||||
}: {
|
||||
initialLocale: AppLocale;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const [locale, setLocaleState] = React.useState<AppLocale>(initialLocale);
|
||||
|
||||
React.useEffect(() => {
|
||||
const browserLocale = readBrowserAppLocale();
|
||||
if (browserLocale !== locale) {
|
||||
setLocaleState(browserLocale);
|
||||
return;
|
||||
}
|
||||
persistAppLocale(locale);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
persistAppLocale(locale);
|
||||
}, [locale]);
|
||||
|
||||
const value = React.useMemo<AppI18nContextValue>(
|
||||
() => ({
|
||||
locale,
|
||||
setLocale: setLocaleState,
|
||||
}),
|
||||
[locale]
|
||||
);
|
||||
|
||||
return <AppI18nContext.Provider value={value}>{children}</AppI18nContext.Provider>;
|
||||
}
|
||||
|
||||
export function useAppI18n(): AppI18nContextValue {
|
||||
const value = React.useContext(AppI18nContext);
|
||||
if (!value) {
|
||||
throw new Error('useAppI18n must be used within AppI18nProvider');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
17
app-instance/frontend/lib/i18n/server.ts
Normal file
17
app-instance/frontend/lib/i18n/server.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { cookies, headers } from 'next/headers';
|
||||
|
||||
import { APP_LOCALE_COOKIE, normalizeAppLocale, type AppLocale } from '@/lib/i18n/core';
|
||||
|
||||
export function getServerAppLocale(): AppLocale {
|
||||
const cookieLocale = cookies().get(APP_LOCALE_COOKIE)?.value;
|
||||
if (cookieLocale) {
|
||||
return normalizeAppLocale(cookieLocale);
|
||||
}
|
||||
|
||||
const acceptLanguage = headers().get('accept-language');
|
||||
if (acceptLanguage) {
|
||||
return normalizeAppLocale(acceptLanguage);
|
||||
}
|
||||
|
||||
return 'zh-CN';
|
||||
}
|
||||
Reference in New Issue
Block a user