Files
cdsl-cad/frontend/src/lib/cad-stream.ts
T
2026-09-02 13:51:35 +08:00

97 lines
5.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { UIMessageChunk } from "ai";
export type BackendSseEvent = {
event: string;
data: Record<string, unknown>;
};
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<string, unknown>
: 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<string, unknown> | undefined)?.status || "") === "rejected"
|| String((item.data.result as Record<string, unknown> | 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<string, string>)[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<string, unknown>).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<string, unknown> } : {}),
...(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;
}