import { runEventSchema, startedRunSchema, type RunEventResponse, type StartedRunResponse, } from "@great-agent/web-contracts"; export type StartRunInput = | { kind: "ordinary"; message: string } | { kind: "existing"; conversationId: string; message: string }; export async function startRun( input: StartRunInput, ): Promise { const response = await fetch("/api/runs", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input), }); const value = await response.json(); if (!response.ok) throw new Error(readErrorMessage(value)); return startedRunSchema.parse(value); } export async function streamRun( runId: string, onEvent: (event: RunEventResponse) => void, ): Promise { const response = await fetch( `/api/runs/${encodeURIComponent(runId)}/events`, { headers: { accept: "text/event-stream" } }, ); if (!response.ok || !response.body) throw new Error("流式连接中断"); const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { done, value } = await reader.read(); buffer += decoder.decode(value, { stream: !done }).replaceAll("\r\n", "\n"); const blocks = buffer.split("\n\n"); buffer = blocks.pop() ?? ""; for (const block of blocks) { const data = block .split("\n") .filter((line) => line.startsWith("data:")) .map((line) => line.slice(5).trimStart()) .join("\n"); if (!data) continue; const event = runEventSchema.parse(JSON.parse(data)); onEvent(event); if (event.type === "run.completed") return; if (event.type === "run.failed") throw new Error(readPayloadMessage(event.payload)); } if (done) break; } throw new Error("流式连接提前结束"); } function readErrorMessage(value: unknown): string { if (typeof value === "object" && value && "error" in value) { const error = value.error; if ( typeof error === "object" && error && "message" in error && typeof error.message === "string" ) return error.message; } return "无法启动 Agent 任务"; } function readPayloadMessage(payload: Record): string { return typeof payload.message === "string" ? payload.message : "模型运行失败"; }