diff --git a/web/src/lib/canvas/canvas-project-generation.ts b/web/src/lib/canvas/canvas-project-generation.ts index 6d545f728..627b770b6 100644 --- a/web/src/lib/canvas/canvas-project-generation.ts +++ b/web/src/lib/canvas/canvas-project-generation.ts @@ -373,7 +373,10 @@ export function buildGenerationConfig(config: AiConfig, node: CanvasNodeData | u const storedModel = resolveCanvasGenerationModel(config, node?.metadata?.model, mode); const preferredModel = storedModel || resolveCanvasGenerationModel(config, defaultModel, mode) || fallbackModel; const imageSize = mode === "image" ? node?.metadata?.size || config.size || defaultConfig.size : undefined; - const model = resolveCompatibleModel(config, preferredModel, imageSize ? { ...requirements, imageSize } : requirements) || preferredModel; + // 无 requirements 的调用(重试、媒体工具等)也按当前能力与尺寸路由到组内最低价兼容模型, + // 避免旧 metadata.model 不支持当前尺寸导致生成时被 normalize 回退。 + const baseRequirements = requirements?.capability ? requirements : { capability: mode }; + const model = resolveCompatibleModel(config, preferredModel, imageSize ? { ...baseRequirements, imageSize } : baseRequirements) || preferredModel; const imageProfile = mode === "image" ? modelCapabilityConfigFor(config, model).image! : undefined; const normalizedImage = imageProfile ? normalizeImageValue(imageProfile, { diff --git a/web/src/lib/model-selection.ts b/web/src/lib/model-selection.ts index 2afc949a7..7621a0f7d 100644 --- a/web/src/lib/model-selection.ts +++ b/web/src/lib/model-selection.ts @@ -50,30 +50,35 @@ export function configuredModelDisplayName(config: AiConfig, value: string) { export function modelCompatibilityError(config: AiConfig, model: string, requirements?: ModelRequirements) { const capability = requirements?.capability; + if (!capability) return ""; const input = requirements?.input; - if (!capability || !input) return ""; - const visualInputCount = input.imageCount + input.characterCount; + const visualInputCount = input ? input.imageCount + input.characterCount : 0; if (capability === "image") { + const image = modelCapabilityConfigFor(config, model).image!; + // 尺寸兼容不依赖输入摘要:无输入时(如画布重试/工具链)也要按尺寸过滤组内模型。 + if (requirements.imageSize && !image.size.allowCustom && !image.size.values.includes(requirements.imageSize)) return "不支持当前尺寸"; + if (!input) return ""; if (input.videoCount > 0) return "图片模型不支持参考视频"; if (input.audioCount > 0) return "图片模型不支持参考音频"; - const image = modelCapabilityConfigFor(config, model).image!; if (visualInputCount > image.references.maxImages) return `最多支持 ${image.references.maxImages} 张参考图`; - if (requirements.imageSize && !image.size.allowCustom && !image.size.values.includes(requirements.imageSize)) return "不支持当前尺寸"; return ""; } if (capability === "video") { const profile = modelCapabilityConfigFor(config, model).video!; + if (requirements.videoSeconds && !videoDurationAllowed(profile, Number(requirements.videoSeconds))) return "不支持当前视频时长"; + if (!input) return ""; if (visualInputCount > profile.references.maxImages) return `最多支持 ${profile.references.maxImages} 张参考图`; if (input.videoCount > profile.references.maxVideos) return `最多支持 ${profile.references.maxVideos} 个参考视频`; if (input.audioCount > profile.references.maxAudios) return `最多支持 ${profile.references.maxAudios} 个参考音频`; - if (requirements.videoSeconds && !videoDurationAllowed(profile, Number(requirements.videoSeconds))) return "不支持当前视频时长"; const operation = resolveVideoOperation(input, requirements.videoOperation); if (operation !== "concat" && !profile.operations.includes(operation)) return `不支持${videoOperationLabel(operation)}`; return ""; } + if (!input) return ""; + if (capability === "text") { return input.audioCount > 0 ? "文本模型不支持参考音频" : ""; }