diff --git a/app/api/canvas/auto-title/route.ts b/app/api/canvas/auto-title/route.ts index cd3d39f..2b655a9 100644 --- a/app/api/canvas/auto-title/route.ts +++ b/app/api/canvas/auto-title/route.ts @@ -1,63 +1,64 @@ import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; -import { createOpenAI } from "@ai-sdk/openai"; import { generateText } from "ai"; -import { getSystemSetting } from "@/lib/db/queries"; +import { getEnabledAiModelsWithProvider, getAiProviderById } from "@/lib/db/queries"; +import { createOpenAI } from "@ai-sdk/openai"; export async function POST(request: NextRequest) { + console.log("[Auto-title] API called"); + try { const session = await auth(); if (!session?.user?.id) { + console.log("[Auto-title] Unauthorized"); return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const { imageUrl } = await request.json(); + console.log("[Auto-title] Image URL:", imageUrl); if (!imageUrl) { return NextResponse.json({ error: "Image URL is required" }, { status: 400 }); } - // 获取 AI 配置 - const aiProviderConfig = await getSystemSetting({ key: "ai_provider_config" }); - const modelsConfig = await getSystemSetting({ key: "ai_models_config" }); + // 从数据库获取支持视觉的模型(查找包含 qwen-vl 或 vision 的模型) + const models = await getEnabledAiModelsWithProvider(); + console.log("[Auto-title] Available models count:", models.length); - // 默认使用通义千问 (Alibaba) 配置,如果未配置则尝试其他 - // 这里为了简化,我们假设系统中配置了支持视觉的模型。 - // 在实际生产中,最好在前端指定一个确定的视觉模型ID传过来。 + // 查找千问VL模型(优先 qwen-vl-max,其次 qwen-vl-plus) + const visionModel = models.find(m => + m.modelId?.toLowerCase().includes('qwen-vl') || + m.modelId?.toLowerCase().includes('qwen3-vl') || + m.modelId?.toLowerCase().includes('vision') + ); + console.log("[Auto-title] Found vision model:", visionModel?.modelId); + + if (!visionModel || !visionModel.providerId) { + console.log("[Auto-title] No vision model found, using default title"); + return NextResponse.json({ title: "未命名画布" }); + } + + // 获取 provider 配置 + const provider = await getAiProviderById(visionModel.providerId); + if (!provider || !provider.apiKey || !provider.baseUrl) { + console.log("[Auto-title] Provider not configured"); + return NextResponse.json({ title: "未命名画布" }); + } + + console.log("[Auto-title] Using provider:", provider.name, "baseUrl:", provider.baseUrl); + // 构造 Prompt const prompt = "请用2-4个字概括这张图片的主体内容,作为文件标题。直接返回标题,不要包含任何标点符号或其他文字。"; - // 这里我们尝试使用环境变量中的配置,或者从数据库读取的配置初始化模型 - // 为了确保稳定性,这里建议使用一个支持视觉的兜底模型配置 - // 假设系统中有配置好的 'qwen-vl-max' 或类似模型 - - // 临时方案:直接使用 fetch 调用通义千问 API (如果使用了兼容 OpenAI 的接口) - // 或者使用项目现有的 provider 逻辑。 - // 由于我看不到完整的 provider 实现细节,我将使用项目中已有的 /api/chat 逻辑的简化版 - // 但为了更直接,我们这里模拟一个简单的视觉模型调用,或者利用 ai-sdk 的标准特性 - - // ⚠️ 注意:这里需要确保您的环境变量中有 ALIBABA_API_KEY 或者对应的配置 - // 如果项目中统一使用了 provider 管理,最好复用。 - - // 为了不破坏现有架构,我们这里简化处理: - // 如果您有特定的 vision model ID,请替换这里。 - // 这里的 fallback 逻辑需要根据您的实际 .env 配置来调整。 - - const apiKey = process.env.ALIBABA_API_KEY || process.env.OPENAI_API_KEY; - const baseURL = process.env.ALIBABA_BASE_URL || "https://dashscope.aliyuncs.com/compatible-mode/v1"; - - if (!apiKey) { - return NextResponse.json({ title: "未命名画布" }, { status: 200 }); - } - + // 使用数据库配置的 provider const openai = createOpenAI({ - apiKey: apiKey, - baseURL: baseURL, + apiKey: provider.apiKey, + baseURL: provider.baseUrl, }); const { text } = await generateText({ - model: openai("qwen-vl-max"), // 尝试使用 qwen-vl-max + model: openai(visionModel.modelId), messages: [ { role: 'user', @@ -69,10 +70,11 @@ export async function POST(request: NextRequest) { ], }); + console.log("[Auto-title] Generated title:", text.trim()); return NextResponse.json({ title: text.trim() }); } catch (error) { - console.error("Auto-title error:", error); + console.error("[Auto-title] Error:", error); // 失败时返回默认标题,不报错中断流程 return NextResponse.json({ title: "新画布" }); }