fix(canvas): Agent 会话 - 刷新后恢复后台任务结果
This commit is contained in:
73
web/src/lib/canvas/canvas-agent-session.ts
Normal file
73
web/src/lib/canvas/canvas-agent-session.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
agentSessionFailureMessage,
|
||||
createAgentSession,
|
||||
queryAgentSession,
|
||||
type AgentSessionDetail,
|
||||
type CreateSessionInput,
|
||||
} from "@/services/api/task-center";
|
||||
|
||||
const CINEMATIC_SESSION_POLL_INTERVAL_MS = 2000;
|
||||
const CINEMATIC_SESSION_MAX_POLLS = 120;
|
||||
|
||||
type CinematicSessionWaitOptions = {
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
|
||||
type CreateCinematicSessionOptions = CinematicSessionWaitOptions & {
|
||||
onCreated?: (detail: AgentSessionDetail) => void;
|
||||
};
|
||||
|
||||
export async function createCinematicAgentSession(input: CreateSessionInput, options: CreateCinematicSessionOptions = {}) {
|
||||
const created = await createAgentSession(input);
|
||||
throwIfAborted(options.signal);
|
||||
options.onCreated?.(created);
|
||||
return waitForCinematicAgentSession(created, options);
|
||||
}
|
||||
|
||||
export async function resumeCinematicAgentSession(id: string, options: CinematicSessionWaitOptions = {}) {
|
||||
throwIfAborted(options.signal);
|
||||
const detail = await queryAgentSession(id);
|
||||
return waitForCinematicAgentSession(detail, options);
|
||||
}
|
||||
|
||||
export function cinematicAgentSessionOpsJson(detail: AgentSessionDetail) {
|
||||
if (detail.session.status !== "completed") throw new Error("后端影视 Agent 会话尚未完成");
|
||||
if (!detail.session.canvasOpsJson) throw new Error("后端影视 Agent 没有返回画布操作");
|
||||
return detail.session.canvasOpsJson;
|
||||
}
|
||||
|
||||
export function isAgentSessionPollingAbort(error: unknown) {
|
||||
return error instanceof Error && error.name === "AbortError";
|
||||
}
|
||||
|
||||
async function waitForCinematicAgentSession(initialDetail: AgentSessionDetail, options: CinematicSessionWaitOptions) {
|
||||
let detail = initialDetail;
|
||||
for (let attempt = 0; attempt < CINEMATIC_SESSION_MAX_POLLS; attempt += 1) {
|
||||
throwIfAborted(options.signal);
|
||||
if (detail.session.status === "completed") return detail;
|
||||
if (detail.session.status === "failed") throw new Error(agentSessionFailureMessage(detail));
|
||||
await abortableDelay(CINEMATIC_SESSION_POLL_INTERVAL_MS, options.signal);
|
||||
detail = await queryAgentSession(initialDetail.session.id);
|
||||
}
|
||||
throw new Error("后端影视 Agent 会话超时");
|
||||
}
|
||||
|
||||
function abortableDelay(ms: number, signal?: AbortSignal) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
throwIfAborted(signal);
|
||||
const finish = () => {
|
||||
signal?.removeEventListener("abort", abort);
|
||||
resolve();
|
||||
};
|
||||
const abort = () => {
|
||||
window.clearTimeout(timer);
|
||||
reject(new DOMException("Aborted", "AbortError"));
|
||||
};
|
||||
const timer = window.setTimeout(finish, ms);
|
||||
signal?.addEventListener("abort", abort, { once: true });
|
||||
});
|
||||
}
|
||||
|
||||
function throwIfAborted(signal?: AbortSignal) {
|
||||
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
||||
}
|
||||
Reference in New Issue
Block a user