Initial commit: OmniAI Web Frontend

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 12:38:01 +08:00
commit bedee3ba8d
183 changed files with 94805 additions and 0 deletions
+95
View File
@@ -0,0 +1,95 @@
import { aiGenerationClient } from "./aiGenerationClient";
export interface TaskProgressEvent {
taskId: string;
status: string;
progress: number;
resultUrl?: string | null;
error?: string | null;
}
export interface WaitForTaskOptions {
onProgress?: (event: TaskProgressEvent) => void;
abortRef?: { current: boolean };
timeoutMs?: number;
}
const POLL_INTERVAL = 3000;
const DEFAULT_TIMEOUT = 30 * 60 * 1000;
export function waitForTask(
taskId: string,
options: WaitForTaskOptions = {},
): Promise<string | null> {
const { onProgress, abortRef, timeoutMs = DEFAULT_TIMEOUT } = options;
return new Promise((resolve, reject) => {
let settled = false;
let cleanup: (() => void) | null = null;
let timeoutId: ReturnType<typeof setTimeout> | null = null;
let sseConnected = false;
let fallbackTimerId: ReturnType<typeof setTimeout> | null = null;
const settle = (fn: () => void) => {
if (settled) return;
settled = true;
if (timeoutId) clearTimeout(timeoutId);
if (fallbackTimerId) clearTimeout(fallbackTimerId);
if (cleanup) cleanup();
fn();
};
timeoutId = setTimeout(
() => settle(() => reject(new Error("等待任务结果超时,请稍后在任务历史中查看"))),
timeoutMs,
);
const handleUpdate = (event: TaskProgressEvent) => {
if (settled) return;
if (abortRef?.current) {
settle(() => resolve(null));
return;
}
onProgress?.(event);
if (event.status === "completed") {
settle(() => resolve(event.resultUrl || null));
} else if (event.status === "failed" || event.status === "cancelled") {
settle(() => reject(new Error(event.error || "任务失败")));
}
};
// Try SSE first
cleanup = aiGenerationClient.subscribeTaskStatus(taskId, handleUpdate);
sseConnected = true;
// Fallback: if SSE doesn't deliver any event within 5s, switch to polling
fallbackTimerId = setTimeout(() => {
if (settled || !sseConnected) return;
if (cleanup) cleanup();
startPolling();
}, 5000);
function startPolling() {
const poll = async () => {
while (!settled) {
if (abortRef?.current) { settle(() => resolve(null)); return; }
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
if (settled || abortRef?.current) return;
try {
const task = await aiGenerationClient.getTaskStatus(taskId);
handleUpdate({
taskId,
status: task.status,
progress: task.progress || 0,
resultUrl: task.resultUrl,
error: task.error,
});
} catch (e) {
if (!settled) settle(() => reject(e));
}
}
};
poll();
}
});
}