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 { createLocalQwen } from "./providers/local-qwen"; import { createGeminiImage } from "./providers/gemini-image"; import { createQianwenVideo } from "./providers/qianwen-video"; import { createVolcanoVision } from "./providers/volcano-vision"; // 缓存已创建的 provider 实例 const providerCache = new Map>(); /** * 根据提供商类型创建对应的 AI Provider 实例 * 不同供应商使用OpenAI兼容的API格式,但可能有细微差异 * * 支持的供应商类型: * - alibaba: 阿里云 DashScope (通义千问等) * - openai: 官方 OpenAI API * - custom: OpenAI兼容的第三方API (GPT-5 Nano、其他兼容服务等) */ function getOrCreateProvider(provider: Pick) { const cacheKey = `${provider.id}-${provider.baseUrl}`; 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: provider.baseUrl, }); console.log(`[Provider] Created Alibaba DashScope provider: ${provider.baseUrl}`); break; } case "openai": { // 标准 OpenAI API 配置 // 官方 API 地址:https://api.openai.com/v1 providerInstance = createOpenAI({ apiKey: provider.apiKey, baseURL: provider.baseUrl || "https://api.openai.com/v1", }); console.log(`[Provider] Created OpenAI provider: ${provider.baseUrl || "https://api.openai.com/v1"}`); break; } case "local": { // 本地部署的模型(如千问2.5-VL) // 使用自定义的本地模型接口,不兼容 OpenAI 格式 // 本地模型不使用缓存,因为类型不兼容 OpenAIProvider const localProvider = createLocalQwen({ baseURL: provider.baseUrl, apiKey: provider.apiKey, }); console.log(`[Provider] Created local model provider: ${provider.baseUrl}`); return localProvider as any; } case "gemini": { // Gemini 图像生成中转服务 // 使用特殊的响应格式处理(Markdown + inline_data) const geminiProvider = createGeminiImage({ baseURL: provider.baseUrl, apiKey: provider.apiKey, }); console.log(`[Provider] Created Gemini image generation provider: ${provider.baseUrl}`); return geminiProvider as any; } case "qianwen-video": { // 千问视频生成服务(通义万相 I2V) // 支持图片转视频功能,异步任务轮询 const qianwenVideoProvider = createQianwenVideo({ baseURL: provider.baseUrl, apiKey: provider.apiKey, }); console.log(`[Provider] Created Qianwen video generation provider: ${provider.baseUrl}`); return qianwenVideoProvider as any; } case "volcano-vision": { // 火山引擎视觉图片生成服务(Seedream) // 支持文生图、图生图、多图融合等功能 const volcanoVisionProvider = createVolcanoVision({ baseURL: provider.baseUrl, apiKey: provider.apiKey, }); console.log(`[Provider] Created Volcano Vision image generation provider: ${provider.baseUrl}`); return volcanoVisionProvider as any; } case "custom": default: { // OpenAI 兼容的第三方 API(如 GPT-5 Nano、Claude via OpenAI proxy 等) // 这些服务提供与 OpenAI API 完全兼容的接口 // 请求格式遵循 OpenAI Chat Completions 标准:/v1/chat/completions providerInstance = createOpenAI({ apiKey: provider.apiKey, baseURL: provider.baseUrl, }); console.log(`[Provider] Created custom OpenAI-compatible provider: ${provider.baseUrl}`); break; } } providerCache.set(cacheKey, providerInstance); return providerInstance; } // 获取动态配置的语言模型 export async function getDynamicModel( modelId: string ): Promise { try { const model = await getAiModelById({ id: modelId }); if (!model || !model.isEnabled) { console.warn(`[getDynamicModel] Model ${modelId} not found or disabled`); return null; } const provider = await getAiProviderById({ id: model.providerId }); if (!provider || !provider.isEnabled) { console.warn(`[getDynamicModel] Provider for model ${modelId} not found or disabled`); return null; } // 根据供应商类型创建对应的 provider 实例 const providerInstance = getOrCreateProvider({ id: provider.id, type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey, }); if (!providerInstance) { console.error(`[getDynamicModel] Failed to create provider instance for ${provider.type}`); return null; } // 创建基础模型 console.log(`[getDynamicModel] Creating model: ${model.modelId} (${model.displayName}) using provider: ${provider.name} (${provider.type})`); // 根据 provider 类型选择正确的方法和模型ID // customProvider (gemini, local) 使用 .languageModel() // OpenAI-compatible 使用 .chat() let baseModel; if (provider.type === "gemini") { // Gemini 图片生成使用 chat() 方法(与 local-qwen 相同的调用方式) console.log("[getDynamicModel] Getting Gemini chat model..."); // 调试日志 baseModel = providerInstance.chat(model.modelId); console.log("[getDynamicModel] Gemini model retrieved:", baseModel ? "success" : "failed"); // 调试日志 console.log("[getDynamicModel] Gemini model type:", typeof baseModel); // 调试 console.log("[getDynamicModel] Gemini model keys:", baseModel ? Object.keys(baseModel).join(', ') : 'N/A'); // 调试 } else if (provider.type === "local") { // 本地模型使用 chat() 方法 console.log("[getDynamicModel] Getting local chat model..."); // 调试日志 baseModel = providerInstance.chat(model.modelId); console.log("[getDynamicModel] Local model retrieved:", baseModel ? "success" : "failed"); // 调试日志 } else if (provider.type === "qianwen-video") { // 千问视频生成使用 chat() 方法 console.log("[getDynamicModel] Getting Qianwen Video chat model..."); // 调试日志 baseModel = providerInstance.chat(model.modelId); console.log("[getDynamicModel] Qianwen Video model retrieved:", baseModel ? "success" : "failed"); // 调试日志 } else if (provider.type === "volcano-vision") { // 火山引擎视觉生成使用 chat() 方法 console.log("[getDynamicModel] Getting Volcano Vision chat model..."); // 调试日志 baseModel = providerInstance.chat(model.modelId); console.log("[getDynamicModel] Volcano Vision model retrieved:", baseModel ? "success" : "failed"); // 调试日志 } else { // OpenAI-compatible providers console.log("[getDynamicModel] Getting OpenAI-compatible model..."); // 调试日志 baseModel = (providerInstance as any).chat(model.modelId); console.log("[getDynamicModel] OpenAI model retrieved:", baseModel ? "success" : "failed"); // 调试日志 } // 如果模型名称包含特定关键词,可以添加推理中间件 // 例如,为推理模型添加思考过程提取 if (model.displayName.toLowerCase().includes("reasoning") || model.displayName.toLowerCase().includes("推理")) { console.log("[getDynamicModel] Applying reasoning middleware..."); // 调试日志 return wrapLanguageModel({ model: baseModel, middleware: extractReasoningMiddleware({ tagName: "think" }), }); } console.log("[getDynamicModel] Returning base model"); // 调试日志 return baseModel; } catch (error) { console.error("[getDynamicModel] Failed to get dynamic model:", error); return null; } } // 通过模型的数据库 ID 获取语言模型实例 export async function getLanguageModelById( modelId: string ): Promise { return getDynamicModel(modelId); } // 获取所有可用的模型列表(用于前端展示) 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) { console.error("Failed to get available models:", 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; console.log(`[getSystemModel] Using default model for ${purpose}: ${model.displayName} (${provider.name})`); const providerInstance = getOrCreateProvider({ id: provider.id, type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey, }); if (providerInstance) { return providerInstance.chat(model.modelId); } } // 如果没有找到数据库配置,回退到环境变量配置 console.warn(`[getSystemModel] No default model found in database for ${purpose}, falling back to env config`); 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"); } console.error(`[getSystemModel] No model configuration found for ${purpose}`); return null; } catch (error) { console.error(`[getSystemModel] Failed to get system model for ${purpose}:`, error); return null; } }