262 lines
7.6 KiB
TypeScript
262 lines
7.6 KiB
TypeScript
/**
|
||
* 智能模型路由器
|
||
* 根据用户意图选择最佳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", "gemini"],
|
||
requireVision: true,
|
||
requireImageGen: true,
|
||
description: "图片编辑",
|
||
},
|
||
|
||
// 视觉理解:优先千问3 VL(成本低效果好)
|
||
vision: {
|
||
providerTypes: ["alibaba", "gemini"],
|
||
requireVision: true,
|
||
description: "图片理解",
|
||
},
|
||
|
||
// 图片生成:优先灵魂4.5
|
||
image_gen: {
|
||
providerTypes: ["alibaba-image", "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", "gemini"],
|
||
reason: "抠图首选灵魂4.5",
|
||
},
|
||
|
||
// 换背景:alibaba-image 支持场景生成
|
||
replace_background: {
|
||
providerTypes: ["alibaba-image", "gemini"],
|
||
reason: "换背景首选灵魂4.5",
|
||
},
|
||
|
||
// 图片增强
|
||
enhance: {
|
||
providerTypes: ["alibaba-image", "gemini", "volcano-vision"],
|
||
reason: "增强首选灵魂4.5",
|
||
},
|
||
|
||
// 风格迁移:gemini 可能更有创意
|
||
style_transfer: {
|
||
providerTypes: ["gemini", "alibaba-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);
|
||
|
||
// 🆕 检查是否有配置的默认生图模型
|
||
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) {
|
||
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}`,
|
||
};
|
||
} else {
|
||
console.log("[Model Router] ⚠️ Configured default model not found or disabled, falling back to intent routing");
|
||
}
|
||
}
|
||
|
||
// 获取意图对应的配置
|
||
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 };
|
||
}
|
||
}
|
||
|
||
// 按优先级查找匹配的模型
|
||
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;
|
||
}
|