import { inferKreaModelKind } from "@/lib/ai/krea-models"; const STATIC_VIDEO_PROVIDER_TYPES = new Set([ "bltcy-video", "doubao-video", "volcano-video", "qianwen-video", "fal-video", "ruxa-video", ]); const STATIC_IMAGE_PROVIDER_TYPES = new Set([ "gemini", "huanke-gemini", "alibaba-image", "bfl-image", "ruxa-image", "fal-image", "snapgen-image", "grsai-gpt-image", "openai-image", "bananaimage", "visionary", "stepfun-image", "midjourney", "volcano-vision", ]); const STATIC_3D_PROVIDER_TYPES = new Set([ "tripo-3d", "worldlabs-3d", "kittycad-cad", ]); const RUNWAY_IMAGE_MODEL_IDS = new Set([ "gen4_image", "gen4_image_turbo", "gemini_2.5_flash", ]); const RUNWAY_VIDEO_MODEL_PREFIXES = [ "gen4.5", "gen4_turbo", "gen4_aleph", "gen3a_turbo", "veo3.1_fast", "veo3.1", "veo3", "act_two", "upscale_v1", ]; const VIDEO_MODE_KEYS = new Set([ "text-to-video", "image-to-video", "video-to-video", "audio-to-video", "first-frame", "first-last-frame", ]); const VIDEO_KIND_KEYS = new Set([ "video", "text-to-video", "image-to-video", "video-to-video", "audio-to-video", ]); const THREE_D_MODE_KEYS = new Set([ "3d", "text-to-3d", "text_to_3d", "image-to-3d", "image_to_3d", "mesh-generation", "mesh_generation", "cad", "world-generation", "world_generation", ]); const THREE_D_KIND_KEYS = new Set([ "3d", "model-3d", "model_3d", "mesh", "mesh-generation", "mesh_generation", "cad", "world", "world-generation", "world_generation", "3d-world", "3d_world", ]); const TEXT_TO_IMAGE_MODE_KEYS = new Set([ "text-to-image", "text2image", "txt2img", "image-generation", "image_generate", "generate-image", ]); const IMAGE_EDIT_ONLY_KIND_KEYS = new Set([ "image-edit", "image-to-image", "img2img", "reference-image", "reference-images", ]); const TEXT_TO_IMAGE_KIND_KEYS = new Set([ "text-to-image", "text2image", "txt2img", "image", "image-generation", "image_generate", ]); function normalizeText(value?: string | null) { return String(value || "").trim().toLowerCase(); } export function isAlibabaImageTextToImageModelId(modelId?: string | null) { const normalized = normalizeText(modelId); return /^qwen-image-3(?:\.0)?(?:$|[-/])/.test(normalized); } function toRecord(value: unknown) { if (!value || typeof value !== "object" || Array.isArray(value)) { return null; } return value as Record; } function getStringArrayField(source: unknown, key: string) { const record = toRecord(source); const value = record?.[key]; if (!Array.isArray(value)) return [] as string[]; return value.map((item) => normalizeText(String(item || ""))).filter(Boolean); } function getStringField(source: unknown, key: string) { const record = toRecord(source); return normalizeText(String(record?.[key] || "")); } function hasVideoModes(capabilities: unknown) { if (!capabilities || typeof capabilities !== "object") return false; const supportedModes = (capabilities as { supportedModes?: unknown }).supportedModes; if (!Array.isArray(supportedModes)) return false; return supportedModes.some((mode) => VIDEO_MODE_KEYS.has(String(mode || "").trim())); } function hasVideoKind(capabilities: unknown) { return ["kind", "mediaType", "modelType", "outputType"].some((key) => VIDEO_KIND_KEYS.has(getStringField(capabilities, key)) ); } function has3DModes(capabilities: unknown) { const supportedModes = getStringArrayField(capabilities, "supportedModes"); return supportedModes.some((mode) => THREE_D_MODE_KEYS.has(mode)); } function has3DKind(capabilities: unknown) { return ["kind", "mediaType", "modelType", "outputType"].some((key) => THREE_D_KIND_KEYS.has(getStringField(capabilities, key)) ); } function modelIdLooks3D(modelId?: string | null) { const normalized = normalizeText(modelId); if (!normalized) return false; return ( /(?:^|[/_.-])(?:text|image)[_-]to[_-]3d(?:$|[/_.-])/.test(normalized) || /(?:^|[/_.-])(?:hunyuan3d|seed3d|tripo-h3(?:\.\d+)?)(?:$|[/_.-])/.test( normalized ) ); } export function is3DModelDescriptor(params: { providerType?: string | null; modelId?: string | null; capabilities?: unknown; }): boolean { const providerType = normalizeText(params.providerType); return ( STATIC_3D_PROVIDER_TYPES.has(providerType) || has3DModes(params.capabilities) || has3DKind(params.capabilities) || modelIdLooks3D(params.modelId) ); } export function inferRunwayModelKind(modelId?: string | null): "image" | "video" | null { const normalized = normalizeText(modelId); if (!normalized) return null; if (RUNWAY_IMAGE_MODEL_IDS.has(normalized)) return "image"; if (RUNWAY_VIDEO_MODEL_PREFIXES.some((prefix) => normalized === prefix || normalized.startsWith(`${prefix}_`))) { return "video"; } return null; } export function isStaticVideoProviderType(providerType?: string | null): boolean { return STATIC_VIDEO_PROVIDER_TYPES.has(normalizeText(providerType)); } export function isVideoModelDescriptor(params: { providerType?: string | null; modelId?: string | null; capabilities?: unknown; supportsImageGen?: boolean | null; }): boolean { const providerType = normalizeText(params.providerType); if (is3DModelDescriptor(params)) { return false; } if (isStaticVideoProviderType(providerType)) { return true; } if (hasVideoModes(params.capabilities) || hasVideoKind(params.capabilities)) { return true; } if (providerType === "runway") { return inferRunwayModelKind(params.modelId) === "video"; } if (providerType === "krea") { return inferKreaModelKind(params.modelId) === "video"; } return false; } export function isImageGenerationModelDescriptor(params: { providerType?: string | null; modelId?: string | null; capabilities?: unknown; supportsImageGen?: boolean | null; }): boolean { const providerType = normalizeText(params.providerType); if (is3DModelDescriptor(params)) { return false; } if (providerType === "runway") { const runwayModelKind = inferRunwayModelKind(params.modelId); if (runwayModelKind === "image") return true; if (runwayModelKind === "video") return false; } if (providerType === "krea") { const kreaModelKind = inferKreaModelKind(params.modelId); if (kreaModelKind === "image" || kreaModelKind === "image-enhance") return true; if (kreaModelKind === "video") return false; } if (isVideoModelDescriptor(params)) { return false; } if (params.supportsImageGen === true) { return true; } return STATIC_IMAGE_PROVIDER_TYPES.has(providerType); } export function modelSupportsTextToImageDescriptor(params: { providerType?: string | null; modelId?: string | null; capabilities?: unknown; defaultParams?: unknown; supportsImageGen?: boolean | null; }): boolean { if ( !isImageGenerationModelDescriptor({ providerType: params.providerType, modelId: params.modelId, capabilities: params.capabilities, supportsImageGen: params.supportsImageGen, }) ) { return false; } const providerType = normalizeText(params.providerType); const modelId = normalizeText(params.modelId); const capabilityModes = getStringArrayField(params.capabilities, "supportedModes"); const defaultModes = getStringArrayField(params.defaultParams, "supportedModes"); const modes = new Set([...capabilityModes, ...defaultModes]); if ([...modes].some((mode) => TEXT_TO_IMAGE_MODE_KEYS.has(mode))) { return true; } const kind = getStringField(params.capabilities, "kind") || getStringField(params.defaultParams, "kind"); if (TEXT_TO_IMAGE_KIND_KEYS.has(kind)) { return true; } if (providerType === "stepfun-image") { return true; } const runtimeEndpoint = getStringField(params.capabilities, "endpoint") || getStringField(params.defaultParams, "endpoint"); const runtimeModelText = `${modelId} ${runtimeEndpoint}`.trim(); if ( IMAGE_EDIT_ONLY_KIND_KEYS.has(kind) || [...modes].some((mode) => IMAGE_EDIT_ONLY_KIND_KEYS.has(mode)) || runtimeModelText.endsWith("/edit") || runtimeModelText.includes("image-edit") ) { return false; } if (providerType === "alibaba-image") { return isAlibabaImageTextToImageModelId(modelId); } return true; }