diff --git a/src/components/chat/StreamingIndicator.tsx b/src/components/chat/StreamingIndicator.tsx index a3c236c..3edbb47 100644 --- a/src/components/chat/StreamingIndicator.tsx +++ b/src/components/chat/StreamingIndicator.tsx @@ -17,12 +17,9 @@ export function StreamingIndicator(): React.JSX.Element | null { if (!isStreaming) return null; const lastMsg = messages[messages.length - 1]; - // 仅当最后一条是 assistant 消息且有内容时才隐藏 - // 如果最后一条是 user 消息(刚发送,等待 AI 响应),应显示加载指示器 - const isLastAssistant = lastMsg?.role === 'assistant'; - const hasVisibleContent = isLastAssistant && - (!!lastMsg?.content || !!lastMsg?.reasoningContent || !!lastMsg?.toolCalls?.length); - if (hasVisibleContent) return null; + // 最后一条是 assistant 消息时隐藏(即使内容为空,AssistantMessage 会显示"正在思考...") + // 仅当最后一条是 user 消息(刚发送,等待 AI 响应)时显示加载指示器 + if (lastMsg?.role === 'assistant') return null; return ( diff --git a/src/components/trace/TraceStep.tsx b/src/components/trace/TraceStep.tsx index 8a6f686..70dea12 100644 --- a/src/components/trace/TraceStep.tsx +++ b/src/components/trace/TraceStep.tsx @@ -64,13 +64,18 @@ export function TraceStep({ step, isCurrent }: TraceStepProps): React.JSX.Elemen const label = TRACE_STATE_LABELS[step.state] ?? step.state; const duration = step.completedAt ? step.completedAt - step.startedAt : null; + // 构建状态进度链(如 "思考 → 执行 → 观察") + const stateChain = (step.states ?? [step.state]) + .map((s) => TRACE_STATE_LABELS[s] ?? s) + .join(' → '); + return ( {expanded ? : } {isCurrent ? : step.completedAt ? : } #{step.iteration} - {label} + {stateChain} {duration != null && {formatDuration(duration)}} {step.tokenUsage && {formatTokens(step.tokenUsage.totalTokens)} tok} diff --git a/src/hooks/useAgentStream.ts b/src/hooks/useAgentStream.ts index 21fc66a..9f3b8f5 100644 --- a/src/hooks/useAgentStream.ts +++ b/src/hooks/useAgentStream.ts @@ -265,29 +265,41 @@ export function useAgentStream(): void { } } - // --- Trace 步骤管理(每轮迭代一个步骤,状态转换时更新而非新建)--- + // --- Trace 步骤管理(每轮迭代一个步骤,状态转换时追加到 states 数组)--- if (data.state && data.iteration != null) { - const traceSteps = store.traceSteps; - const lastStep = traceSteps[traceSteps.length - 1]; - - // 判断是否需要创建新步骤:同一迭代的首次状态(INIT/THINKING)创建新步骤 - // 后续状态转换(EXECUTING/OBSERVING等)只更新当前步骤的 state 字段 - const isSameIteration = lastStep && lastStep.iteration === data.iteration; - - if (isSameIteration) { - // 同一迭代内的状态转换 → 更新当前步骤状态 - store.updateLastTraceStep({ state: data.state }); + // 跳过 INIT (iteration=0):引擎初始化阶段无实际内容,不创建 trace step + if (data.state === 'INIT' && data.iteration === 0) { + // 仍需更新 Agent 状态映射 } else { - // 新迭代 → 标记上一步完成,创建新步骤 - if (lastStep && !lastStep.completedAt) { - store.updateLastTraceStep({ completedAt: Date.now() }); + const traceSteps = store.traceSteps; + const lastStep = traceSteps[traceSteps.length - 1]; + + // 判断是否需要创建新步骤:同一迭代的首次状态创建新步骤 + // 后续状态转换(EXECUTING/OBSERVING等)追加到 states 数组 + const isSameIteration = lastStep && lastStep.iteration === data.iteration; + + if (isSameIteration) { + // 同一迭代内的状态转换 → 追加状态到 states 数组,更新当前 state + const currentStates = lastStep.states ?? [lastStep.state]; + if (currentStates[currentStates.length - 1] !== data.state) { + store.updateLastTraceStep({ + state: data.state, + states: [...currentStates, data.state], + }); + } + } else { + // 新迭代 → 标记上一步完成,创建新步骤 + if (lastStep && !lastStep.completedAt) { + store.updateLastTraceStep({ completedAt: Date.now() }); + } + store.addTraceStep({ + id: `trace_${data.iteration}_${data.state}_${Date.now()}`, + iteration: data.iteration, + state: data.state, + states: [data.state], + startedAt: Date.now(), + }); } - store.addTraceStep({ - id: `trace_${data.iteration}_${data.state}_${Date.now()}`, - iteration: data.iteration, - state: data.state, - startedAt: Date.now(), - }); } // --- Agent 状态映射 --- diff --git a/src/stores/agent-store.ts b/src/stores/agent-store.ts index b6d1430..cb2ac91 100644 --- a/src/stores/agent-store.ts +++ b/src/stores/agent-store.ts @@ -59,6 +59,7 @@ export interface TraceStep { id: string; iteration: number; state: string; + states: string[]; startedAt: number; completedAt?: number; thought?: string; @@ -163,10 +164,11 @@ export const useAgentStore = create((set, get) => ({ window.metona.sessions.getTrace(id).then((data) => { if (data) { if (data.traceSteps) { - // 兼容旧数据:为缺少 id 的 trace 步骤生成 id + // 兼容旧数据:为缺少 id/states 的 trace 步骤补全 const steps = (data.traceSteps as TraceStep[]).map((t, i) => ({ ...t, id: t.id ?? `trace_legacy_${t.iteration}_${t.state}_${i}`, + states: t.states ?? [t.state], })); set({ traceSteps: steps }); }