diff --git a/lib/canvas/generate-image.ts b/lib/canvas/generate-image.ts index 81b2eb029..07c47506c 100644 --- a/lib/canvas/generate-image.ts +++ b/lib/canvas/generate-image.ts @@ -3128,6 +3128,22 @@ function getCanvasScaleAdjustFailoverPriority(item: CanvasModelWithProvider) { ); } +function isCanvasScaleAdjustEmergencyFalEditModel(item: CanvasModelWithProvider) { + if (item.provider.type !== "fal-image") return false; + const descriptor = JSON.stringify([ + item.model.modelId, + item.model.displayName, + item.model.capabilities, + item.model.defaultParams, + ]).toLowerCase(); + return ( + descriptor.includes("image-edit") || + descriptor.includes("image_urls") || + descriptor.includes("/edit") || + descriptor.includes(" edit") + ); +} + interface CanvasImageGenConfig { maxImages?: number; supports1K?: boolean; @@ -3349,16 +3365,28 @@ function findCanvasFailoverModel(params: { .map((item) => String(item || "").trim()) .filter(Boolean); - const isUsableFallback = (item: CanvasModelWithProvider) => { + const isUsableFallback = ( + item: CanvasModelWithProvider, + options: { allowEmergencyFalEdit?: boolean } = {} + ) => { const isSameFamilyScaleAdjustFallback = strictScaleAdjustModelFamily ? getCanvasScaleAdjustModelFamily(item) === strictScaleAdjustModelFamily : false; + const isEmergencyFalEditFallback = + options.allowEmergencyFalEdit === true && + params.preferLowCostSameNameProviders && + isCanvasScaleAdjustEmergencyFalEditModel(item); const isSameNameScaleAdjustFallback = params.preferLowCostSameNameProviders && (isSameFamilyScaleAdjustFallback || + isEmergencyFalEditFallback || scoreModelNameSimilarity(params.selectedModel, item) >= CANVAS_FAILOVER_SAME_NAME_THRESHOLD); - if (strictScaleAdjustModelFamily && !isSameFamilyScaleAdjustFallback) { + if ( + strictScaleAdjustModelFamily && + !isSameFamilyScaleAdjustFallback && + !isEmergencyFalEditFallback + ) { return false; } if ( @@ -3403,7 +3431,18 @@ function findCanvasFailoverModel(params: { } } - const usableModels = params.imageModels.filter(isUsableFallback); + let usingEmergencyFalEditFallback = false; + let usableModels = params.imageModels.filter((item) => isUsableFallback(item)); + if ( + usableModels.length === 0 && + strictScaleAdjustModelFamily && + params.preferLowCostSameNameProviders + ) { + usingEmergencyFalEditFallback = true; + usableModels = params.imageModels.filter((item) => + isUsableFallback(item, { allowEmergencyFalEdit: true }) + ); + } const scoredModels = usableModels .map((item, index) => ({ item, @@ -3412,6 +3451,12 @@ function findCanvasFailoverModel(params: { sameProviderType: item.provider.type === params.selectedModel.provider.type, })) .sort((left, right) => { + if (usingEmergencyFalEditFallback) { + const leftPriority = getCanvasScaleAdjustFailoverPriority(left.item); + const rightPriority = getCanvasScaleAdjustFailoverPriority(right.item); + if (leftPriority !== rightPriority) return leftPriority - rightPriority; + return left.index - right.index; + } if (strictScaleAdjustModelFamily) { const leftPriority = getCanvasScaleAdjustFailoverPriority(left.item); const rightPriority = getCanvasScaleAdjustFailoverPriority(right.item);