diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx
index 8fddac0c6..39676496f 100644
--- a/app/(admin)/settings/admin-settings-client.tsx
+++ b/app/(admin)/settings/admin-settings-client.tsx
@@ -98,7 +98,7 @@ type ToolConfig = {
type AiProvider = {
id: string;
name: string;
- type: "alibaba" | "openai" | "custom" | "local" | "gemini" | "huanke-gemini" | "qianwen-video" | "volcano-vision" | "volcano-video" | "alibaba-image" | "ruxa-image";
+ type: "alibaba" | "openai" | "custom" | "local" | "gemini" | "huanke-gemini" | "qianwen-video" | "volcano-vision" | "volcano-video" | "alibaba-image" | "ruxa-image" | "zhipu";
baseUrl: string;
apiKey: string;
isEnabled: boolean;
@@ -128,6 +128,7 @@ type AiModel = {
isEnabled: boolean;
isDefault: boolean;
sortOrder: number;
+ isAgentModel?: boolean;
imageGenConfig?: ImageGenConfig; // 🆕 图片生成特性配置
};
@@ -284,6 +285,7 @@ const PROVIDER_TYPES = [
{ value: "qianwen-video", label: "千问视频生成" },
{ value: "volcano-vision", label: "火山引擎视觉" },
{ value: "volcano-video", label: "火山引擎视频" },
+ { value: "zhipu", label: "智普官方 4.7" },
];
export function AdminSettingsClient() {
@@ -740,7 +742,7 @@ export function AdminSettingsClient() {
const [providerForm, setProviderForm] = useState({
id: "",
name: "",
- type: "openai" as "alibaba" | "openai" | "custom" | "local" | "gemini" | "huanke-gemini" | "qianwen-video" | "volcano-vision" | "volcano-video" | "alibaba-image" | "ruxa-image",
+ type: "openai" as "alibaba" | "openai" | "custom" | "local" | "gemini" | "huanke-gemini" | "qianwen-video" | "volcano-vision" | "volcano-video" | "alibaba-image" | "ruxa-image" | "zhipu",
baseUrl: "",
apiKey: "",
isEnabled: true,
@@ -759,6 +761,7 @@ export function AdminSettingsClient() {
supportsImageGen: false,
isEnabled: true,
isDefault: false,
+ isAgentModel: false,
sortOrder: 0,
fallbackModelId1: "", // 备用模型 ID 1
fallbackModelId2: "", // 备用模型 ID 2
@@ -1353,6 +1356,7 @@ export function AdminSettingsClient() {
supportsImageGen: false,
isEnabled: true,
isDefault: false,
+ isAgentModel: false,
sortOrder: 0,
fallbackModelId1: "",
fallbackModelId2: "",
@@ -2307,6 +2311,7 @@ export function AdminSettingsClient() {
"alibaba-image": "bg-amber-500",
"ruxa-image": "bg-emerald-500",
"openai": "bg-green-500",
+ "zhipu": "bg-lime-500",
"qianwen-video": "bg-cyan-500",
"custom": "bg-gray-500",
"local": "bg-yellow-500",
@@ -3617,6 +3622,8 @@ export function AdminSettingsClient() {
baseUrl:
!prev.baseUrl && value === "ruxa-image"
? "https://api.ruxa.ai"
+ : !prev.baseUrl && value === "zhipu"
+ ? "https://open.bigmodel.cn/api/paas/v4"
: prev.baseUrl,
}))
}
@@ -3634,7 +3641,13 @@ export function AdminSettingsClient() {
setProviderForm({ ...providerForm, baseUrl: e.target.value })}
- placeholder={providerForm.type === "ruxa-image" ? "https://api.ruxa.ai" : "https://api.openai.com/v1"}
+ placeholder={
+ providerForm.type === "ruxa-image"
+ ? "https://api.ruxa.ai"
+ : providerForm.type === "zhipu"
+ ? "https://open.bigmodel.cn/api/paas/v4"
+ : "https://api.openai.com/v1"
+ }
/>
@@ -3899,6 +3912,13 @@ export function AdminSettingsClient() {
+
+ setModelForm({ ...modelForm, isAgentModel: checked })}
+ />
+
+
{/* 🆕 图片生成模型特性配置 */}
{modelForm.supportsImageGen && (
@@ -4029,6 +4049,7 @@ export function AdminSettingsClient() {
{model.displayName}
{model.isDefault && 默认}
+ {model.isAgentModel && Agent}
ID: {model.modelId} | 提供商: {provider?.name ?? "未知"}
@@ -4046,6 +4067,7 @@ export function AdminSettingsClient() {
supportsImageGen: model.supportsImageGen,
isEnabled: model.isEnabled,
isDefault: model.isDefault,
+ isAgentModel: model.isAgentModel || false,
sortOrder: model.sortOrder || 0,
fallbackModelId1: (model as any).fallbackModelId1 || "",
fallbackModelId2: (model as any).fallbackModelId2 || "",
diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts
index 9e8c990fb..8f47fe4dd 100644
--- a/app/(chat)/api/chat/route.ts
+++ b/app/(chat)/api/chat/route.ts
@@ -54,6 +54,7 @@ import { uploadFile } from "@/lib/storage/service";
import type { ChatMessage } from "@/lib/types";
import type { AppUsage } from "@/lib/usage";
import { convertToUIMessages, generateUUID } from "@/lib/utils";
+import { isAiModelAgentAuto } from "@/lib/db/ai-model-flags";
import { generateTitleFromUserMessage } from "../../actions";
import { type PostRequestBody, postRequestBodySchema } from "./schema";
@@ -832,7 +833,13 @@ export async function POST(request: Request) {
if (selectedModelIsUUID) {
const selectedModelConfig = await getAiModelById({ id: requestedModelId });
- if (selectedModelConfig?.supportsImageGen && !selectedModelConfig?.isSubModel) {
+ if (selectedModelConfig && isAiModelAgentAuto(selectedModelConfig)) {
+ shouldRunAgentConversation = true;
+ shouldKeepSelectedModel = false;
+ console.log(
+ `[Smart Auto Mode] 🧠 Agent-only model detected, force auto routing: ${selectedModelConfig.displayName}`
+ );
+ } else if (selectedModelConfig?.supportsImageGen && !selectedModelConfig?.isSubModel) {
shouldRunAgentConversation = true;
shouldKeepSelectedModel = true;
console.log(
diff --git a/app/(chat)/api/models/route.ts b/app/(chat)/api/models/route.ts
index 5b13e17d1..80d11f1e6 100644
--- a/app/(chat)/api/models/route.ts
+++ b/app/(chat)/api/models/route.ts
@@ -1,6 +1,7 @@
import { auth } from "@/app/(auth)/auth";
import { getEnabledAiModelsWithProvider, getDefaultAiModel } from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
+import { isAiModelAgentAuto } from "@/lib/db/ai-model-flags";
// 获取前端可用的 AI 模型列表(需要登录但不需要管理员权限)
export async function GET() {
@@ -14,9 +15,12 @@ export async function GET() {
// 只获取主模型(不包括子模型),子模型由后端根据 videoParams.mode 自动选择
const modelsWithProvider = await getEnabledAiModelsWithProvider(false);
const defaultModel = await getDefaultAiModel();
+ const visibleModelsWithProvider = modelsWithProvider.filter(
+ ({ model }) => !isAiModelAgentAuto(model)
+ );
// 转换为前端需要的格式
- const models = modelsWithProvider.map(({ model, provider }) => ({
+ const models = visibleModelsWithProvider.map(({ model, provider }) => ({
id: model.id,
modelId: model.modelId,
displayName: model.displayName,
@@ -31,9 +35,18 @@ export async function GET() {
capabilities: model.capabilities, // 🆕 返回子模型的能力配置
}));
+ const resolvedDefaultModelId =
+ (defaultModel &&
+ !isAiModelAgentAuto(defaultModel) &&
+ models.some((model) => model.id === defaultModel.id)
+ ? defaultModel.id
+ : models.find((model) => model.isDefault)?.id) ??
+ models[0]?.id ??
+ null;
+
return Response.json({
models,
- defaultModelId: defaultModel?.id ?? models[0]?.id ?? null,
+ defaultModelId: resolvedDefaultModelId,
});
} catch (error) {
if (error instanceof ChatSDKError) {
@@ -42,4 +55,3 @@ export async function GET() {
return new ChatSDKError("offline:chat").toResponse();
}
}
-
diff --git a/app/(chat)/page.tsx b/app/(chat)/page.tsx
index 50b3d68dd..d71d0f896 100644
--- a/app/(chat)/page.tsx
+++ b/app/(chat)/page.tsx
@@ -6,6 +6,7 @@ import { DataStreamHandler } from "@/components/data-stream-handler";
import { generateUUID } from "@/lib/utils";
import { auth } from "../(auth)/auth";
import { getDefaultAiModel, getEnabledAiModelsWithProvider } from "@/lib/db/queries";
+import { isAiModelAgentAuto } from "@/lib/db/ai-model-flags";
export default function Page() {
return (
@@ -33,14 +34,17 @@ async function NewChatPage() {
if (!initialModelId) {
// 尝试获取默认模型
const defaultModel = await getDefaultAiModel();
-
- if (defaultModel) {
+
+ if (defaultModel && !isAiModelAgentAuto(defaultModel)) {
initialModelId = defaultModel.id;
} else {
// 如果没有设置默认模型,获取第一个启用的模型
const enabledModels = await getEnabledAiModelsWithProvider();
- if (enabledModels.length > 0) {
- initialModelId = enabledModels[0].model.id;
+ const visibleModels = enabledModels.filter(
+ ({ model }) => !isAiModelAgentAuto(model)
+ );
+ if (visibleModels.length > 0) {
+ initialModelId = visibleModels[0].model.id;
}
}
}
diff --git a/app/api/admin/ai-models/route.ts b/app/api/admin/ai-models/route.ts
index 1e06e9009..29b6f6ac1 100644
--- a/app/api/admin/ai-models/route.ts
+++ b/app/api/admin/ai-models/route.ts
@@ -10,6 +10,7 @@ import {
getAiProviderById,
} from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
+import { isAiModelAgentAuto } from "@/lib/db/ai-model-flags";
// 🆕 图片生成模型特性配置 Schema
const imageGenConfigSchema = z.object({
@@ -35,6 +36,7 @@ const createModelSchema = z.object({
sortOrder: z.number().optional(),
fallbackModelId1: z.string().max(128).optional(),
fallbackModelId2: z.string().max(128).optional(),
+ isAgentModel: z.boolean().optional(),
imageGenConfig: imageGenConfigSchema, // 🆕 图片生成特性配置
});
@@ -51,9 +53,17 @@ const updateModelSchema = z.object({
sortOrder: z.number().optional(),
fallbackModelId1: z.string().max(128).optional(),
fallbackModelId2: z.string().max(128).optional(),
+ isAgentModel: z.boolean().optional(),
imageGenConfig: imageGenConfigSchema, // 🆕 图片生成特性配置
});
+function normalizeModel(model: any) {
+ return {
+ ...model,
+ isAgentModel: isAiModelAgentAuto(model),
+ };
+}
+
export async function GET(request: Request) {
try {
await requireAdmin();
@@ -67,16 +77,16 @@ export async function GET(request: Request) {
if (!model) {
return new ChatSDKError("bad_request:api", "Model not found").toResponse();
}
- return Response.json({ model });
+ return Response.json({ model: normalizeModel(model) });
}
if (providerId) {
const models = await getAiModelsByProviderId({ providerId });
- return Response.json({ models });
+ return Response.json({ models: models.map(normalizeModel) });
}
const models = await getAllAiModels();
- return Response.json({ models });
+ return Response.json({ models: models.map(normalizeModel) });
} catch (error) {
if (error instanceof ChatSDKError) {
return error.toResponse();
@@ -100,7 +110,7 @@ export async function POST(request: Request) {
const model = await createAiModel(data);
- return Response.json({ model: model[0] });
+ return Response.json({ model: normalizeModel(model[0]) });
} catch (error) {
if (error instanceof ChatSDKError) {
return error.toResponse();
@@ -129,7 +139,7 @@ export async function PUT(request: Request) {
}
console.log("[Admin API] Model updated successfully:", model[0].id); // Debug log
- return Response.json({ model: model[0] });
+ return Response.json({ model: normalizeModel(model[0]) });
} catch (error) {
console.error("[Admin API] Update model error:", error); // Debug log
if (error instanceof ChatSDKError) {
@@ -164,4 +174,3 @@ export async function DELETE(request: Request) {
return new ChatSDKError("offline:admin").toResponse();
}
}
-
diff --git a/app/api/admin/ai-providers/route.ts b/app/api/admin/ai-providers/route.ts
index 0e53d69eb..929077590 100644
--- a/app/api/admin/ai-providers/route.ts
+++ b/app/api/admin/ai-providers/route.ts
@@ -9,9 +9,26 @@ import {
} from "@/lib/db/queries";
import { ChatSDKError } from "@/lib/errors";
+const providerTypes = [
+ "alibaba",
+ "openai",
+ "custom",
+ "local",
+ "gemini",
+ "huanke-gemini",
+ "qianwen-video",
+ "volcano-vision",
+ "volcano-video",
+ "alibaba-image",
+ "ruxa-image",
+ "zhipu",
+] as const;
+type ProviderType = (typeof providerTypes)[number];
+type StoredProviderType = Exclude
;
+
const createProviderSchema = z.object({
name: z.string().min(1).max(128),
- type: z.enum(["alibaba", "openai", "custom", "local", "gemini", "huanke-gemini", "qianwen-video", "volcano-vision", "volcano-video", "alibaba-image", "ruxa-image"]),
+ type: z.enum(providerTypes),
baseUrl: z.string().min(1), // 改为简单的字符串验证,支持 localhost
apiKey: z.string().min(1),
isEnabled: z.boolean().optional(),
@@ -20,12 +37,32 @@ const createProviderSchema = z.object({
const updateProviderSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(128).optional(),
- type: z.enum(["alibaba", "openai", "custom", "local", "gemini", "huanke-gemini", "qianwen-video", "volcano-vision", "volcano-video", "alibaba-image", "ruxa-image"]).optional(),
+ type: z.enum(providerTypes).optional(),
baseUrl: z.string().min(1).optional(), // 改为简单的字符串验证,支持 localhost
apiKey: z.string().min(1).optional(),
isEnabled: z.boolean().optional(),
});
+const ZHIPU_BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
+
+function normalizeProviderTypeForStorage(
+ type?: ProviderType
+): StoredProviderType | undefined {
+ if (!type) return undefined;
+ return type === "zhipu" ? "custom" : type;
+}
+
+function isZhipuBaseUrl(baseUrl: string) {
+ return baseUrl.includes("open.bigmodel.cn");
+}
+
+function normalizeProviderTypeForResponse(provider: Record) {
+ if (provider.type === "custom" && typeof provider.baseUrl === "string" && isZhipuBaseUrl(provider.baseUrl)) {
+ return { ...provider, type: "zhipu" };
+ }
+ return provider;
+}
+
function maskApiKey(apiKey?: string) {
if (!apiKey) return undefined;
if (apiKey.length <= 8) return "****";
@@ -55,20 +92,22 @@ export async function GET(request: Request) {
}
// 隐藏 API Key 的部分内容
return Response.json({
- provider: {
+ provider: normalizeProviderTypeForResponse({
...provider,
apiKey: provider.apiKey.slice(0, 8) + "****" + provider.apiKey.slice(-4),
- },
+ }),
});
}
const providers = await getAllAiProviders();
// 隐藏 API Key 的部分内容
return Response.json({
- providers: providers.map((p) => ({
- ...p,
- apiKey: p.apiKey.slice(0, 8) + "****" + p.apiKey.slice(-4),
- })),
+ providers: providers.map((p) =>
+ normalizeProviderTypeForResponse({
+ ...p,
+ apiKey: p.apiKey.slice(0, 8) + "****" + p.apiKey.slice(-4),
+ })
+ ),
});
} catch (error) {
if (error instanceof ChatSDKError) {
@@ -87,14 +126,17 @@ export async function POST(request: Request) {
const data = createProviderSchema.parse(body);
console.log("[Admin API] Validation passed, creating provider...");
+ const storageType = normalizeProviderTypeForStorage(data.type) ?? "custom";
const provider = await createAiProvider({
...data,
+ type: storageType,
+ baseUrl: data.type === "zhipu" ? data.baseUrl || ZHIPU_BASE_URL : data.baseUrl,
updatedBy: session.user.id,
});
console.log("[Admin API] Provider created successfully:", provider[0].id);
- return Response.json({ provider: provider[0] });
+ return Response.json({ provider: normalizeProviderTypeForResponse(provider[0] as any) });
} catch (error) {
console.error("[Admin API] Create provider error:", error);
if (error instanceof ChatSDKError) {
@@ -117,6 +159,7 @@ export async function PUT(request: Request) {
const provider = await updateAiProvider({
...data,
+ type: normalizeProviderTypeForStorage(data.type),
updatedBy: session.user.id,
});
@@ -124,7 +167,7 @@ export async function PUT(request: Request) {
return new ChatSDKError("bad_request:api", "Provider not found").toResponse();
}
- return Response.json({ provider: provider[0] });
+ return Response.json({ provider: normalizeProviderTypeForResponse(provider[0] as any) });
} catch (error) {
if (error instanceof ChatSDKError) {
return error.toResponse();
diff --git a/components/multimodal-input.tsx b/components/multimodal-input.tsx
index 2b30fa7ef..b48765aa3 100644
--- a/components/multimodal-input.tsx
+++ b/components/multimodal-input.tsx
@@ -1058,6 +1058,26 @@ function PureModelSelectorCompact({
setOptimisticModelId(selectedModelId);
}, [selectedModelId]);
+ useEffect(() => {
+ if (displayModels.length === 0) return;
+
+ const currentExists = displayModels.some((m) => m.id === selectedModelId);
+ if (currentExists) return;
+
+ const fallbackModelId =
+ dynamicModels.find((m) => m.isDefault)?.id ??
+ dynamicModels[0]?.id ??
+ (showAutoModel ? "auto" : "");
+
+ if (!fallbackModelId) return;
+
+ setOptimisticModelId(fallbackModelId);
+ onModelChange?.(fallbackModelId);
+ startTransition(() => {
+ saveChatModelAsCookie(fallbackModelId);
+ });
+ }, [displayModels, dynamicModels, onModelChange, selectedModelId, showAutoModel]);
+
// 自动选择模型的逻辑 - 必须在所有条件 return 之前定义
const selectBestModel = useCallback(() => {
const hasAttachments = attachments && attachments.length > 0;
diff --git a/lib/ai/auto-agent-graph.ts b/lib/ai/auto-agent-graph.ts
index 002184e07..e4a434ca1 100644
--- a/lib/ai/auto-agent-graph.ts
+++ b/lib/ai/auto-agent-graph.ts
@@ -65,16 +65,25 @@ async function generateAgentConversation(
state: AutoAgentStateType
): Promise {
try {
- const { getEnabledAiModelsWithProvider } = await import("@/lib/db/queries");
+ const {
+ getEnabledAiModelsWithProvider,
+ getAgentAiModelWithProvider,
+ } = await import("@/lib/db/queries");
const { getLanguageModelById } = await import("./dynamic-provider");
const allModels = await getEnabledAiModelsWithProvider();
+ const agentModel = await getAgentAiModelWithProvider();
+ const canUseAgentModel = Boolean(
+ agentModel && (!state.hasImage || agentModel.model.supportsVision)
+ );
const preferredVisionModel = allModels.find(
(m) => m.provider.type === "alibaba" && m.model.supportsVision && m.model.isEnabled
);
- const candidateModel = state.hasImage
- ? preferredVisionModel || allModels.find((m) => m.model.supportsVision && m.model.isEnabled)
- : allModels.find((m) => m.model.isEnabled && !m.model.isSubModel);
+ const candidateModel =
+ (canUseAgentModel ? agentModel : undefined) ||
+ (state.hasImage
+ ? preferredVisionModel || allModels.find((m) => m.model.supportsVision && m.model.isEnabled)
+ : allModels.find((m) => m.model.isEnabled && !m.model.isSubModel));
if (!candidateModel) {
return analysis.conversationalReply || null;
diff --git a/lib/ai/dynamic-provider.ts b/lib/ai/dynamic-provider.ts
index 5ee6982c8..ff5faa554 100644
--- a/lib/ai/dynamic-provider.ts
+++ b/lib/ai/dynamic-provider.ts
@@ -66,6 +66,17 @@ function getOrCreateProvider(provider: Pick !isAiModelAgentAuto(item.model)
+ );
+ const allModels = nonAgentModels.length > 0 ? nonAgentModels : allModelsRaw;
console.log("[Model Router] Available models:", allModels.length);
// 获取意图对应的配置
diff --git a/lib/ai/prompt-optimizer.ts b/lib/ai/prompt-optimizer.ts
index 890ee6aa5..87567121a 100644
--- a/lib/ai/prompt-optimizer.ts
+++ b/lib/ai/prompt-optimizer.ts
@@ -294,21 +294,26 @@ export async function optimizePromptWithLLM(params: {
try {
// 动态导入避免循环依赖
- const { getEnabledAiModelsWithProvider } = await import("@/lib/db/queries");
+ const {
+ getEnabledAiModelsWithProvider,
+ getAgentAiModelWithProvider,
+ } = await import("@/lib/db/queries");
const { getLanguageModelById } = await import("./dynamic-provider");
// 获取千问3 VL 模型
const allModels = await getEnabledAiModelsWithProvider();
+ const agentModel = await getAgentAiModelWithProvider();
const qwenModel = allModels.find(
(m) => m.provider.type === "alibaba" && m.model.supportsVision
);
+ const selectedOptimizerModel = agentModel || qwenModel;
- if (!qwenModel) {
+ if (!selectedOptimizerModel) {
console.log("[Prompt Optimizer LLM] No Qwen3 VL model, falling back to template");
return optimizePrompt({ originalPrompt, intent, subIntent });
}
- const model = await getLanguageModelById(qwenModel.model.id);
+ const model = await getLanguageModelById(selectedOptimizerModel.model.id);
if (!model) {
console.log("[Prompt Optimizer LLM] Failed to get model, falling back to template");
return optimizePrompt({ originalPrompt, intent, subIntent });
diff --git a/lib/ai/smart-analyzer.ts b/lib/ai/smart-analyzer.ts
index 091b4c20c..71aa78b70 100644
--- a/lib/ai/smart-analyzer.ts
+++ b/lib/ai/smart-analyzer.ts
@@ -206,21 +206,29 @@ async function analyzWithLLM(
context?: string
): Promise {
// 使用动态导入避免循环依赖问题
- const { getEnabledAiModelsWithProvider } = await import("@/lib/db/queries");
+ const {
+ getEnabledAiModelsWithProvider,
+ getAgentAiModelWithProvider,
+ } = await import("@/lib/db/queries");
const { getLanguageModelById } = await import("./dynamic-provider");
// 获取千问3 VL 模型
const allModels = await getEnabledAiModelsWithProvider();
+ const agentModel = await getAgentAiModelWithProvider();
const qwenModel = allModels.find(
(m) => m.provider.type === "alibaba" && m.model.supportsVision
);
+ const selectedAnalyzerModel =
+ (agentModel && (!hasImage || agentModel.model.supportsVision)
+ ? agentModel
+ : undefined) || qwenModel;
- if (!qwenModel) {
+ if (!selectedAnalyzerModel) {
console.log("[Smart Analyzer] No Qwen3 VL model found");
return null;
}
- const model = await getLanguageModelById(qwenModel.model.id);
+ const model = await getLanguageModelById(selectedAnalyzerModel.model.id);
if (!model) {
console.log("[Smart Analyzer] Failed to get language model");
return null;
diff --git a/lib/db/ai-model-flags.ts b/lib/db/ai-model-flags.ts
new file mode 100644
index 000000000..6ddde95b5
--- /dev/null
+++ b/lib/db/ai-model-flags.ts
@@ -0,0 +1,40 @@
+const AGENT_AUTO_MODEL_KEY = "agentAutoModel";
+
+function toRecord(value: unknown): Record | null {
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
+ return null;
+ }
+ return value as Record;
+}
+
+export function isAiModelAgentAuto(input: {
+ defaultParams?: unknown;
+ capabilities?: unknown;
+}): boolean {
+ const defaultParams = toRecord(input.defaultParams);
+ if (typeof defaultParams?.[AGENT_AUTO_MODEL_KEY] === "boolean") {
+ return defaultParams[AGENT_AUTO_MODEL_KEY] as boolean;
+ }
+
+ const capabilities = toRecord(input.capabilities);
+ if (typeof capabilities?.[AGENT_AUTO_MODEL_KEY] === "boolean") {
+ return capabilities[AGENT_AUTO_MODEL_KEY] as boolean;
+ }
+
+ return false;
+}
+
+export function withAgentAutoModelFlag(
+ defaultParams: unknown,
+ enabled: boolean
+): Record | null {
+ const next = { ...(toRecord(defaultParams) || {}) };
+
+ if (enabled) {
+ next[AGENT_AUTO_MODEL_KEY] = true;
+ } else {
+ delete next[AGENT_AUTO_MODEL_KEY];
+ }
+
+ return Object.keys(next).length > 0 ? next : null;
+}
diff --git a/lib/db/migrate.ts b/lib/db/migrate.ts
index 576a2e01a..462042b46 100644
--- a/lib/db/migrate.ts
+++ b/lib/db/migrate.ts
@@ -28,7 +28,7 @@ const runMigrate = async () => {
ADD CONSTRAINT "AiProvider_type_check"
CHECK (type IN (
'alibaba', 'openai', 'custom', 'local', 'gemini',
- 'huanke-gemini', 'qianwen-video', 'volcano-vision', 'volcano-video', 'alibaba-image', 'ruxa-image'
+ 'huanke-gemini', 'qianwen-video', 'volcano-vision', 'volcano-video', 'alibaba-image', 'ruxa-image', 'zhipu'
));
`);
const end = Date.now();
diff --git a/lib/db/queries.ts b/lib/db/queries.ts
index e0524f9ee..ebf7153c1 100644
--- a/lib/db/queries.ts
+++ b/lib/db/queries.ts
@@ -51,6 +51,10 @@ import {
generatedImage,
} from "./schema";
import { generateHashedPassword } from "./utils";
+import {
+ isAiModelAgentAuto,
+ withAgentAutoModelFlag,
+} from "./ai-model-flags";
// Optionally, if not using email/pass login, you can
// use the Drizzle adapter for Auth.js / NextAuth
@@ -941,7 +945,7 @@ export async function createAiProvider({
updatedBy,
}: {
name: string;
- type: "alibaba" | "openai" | "custom" | "local" | "gemini" | "huanke-gemini" | "qianwen-video" | "volcano-vision" | "volcano-video" | "alibaba-image" | "ruxa-image";
+ type: "alibaba" | "openai" | "custom" | "local" | "gemini" | "huanke-gemini" | "qianwen-video" | "volcano-vision" | "volcano-video" | "alibaba-image" | "ruxa-image" | "zhipu";
baseUrl: string;
apiKey: string;
isEnabled?: boolean;
@@ -979,7 +983,7 @@ export async function updateAiProvider({
}: {
id: string;
name?: string;
- type?: "alibaba" | "openai" | "custom" | "local" | "gemini" | "huanke-gemini" | "qianwen-video" | "volcano-vision" | "volcano-video" | "alibaba-image" | "ruxa-image";
+ type?: "alibaba" | "openai" | "custom" | "local" | "gemini" | "huanke-gemini" | "qianwen-video" | "volcano-vision" | "volcano-video" | "alibaba-image" | "ruxa-image" | "zhipu";
baseUrl?: string;
apiKey?: string;
isEnabled?: boolean;
@@ -1126,6 +1130,7 @@ export async function createAiModel({
fallbackModelId1,
fallbackModelId2,
imageGenConfig,
+ isAgentModel,
}: {
providerId: string;
modelId: string;
@@ -1139,6 +1144,7 @@ export async function createAiModel({
fallbackModelId1?: string;
fallbackModelId2?: string;
imageGenConfig?: ImageGenConfig;
+ isAgentModel?: boolean;
}): Promise {
try {
// 如果设置为默认,先取消其他默认
@@ -1164,6 +1170,10 @@ export async function createAiModel({
fallbackModelId1: fallbackModelId1 || null,
fallbackModelId2: fallbackModelId2 || null,
imageGenConfig: imageGenConfig || null,
+ defaultParams:
+ isAgentModel === undefined
+ ? null
+ : withAgentAutoModelFlag(null, isAgentModel),
})
.returning();
} catch (_error) {
@@ -1188,6 +1198,7 @@ export async function updateAiModel({
fallbackModelId1,
fallbackModelId2,
imageGenConfig,
+ isAgentModel,
}: {
id: string;
providerId?: string;
@@ -1202,6 +1213,7 @@ export async function updateAiModel({
fallbackModelId1?: string;
fallbackModelId2?: string;
imageGenConfig?: ImageGenConfig;
+ isAgentModel?: boolean;
}): Promise {
try {
// 如果设置为默认,先取消其他默认
@@ -1228,6 +1240,22 @@ export async function updateAiModel({
if (fallbackModelId2 !== undefined) updateData.fallbackModelId2 = fallbackModelId2 || null;
// 🆕 图片生成配置
if (imageGenConfig !== undefined) updateData.imageGenConfig = imageGenConfig || null;
+ if (isAgentModel !== undefined) {
+ const [existingModel] = await db
+ .select({ defaultParams: aiModel.defaultParams })
+ .from(aiModel)
+ .where(eq(aiModel.id, id))
+ .limit(1);
+
+ if (!existingModel) {
+ return [];
+ }
+
+ updateData.defaultParams = withAgentAutoModelFlag(
+ existingModel.defaultParams,
+ isAgentModel
+ );
+ }
return await db
.update(aiModel)
@@ -1476,6 +1504,20 @@ export async function getEnabledAiModelsWithProvider(includeSubModels: boolean =
}
}
+export async function getAgentAiModelWithProvider() {
+ const models = await getEnabledAiModelsWithProvider(false);
+ const candidates = models
+ .filter(({ model }) => isAiModelAgentAuto(model))
+ .sort((a, b) => {
+ if (a.model.isDefault !== b.model.isDefault) {
+ return Number(b.model.isDefault) - Number(a.model.isDefault);
+ }
+ return (a.model.sortOrder ?? 0) - (b.model.sortOrder ?? 0);
+ });
+
+ return candidates[0];
+}
+
/**
* 🆕 获取指定父模型的所有子模型
* 用于火山引擎视频等需要智能选择模型的场景
diff --git a/lib/db/schema.ts b/lib/db/schema.ts
index 3988ed684..0fe20219e 100644
--- a/lib/db/schema.ts
+++ b/lib/db/schema.ts
@@ -215,7 +215,7 @@ export type ToolConfig = InferSelectModel;
export const aiProvider = pgTable("AiProvider", {
id: uuid("id").primaryKey().notNull().defaultRandom(),
name: varchar("name", { length: 128 }).notNull(), // 提供商名称,如 "阿里云通义千问"
- type: varchar("type", { enum: ["alibaba", "openai", "custom", "local", "gemini", "huanke-gemini", "qianwen-video", "volcano-vision", "volcano-video", "alibaba-image", "ruxa-image"] })
+ type: varchar("type", { enum: ["alibaba", "openai", "custom", "local", "gemini", "huanke-gemini", "qianwen-video", "volcano-vision", "volcano-video", "alibaba-image", "ruxa-image", "zhipu"] })
.notNull()
.default("custom"), // 提供商类型
baseUrl: text("baseUrl").notNull(), // API 基础 URL