refactor(beaver): 移除Hermes相关引用和迁移代码,完善Beaver后端主线实现
移除了所有Hermes相关的命名引用,包括: - 从.gitignore中清理相关构建缓存文件 - 将README中的beaver-home路径配置更新 - 完善backend/README.md文档说明Beaver后端主线实现 - 移除Hermes风格的相关注释和兼容性代码 - 清理nanobot环境变量兼容性处理 - 删除技能迁移和服务迁移相关功能代码 - 更新测试用例中相关命名和函数名 BREAKING CHANGE: 移除了Hermes迁移相关API和CLI命令,不再支持nanobot环境变量兼容性
This commit is contained in:
@ -149,7 +149,6 @@ export default function NotificationDetailPage() {
|
||||
processArtifacts={[]}
|
||||
selectedRunId={null}
|
||||
onSelectRun={() => {}}
|
||||
onCancelRun={() => {}}
|
||||
onFeedback={() => {}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -7,7 +7,6 @@ import { Brain, Plus, Send, Trash2, X } from 'lucide-react';
|
||||
import { ChatWorkbench } from '@/components/chat-workbench/ChatWorkbench';
|
||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import {
|
||||
cancelDelegation,
|
||||
archiveSession,
|
||||
createSession,
|
||||
getActiveTask,
|
||||
@ -464,18 +463,6 @@ export default function ChatPage() {
|
||||
setSessionId(key);
|
||||
};
|
||||
|
||||
const handleCancelRun = useCallback(async (runId: string) => {
|
||||
try {
|
||||
await cancelDelegation(runId);
|
||||
} catch (err: any) {
|
||||
addMessage({
|
||||
role: 'assistant',
|
||||
content: pickAppText(locale, `取消任务 ${runId} 失败:${err.message || '未知错误'}`, `Failed to cancel task ${runId}: ${err.message || 'Unknown error'}`),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}, [addMessage, locale]);
|
||||
|
||||
const removePendingFile = useCallback((file: File) => {
|
||||
setPendingFiles((prev) => prev.filter((item) => item.file !== file));
|
||||
}, []);
|
||||
@ -566,7 +553,6 @@ export default function ChatPage() {
|
||||
processArtifacts={sessionProcessArtifacts}
|
||||
selectedRunId={selectedSessionRunId}
|
||||
onSelectRun={(runId) => setSelectedRunId(selectedSessionRunId === runId ? null : runId)}
|
||||
onCancelRun={handleCancelRun}
|
||||
onFeedback={handleFeedback}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -40,7 +40,6 @@ import {
|
||||
listSkillCandidates,
|
||||
listSkillDrafts,
|
||||
listSkills,
|
||||
migrateSkills,
|
||||
publishSkillDraft,
|
||||
regenerateSkillDraft,
|
||||
rejectSkillDraft,
|
||||
@ -207,15 +206,6 @@ export default function SkillsPage() {
|
||||
<Upload className="mr-2 h-4 w-4" />
|
||||
{t('上传技能', 'Upload skill')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => void runAction('migrate-skills', () => migrateSkills())}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={Boolean(actionId)}
|
||||
>
|
||||
{actionId === 'migrate-skills' ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Rocket className="mr-2 h-4 w-4" />}
|
||||
{t('迁移旧技能', 'Migrate legacy skills')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -15,17 +15,7 @@ import {
|
||||
Settings2,
|
||||
ScrollText,
|
||||
} from 'lucide-react';
|
||||
import { getStatus, restartSystem, updateProviderConfig } from '@/lib/api';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { getStatus, updateProviderConfig } from '@/lib/api';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@ -57,9 +47,6 @@ export default function StatusPage() {
|
||||
const [status, setStatus] = useState<SystemStatus | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [restartDialogOpen, setRestartDialogOpen] = useState(false);
|
||||
const [restarting, setRestarting] = useState(false);
|
||||
const [restartError, setRestartError] = useState<string | null>(null);
|
||||
const [selectedProvider, setSelectedProvider] = useState<ProviderStatus | null>(null);
|
||||
const [providerForm, setProviderForm] = useState<ProviderFormState>(() => ({
|
||||
enabled: false,
|
||||
@ -88,36 +75,6 @@ export default function StatusPage() {
|
||||
loadStatus();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!restarting) {
|
||||
return;
|
||||
}
|
||||
|
||||
const intervalId = window.setInterval(async () => {
|
||||
try {
|
||||
await getStatus();
|
||||
window.location.reload();
|
||||
} catch {
|
||||
// Ignore failures until the container is back.
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
return () => {
|
||||
window.clearInterval(intervalId);
|
||||
};
|
||||
}, [restarting]);
|
||||
|
||||
const handleRestart = async () => {
|
||||
setRestartError(null);
|
||||
try {
|
||||
await restartSystem();
|
||||
setRestartDialogOpen(false);
|
||||
setRestarting(true);
|
||||
} catch (err: any) {
|
||||
setRestartError(err.message || pickAppText(locale, '重启失败', 'Restart failed'));
|
||||
}
|
||||
};
|
||||
|
||||
const openProviderDialog = (provider: ProviderStatus) => {
|
||||
setSelectedProvider(provider);
|
||||
setProviderError(null);
|
||||
@ -204,7 +161,7 @@ export default function StatusPage() {
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={loadStatus} variant="outline" size="sm" disabled={restarting}>
|
||||
<Button onClick={loadStatus} variant="outline" size="sm">
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
{pickAppText(locale, '刷新', 'Refresh')}
|
||||
</Button>
|
||||
@ -223,13 +180,8 @@ export default function StatusPage() {
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium">{pickAppText(locale, '运行与调试', 'Runtime and debugging')}</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{restarting
|
||||
? pickAppText(locale, '正在重启当前 docker,服务恢复后页面会自动刷新。', 'Restarting the current Docker container. The page will refresh automatically once the service is back.')
|
||||
: pickAppText(locale, '查看每次对话的运行日志,或重启当前 docker 容器。重启完成后需要重新登录。', 'Inspect per-chat runtime logs or restart the current Docker container. You will need to sign in again afterwards.')}
|
||||
{pickAppText(locale, '查看每次对话的运行日志和当前实例运行状态。', 'Inspect per-chat runtime logs and current instance status.')}
|
||||
</p>
|
||||
{restartError ? (
|
||||
<p className="text-sm text-destructive">{restartError}</p>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-wrap justify-start gap-2 lg:justify-end">
|
||||
<Button asChild variant="outline">
|
||||
@ -238,34 +190,6 @@ export default function StatusPage() {
|
||||
{pickAppText(locale, '运行日志', 'Runtime Logs')}
|
||||
</Link>
|
||||
</Button>
|
||||
<AlertDialog open={restartDialogOpen} onOpenChange={setRestartDialogOpen}>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={() => setRestartDialogOpen(true)}
|
||||
disabled={restarting}
|
||||
>
|
||||
{restarting ? (
|
||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
||||
) : (
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
)}
|
||||
Restart
|
||||
</Button>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{pickAppText(locale, '确认重启当前实例?', 'Restart the current instance?')}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{pickAppText(locale, '这会重启当前 docker 容器,页面会短暂不可用。由于当前登录态保存在内存里,重启完成后需要重新登录。', 'This restarts the current Docker container and the page will be temporarily unavailable. Because the current sign-in state is stored in memory, you will need to sign in again after the restart.')}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={restarting}>{pickAppText(locale, '取消', 'Cancel')}</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={handleRestart} disabled={restarting}>
|
||||
{restarting ? pickAppText(locale, '重启中...', 'Restarting...') : pickAppText(locale, '确认重启', 'Confirm restart')}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-3 border-t pt-5 md:grid-cols-2">
|
||||
|
||||
@ -3,21 +3,21 @@
|
||||
import Link from 'next/link';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { AlertCircle, ArrowLeft, Bot, CheckCircle2, Download, FileText, HelpCircle, MessageSquare, RefreshCw, RotateCcw, Trash2, User, XCircle } from 'lucide-react';
|
||||
import { AlertCircle, ArrowLeft, Bot, CheckCircle2, Download, FileText, HelpCircle, MessageSquare, RefreshCw, Trash2, User, XCircle } from 'lucide-react';
|
||||
|
||||
import { OfficeStatusBadge, formatOfficeDuration, formatOfficeTime, progressPercent } from '@/components/office/OfficeShared';
|
||||
import { TaskRuntimeStatusBadge, formatTaskRuntimeDuration, formatTaskRuntimeTime, progressPercent } from '@/components/task-runtime/TaskRuntimeShared';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { cancelDelegation, deleteBackendTask, getBackendTask, getFileUrl, retryDelegation, submitChatFeedback } from '@/lib/api';
|
||||
import { deleteBackendTask, getBackendTask, getFileUrl, submitChatFeedback } from '@/lib/api';
|
||||
import { pickAppText } from '@/lib/i18n/core';
|
||||
import { useAppI18n } from '@/lib/i18n/provider';
|
||||
import { buildOfficeView, isOfficeTaskTerminal, type OfficeTaskView } from '@/lib/office';
|
||||
import { buildTaskRuntimeView, type TaskRuntimeNodeView } from '@/lib/task-runtime';
|
||||
import { useChatStore } from '@/lib/store';
|
||||
import type { BackendTask, BackendTaskRun, ProcessArtifact, ProcessEvent } from '@/types';
|
||||
|
||||
function taskVisibleStatus(task: OfficeTaskView, locale: 'zh-CN' | 'en-US') {
|
||||
function taskVisibleStatus(task: TaskRuntimeNodeView, locale: 'zh-CN' | 'en-US') {
|
||||
if (task.status === 'error') return pickAppText(locale, '任务失败', 'Task failed');
|
||||
if (task.status === 'cancelled') return pickAppText(locale, '已取消', 'Cancelled');
|
||||
return task.stageLabel || task.status;
|
||||
@ -46,7 +46,7 @@ export default function TaskDetailPage() {
|
||||
const updateMessageFeedback = useChatStore((state) => state.updateMessageFeedback);
|
||||
|
||||
const task = useMemo(
|
||||
() => buildOfficeView(taskId, { sessions, processRuns, processEvents, processArtifacts }, locale),
|
||||
() => buildTaskRuntimeView(taskId, { sessions, processRuns, processEvents, processArtifacts }, locale),
|
||||
[locale, processArtifacts, processEvents, processRuns, sessions, taskId]
|
||||
);
|
||||
const [backendTask, setBackendTask] = useState<BackendTask | null>(null);
|
||||
@ -105,7 +105,7 @@ export default function TaskDetailPage() {
|
||||
return map;
|
||||
}, [artifacts]);
|
||||
const phaseGroups = useMemo(() => {
|
||||
const groups = new Map<string, OfficeTaskView[]>();
|
||||
const groups = new Map<string, TaskRuntimeNodeView[]>();
|
||||
for (const item of task?.tasks ?? []) {
|
||||
const label = item.stageLabel || taskVisibleStatus(item, locale);
|
||||
groups.set(label, [...(groups.get(label) ?? []), item]);
|
||||
@ -180,7 +180,7 @@ export default function TaskDetailPage() {
|
||||
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
|
||||
<span>{pickAppText(locale, '来源会话', 'Session')}: {backendTask.session_id}</span>
|
||||
<span>{pickAppText(locale, '创建者', 'Creator')}: {backendTask.creator}</span>
|
||||
<span>{pickAppText(locale, '更新', 'Updated')}: {formatOfficeTime(backendTask.updated_at, locale)}</span>
|
||||
<span>{pickAppText(locale, '更新', 'Updated')}: {formatTaskRuntimeTime(backendTask.updated_at, locale)}</span>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -242,7 +242,7 @@ export default function TaskDetailPage() {
|
||||
<div key={index} className="rounded-md border border-border p-3">
|
||||
<div className="font-medium">{humanFeedback(String(item.feedback_type || ''), locale)}</div>
|
||||
{item.comment ? <p className="mt-1 text-muted-foreground">{String(item.comment)}</p> : null}
|
||||
{item.created_at ? <p className="mt-1 text-xs text-muted-foreground">{formatOfficeTime(String(item.created_at), locale)}</p> : null}
|
||||
{item.created_at ? <p className="mt-1 text-xs text-muted-foreground">{formatTaskRuntimeTime(String(item.created_at), locale)}</p> : null}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
@ -295,25 +295,6 @@ export default function TaskDetailPage() {
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={Boolean(actionBusy) || isOfficeTaskTerminal(task.status)}
|
||||
onClick={() => void runAction('cancel', () => cancelDelegation(task.rootRunId))}
|
||||
>
|
||||
<XCircle className="mr-2 h-4 w-4" />
|
||||
{pickAppText(locale, '取消任务', 'Cancel task')}
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
disabled={Boolean(actionBusy) || !isOfficeTaskTerminal(task.status)}
|
||||
onClick={() => void runAction('retry', () => retryDelegation(task.rootRunId))}
|
||||
>
|
||||
<RotateCcw className="mr-2 h-4 w-4" />
|
||||
{pickAppText(locale, '重试任务', 'Retry task')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
@ -322,20 +303,20 @@ export default function TaskDetailPage() {
|
||||
<div className="min-w-0">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<h1 className="truncate text-2xl font-semibold">{task.title}</h1>
|
||||
<OfficeStatusBadge status={task.status} />
|
||||
<TaskRuntimeStatusBadge status={task.status} />
|
||||
</div>
|
||||
<div className="mt-3 flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
|
||||
<span>{pickAppText(locale, '来源会话', 'Session')}: {task.sourceSessionLabel}</span>
|
||||
<span>{pickAppText(locale, '主 Agent', 'Lead agent')}: {task.rootActorName}</span>
|
||||
<span>{pickAppText(locale, '开始', 'Started')}: {formatOfficeTime(task.createdAt, locale)}</span>
|
||||
<span>{pickAppText(locale, '耗时', 'Duration')}: {formatOfficeDuration(task.durationMs, locale)}</span>
|
||||
<span>{pickAppText(locale, '开始', 'Started')}: {formatTaskRuntimeTime(task.createdAt, locale)}</span>
|
||||
<span>{pickAppText(locale, '耗时', 'Duration')}: {formatTaskRuntimeDuration(task.durationMs, locale)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid w-full gap-3 sm:grid-cols-4 lg:w-[520px]">
|
||||
<Metric label={pickAppText(locale, '节点', 'Nodes')} value={String(task.stats.totalRuns)} />
|
||||
<Metric label={pickAppText(locale, '活跃', 'Active')} value={String(task.stats.activeRuns)} />
|
||||
<Metric label={pickAppText(locale, '产物', 'Artifacts')} value={String(task.stats.artifactCount)} />
|
||||
<Metric label={pickAppText(locale, '异常', 'Alerts')} value={String(task.alerts.length)} />
|
||||
<Metric label={pickAppText(locale, '异常', 'Alerts')} value={String(task.stats.alertCount)} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 space-y-2">
|
||||
@ -398,7 +379,7 @@ export default function TaskDetailPage() {
|
||||
<div className="truncate font-medium">{node.title}</div>
|
||||
<div className="mt-1 text-xs text-muted-foreground">{node.actorName}</div>
|
||||
</div>
|
||||
<OfficeStatusBadge status={node.status} />
|
||||
<TaskRuntimeStatusBadge status={node.status} />
|
||||
</div>
|
||||
<div className="mt-3 text-sm text-muted-foreground">
|
||||
{node.summary || taskVisibleStatus(node, locale)}
|
||||
@ -426,13 +407,13 @@ export default function TaskDetailPage() {
|
||||
<div className="font-medium">{selectedNode.title}</div>
|
||||
<div className="mt-1 text-xs text-muted-foreground">{selectedNode.runId}</div>
|
||||
</div>
|
||||
<OfficeStatusBadge status={selectedNode.status} />
|
||||
<TaskRuntimeStatusBadge status={selectedNode.status} />
|
||||
<p className="text-sm text-muted-foreground">{selectedNode.summary || '-'}</p>
|
||||
<div className="space-y-2">
|
||||
{(eventsByRun.get(selectedNode.runId) ?? []).slice(-5).map((event) => (
|
||||
<div key={event.event_id} className="rounded-md border border-border bg-muted/30 p-2 text-xs">
|
||||
<div className="font-medium">{event.kind}</div>
|
||||
<div className="mt-1 text-muted-foreground">{event.text || formatOfficeTime(event.created_at, locale)}</div>
|
||||
<div className="mt-1 text-muted-foreground">{event.text || formatTaskRuntimeTime(event.created_at, locale)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -548,7 +529,7 @@ function BackendRunConversation({ run, index }: { run: BackendTaskRun; index: nu
|
||||
<div>
|
||||
<div className="font-medium">{run.title || pickAppText(locale, `Agent ${index + 1}`, `Agent ${index + 1}`)}</div>
|
||||
<div className="mt-1 text-xs text-muted-foreground">
|
||||
{run.started_at ? formatOfficeTime(run.started_at, locale) : pickAppText(locale, '时间未知', 'Unknown time')}
|
||||
{run.started_at ? formatTaskRuntimeTime(run.started_at, locale) : pickAppText(locale, '时间未知', 'Unknown time')}
|
||||
{run.finish_reason ? ` · ${humanFinishReason(run.finish_reason, locale)}` : ''}
|
||||
</div>
|
||||
</div>
|
||||
@ -573,7 +554,7 @@ function BackendRunConversation({ run, index }: { run: BackendTaskRun; index: nu
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="mb-1 flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<span>{isAssistant ? run.title || pickAppText(locale, 'Agent 回复', 'Agent reply') : isTool ? message.tool_name || pickAppText(locale, '工具结果', 'Tool result') : pickAppText(locale, '用户要求', 'User request')}</span>
|
||||
{message.created_at ? <span>{formatOfficeTime(message.created_at, locale)}</span> : null}
|
||||
{message.created_at ? <span>{formatTaskRuntimeTime(message.created_at, locale)}</span> : null}
|
||||
</div>
|
||||
<div className="whitespace-pre-wrap rounded-md border border-border bg-muted/20 px-3 py-2 text-sm leading-6">
|
||||
{message.content}
|
||||
|
||||
@ -5,7 +5,7 @@ import { useSearchParams } from 'next/navigation';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { AlertCircle, ArrowRight, Clock3, FolderDown, ListTodo, Loader2, Play, Plus, RefreshCw, Trash2, X } from 'lucide-react';
|
||||
|
||||
import { formatOfficeTime } from '@/components/office/OfficeShared';
|
||||
import { formatTaskRuntimeTime } from '@/components/task-runtime/TaskRuntimeShared';
|
||||
import { TaskManagementTabs } from '@/components/task-management/TaskManagementTabs';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@ -151,7 +151,7 @@ function OrdinaryTasks() {
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{task.run_ids.length}</TableCell>
|
||||
<TableCell className="text-sm text-muted-foreground">{task.skill_names.length}</TableCell>
|
||||
<TableCell className="text-xs text-muted-foreground">{formatOfficeTime(task.updated_at, locale)}</TableCell>
|
||||
<TableCell className="text-xs text-muted-foreground">{formatTaskRuntimeTime(task.updated_at, locale)}</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-1">
|
||||
<Button asChild size="sm" variant="outline">
|
||||
|
||||
@ -8,7 +8,7 @@ import { pickAppText } from '@/lib/i18n/core';
|
||||
import { useAppI18n } from '@/lib/i18n/provider';
|
||||
import { useChatStore } from '@/lib/store';
|
||||
|
||||
const HANDOFF_STATE_KEY = 'nanobot_handoff_state';
|
||||
const HANDOFF_STATE_KEY = 'beaver_handoff_state';
|
||||
|
||||
type HandoffState = {
|
||||
code?: string;
|
||||
|
||||
Reference in New Issue
Block a user