import type { UIMessageChunk } from "ai"; export type BackendSseEvent = { event: string; data: Record; }; export function backendEventToUiChunk( item: BackendSseEvent, textId: string, sequence = 0, ): (UIMessageChunk & { id?: string }) | null { if (item.event === "text_delta") { return { type: "text-delta", id: textId, delta: String(item.data.text || "") }; } if (item.event === "progress") { return { type: "data-cad-progress", id: `progress_${String(item.data.taskId || "task")}_${sequence}`, data: { ...item.data, sequence }, }; } if (["image_observation", "requirements_document_ready", "completion_target_ready", "requirements_compiled", "modeling_plan_ready", "completion_result_ready", "action_selection", "tool_call", "candidate_result", "candidate_review", "final_review", "task_terminal"].includes(item.event)) { const review = item.data.review && typeof item.data.review === "object" ? item.data.review as Record : null; const lifecycle = String(item.data.lifecycle || ""); const status = item.event === "task_terminal" ? (lifecycle === "failed" ? "error" : lifecycle === "waiting_for_user" || lifecycle === "waiting_retry" ? "waiting" : "success") : String((item.data.result as Record | undefined)?.status || "") === "rejected" || String((item.data.result as Record | undefined)?.status || "") === "repair" ? "error" : String(item.data.status || "running"); const taskId = String(item.data.taskId || "task"); const eventId = String(item.data.eventId || `${taskId}_${sequence}_${item.event}`); const metadata = sequence > 0 || item.data.eventId ? { eventId, sequence, ...(review ? { review } : {}), } : {}; return { type: "data-cad-progress", id: `event_${eventId}`, data: { step: item.event, label: ({ image_observation: "参考图片观察", requirements_document_ready: "需求文档已冻结", completion_target_ready: "完成目标已冻结", requirements_compiled: "需求合同已编译", modeling_plan_ready: "建模计划已冻结", completion_result_ready: "完成结果已就绪", action_selection: "动作选择", tool_call: "建模工具", candidate_result: "候选构建", candidate_review: "候选独立复核", final_review: "最终独立复核", task_terminal: "生成任务", } as Record)[item.event], status, ...metadata, message: String( item.data.message || item.data.reason || (Array.isArray(item.data.questions) ? item.data.questions.map(String).filter(Boolean).join(";") : "") || (review?.evidence instanceof Array ? review.evidence.join(";") : "") || (review?.issues instanceof Array ? review.issues.map((issue) => typeof issue === "object" && issue ? String((issue as Record).message || "") : String(issue)).filter(Boolean).join(";") : ""), ), ...(item.data.taskId ? { taskId } : {}), ...(item.data.nodeId ? { nodeId: String(item.data.nodeId) } : {}), ...(item.data.lifecycle ? { lifecycle: String(item.data.lifecycle) } : {}), ...(Array.isArray(item.data.questions) ? { questions: item.data.questions.map(String).filter(Boolean) } : {}), ...(Array.isArray(item.data.issues) ? { issues: item.data.issues.map(String).filter(Boolean) } : {}), ...(item.data.blockerType ? { blockerType: String(item.data.blockerType) } : {}), ...(typeof item.data.userActionRequired === "boolean" ? { userActionRequired: item.data.userActionRequired } : {}), ...(item.data.verificationStatus ? { verificationStatus: String(item.data.verificationStatus) } : {}), ...(Array.isArray(item.data.verificationWarnings) ? { verificationWarnings: item.data.verificationWarnings.map(String).filter(Boolean) } : {}), ...(item.data.clarificationPath ? { clarificationPath: String(item.data.clarificationPath) } : {}), ...(item.data.timestamp ? { timestamp: String(item.data.timestamp) } : {}), ...(item.data.markdown ? { markdown: String(item.data.markdown) } : {}), ...(item.data.tool ? { tool: String(item.data.tool) } : {}), ...(item.data.invocationId ? { invocationId: String(item.data.invocationId) } : {}), ...(item.data.arguments && typeof item.data.arguments === "object" ? { arguments: item.data.arguments as Record } : {}), ...(item.data.result !== undefined ? { result: item.data.result } : {}), ...(Array.isArray(item.data.evidence) ? { evidence: item.data.evidence.map(String) } : {}), }, }; } if (item.event === "cad_result") { const taskId = String(item.data.taskId || ""); const revisionId = String(item.data.revisionId || Date.now()); return { type: "data-cad-result", // Each revision is a distinct progressive result. Reusing only the task // id makes the AI SDK reconcile intermediate revisions into one part. id: `result_${taskId}_${revisionId}`, data: item.data, }; } if (item.event === "cad_error") { return { type: "data-cad-error", id: `error_${Date.now()}`, data: { ...item.data, ...(Array.isArray(item.data.fieldErrors) ? { fieldErrors: item.data.fieldErrors } : {}), }, }; } return null; }