From e601f8079fe434ed4016addb29ebf04b1c24bb66 Mon Sep 17 00:00:00 2001 From: 134 Date: Wed, 19 Aug 2026 13:45:10 +0800 Subject: [PATCH] fix(world): harden world task create external-marble fallback --- app/api/canvas/world/tasks/route.ts | 85 +++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/app/api/canvas/world/tasks/route.ts b/app/api/canvas/world/tasks/route.ts index 3ddcd7fe5..1bb4e05df 100644 --- a/app/api/canvas/world/tasks/route.ts +++ b/app/api/canvas/world/tasks/route.ts @@ -200,6 +200,22 @@ function buildExternalWorldTaskPayload( return payload; } +function buildOfficialWorldTaskPayload( + displayName: string, + worldPrompt: Record, + officialMarbleModel: string, + body: CreateWorldTaskRequest +) { + return { + display_name: displayName, + model: officialMarbleModel, + permission: { + public: body.makePublic === true, + }, + world_prompt: worldPrompt, + }; +} + function buildWorldPrompt(body: CreateWorldTaskRequest, mode: WorldTaskMode) { const textPrompt = readOptionalTrimmedText(body.prompt); @@ -404,6 +420,7 @@ export async function POST(request: Request) { try { const useExternalTaskApi = shouldUseExternalTaskCreateApi(candidate, mode); assertWorldModeSupportedByCandidate(candidate, mode); + let requestUsedExternalTaskApi = false; if (useExternalTaskApi) { const payload = buildExternalWorldTaskPayload( mode, @@ -411,32 +428,64 @@ export async function POST(request: Request) { readOptionalTrimmedText(body.prompt), displayName, ); - const externalResult = await worldLabsRequest< - WorldLabsExternalTaskResponse - >(candidate, { - method: "POST", - path: "/api/external/task/generate", - timeoutMs: resolveWorldLabsCreateTimeoutMs(), - json: payload, - }); - result = normalizeExternalTaskCreateOperation(externalResult); + try { + const externalResult = await worldLabsRequest< + WorldLabsExternalTaskResponse + >(candidate, { + method: "POST", + path: "/api/external/task/generate", + timeoutMs: resolveWorldLabsCreateTimeoutMs(), + json: payload, + }); + result = normalizeExternalTaskCreateOperation(externalResult); + requestUsedExternalTaskApi = true; + } catch (error) { + const externalError = error instanceof Error ? error.message : String(error); + logger.warn("external world create failed, fallback to marble endpoint", { + providerId: candidate.providerId || null, + error: externalError, + }); + if (isCredentialOrBillingProviderError(error)) { + await reportWorldLabsProviderRuntimeFailure( + candidate.providerId, + externalError + ); + } + + result = await worldLabsRequest(candidate, { + method: "POST", + path: "/marble/v1/worlds:generate", + timeoutMs: resolveWorldLabsCreateTimeoutMs(), + json: buildOfficialWorldTaskPayload( + displayName, + worldPrompt, + officialMarbleModel, + body + ), + }); + requestUsedExternalTaskApi = false; + } } else { + requestUsedExternalTaskApi = false; result = await worldLabsRequest(candidate, { method: "POST", path: "/marble/v1/worlds:generate", timeoutMs: resolveWorldLabsCreateTimeoutMs(), - json: { - display_name: displayName, - model: officialMarbleModel, - permission: { - public: body.makePublic === true, - }, - world_prompt: worldPrompt, - }, + json: buildOfficialWorldTaskPayload( + displayName, + worldPrompt, + officialMarbleModel, + body + ), }); } + + if (!result?.operation_id) { + throw new Error("创建 World Labs 世界任务返回缺少 operation_id"); + } + usedProvider = candidate; - usedExternalTaskApi = useExternalTaskApi; + usedExternalTaskApi = requestUsedExternalTaskApi; providerForLog = candidate.providerId || null; break; } catch (error) {