ai-chatbot/lib/video-canvas/task-recovery.ts

43 lines
1.5 KiB
TypeScript

// Resume only server-recorded enhancement jobs. A lost request without an upstream ID is never resubmitted.
export type RecoveryTask = {
status: string;
createdAt: string;
updatedAt?: string;
attempts?: number;
request: { kind: string; params: Record<string, unknown> };
result?: Record<string, any> | null;
};
export function getEnhanceCheckpoint(task: Pick<RecoveryTask, "request" | "result">) {
const upstream = task.result?.upstreamTask;
if (
task.request.kind !== "image-operation" ||
task.request.params.operation !== "enhance" ||
upstream?.operation !== "enhance" ||
typeof upstream.taskId !== "string" ||
!upstream.taskId.trim() ||
upstream.sourceImageUrl !== task.request.params.imageUrl
)
return null;
return {
operation: "enhance" as const,
taskId: upstream.taskId,
sourceImageUrl: String(upstream.sourceImageUrl),
};
}
export function hasRecoverableCanvasTask(task: RecoveryTask, now = Date.now()) {
const created = Date.parse(task.createdAt);
return (
Boolean(getEnhanceCheckpoint(task)) &&
Number.isFinite(created) &&
now >= created &&
now - created <= 86400000 &&
(task.attempts || 0) < 20
);
}
export function canResumeCanvasTask(task: RecoveryTask, now = Date.now()) {
if (task.status !== "interrupted" || !hasRecoverableCanvasTask(task, now)) return false;
const updated = Date.parse(task.updatedAt || "");
const delay = Math.min(300000, 30000 * 2 ** Math.min(4, Math.max(0, (task.attempts || 1) - 1)));
return Number.isFinite(updated) && now - updated >= delay;
}