ai-chatbot/lib/ai/model-router.ts
Your Name 3a20c5a36d
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
新增RUXA生图服务商并打通聊天/画布链路
2026-02-07 20:09:31 +08:00

268 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 智能模型路由器
* 根据用户意图选择最佳AI模型
*
* 核心逻辑:图片编辑场景优先使用 alibaba-image (灵魂4.5)
*/
// 使用动态导入避免循环依赖
// import { getEnabledAiModelsWithProvider } from "@/lib/db/queries";
// 路由结果类型
export interface RouteResult {
id: string;
displayName: string;
providerType: string;
modelId: string;
reason: string;
}
// 意图到模型的映射配置
// providerTypes 按优先级排序,第一个是首选
const INTENT_MODEL_MAP: Record<
string,
{
providerTypes: string[];
requireVision?: boolean;
requireImageGen?: boolean;
description: string;
}
> = {
// 图片编辑:优先 alibaba-image(灵魂4.5,支持提示词扩展)
image_edit: {
providerTypes: ["alibaba-image", "ruxa-image", "gemini", "huanke-gemini"],
requireVision: true,
requireImageGen: true,
description: "图片编辑",
},
// 视觉理解:优先千问3 VL(成本低效果好)
vision: {
providerTypes: ["alibaba", "gemini", "huanke-gemini"],
requireVision: true,
description: "图片理解",
},
// 图片生成:优先灵魂4.5
image_gen: {
providerTypes: ["alibaba-image", "ruxa-image", "gemini", "huanke-gemini", "volcano-vision"],
requireImageGen: true,
description: "图片生成",
},
// 视频生成
video_gen: {
providerTypes: ["volcano-video", "qianwen-video"],
description: "视频生成",
},
// 普通对话:优先千问3
chat: {
providerTypes: ["alibaba", "openai", "local"],
description: "对话",
},
};
// 子意图的特殊路由配置(覆盖默认配置)
const SUB_INTENT_OVERRIDES: Record<
string,
{
providerTypes?: string[];
reason?: string;
}
> = {
// 抠图:alibaba-image 效果最好
remove_background: {
providerTypes: ["alibaba-image", "ruxa-image", "gemini"],
reason: "抠图首选灵魂4.5",
},
// 换背景:alibaba-image 支持场景生成
replace_background: {
providerTypes: ["alibaba-image", "ruxa-image", "gemini"],
reason: "换背景首选灵魂4.5",
},
// 图片增强
enhance: {
providerTypes: ["alibaba-image", "ruxa-image", "gemini", "volcano-vision"],
reason: "增强首选灵魂4.5",
},
// 风格迁移:gemini 可能更有创意
style_transfer: {
providerTypes: ["gemini", "alibaba-image", "ruxa-image"],
reason: "风格迁移优选Gemini",
},
};
/**
* 路由到最佳模型
*/
export async function routeToModel(params: {
intent: string;
subIntent?: string;
hasImage: boolean;
}): Promise<RouteResult> {
const { intent, subIntent, hasImage } = params;
console.log("[Model Router] Routing:", { intent, subIntent, hasImage });
// 使用动态导入避免循环依赖
const { getEnabledAiModelsWithProvider } = await import("@/lib/db/queries");
const { db } = await import("@/lib/db");
const { siteSettings } = await import("@/lib/db/schema");
// 获取所有可用模型
const allModels = await getEnabledAiModelsWithProvider();
console.log("[Model Router] Available models:", allModels.length);
// 获取意图对应的配置
let config = INTENT_MODEL_MAP[intent] || INTENT_MODEL_MAP["chat"];
// 检查是否有子意图的特殊配置
if (subIntent && SUB_INTENT_OVERRIDES[subIntent]) {
const override = SUB_INTENT_OVERRIDES[subIntent];
if (override.providerTypes) {
config = { ...config, providerTypes: override.providerTypes };
}
}
// 🆕 检查是否有配置的默认生图模型(仅在能力匹配时使用)
const [settings] = await db.select().from(siteSettings).limit(1);
const configuredDefaultModelId = settings?.canvasAutoModeDefaultModelId;
if (configuredDefaultModelId) {
const configuredModel = allModels.find(
(m) => m.model.id === configuredDefaultModelId && m.model.isEnabled && !m.model.isSubModel
);
if (configuredModel) {
const matchesVision = !config.requireVision || configuredModel.model.supportsVision;
const matchesImageGen = !config.requireImageGen || configuredModel.model.supportsImageGen;
if (matchesVision && matchesImageGen) {
console.log("[Model Router] ✅ Using configured default model:", configuredModel.model.displayName);
return {
id: configuredModel.model.id,
displayName: configuredModel.model.displayName,
providerType: configuredModel.provider.type,
modelId: configuredModel.model.modelId,
reason: `管理员配置的默认模型 → ${configuredModel.model.displayName}`,
};
}
console.log("[Model Router] ⚠️ Configured default model does not match intent requirements, falling back");
} else {
console.log("[Model Router] ⚠️ Configured default model not found or disabled, falling back to intent routing");
}
}
// 按优先级查找匹配的模型
for (const providerType of config.providerTypes) {
const candidates = allModels.filter((m) => {
// 匹配 provider 类型
if (m.provider.type !== providerType) return false;
// 检查能力要求
if (config.requireVision && !m.model.supportsVision) return false;
if (config.requireImageGen && !m.model.supportsImageGen) return false;
// 排除子模型(只选主模型)
if (m.model.isSubModel) return false;
// 必须启用
if (!m.model.isEnabled) return false;
return true;
});
if (candidates.length > 0) {
// 优先选择默认模型
const defaultModel = candidates.find((c) => c.model.isDefault);
const selected = defaultModel || candidates[0];
const result: RouteResult = {
id: selected.model.id,
displayName: selected.model.displayName,
providerType: selected.provider.type,
modelId: selected.model.modelId,
reason: subIntent
? `${config.description}/${subIntent} → ${selected.model.displayName}`
: `${config.description} → ${selected.model.displayName}`,
};
console.log("[Model Router] ✅ Selected:", result);
return result;
}
}
// 兜底逻辑:返回任意可用的图片编辑模型
const fallbackCandidates = allModels.filter(
(m) =>
!m.model.isSubModel &&
m.model.isEnabled &&
(m.model.supportsImageGen || m.model.supportsVision)
);
if (fallbackCandidates.length > 0) {
const fallback = fallbackCandidates[0];
console.log("[Model Router] Using fallback (with image capability):", fallback.model.displayName);
return {
id: fallback.model.id,
displayName: fallback.model.displayName,
providerType: fallback.provider.type,
modelId: fallback.model.modelId,
reason: `fallback (${config.description})`,
};
}
// 最终兜底:返回任意可用模型
const anyModel = allModels.find((m) => !m.model.isSubModel && m.model.isEnabled);
if (anyModel) {
console.log("[Model Router] Using any available model:", anyModel.model.displayName);
return {
id: anyModel.model.id,
displayName: anyModel.model.displayName,
providerType: anyModel.provider.type,
modelId: anyModel.model.modelId,
reason: "fallback (any)",
};
}
// 如果没有任何模型,抛出错误
throw new Error("No available models for routing");
}
/**
* 获取意图的推荐模型列表(用于 UI 展示)
*/
export function getRecommendedModelsForIntent(intent: string): string[] {
const config = INTENT_MODEL_MAP[intent];
return config ? config.providerTypes : INTENT_MODEL_MAP["chat"].providerTypes;
}
/**
* 检查模型是否适合指定意图
*/
export function isModelSuitableForIntent(
providerType: string,
intent: string,
supportsVision: boolean,
supportsImageGen: boolean
): boolean {
const config = INTENT_MODEL_MAP[intent];
if (!config) return true; // 未知意图,允许任何模型
// 检查 provider 类型是否匹配
if (!config.providerTypes.includes(providerType)) {
return false;
}
// 检查能力要求
if (config.requireVision && !supportsVision) return false;
if (config.requireImageGen && !supportsImageGen) return false;
return true;
}