"use strict"; const DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com"; const KUAIKUAI_IMAGE_BASE_URL = process.env.KUAIKUAI_IMAGE_BASE_URL || "https://ai-api.kkidc.com"; const KUAIKUAI_GEMINI_ENDPOINT = "/v1beta/models/{model}:generateContent"; const KUAIKUAI_OPENAI_ENDPOINT = "/v1/chat/completions"; const GRSAI_IMAGE_BASE_URL = "https://grsai.dakka.com.cn"; const GRSAI_TEXT_BASE_URL = "https://grsai.dakka.com.cn"; const RIGHTCODE_IMAGE_BASE_URL = process.env.RIGHTCODE_IMAGE_BASE_URL || process.env.RIGHTCODE_BASE_URL || "https://www.right.codes/draw"; const SEEDANCE_BASE_URL = "https://ai-api.kkidc.com"; const SEEDANCE_ARK_BASE_URL = "https://ark.cn-beijing.volces.com"; const KLING_BASE_URL = "https://api-beijing.klingai.com"; const DASHSCOPE_IMAGE_ENDPOINT = "/api/v1/services/aigc/multimodal-generation/generation"; const DASHSCOPE_VIDEO_ENDPOINT = "/api/v1/services/aigc/video-generation/video-synthesis"; const DASHSCOPE_S2V_ENDPOINT = "/api/v1/services/aigc/image2video/video-synthesis"; const DASHSCOPE_S2V_DETECT_ENDPOINT = "/api/v1/services/aigc/image2video/face-detect"; const DASHSCOPE_ANIMATE_ENDPOINT = "/api/v1/services/aigc/image2video/video-synthesis"; const GRSAI_IMAGE_ENDPOINT = "/v1/api/generate"; const GRSAI_RESULT_ENDPOINT = "/v1/api/result"; const RIGHTCODE_IMAGE_ENDPOINT = process.env.RIGHTCODE_IMAGE_ENDPOINT || "/v1/images/generations"; const IMAGE_MODELS = { "nano-banana-pro": { provider: "grsai", transport: "grsai-image", baseUrl: GRSAI_IMAGE_BASE_URL, endpoint: GRSAI_IMAGE_ENDPOINT, resultEndpoint: GRSAI_RESULT_ENDPOINT }, "nano-banana-2": { provider: "grsai", transport: "grsai-image", baseUrl: GRSAI_IMAGE_BASE_URL, endpoint: GRSAI_IMAGE_ENDPOINT, resultEndpoint: GRSAI_RESULT_ENDPOINT }, "nano-banana-fast": { provider: "grsai", transport: "grsai-image", baseUrl: GRSAI_IMAGE_BASE_URL, endpoint: GRSAI_IMAGE_ENDPOINT, resultEndpoint: GRSAI_RESULT_ENDPOINT }, "gpt-image-2": { provider: "grsai", transport: "grsai-image", baseUrl: GRSAI_IMAGE_BASE_URL, endpoint: GRSAI_IMAGE_ENDPOINT, resultEndpoint: GRSAI_RESULT_ENDPOINT }, "gpt-image-2-vip": { provider: "grsai", transport: "grsai-image", baseUrl: GRSAI_IMAGE_BASE_URL, endpoint: GRSAI_IMAGE_ENDPOINT, resultEndpoint: GRSAI_RESULT_ENDPOINT }, "wan2.7-image": { provider: "dashscope-wan2.7", transport: "dashscope-image", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_IMAGE_ENDPOINT }, "wan2.7-image-pro": { provider: "dashscope-wan2.7-pro", transport: "dashscope-image", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_IMAGE_ENDPOINT }, }; const RIGHTCODE_IMAGE_MODEL_MAP = { "gpt-image-2": "gpt-image-2", "gpt-image-2-vip": "gpt-image-2-vip", "nano-banana-fast": "nano-banana", "nano-banana-2": "nano-banana-2", "nano-banana-pro": "nano-banana-pro", }; const KUAIKUAI_IMAGE_MODEL_MAP = { "nano-banana-fast": "gemini-2.5-flash-image", "nano-banana-2": "gemini-3.1-flash-image-preview", "nano-banana-pro": "gemini-3-pro-image-preview", "gpt-image-2": "gpt-image-2", "gpt-image-2-vip": "gpt-image-2-vip", }; const VIDEO_MODELS = { "seedance-2": { provider: "seedance-2.0", protocol: "seed-video", baseUrl: SEEDANCE_BASE_URL, endpoint: "/v1/video/generations", model: "seed-2" }, "seedance-2-fast": { provider: "seedance-2.0-fast", protocol: "seed-video", baseUrl: SEEDANCE_BASE_URL, endpoint: "/v1/video/generations", model: "seed-2-fast" }, "seedance-2-official": { provider: "seedance-2.0-ark", protocol: "seed-video-ark", baseUrl: SEEDANCE_ARK_BASE_URL, endpoint: "/api/v3/contents/generations/tasks", model: "doubao-seedance-2-0-260128" }, "seedance-2-fast-official": { provider: "seedance-2.0-fast-ark", protocol: "seed-video-ark", baseUrl: SEEDANCE_ARK_BASE_URL, endpoint: "/api/v3/contents/generations/tasks", model: "doubao-seedance-2-0-fast-260128" }, "happyhorse-1.0-t2v": { provider: "dashscope-happyhorse-t2v", protocol: "happyhorse-t2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "happyhorse-1.0-t2v" }, "happyhorse-1.0-i2v": { provider: "dashscope-happyhorse-i2v", protocol: "happyhorse-i2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "happyhorse-1.0-i2v" }, "happyhorse-1.0-r2v": { provider: "dashscope-happyhorse-r2v", protocol: "happyhorse-r2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "happyhorse-1.0-r2v" }, "wan2.7-i2v": { provider: "dashscope-wan-i2v", protocol: "wan-i2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "wan2.7-i2v" }, "wan2.7-t2v": { provider: "dashscope-wan-t2v", protocol: "wan-t2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "wan2.7-t2v" }, "wan2.2-s2v": { provider: "dashscope-wan-s2v", protocol: "wan-s2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_S2V_ENDPOINT, detectEndpoint: DASHSCOPE_S2V_DETECT_ENDPOINT, model: "wan2.2-s2v", detectModel: "wan2.2-s2v-detect" }, "wan2.2-animate-mix": { provider: "dashscope-wan-animate", protocol: "wan-animate-mix", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_ANIMATE_ENDPOINT, model: "wan2.2-animate-mix" }, "Kling-V3-Omni": { provider: "kling-official", protocol: "kling-omni", baseUrl: KLING_BASE_URL, endpoint: "/v1/videos/omni-video", model: "Kling-V3-Omni" }, "kling-2-1": { provider: "kling-official", protocol: "kling-omni", baseUrl: KLING_BASE_URL, endpoint: "/v1/videos/omni-video", model: "Kling-V3-Omni" }, "kling-3.0-dashscope": { provider: "dashscope-kling", protocol: "kling-dashscope", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "kling/kling-v3-omni-video-generation" }, "Kling-V3-Omni-dashscope": { provider: "dashscope-kling", protocol: "kling-dashscope", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "kling/kling-v3-omni-video-generation" }, "kling-v3-omni-dashscope": { provider: "dashscope-kling", protocol: "kling-dashscope", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "kling/kling-v3-omni-video-generation" }, "kling/kling-v3-omni-video-generation": { provider: "dashscope-kling", protocol: "kling-dashscope", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "kling/kling-v3-omni-video-generation" }, "vidu-q3-turbo-t2v": { provider: "dashscope-vidu-t2v", protocol: "vidu-t2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "vidu/viduq3-turbo_text2video" }, "vidu-q3-turbo-i2v": { provider: "dashscope-vidu-i2v", protocol: "vidu-i2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "vidu/viduq3-turbo_img2video" }, "pixverse-c1-t2v": { provider: "dashscope-pixverse-t2v", protocol: "pixverse-t2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "pixverse/pixverse-c1-t2v" }, "pixverse-c1-i2v": { provider: "dashscope-pixverse-i2v", protocol: "pixverse-i2v", baseUrl: DASHSCOPE_BASE_URL, endpoint: DASHSCOPE_VIDEO_ENDPOINT, model: "pixverse/pixverse-c1-it2v" }, }; const TEXT_MODELS = { "gemini-3.1-pro": { provider: "grsai", transport: "openai-chat", baseUrl: GRSAI_TEXT_BASE_URL, endpoint: "/v1/chat/completions" }, "gemini-2.5-pro": { provider: "grsai", transport: "openai-chat", baseUrl: GRSAI_TEXT_BASE_URL, endpoint: "/v1/chat/completions" }, "gpt-4o": { provider: "grsai", transport: "openai-chat", baseUrl: GRSAI_TEXT_BASE_URL, endpoint: "/v1/chat/completions" }, }; const DASHSCOPE_TEXT_PREFIXES = ["qwen", "qwq", "deepseek-"]; const DASHSCOPE_TEXT_CONFIG = { provider: "dashscope-text", transport: "dashscope-chat", baseUrl: DASHSCOPE_BASE_URL, endpoint: "/compatible-mode/v1/chat/completions" }; function normalizeModel(model, fallback) { return String(model || fallback || "").trim(); } function withImageBillingProvider(config, billingProvider) { return { ...config, billingProvider }; } function createUnsupportedImageModelError(model) { const error = new Error(`Unsupported image model: ${model}`); error.status = 400; return error; } function resolveImageProvider(model) { const requestedModel = normalizeModel(model, "nano-banana-pro"); const lower = requestedModel.toLowerCase(); const config = IMAGE_MODELS[requestedModel] || IMAGE_MODELS[lower]; if (config) return { ...config, model: requestedModel, requestedModel }; throw createUnsupportedImageModelError(requestedModel); } function resolveImageProviderCandidates(model) { const primary = resolveImageProvider(model); const billingProvider = primary.billingProvider || primary.provider; if (primary.transport !== "grsai-image" || primary.provider !== "grsai") { return [withImageBillingProvider(primary, billingProvider)]; } const rightcodeModel = RIGHTCODE_IMAGE_MODEL_MAP[String(primary.requestedModel || primary.model || "").toLowerCase()]; const fallbackModel = rightcodeModel || primary.requestedModel || primary.model; const kuaikuaiModel = KUAIKUAI_IMAGE_MODEL_MAP[String(primary.requestedModel || primary.model || "").toLowerCase()] || primary.requestedModel || primary.model; const modelKey = String(primary.requestedModel || primary.model || "").toLowerCase(); const isGptModel = modelKey.startsWith("gpt"); return [ withImageBillingProvider(primary, billingProvider), { ...primary, provider: "rightcode", transport: "rightcode-image", baseUrl: RIGHTCODE_IMAGE_BASE_URL, endpoint: RIGHTCODE_IMAGE_ENDPOINT, model: fallbackModel, billingProvider, fallbackOf: primary.provider, }, { ...primary, provider: isGptModel ? "kuaikuai-gpt" : "kuaikuai-nano", transport: isGptModel ? "openai-image" : "gemini-image", baseUrl: KUAIKUAI_IMAGE_BASE_URL, endpoint: isGptModel ? KUAIKUAI_OPENAI_ENDPOINT : KUAIKUAI_GEMINI_ENDPOINT, model: kuaikuaiModel, billingProvider, fallbackOf: primary.provider, }, ]; } function sanitizeImageProviderCandidate(config) { return { provider: config.provider, transport: config.transport, model: config.model, requestedModel: config.requestedModel, billingProvider: config.billingProvider, fallbackOf: config.fallbackOf, }; } function buildImageProviderDebug(model) { const candidates = resolveImageProviderCandidates(model).map(sanitizeImageProviderCandidate); return { requestedModel: candidates[0]?.requestedModel || normalizeModel(model, "nano-banana-pro"), effectiveModel: candidates[0]?.model, primaryProvider: candidates[0]?.provider, fallbackProviders: candidates.slice(1).map((candidate) => candidate.provider).filter(Boolean), route: candidates.map((candidate) => candidate.provider).filter(Boolean), candidates, }; } function resolveVideoProvider(model) { const requestedModel = normalizeModel(model, "seedance-2"); const lower = requestedModel.toLowerCase(); const config = VIDEO_MODELS[requestedModel] || VIDEO_MODELS[lower]; if (config) return { ...config, requestedModel }; if (lower.includes("happyhorse") && lower.includes("i2v")) { return { ...VIDEO_MODELS["happyhorse-1.0-i2v"], model: requestedModel, requestedModel }; } if (lower.includes("happyhorse") && lower.includes("r2v")) { return { ...VIDEO_MODELS["happyhorse-1.0-r2v"], model: requestedModel, requestedModel }; } if (lower.includes("happyhorse")) { return { ...VIDEO_MODELS["happyhorse-1.0-t2v"], model: requestedModel, requestedModel }; } if (lower.startsWith("wan2.") && lower.includes("i2v")) { return { ...VIDEO_MODELS["wan2.7-i2v"], model: requestedModel, requestedModel }; } if (lower.startsWith("wan2.")) { return { ...VIDEO_MODELS["wan2.7-t2v"], model: requestedModel, requestedModel }; } if (lower.startsWith("kling/")) { return { ...VIDEO_MODELS["kling/kling-v3-omni-video-generation"], model: requestedModel, requestedModel }; } if (lower.includes("kling")) { return { ...VIDEO_MODELS["Kling-V3-Omni"], model: requestedModel, requestedModel }; } if (lower.includes("vidu") && lower.includes("i2v")) { return { ...VIDEO_MODELS["vidu-q3-turbo-i2v"], model: requestedModel, requestedModel }; } if (lower.includes("vidu")) { return { ...VIDEO_MODELS["vidu-q3-turbo-t2v"], model: requestedModel, requestedModel }; } if (lower.includes("pixverse") && lower.includes("i2v")) { return { ...VIDEO_MODELS["pixverse-c1-i2v"], model: requestedModel, requestedModel }; } if (lower.includes("pixverse")) { return { ...VIDEO_MODELS["pixverse-c1-t2v"], model: requestedModel, requestedModel }; } const error = new Error(`不支持的视频模型: ${requestedModel}`); error.status = 400; throw error; } function resolveTextProvider(model) { const m = normalizeModel(model, "gemini-3.1-pro").toLowerCase(); if (DASHSCOPE_TEXT_PREFIXES.some((p) => m.startsWith(p))) { return { ...DASHSCOPE_TEXT_CONFIG, model: m, requestedModel: m }; } const config = TEXT_MODELS[m]; if (config) return { ...config, model: m, requestedModel: m }; return { ...TEXT_MODELS["gemini-3.1-pro"], model: "gemini-3.1-pro", requestedModel: m }; } function listKnownRoutes() { return [ ...Object.keys(IMAGE_MODELS).flatMap((model) => resolveImageProviderCandidates(model).map((config) => ({ type: "image", ...config })), ), ...Object.keys(VIDEO_MODELS).map((model) => ({ type: "video", ...resolveVideoProvider(model) })), ...Object.keys(TEXT_MODELS).map((model) => ({ type: "text", ...resolveTextProvider(model) })), { type: "text", ...resolveTextProvider("qwen-plus") }, ]; } function getPostUrl(config) { const base = `${config.baseUrl || ""}`; const endpoint = String(config.endpoint || "").replace("{model}", encodeURIComponent(config.model || "")); return `${base}${endpoint}`; } function getPollUrl(config) { if (config.transport === "dashscope-image") return `${DASHSCOPE_BASE_URL}/api/v1/tasks/{task_id}`; if (config.transport === "grsai-image") return `${config.baseUrl}${config.resultEndpoint || GRSAI_RESULT_ENDPOINT}?id={task_id}`; if (config.transport === "rightcode-image" || config.transport === "gemini-image" || config.transport === "openai-image") return ""; if ( config.protocol === "wan-i2v" || config.protocol === "wan-t2v" || config.protocol === "wan-s2v" || config.protocol === "wan-animate-mix" || config.protocol === "kling-dashscope" || String(config.protocol || "").startsWith("happyhorse-") || String(config.protocol || "").startsWith("vidu-") || String(config.protocol || "").startsWith("pixverse-") ) return `${config.baseUrl}/api/v1/tasks/{task_id}`; if (config.protocol === "kling-omni") return `${config.baseUrl}/v1/videos/omni-video/{task_id}`; if (config.protocol === "seed-video" || config.protocol === "seed-video-ark") return `${config.baseUrl}${config.endpoint}/{task_id}`; return ""; } module.exports = { resolveImageProvider, resolveImageProviderCandidates, buildImageProviderDebug, resolveVideoProvider, resolveTextProvider, listKnownRoutes, getPostUrl, getPollUrl, IMAGE_MODELS, VIDEO_MODELS, TEXT_MODELS, };