import "server-only"; import { createOpenAI } from "@ai-sdk/openai"; import { extractReasoningMiddleware, wrapLanguageModel, type LanguageModel, } from "ai"; import { getEnabledAiModelsWithProvider, getAiProviderById, getAiModelById, } from "@/lib/db/queries"; import type { AiProvider } from "@/lib/db/schema"; import { createLogger } from "@/lib/observability/logger"; import { createLocalQwen } from "./providers/local-qwen"; import { createGeminiImage } from "./providers/gemini-image"; import { createHuankeGemini } from "./providers/huanke-gemini"; import { createQianwenVideo } from "./providers/qianwen-video"; import { createVolcanoVision } from "./providers/volcano-vision"; import { createAlibabaImage } from "./providers/alibaba-image"; import { createBflImage } from "./providers/bfl-image"; import { createRuxaImage } from "./providers/ruxa-image"; import { createFalImage } from "./providers/fal-image"; import { createOpenAIImage } from "./providers/openai-image"; import { createStepfunImage } from "./providers/stepfun-image"; import { createMidjourney } from "./providers/midjourney"; import { createFalVideo } from "./providers/fal-video"; import { createRuxaVideo } from "./providers/ruxa-video"; import { createRunway } from "./providers/runway"; import { createKrea } from "./providers/krea"; import { createVolcanoMultimodal } from "./providers/volcano-multimodal"; import { createBltcyVideo } from "./providers/bltcy-video"; import { getProviderDefaultBaseUrl } from "./provider-defaults"; import { createMoonshotKimiFetch, isMoonshotOfficialBaseUrl, } from "./detail-agent-kimi-profile"; import { isBltcyVideoBaseUrl } from "./bltcy-video-models"; // 缓存已创建的 provider 实例 const providerCache = new Map(); const logger = createLogger("dynamic-provider"); function isVolcanoMultimodalBaseUrl(baseUrl?: string | null) { if (!baseUrl) return false; return /ark\.cn-beijing\.volces\.com/i.test(baseUrl); } function getOpenAICompatibleFetch(baseUrl?: string | null) { return isMoonshotOfficialBaseUrl(baseUrl) ? createMoonshotKimiFetch() : undefined; } function logProviderCreated(providerType: string, baseUrl?: string | null) { logger.debug("provider created", { providerType, baseUrl }); } function logChatModelResult(providerType: string, baseModel: unknown) { logger.debug("chat model resolved", { providerType, success: Boolean(baseModel), modelType: typeof baseModel, modelKeyCount: baseModel && typeof baseModel === "object" ? Object.keys(baseModel).length : 0, }); } /** * 根据提供商类型创建对应的 AI Provider 实例 * 不同供应商使用OpenAI兼容的API格式,但可能有细微差异 * * 支持的供应商类型: * - alibaba: 阿里云 DashScope (通义千问等) * - openai: 官方 OpenAI API * - custom: OpenAI兼容的第三方API (GPT-5 Nano、其他兼容服务等) */ function getOrCreateProvider(provider: Pick) { const resolvedBaseUrl = provider.baseUrl?.trim() || getProviderDefaultBaseUrl(provider.type) || ""; const cacheKey = `${provider.id}-${resolvedBaseUrl}`; if (providerCache.has(cacheKey)) { return providerCache.get(cacheKey); } let providerInstance: ReturnType; // 根据供应商类型创建不同的配置 // 虽然都使用 createOpenAI,但通过 baseURL 和 apiKey 的不同来区分不同供应商 switch (provider.type) { case "alibaba": { // 阿里云 DashScope API 配置 // 阿里云提供 OpenAI 兼容的接口:https://dashscope.aliyuncs.com/compatible-mode/v1 providerInstance = createOpenAI({ apiKey: provider.apiKey, baseURL: resolvedBaseUrl, fetch: getOpenAICompatibleFetch(resolvedBaseUrl), }); logProviderCreated("alibaba", resolvedBaseUrl); break; } case "openai": { // 标准 OpenAI API 配置 // 官方 API 地址:https://api.openai.com/v1 providerInstance = createOpenAI({ apiKey: provider.apiKey, baseURL: resolvedBaseUrl || "https://api.openai.com/v1", fetch: getOpenAICompatibleFetch(resolvedBaseUrl), }); logProviderCreated( "openai", resolvedBaseUrl || "https://api.openai.com/v1" ); break; } case "zhipu": { providerInstance = createOpenAI({ apiKey: provider.apiKey, baseURL: resolvedBaseUrl || "https://open.bigmodel.cn/api/paas/v4", fetch: getOpenAICompatibleFetch(resolvedBaseUrl), }); logProviderCreated( "zhipu", resolvedBaseUrl || "https://open.bigmodel.cn/api/paas/v4" ); break; } case "local": { // 本地部署的模型(如千问2.5-VL) // 使用自定义的本地模型接口,不兼容 OpenAI 格式 // 本地模型不使用缓存,因为类型不兼容 OpenAIProvider const localProvider = createLocalQwen({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("local", resolvedBaseUrl); return localProvider as any; } case "gemini": { // Gemini 图像生成中转服务 // 使用特殊的响应格式处理(Markdown + inline_data) const geminiProvider = createGeminiImage({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("gemini", resolvedBaseUrl); return geminiProvider as any; } case "grsai-gpt-image": { const grsaiGptImageProvider = createGeminiImage({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("grsai-gpt-image", resolvedBaseUrl); return grsaiGptImageProvider as any; } case "grsai-video": { logger.warn("provider adapter unavailable", { providerType: "grsai-video", baseUrl: resolvedBaseUrl, }); return null; } case "huanke-gemini": { // 环科 Gemini 原生格式 API // 使用 /v1beta/models/{model}:generateContent 端点 const huankeGeminiProvider = createHuankeGemini({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("huanke-gemini", resolvedBaseUrl); return huankeGeminiProvider as any; } case "qianwen-video": { // 千问视频生成服务(通义万相 I2V) // 支持图片转视频功能,异步任务轮询 const qianwenVideoProvider = createQianwenVideo({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("qianwen-video", resolvedBaseUrl); return qianwenVideoProvider as any; } case "doubao-video": { providerInstance = createOpenAI({ apiKey: provider.apiKey, baseURL: resolvedBaseUrl || "https://ark.cn-beijing.volces.com/api/v3", fetch: getOpenAICompatibleFetch(resolvedBaseUrl), }); logProviderCreated( "doubao-video", resolvedBaseUrl || "https://ark.cn-beijing.volces.com/api/v3" ); break; } case "fal-video": { // FAL 视频生成服务(Kling O3 等) const falVideoProvider = createFalVideo({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("fal-video", resolvedBaseUrl); return falVideoProvider as any; } case "ruxa-video": { // RUXA 视频生成服务(Veo 3.1) const ruxaVideoProvider = createRuxaVideo({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("ruxa-video", resolvedBaseUrl); return ruxaVideoProvider as any; } case "volcano-vision": { // 火山引擎视觉图片生成服务(Seedream) // 支持文生图、图生图、多图融合等功能 const volcanoVisionProvider = createVolcanoVision({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("volcano-vision", resolvedBaseUrl); return volcanoVisionProvider as any; } case "alibaba-image": { // 阿里云千问图片生成/编辑服务 // 支持 qwen-image-edit-plus(1-6张输出)和 qwen-image-edit(1张输出) const alibabaImageProvider = createAlibabaImage({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("alibaba-image", resolvedBaseUrl); return alibabaImageProvider as any; } case "bfl-image": { const bflImageProvider = createBflImage({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("bfl-image", resolvedBaseUrl); return bflImageProvider as any; } case "ruxa-image": { // RUXA 图片编辑服务 const ruxaImageProvider = createRuxaImage({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("ruxa-image", resolvedBaseUrl); return ruxaImageProvider as any; } case "fal-image": { // FAL 图片编辑服务(Nano Banana Pro) const falImageProvider = createFalImage({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("fal-image", resolvedBaseUrl); return falImageProvider as any; } case "openai-image": { const openAIImageProvider = createOpenAIImage({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("openai-image", resolvedBaseUrl); return openAIImageProvider as any; } case "stepfun-image": { const stepfunImageProvider = createStepfunImage({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("stepfun-image", resolvedBaseUrl); return stepfunImageProvider as any; } case "midjourney": { const midjourneyProvider = createMidjourney({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("midjourney", resolvedBaseUrl); return midjourneyProvider as any; } case "runway": { const runwayProvider = createRunway({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("runway", resolvedBaseUrl); return runwayProvider as any; } case "krea": { const kreaProvider = createKrea({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("krea", resolvedBaseUrl); return kreaProvider as any; } case "custom": default: { if (isBltcyVideoBaseUrl(resolvedBaseUrl)) { const bltcyVideoProvider = createBltcyVideo({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("bltcy-video", resolvedBaseUrl); return bltcyVideoProvider as any; } if (isVolcanoMultimodalBaseUrl(resolvedBaseUrl)) { const volcanoMultimodalProvider = createVolcanoMultimodal({ baseURL: resolvedBaseUrl, apiKey: provider.apiKey, }); logProviderCreated("volcano-multimodal", resolvedBaseUrl); return volcanoMultimodalProvider as any; } // OpenAI 兼容的第三方 API(如 GPT-5 Nano、Claude via OpenAI proxy 等) // 这些服务提供与 OpenAI API 完全兼容的接口 // 请求格式遵循 OpenAI Chat Completions 标准:/v1/chat/completions providerInstance = createOpenAI({ apiKey: provider.apiKey, baseURL: resolvedBaseUrl || provider.baseUrl, fetch: getOpenAICompatibleFetch(resolvedBaseUrl || provider.baseUrl), }); logProviderCreated("custom", resolvedBaseUrl || provider.baseUrl); break; } } providerCache.set(cacheKey, providerInstance); return providerInstance; } // 获取动态配置的语言模型 export async function getDynamicModel( modelId: string, overrideModelId?: string | null ): Promise { try { const model = await getAiModelById({ id: modelId }); if (!model || !model.isEnabled) { logger.warn("model unavailable", { modelId }); return null; } const provider = await getAiProviderById({ id: model.providerId }); if (!provider || !provider.isEnabled) { logger.warn("provider unavailable", { modelId, providerId: model.providerId }); return null; } // 根据供应商类型创建对应的 provider 实例 const providerInstance = getOrCreateProvider({ id: provider.id, type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey, }); if (!providerInstance) { logger.error("provider instance creation failed", { providerType: provider.type, }); return null; } const resolvedModelId = String(overrideModelId || "").trim() || model.modelId; // 创建基础模型 logger.debug("model creation started", { modelId, resolvedModelId, providerId: provider.id, providerType: provider.type, }); // 根据 provider 类型选择正确的方法和模型ID // customProvider (gemini, local) 使用 .languageModel() // OpenAI-compatible 使用 .chat() let baseModel; if (provider.type === "gemini" || provider.type === "grsai-gpt-image") { const imageProviderLabel = provider.type === "grsai-gpt-image" ? "Grsai GPT Image" : "Gemini image"; // 图片生成 provider 使用 chat() 方法(与 local-qwen 相同的调用方式) baseModel = providerInstance.chat(resolvedModelId); logChatModelResult(imageProviderLabel, baseModel); } else if (provider.type === "local") { // 本地模型使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("local", baseModel); } else if (provider.type === "qianwen-video") { // 千问视频生成使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("qianwen-video", baseModel); } else if (provider.type === "fal-video") { // FAL 视频生成使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("fal-video", baseModel); } else if (provider.type === "ruxa-video") { // RUXA 视频生成使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("ruxa-video", baseModel); } else if (provider.type === "volcano-vision") { // 火山引擎视觉生成使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("volcano-vision", baseModel); } else if (provider.type === "alibaba-image") { // 阿里云千问图片生成使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("alibaba-image", baseModel); } else if (provider.type === "bfl-image") { baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("bfl-image", baseModel); } else if (provider.type === "ruxa-image") { // RUXA 图片编辑生成使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("ruxa-image", baseModel); } else if (provider.type === "fal-image") { // FAL 图片编辑生成使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("fal-image", baseModel); } else if (provider.type === "openai-image") { // OpenAI Images 兼容图片生成/编辑使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("openai-image", baseModel); } else if (provider.type === "stepfun-image") { baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("stepfun-image", baseModel); } else if (provider.type === "midjourney") { baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("midjourney", baseModel); } else if (provider.type === "runway") { baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("runway", baseModel); } else if (provider.type === "krea") { baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("krea", baseModel); } else if (provider.type === "huanke-gemini") { // 环科 Gemini 原生格式使用 chat() 方法 baseModel = providerInstance.chat(resolvedModelId); logChatModelResult("huanke-gemini", baseModel); } else { // OpenAI-compatible providers baseModel = (providerInstance as any).chat(resolvedModelId); logChatModelResult("openai-compatible", baseModel); } // 如果模型名称包含特定关键词,可以添加推理中间件 // 例如,为推理模型添加思考过程提取 if (model.displayName.toLowerCase().includes("reasoning") || model.displayName.toLowerCase().includes("推理")) { logger.debug("reasoning middleware applied", { modelId, resolvedModelId }); return wrapLanguageModel({ model: baseModel, middleware: extractReasoningMiddleware({ tagName: "think" }), }); } logger.debug("base model returned", { modelId, resolvedModelId }); return baseModel; } catch (error) { logger.error("dynamic model resolution failed", error, { modelId }); return null; } } // 通过模型的数据库 ID 获取语言模型实例 export async function getLanguageModelById( modelId: string, overrideModelId?: string | null ): Promise { return getDynamicModel(modelId, overrideModelId); } // 获取所有可用的模型列表(用于前端展示) export async function getAvailableModels() { try { const modelsWithProvider = await getEnabledAiModelsWithProvider(); return modelsWithProvider.map(({ model, provider }) => ({ id: model.id, modelId: model.modelId, displayName: model.displayName, description: model.description, supportsVision: model.supportsVision, supportsImageGen: model.supportsImageGen, isDefault: model.isDefault, providerName: provider.name, providerType: provider.type, })); } catch (error) { logger.error("available models load failed", error); return []; } } // 获取系统模型(标题生成、文档生成等) // 这些模型使用数据库中标记为默认的模型,如果没有则回退到环境变量配置 export async function getSystemModel( purpose: "title" | "artifact" ): Promise { try { // 首先尝试从数据库获取默认模型 const modelsWithProvider = await getEnabledAiModelsWithProvider(); // 找到默认模型 const defaultModelData = modelsWithProvider.find( ({ model }) => model.isDefault ); if (defaultModelData) { const { model, provider } = defaultModelData; logger.debug("system model default selected", { purpose, modelId: model.id, providerId: provider.id, providerType: provider.type, }); const providerInstance = getOrCreateProvider({ id: provider.id, type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey, }); if (providerInstance) { return providerInstance.chat(model.modelId); } } // 如果没有找到数据库配置,回退到环境变量配置 logger.warn("system model default missing, using env fallback", { purpose }); if (process.env.DASHSCOPE_API_KEY && process.env.DASHSCOPE_BASE_URL) { const fallbackProvider = createOpenAI({ apiKey: process.env.DASHSCOPE_API_KEY, baseURL: process.env.DASHSCOPE_BASE_URL, }); return fallbackProvider.chat("qwen-turbo"); } logger.error("system model configuration missing", { purpose }); return null; } catch (error) { logger.error("system model resolution failed", error, { purpose }); return null; } }