From 4222e723b0a22e19eadf923912cb95bbe3c05bc0 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 20 Feb 2026 22:10:15 +0800 Subject: [PATCH] fix: force canvas vision tasks to qwen vl models --- app/api/canvas/auto-title/route.ts | 50 +++++++++--------------- app/api/pick-object/route.ts | 61 ++++++++++++------------------ 2 files changed, 42 insertions(+), 69 deletions(-) diff --git a/app/api/canvas/auto-title/route.ts b/app/api/canvas/auto-title/route.ts index fe898c106..6715f9f2e 100644 --- a/app/api/canvas/auto-title/route.ts +++ b/app/api/canvas/auto-title/route.ts @@ -1,12 +1,8 @@ import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; import { generateText } from "ai"; -import { - getAgentAiModelWithProvider, - getEnabledAiModelsWithProvider, -} from "@/lib/db/queries"; +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; import { getLanguageModelById } from "@/lib/ai/dynamic-provider"; -import { isAiModelMultimodal } from "@/lib/db/ai-model-flags"; const BLOCKED_PROVIDER_TYPES = new Set([ "fal-video", @@ -21,41 +17,33 @@ const BLOCKED_PROVIDER_TYPES = new Set([ type VisionModelItem = Awaited>[number]; -function isVisionCandidate(item: VisionModelItem) { +function isQwenVisionModel(item: VisionModelItem) { const providerType = String(item.provider?.type || ""); if (BLOCKED_PROVIDER_TYPES.has(providerType)) return false; - - if (item.model.supportsVision) return true; - if (isAiModelMultimodal(item.model)) return true; - const modelText = `${item.model.modelId || ""} ${item.model.displayName || ""}`.toLowerCase(); - if (/vl|vision|多模态|glm-4v|gpt-4o|doubao-seed-2-0|seed-2-0/.test(modelText)) { - return true; - } + return /qwen.*vl|通义.*vl|千问.*vl/.test(modelText); +} - return false; +function isQwen25VisionModel(item: VisionModelItem) { + if (!isQwenVisionModel(item)) return false; + const modelText = `${item.model.modelId || ""} ${item.model.displayName || ""}`.toLowerCase(); + return /qwen[-_. ]?2\.?5[-_. ]?vl|qwen2\.5[-_. ]?vl/.test(modelText); } function scoreVisionCandidate(item: VisionModelItem) { let score = 0; const providerType = String(item.provider?.type || ""); - if (isAiModelMultimodal(item.model)) score += 220; - if (item.model.supportsVision) score += 160; - if (providerType === "volcano-multimodal") score += 80; - if (providerType === "zhipu") score += 50; - if (providerType === "alibaba") score += 40; - if (providerType === "custom") score += 30; + if (isQwen25VisionModel(item)) score += 400; + if (item.model.supportsVision) score += 120; + if (providerType === "alibaba") score += 80; + if (providerType === "custom") score += 40; if (item.model.isDefault) score += 20; score -= Math.max(0, item.model.sortOrder ?? 0); return score; } -function pickAutoTitleModel(allModels: VisionModelItem[], agentModel: VisionModelItem | undefined) { - if (agentModel && isVisionCandidate(agentModel)) { - return agentModel; - } - - const candidates = allModels.filter((item) => isVisionCandidate(item)); +function pickAutoTitleModel(allModels: VisionModelItem[]) { + const candidates = allModels.filter((item) => isQwenVisionModel(item)); if (candidates.length === 0) return null; return [...candidates].sort((a, b) => scoreVisionCandidate(b) - scoreVisionCandidate(a))[0]; } @@ -77,25 +65,23 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: "Image URL is required" }, { status: 400 }); } - // 从数据库获取视觉模型,优先使用 AGENT 多模态模型 + // 固定使用 Qwen2.5-VL(若未配置则降级到其它 Qwen-VL) const modelsData = await getEnabledAiModelsWithProvider(); - const agentModel = await getAgentAiModelWithProvider(); console.log("[Auto-title] Available models count:", modelsData.length); console.log("[Auto-title] All model IDs:", modelsData.map(m => m.model.modelId)); - const visionModelData = pickAutoTitleModel(modelsData, agentModel); + const visionModelData = pickAutoTitleModel(modelsData); console.log("[Auto-title] Selected vision model:", { modelId: visionModelData?.model.modelId, displayName: visionModelData?.model.displayName, providerType: visionModelData?.provider?.type, - usedAgentModel: Boolean(agentModel && visionModelData && agentModel.model.id === visionModelData.model.id), - isMultimodal: Boolean(visionModelData && isAiModelMultimodal(visionModelData.model)), + isQwen25: Boolean(visionModelData && isQwen25VisionModel(visionModelData)), }); if (!visionModelData) { - console.log("[Auto-title] No compatible vision model found, using default title"); + console.log("[Auto-title] No Qwen VL model found, using default title"); return NextResponse.json({ title: "未命名画布" }); } diff --git a/app/api/pick-object/route.ts b/app/api/pick-object/route.ts index 6572d477b..17850ba88 100644 --- a/app/api/pick-object/route.ts +++ b/app/api/pick-object/route.ts @@ -1,8 +1,7 @@ import { NextResponse } from "next/server"; import { generateText } from "ai"; -import { getAgentAiModelWithProvider, getEnabledAiModelsWithProvider } from "@/lib/db/queries"; +import { getEnabledAiModelsWithProvider } from "@/lib/db/queries"; import { getLanguageModelById } from "@/lib/ai/dynamic-provider"; -import { isAiModelMultimodal } from "@/lib/db/ai-model-flags"; import { uploadFile } from "@/lib/storage/service"; export const runtime = "nodejs"; @@ -40,45 +39,36 @@ const BLOCKED_PROVIDER_TYPES = new Set([ "doubao-video", ]); -function isVisionCandidate(item: Awaited>[number]) { +type VisionModelItem = Awaited>[number]; + +function isQwenVisionModel(item: VisionModelItem) { if (BLOCKED_PROVIDER_TYPES.has(String(item.provider?.type || ""))) return false; - - if (item.model.supportsVision) return true; - if (isAiModelMultimodal(item.model)) return true; - const modelText = `${item.model.displayName || ""} ${item.model.modelId || ""}`.toLowerCase(); - if (/vl|vision|多模态|glm-4v|gpt-4o|doubao-seed-2-0|seed-2-0/.test(modelText)) { - return true; - } - - return false; + return /qwen.*vl|通义.*vl|千问.*vl/.test(modelText); } -function scoreVisionCandidate(item: Awaited>[number]) { +function isQwen25VisionModel(item: VisionModelItem) { + if (!isQwenVisionModel(item)) return false; + const modelText = `${item.model.displayName || ""} ${item.model.modelId || ""}`.toLowerCase(); + return /qwen[-_. ]?2\.?5[-_. ]?vl|qwen2\.5[-_. ]?vl/.test(modelText); +} + +function scoreQwenVisionCandidate(item: VisionModelItem) { let score = 0; - if (isAiModelMultimodal(item.model)) score += 200; - if (item.model.supportsVision) score += 160; + if (isQwen25VisionModel(item)) score += 400; + if (item.model.supportsVision) score += 120; const providerType = String(item.provider?.type || ""); - if (providerType === "volcano-multimodal") score += 80; - if (providerType === "zhipu") score += 40; - if (providerType === "alibaba") score += 30; + if (providerType === "alibaba") score += 80; + if (providerType === "custom") score += 40; if (item.model.isDefault) score += 20; score -= Math.max(0, item.model.sortOrder ?? 0); return score; } -function pickObjectVisionModel( - allModels: Awaited>, - agentModel: Awaited>, -) { - if (agentModel && isVisionCandidate(agentModel)) { - return agentModel; - } - - const candidates = allModels.filter((item) => isVisionCandidate(item)); +function pickObjectVisionModel(allModels: VisionModelItem[]) { + const candidates = allModels.filter((item) => isQwenVisionModel(item)); if (candidates.length === 0) return null; - - return [...candidates].sort((a, b) => scoreVisionCandidate(b) - scoreVisionCandidate(a))[0]; + return [...candidates].sort((a, b) => scoreQwenVisionCandidate(b) - scoreQwenVisionCandidate(a))[0]; } function inferFileExtFromMime(mimeType: string) { @@ -134,16 +124,15 @@ export async function POST(request: Request) { const normalizedImage = await normalizeImageInput(imageData); - // 优先使用「AGENT + 多模态/视觉」模型 + // 固定使用 Qwen2.5-VL(若未配置则降级到其它 Qwen-VL) const allModels = await getEnabledAiModelsWithProvider(); - const agentModel = await getAgentAiModelWithProvider(); - const selectedModel = pickObjectVisionModel(allModels, agentModel); + const selectedModel = pickObjectVisionModel(allModels); if (!selectedModel) { - console.log("[PickObject] No vision model found"); + console.log("[PickObject] No Qwen VL model found"); return NextResponse.json({ success: false, - error: '未配置可用的视觉/多模态模型', + error: '未配置可用的 Qwen-VL 模型(请配置 qwen2.5-vl)', }, { status: 500 }); } @@ -153,9 +142,7 @@ export async function POST(request: Request) { selectedModel.model.displayName, selectedModel.provider.type, { - isAgentModel: - agentModel?.model?.id === selectedModel.model.id, - isMultimodal: isAiModelMultimodal(selectedModel.model), + isQwen25: isQwen25VisionModel(selectedModel), } );