diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index 27964ad..23f8a49 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -1588,6 +1588,85 @@ export async function POST(request: Request) { console.error(`[Chat API] ❌ Alibaba fallback model ${fallbackModelId} failed:`, fallbackError); } } + + if (!fallbackSuccess) { + const providerPriority: Record = { + "volcano-vision": 0, + gemini: 1, + "huanke-gemini": 2, + "alibaba-image": 3, + "ruxa-image": 4, + }; + const crossProviderCandidates = allModels + .filter((m) => { + if (!m.model.supportsImageGen || !m.model.isEnabled || m.model.isSubModel) { + return false; + } + if (m.model.id === finalModelId) return false; + if (m.provider.id === alibabaModelConfig?.providerId) return false; + return true; + }) + .sort((a, b) => { + const left = providerPriority[a.provider.type as string] ?? 99; + const right = providerPriority[b.provider.type as string] ?? 99; + if (left !== right) return left - right; + return (a.model.sortOrder ?? 0) - (b.model.sortOrder ?? 0); + }); + + for (const candidate of crossProviderCandidates) { + try { + console.log( + `[Chat API] 🔁 Cross-provider failover attempt: ${candidate.model.displayName} (${candidate.provider.type})` + ); + + const candidateLanguageModel = await getLanguageModelById(candidate.model.id); + if (!candidateLanguageModel || typeof (candidateLanguageModel as any).doGenerate !== "function") { + console.log(`[Chat API] Skip candidate without doGenerate: ${candidate.model.id}`); + continue; + } + + const convertProvider = + candidate.provider.type === "ruxa-image" + ? "ruxa-image" + : candidate.provider.type === "gemini" + ? "gemini" + : candidate.provider.type === "huanke-gemini" + ? "huanke-gemini" + : candidate.provider.type === "volcano-vision" + ? "volcano" + : "alibaba-image"; + + const candidateMessages = await convertMessagesWithImages( + messagesForAlibaba, + convertProvider + ); + + const candidateResult = await (candidateLanguageModel as any).doGenerate({ + messages: candidateMessages, + imageParams: requestBody.imageParams || { aspectRatio: "auto", imageSize: "2K", imageCount: 1 }, + modelDisplayName: candidate.model.displayName, + overrideModelId: candidate.model.modelId, + }); + + if (candidateResult?.finishReason === "error") { + throw new Error(candidateResult.text || "图片生成失败"); + } + + imageMarkdown = candidateResult.text || "图片生成失败"; + usedModelId = candidate.model.modelId; + fallbackSuccess = true; + console.log( + `[Chat API] ✅ Cross-provider failover succeeded: ${candidate.model.displayName} (${candidate.provider.type})` + ); + break; + } catch (crossError) { + console.error( + `[Chat API] ❌ Cross-provider failover failed: ${candidate.model.displayName} (${candidate.provider.type})`, + crossError + ); + } + } + } if (!fallbackSuccess) { throw primaryError;