import { useCallback, useEffect, useRef, useState } from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import { useSession } from '@/hooks/useSession'; import { useTheme } from '../../../contexts/ThemeContext'; import ConfigPanel from './components/ConfigPanel'; import Sidebar from './components/Sidebar'; import Timeline from './components/Timeline'; import ProgressSidebar from './components/ProgressSidebar'; import ScenarioConfigPage from './components/ScenarioConfigPage'; import './MopeChat.css'; type StartConfig = { task_context?: string; [key: string]: any; }; type SessionError = { agent_id?: string; error?: string; }; type SessionHistoryPayload = { typeName: string; templateName: string; resultJson: Record; }; const MOPE_HISTORY_API_BASE = '/api/mope/history'; /** * 保存模型完整生成结果。 * * 接口暂未提供,先把调用方法和请求体结构固定下来: * - typeName:类型名字,目前使用用户输入的任务背景。 * - templateName:用户选择的生成模板名称。 * - resultJson:完整结果 JSON,包含 timeline、agentStates、reportSchema、finalReport 等恢复页面所需数据。 */ async function saveMopeHistory(payload: SessionHistoryPayload) { const response = await fetch(MOPE_HISTORY_API_BASE, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error(`保存 MOPE 历史失败:HTTP ${response.status}`); } return response.json().catch(() => null); } /** * 获取当前用户最近一次 MOPE 生成历史。 * * 接口暂未提供,先约定返回值就是保存时的 resultJson。 * 如果接口没有历史数据,应返回 null / 空对象 / 404,页面会保持当前未生成状态。 */ async function fetchMopeHistory(): Promise | null> { const response = await fetch(MOPE_HISTORY_API_BASE, { method: 'GET' }); if (response.status === 404 || response.status === 204) return null; if (!response.ok) { throw new Error(`获取 MOPE 历史失败:HTTP ${response.status}`); } const data = await response.json(); return data?.resultJson || data || null; } export default function MopeChat() { const session = useSession() as any; const { theme } = useTheme() as { theme?: string }; const isDark = theme === 'dark'; const [taskContext, setTaskContext] = useState(''); const [idlePage, setIdlePage] = useState('home'); const [isLeftSidebarCollapsed, setIsLeftSidebarCollapsed] = useState(false); const [isRightSidebarCollapsed, setIsRightSidebarCollapsed] = useState(false); const { status, phase, coordinatorMode, currentRound, divergeRounds, reportSchema, agents, agentStates, timeline, sharedMemory, finalReportPartial, currentFinalField, pendingReview, reviewSubmitting, reviewRevising, errors, startSession, stopSession, approveCoordinator, reviseCoordinator, hydrateFromSnapshot, reset, } = session; const historyLoadedRef = useRef(false); const savedHistoryKeyRef = useRef(null); const currentConfigRef = useRef(null); const buildHistorySnapshot = useCallback(() => ({ status, phase, coordinatorMode, currentRound, divergeRounds, reportSchema, agents, agentStates, timeline, sharedMemory, finalReportPartial, currentFinalField, config: currentConfigRef.current, }), [ status, phase, coordinatorMode, currentRound, divergeRounds, reportSchema, agents, agentStates, timeline, sharedMemory, finalReportPartial, currentFinalField, ]); const handleStart = (config: StartConfig, backendUrl: string) => { currentConfigRef.current = config; savedHistoryKeyRef.current = null; setTaskContext(config.task_context || ''); startSession(config, backendUrl); }; const handleStop = () => { stopSession(); }; const handleReset = () => { reset(); currentConfigRef.current = null; savedHistoryKeyRef.current = null; setTaskContext(''); }; useEffect(() => { if (historyLoadedRef.current) return; historyLoadedRef.current = true; void fetchMopeHistory() .then((snapshot) => { if (!snapshot || !Array.isArray(snapshot.timeline) || snapshot.timeline.length === 0) return; currentConfigRef.current = snapshot.config || null; setTaskContext(snapshot.config?.task_context || snapshot.typeName || ''); hydrateFromSnapshot(snapshot); }) .catch((error) => { console.warn('MOPE 历史记录获取失败,保持默认空状态:', error); }); }, [hydrateFromSnapshot]); useEffect(() => { if (status !== 'done' || !timeline?.length) return; const snapshot = buildHistorySnapshot(); const templateName = currentConfigRef.current?.template_name || '未命名模板'; const typeName = taskContext || currentConfigRef.current?.task_context || '未命名任务'; const saveKey = `${typeName}::${templateName}::${timeline.length}`; if (savedHistoryKeyRef.current === saveKey) return; savedHistoryKeyRef.current = saveKey; void saveMopeHistory({ typeName, templateName, resultJson: { ...snapshot, typeName, templateName, savedAt: new Date().toISOString(), }, }).catch((error) => { console.warn('MOPE 历史记录保存失败:', error); savedHistoryKeyRef.current = null; }); }, [status, timeline, taskContext, buildHistorySnapshot]); if (status === 'idle') { if (idlePage === 'scenario_config') { return (
setIdlePage('home')} />
); } return (
setIdlePage('scenario_config')} />
); } return (
MOPE {taskContext ? `"${taskContext.slice(0, 80)}${taskContext.length > 80 ? '…' : ''}"` : '分析中…'} {coordinatorMode === 'human_review' ? '人工确认' : '全自动'} {status === 'waiting_human' && ( 等待人工确认 )}
{(status === 'running' || status === 'waiting_human') && ( )} {(status === 'done' || status === 'error') && ( )}
{errors.length > 0 && errors.map((e: SessionError, i: number) => (
{e.agent_id ? `[${e.agent_id}] ` : ''}{e.error}
))}
); }