78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
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<StartedRunResponse> {
|
|
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<void> {
|
|
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, unknown>): string {
|
|
return typeof payload.message === "string" ? payload.message : "模型运行失败";
|
|
}
|