fix: avoid failed providers during canvas image failover

This commit is contained in:
134 2026-07-04 19:33:18 +08:00
parent 2ae5ceb234
commit d724a542bb

View File

@ -27,6 +27,7 @@ import {
recordCanvasImageModelExecutionDuration, recordCanvasImageModelExecutionDuration,
} from "./image-concurrency"; } from "./image-concurrency";
import { import {
classifyAiModelHealthFailure,
recordAiModelHealthFailure, recordAiModelHealthFailure,
recordAiModelHealthSuccess, recordAiModelHealthSuccess,
shouldFailoverAiModelError, shouldFailoverAiModelError,
@ -126,6 +127,7 @@ export interface CanvasGenerateImageRequest {
internalRetryVariantIndex?: number; internalRetryVariantIndex?: number;
allowFailover?: boolean; allowFailover?: boolean;
internalFailoverTriedModelRefs?: string[]; internalFailoverTriedModelRefs?: string[];
internalFailoverTriedProviderTypes?: string[];
onQueueStatusChange?: ( onQueueStatusChange?: (
status: CanvasImageModelQueueStatus & { status: CanvasImageModelQueueStatus & {
modelName?: string | null; modelName?: string | null;
@ -3320,6 +3322,7 @@ function findCanvasFailoverModel(params: {
selectedModel: CanvasModelWithProvider; selectedModel: CanvasModelWithProvider;
imageModels: CanvasModelWithProvider[]; imageModels: CanvasModelWithProvider[];
failedModelRefs: string[]; failedModelRefs: string[];
failedProviderTypes?: string[];
preferLowCostSameNameProviders?: boolean; preferLowCostSameNameProviders?: boolean;
requiresImageInput?: boolean; requiresImageInput?: boolean;
requiresTextToImage?: boolean; requiresTextToImage?: boolean;
@ -3328,6 +3331,11 @@ function findCanvasFailoverModel(params: {
const failedRefs = new Set( const failedRefs = new Set(
params.failedModelRefs.map((item) => String(item || "").trim()).filter(Boolean) params.failedModelRefs.map((item) => String(item || "").trim()).filter(Boolean)
); );
const failedProviderTypes = new Set(
(params.failedProviderTypes || [])
.map((item) => String(item || "").trim())
.filter(Boolean)
);
const strictScaleAdjustModelFamily = const strictScaleAdjustModelFamily =
params.preferLowCostSameNameProviders && params.preferLowCostSameNameProviders &&
typeof params.scaleAdjustModelFamily === "string" && typeof params.scaleAdjustModelFamily === "string" &&
@ -3360,6 +3368,7 @@ function findCanvasFailoverModel(params: {
) { ) {
return false; return false;
} }
if (failedProviderTypes.has(item.provider.type)) return false;
if (!item.model.supportsImageGen) return false; if (!item.model.supportsImageGen) return false;
if ( if (
params.requiresImageInput && params.requiresImageInput &&
@ -4210,11 +4219,25 @@ export async function generateCanvasImage(
(!scaleAdjustRejected && (!scaleAdjustRejected &&
Boolean(debugMeta?.detailFixedModelApplied) && Boolean(debugMeta?.detailFixedModelApplied) &&
Boolean(caughtError)); Boolean(caughtError));
const failureClassification = classifyAiModelHealthFailure(caughtError);
const failedProviderTypes = Array.from(
new Set([
...(Array.isArray(request.internalFailoverTriedProviderTypes)
? request.internalFailoverTriedProviderTypes
: []),
...(shouldFailover &&
failureClassification.reason !== "non_provider_failure" &&
failureClassification.reason !== "user_cancelled"
? [provider.type]
: []),
].filter(Boolean))
);
const fallbackModel = allowFailover && shouldFailover const fallbackModel = allowFailover && shouldFailover
? findCanvasFailoverModel({ ? findCanvasFailoverModel({
selectedModel, selectedModel,
imageModels, imageModels,
failedModelRefs, failedModelRefs,
failedProviderTypes,
preferLowCostSameNameProviders: Boolean( preferLowCostSameNameProviders: Boolean(
debugMeta?.scaleAdjustPreferSameNameProviderFailover debugMeta?.scaleAdjustPreferSameNameProviderFailover
), ),
@ -4240,6 +4263,7 @@ export async function generateCanvasImage(
...request, ...request,
model: fallbackModel.model.id, model: fallbackModel.model.id,
internalFailoverTriedModelRefs: failedModelRefs, internalFailoverTriedModelRefs: failedModelRefs,
internalFailoverTriedProviderTypes: failedProviderTypes,
debugMeta: { debugMeta: {
...debugMeta, ...debugMeta,
failoverFromModelId: selectedModel.model.modelId, failoverFromModelId: selectedModel.model.modelId,