46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import type { ChatMessage, PhaseEvent } from '../types/workbench';
|
|
|
|
const TERMINAL_REPLAY_STATUSES = new Set([
|
|
'completed',
|
|
'failed',
|
|
'quality_failed',
|
|
'cancelled',
|
|
'pending_author_confirmation',
|
|
'blocked_pending_governance',
|
|
]);
|
|
|
|
/** 刷新后需要从 task_run_events 重建进度条的气泡。已有步骤或快照则不再拉。 */
|
|
export function messageNeedsPhaseReplay(
|
|
message: Pick<
|
|
ChatMessage,
|
|
'role' | 'taskRunId' | 'phaseEvents' | 'phaseSnapshot' | 'status' | 'errorInfo'
|
|
>,
|
|
): boolean {
|
|
if (message.role !== 'assistant' || !message.taskRunId) return false;
|
|
if (message.phaseEvents && message.phaseEvents.length > 0) return false;
|
|
if (message.phaseSnapshot?.steps && message.phaseSnapshot.steps.length > 0) return false;
|
|
if (message.status === 'streaming_status') return true;
|
|
if (message.errorInfo?.code === 'TASK_INTERRUPTED') return true;
|
|
// 存量终态消息:无 snapshot、无步骤时扩面重放,避免卡片退化成纯文本。
|
|
if (message.status && TERMINAL_REPLAY_STATUSES.has(message.status)) return true;
|
|
return false;
|
|
}
|
|
|
|
export function phaseReplayKey(
|
|
messages: Pick<
|
|
ChatMessage,
|
|
'id' | 'role' | 'taskRunId' | 'phaseEvents' | 'phaseSnapshot' | 'status' | 'errorInfo'
|
|
>[],
|
|
): string {
|
|
return messages
|
|
.filter(messageNeedsPhaseReplay)
|
|
.map((message) => `${message.id}:${message.taskRunId}:${message.status}`)
|
|
.sort()
|
|
.join('|');
|
|
}
|
|
|
|
export type PhaseReplayCacheEntry = {
|
|
events: PhaseEvent[];
|
|
label?: string;
|
|
};
|