diff --git a/components/protocol-debug-panel.tsx b/components/protocol-debug-panel.tsx index adf31aad8..3be78d5f7 100644 --- a/components/protocol-debug-panel.tsx +++ b/components/protocol-debug-panel.tsx @@ -8,6 +8,113 @@ type ProtocolDebugEvent = { payload: Record; }; +type TaskBoundaryDecision = { + taskType?: string; + taskMode?: string; + reason?: string; + confidence?: number; + shouldUseCanvasContext?: boolean; + shouldUseHistoryImages?: boolean; + shouldUseWorkflowMemory?: boolean; + shouldAllowExecution?: boolean; +}; + +type AgentModelSlot = { + slot?: string; + modelId?: string; + displayName?: string; + providerType?: string; + providerKey?: string; + source?: string; + reason?: string; + locked?: boolean; +}; + +type ProviderHealthSnapshot = { + providerKey?: string; + providerType?: string; + lastEventType?: string; + lastError?: string; + consecutiveFailures?: number; + updatedAt?: number; +}; + +function asRecord(value: unknown): Record | null { + return value && typeof value === "object" && !Array.isArray(value) + ? (value as Record) + : null; +} + +function toTaskBoundaryDecision(value: unknown): TaskBoundaryDecision | null { + const record = asRecord(value); + if (!record) return null; + return { + taskType: typeof record.taskType === "string" ? record.taskType : undefined, + taskMode: typeof record.taskMode === "string" ? record.taskMode : undefined, + reason: typeof record.reason === "string" ? record.reason : undefined, + confidence: + typeof record.confidence === "number" && Number.isFinite(record.confidence) + ? record.confidence + : undefined, + shouldUseCanvasContext: + typeof record.shouldUseCanvasContext === "boolean" + ? record.shouldUseCanvasContext + : undefined, + shouldUseHistoryImages: + typeof record.shouldUseHistoryImages === "boolean" + ? record.shouldUseHistoryImages + : undefined, + shouldUseWorkflowMemory: + typeof record.shouldUseWorkflowMemory === "boolean" + ? record.shouldUseWorkflowMemory + : undefined, + shouldAllowExecution: + typeof record.shouldAllowExecution === "boolean" + ? record.shouldAllowExecution + : undefined, + }; +} + +function toAgentModelSlots(value: unknown) { + const record = asRecord(value); + if (!record) return [] as AgentModelSlot[]; + + const slots: AgentModelSlot[] = []; + for (const [slotName, slotValue] of Object.entries(record)) { + const slotRecord = asRecord(slotValue); + if (!slotRecord) continue; + slots.push({ + slot: slotName, + modelId: typeof slotRecord.modelId === "string" ? slotRecord.modelId : undefined, + displayName: + typeof slotRecord.displayName === "string" ? slotRecord.displayName : undefined, + providerType: + typeof slotRecord.providerType === "string" ? slotRecord.providerType : undefined, + providerKey: + typeof slotRecord.providerKey === "string" ? slotRecord.providerKey : undefined, + source: typeof slotRecord.source === "string" ? slotRecord.source : undefined, + reason: typeof slotRecord.reason === "string" ? slotRecord.reason : undefined, + locked: typeof slotRecord.locked === "boolean" ? slotRecord.locked : undefined, + }); + } + return slots; +} + +function toProviderHealthSnapshot(value: unknown): ProviderHealthSnapshot | null { + const record = asRecord(value); + if (!record) return null; + return { + providerKey: typeof record.providerKey === "string" ? record.providerKey : undefined, + providerType: typeof record.providerType === "string" ? record.providerType : undefined, + lastEventType: + typeof record.lastEventType === "string" ? record.lastEventType : undefined, + lastError: typeof record.lastError === "string" ? record.lastError : undefined, + consecutiveFailures: + typeof record.consecutiveFailures === "number" ? record.consecutiveFailures : undefined, + updatedAt: typeof record.updatedAt === "number" ? record.updatedAt : undefined, + }; +} + export function ProtocolDebugPanel({ enabled, events, @@ -33,6 +140,29 @@ export function ProtocolDebugPanel({ })); }, [events]); + const latestTaskBoundary = useMemo(() => { + for (let index = events.length - 1; index >= 0; index -= 1) { + const payload = events[index]?.payload; + const boundary = toTaskBoundaryDecision(payload?.taskBoundary); + if (boundary?.taskType) return boundary; + } + return null; + }, [events]); + + const latestModelSlotSummary = useMemo(() => { + for (let index = events.length - 1; index >= 0; index -= 1) { + const payload = events[index]?.payload; + const slots = toAgentModelSlots(payload?.modelSlots); + if (slots.length > 0) { + return { + slots, + providerHealth: toProviderHealthSnapshot(payload?.providerHealth), + }; + } + } + return null; + }, [events]); + return (
@@ -60,6 +190,94 @@ export function ProtocolDebugPanel({ 清空
+ {(latestTaskBoundary || latestModelSlotSummary) && ( +
+ {latestTaskBoundary && ( +
+
当前任务边界
+
+ + type: {latestTaskBoundary.taskType || "unknown"} + + + mode: {latestTaskBoundary.taskMode || "unknown"} + + {typeof latestTaskBoundary.confidence === "number" && ( + + confidence: {latestTaskBoundary.confidence} + + )} +
+
+ {latestTaskBoundary.reason || "无原因说明"} +
+
+ + canvas {latestTaskBoundary.shouldUseCanvasContext ? "on" : "off"} + + + history {latestTaskBoundary.shouldUseHistoryImages ? "on" : "off"} + + + workflow {latestTaskBoundary.shouldUseWorkflowMemory ? "on" : "off"} + + + execution {latestTaskBoundary.shouldAllowExecution ? "yes" : "no"} + +
+
+ )} + {latestModelSlotSummary && ( +
+
当前模型槽位
+
+ {latestModelSlotSummary.slots.map((slot) => ( +
+
+ + {slot.slot} + + + {slot.displayName || slot.modelId || "unknown"} + + {slot.locked && ( + + locked + + )} +
+
+ provider: {slot.providerType || "unknown"} · source: {slot.source || "unknown"} +
+ {slot.reason && ( +
+ reason: {slot.reason} +
+ )} +
+ ))} + {latestModelSlotSummary.providerHealth && ( +
+ provider health: {latestModelSlotSummary.providerHealth.lastEventType || "unknown"} + {typeof latestModelSlotSummary.providerHealth.consecutiveFailures === "number" && + ` · failures ${latestModelSlotSummary.providerHealth.consecutiveFailures}`} + {latestModelSlotSummary.providerHealth.providerType && + ` · ${latestModelSlotSummary.providerHealth.providerType}`} + {latestModelSlotSummary.providerHealth.lastError && ( +
+ {latestModelSlotSummary.providerHealth.lastError} +
+ )} +
+ )} +
+
+ )} +
+ )} {events.length === 0 ? (
暂无协议调试事件