diff --git a/lib/ai/providers/ruxa-image.ts b/lib/ai/providers/ruxa-image.ts index d448b10..0214eda 100644 --- a/lib/ai/providers/ruxa-image.ts +++ b/lib/ai/providers/ruxa-image.ts @@ -154,6 +154,21 @@ function collectImageUrls(payload: any): string[] { if (typeof value === "string") { if (isLikelyImageUrl(value) || (isImageLikeKey(parentKey) && /^https?:\/\//i.test(value))) { urls.add(value); + return; + } + + const trimmed = value.trim(); + if ( + trimmed.length > 1 && + trimmed.length < 200000 && + (trimmed.startsWith("{") || trimmed.startsWith("[")) + ) { + try { + const parsed = JSON.parse(trimmed); + walk(parsed, parentKey); + } catch { + // ignore non-JSON strings + } } return; } @@ -244,41 +259,44 @@ async function waitForTaskResult(baseURL: string, apiKey: string, taskId: string (id: string) => joinApiPath(baseURL, `/api/v1/tasks/${id}`), (id: string) => `${joinApiPath(baseURL, "/api/v1/tasks/query")}?taskId=${encodeURIComponent(id)}`, ]; + let endpointIndex = 0; for (let attempt = 1; attempt <= POLL_MAX_ATTEMPTS; attempt++) { - for (const endpointBuilder of endpointCandidates) { - try { - const endpoint = endpointBuilder(taskId); - const response = await fetch(endpoint, { - headers: { - Authorization: `Bearer ${apiKey}`, - "Content-Type": "application/json", - }, - }); + try { + const endpoint = endpointCandidates[endpointIndex]!(taskId); + const response = await fetch(endpoint, { + headers: { + Authorization: `Bearer ${apiKey}`, + "Content-Type": "application/json", + }, + }); - if (!response.ok) { - continue; + if (!response.ok) { + if (response.status === 404 && endpointIndex < endpointCandidates.length - 1) { + endpointIndex++; } + await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)); + continue; + } - const payload = await response.json(); - const imageUrls = filterSourceImages(collectImageUrls(payload), sourceUrls); - const status = extractStatus(payload); + const payload = await response.json(); + const imageUrls = filterSourceImages(collectImageUrls(payload), sourceUrls); + const status = extractStatus(payload); - if (imageUrls.length > 0) { - return imageUrls; - } + if (imageUrls.length > 0) { + return imageUrls; + } - if (status && ["failed", "error", "cancelled", "canceled"].includes(status)) { - throw new Error(extractErrorMessage(payload)); - } + if (status && ["failed", "error", "cancelled", "canceled"].includes(status)) { + throw new Error(extractErrorMessage(payload)); + } - if (status && ["success", "succeeded", "completed", "done", "finished"].includes(status)) { - throw new Error("任务已完成但未返回图片地址"); - } - } catch (error) { - if (error instanceof Error && error.message !== "任务已完成但未返回图片地址") { - throw error; - } + if (status && ["success", "succeeded", "completed", "done", "finished"].includes(status)) { + throw new Error("任务已完成但未返回图片地址"); + } + } catch (error) { + if (error instanceof Error && error.message !== "任务已完成但未返回图片地址") { + throw error; } }