fix: align canvas auto-title model routing with agent multimodal
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-20 21:58:57 +08:00
parent 4524bc6d9f
commit 7141704f65

View File

@ -1,8 +1,64 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/app/(auth)/auth";
import { generateText } from "ai";
import { getEnabledAiModelsWithProvider } from "@/lib/db/queries";
import {
getAgentAiModelWithProvider,
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",
"qianwen-video",
"volcano-video",
"doubao-video",
"alibaba-image",
"ruxa-image",
"fal-image",
"volcano-vision",
]);
type VisionModelItem = Awaited<ReturnType<typeof getEnabledAiModelsWithProvider>>[number];
function isVisionCandidate(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 false;
}
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 (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));
if (candidates.length === 0) return null;
return [...candidates].sort((a, b) => scoreVisionCandidate(b) - scoreVisionCandidate(a))[0];
}
export async function POST(request: NextRequest) {
console.log("[Auto-title] API called");
@ -21,42 +77,25 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: "Image URL is required" }, { status: 400 });
}
const blockedProviderTypes = new Set([
"fal-video",
"qianwen-video",
"volcano-video",
"doubao-video",
]);
// 从数据库获取支持视觉的模型(优先使用 supportsVision)
// 从数据库获取视觉模型,优先使用 AGENT 多模态模型
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));
let visionModelData = modelsData.find(
(m) => m.model.supportsVision && !blockedProviderTypes.has(String(m.provider?.type || "")),
);
if (!visionModelData) {
// 兼容旧配置:根据模型名称兜底匹配
visionModelData = modelsData.find(m => {
if (blockedProviderTypes.has(String(m.provider?.type || ""))) {
return false;
}
const id = m.model.modelId?.toLowerCase() || '';
const name = m.model.displayName?.toLowerCase() || '';
return id.includes('qwen-vl') ||
id.includes('qwen3-vl') ||
id.includes('qwen2.5-vl') ||
id.includes('vision') ||
name.includes('vl') ||
name.includes('视觉');
});
}
console.log("[Auto-title] Found vision model:", visionModelData?.model.modelId, visionModelData?.model.displayName);
const visionModelData = pickAutoTitleModel(modelsData, agentModel);
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)),
});
if (!visionModelData) {
console.log("[Auto-title] No vision model found, using default title");
console.log("[Auto-title] No compatible vision model found, using default title");
return NextResponse.json({ title: "未命名画布" });
}
@ -82,7 +121,7 @@ export async function POST(request: NextRequest) {
},
],
temperature: 0.2,
maxTokens: 50,
maxOutputTokens: 50,
});
let title = (text || "").trim();