fix: enable local moonshot detail agent option
This commit is contained in:
parent
b702440347
commit
b99eb6cf7a
@ -22,6 +22,13 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import {
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL,
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_MODEL_ID,
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID,
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_SETTING_KEY,
|
||||||
|
normalizeDetailAgentLocalMoonshotConfig,
|
||||||
|
} from "@/lib/ai/detail-agent-model-config";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import {
|
import {
|
||||||
@ -511,9 +518,6 @@ const PROVIDER_DEFAULT_BASE_URL: Record<string, string> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const LEMON_OPENAI_BASE_URL = "https://new.lemonapi.site/v1";
|
const LEMON_OPENAI_BASE_URL = "https://new.lemonapi.site/v1";
|
||||||
const MOONSHOT_OFFICIAL_BASE_URL = "https://api.moonshot.cn/v1";
|
|
||||||
const MOONSHOT_KIMI_K25_MODEL_ID = "kimi-k2.5";
|
|
||||||
|
|
||||||
const LEMON_PROVIDER_PRESET = {
|
const LEMON_PROVIDER_PRESET = {
|
||||||
name: "LemonAPI Gemini",
|
name: "LemonAPI Gemini",
|
||||||
type: "openai" as const,
|
type: "openai" as const,
|
||||||
@ -545,7 +549,10 @@ function isLemonApiBaseUrl(value?: string | null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isMoonshotOfficialBaseUrl(value?: string | null) {
|
function isMoonshotOfficialBaseUrl(value?: string | null) {
|
||||||
return String(value || "").trim().replace(/\/+$/, "") === MOONSHOT_OFFICIAL_BASE_URL;
|
return (
|
||||||
|
String(value || "").trim().replace(/\/+$/, "") ===
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AdminSettingsClient() {
|
export function AdminSettingsClient() {
|
||||||
@ -605,6 +612,11 @@ export function AdminSettingsClient() {
|
|||||||
"/api/auth-settings",
|
"/api/auth-settings",
|
||||||
fetcher,
|
fetcher,
|
||||||
);
|
);
|
||||||
|
const { data: moonshotDetailAgentSettingData, mutate: mutateMoonshotDetailAgentSetting } =
|
||||||
|
useSWR<{ setting?: { value?: unknown } }>(
|
||||||
|
`/api/admin/settings?key=${DETAIL_AGENT_LOCAL_MOONSHOT_SETTING_KEY}`,
|
||||||
|
fetcher,
|
||||||
|
);
|
||||||
|
|
||||||
// 🆕 生图记录数据
|
// 🆕 生图记录数据
|
||||||
const imageQueryParams = new URLSearchParams({
|
const imageQueryParams = new URLSearchParams({
|
||||||
@ -870,48 +882,20 @@ export function AdminSettingsClient() {
|
|||||||
}, [siteSettingsData]);
|
}, [siteSettingsData]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const configuredModelId = siteSettings.canvasDetailAgentModelId;
|
const normalized = normalizeDetailAgentLocalMoonshotConfig(
|
||||||
const providerList = providersData?.providers || [];
|
moonshotDetailAgentSettingData?.setting?.value,
|
||||||
const modelList = modelsData?.models || [];
|
);
|
||||||
|
setMoonshotDetailAgentConfig({
|
||||||
if (!configuredModelId) {
|
providerId: "",
|
||||||
const moonshotProvider = providerList.find(
|
modelEntryId: "",
|
||||||
(provider) => provider.type === "openai" && isMoonshotOfficialBaseUrl(provider.baseUrl),
|
providerName: normalized.providerName,
|
||||||
);
|
baseUrl: normalized.baseUrl,
|
||||||
setMoonshotDetailAgentConfig((prev) => ({
|
apiKey: "",
|
||||||
...prev,
|
modelId: normalized.modelId,
|
||||||
providerId: moonshotProvider?.id || "",
|
displayName: normalized.displayName,
|
||||||
providerName: moonshotProvider?.name || "Moonshot AI (Official)",
|
isEnabled: normalized.enabled,
|
||||||
baseUrl: moonshotProvider?.baseUrl || MOONSHOT_OFFICIAL_BASE_URL,
|
});
|
||||||
modelId: prev.modelId || MOONSHOT_KIMI_K25_MODEL_ID,
|
}, [moonshotDetailAgentSettingData]);
|
||||||
}));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const configuredModel = modelList.find((model) => model.id === configuredModelId) || null;
|
|
||||||
const configuredProvider = configuredModel
|
|
||||||
? providerList.find((provider) => provider.id === configuredModel.providerId)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (!configuredModel || !configuredProvider || !isMoonshotOfficialBaseUrl(configuredProvider.baseUrl)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setMoonshotDetailAgentConfig((prev) => ({
|
|
||||||
...prev,
|
|
||||||
providerId: configuredProvider.id,
|
|
||||||
modelEntryId: configuredModel.id,
|
|
||||||
providerName: configuredProvider.name || "Moonshot AI (Official)",
|
|
||||||
baseUrl: configuredProvider.baseUrl || MOONSHOT_OFFICIAL_BASE_URL,
|
|
||||||
modelId: configuredModel.modelId || "",
|
|
||||||
displayName: configuredModel.displayName || "官方 Kimi K2.5",
|
|
||||||
isEnabled: configuredModel.isEnabled !== false && configuredProvider.isEnabled !== false,
|
|
||||||
}));
|
|
||||||
}, [
|
|
||||||
modelsData?.models,
|
|
||||||
providersData?.providers,
|
|
||||||
siteSettings.canvasDetailAgentModelId,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const featuredCategoryOptions = useMemo(() => {
|
const featuredCategoryOptions = useMemo(() => {
|
||||||
const values = (siteSettings.surpriseCategories || [])
|
const values = (siteSettings.surpriseCategories || [])
|
||||||
@ -1022,9 +1006,9 @@ export function AdminSettingsClient() {
|
|||||||
providerId: "",
|
providerId: "",
|
||||||
modelEntryId: "",
|
modelEntryId: "",
|
||||||
providerName: "Moonshot AI (Official)",
|
providerName: "Moonshot AI (Official)",
|
||||||
baseUrl: MOONSHOT_OFFICIAL_BASE_URL,
|
baseUrl: DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL,
|
||||||
apiKey: "",
|
apiKey: "",
|
||||||
modelId: MOONSHOT_KIMI_K25_MODEL_ID,
|
modelId: DETAIL_AGENT_LOCAL_MOONSHOT_MODEL_ID,
|
||||||
displayName: "官方 Kimi K2.5",
|
displayName: "官方 Kimi K2.5",
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
});
|
});
|
||||||
@ -2697,9 +2681,11 @@ export function AdminSettingsClient() {
|
|||||||
const handleSaveMoonshotDetailAgentConfig = async () => {
|
const handleSaveMoonshotDetailAgentConfig = async () => {
|
||||||
try {
|
try {
|
||||||
const trimmedBaseUrl =
|
const trimmedBaseUrl =
|
||||||
moonshotDetailAgentConfig.baseUrl.trim() || MOONSHOT_OFFICIAL_BASE_URL;
|
moonshotDetailAgentConfig.baseUrl.trim() ||
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL;
|
||||||
const trimmedModelId =
|
const trimmedModelId =
|
||||||
moonshotDetailAgentConfig.modelId.trim() || MOONSHOT_KIMI_K25_MODEL_ID;
|
moonshotDetailAgentConfig.modelId.trim() ||
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_MODEL_ID;
|
||||||
const trimmedDisplayName =
|
const trimmedDisplayName =
|
||||||
moonshotDetailAgentConfig.displayName.trim() || "官方 Kimi K2.5";
|
moonshotDetailAgentConfig.displayName.trim() || "官方 Kimi K2.5";
|
||||||
const trimmedProviderName =
|
const trimmedProviderName =
|
||||||
@ -2720,90 +2706,31 @@ export function AdminSettingsClient() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const providerPayload: Record<string, any> = moonshotDetailAgentConfig.providerId
|
const settingResponse = await fetch("/api/admin/settings", {
|
||||||
? {
|
method: "POST",
|
||||||
id: moonshotDetailAgentConfig.providerId,
|
headers: { "Content-Type": "application/json" },
|
||||||
name: trimmedProviderName,
|
body: JSON.stringify({
|
||||||
type: "openai",
|
key: DETAIL_AGENT_LOCAL_MOONSHOT_SETTING_KEY,
|
||||||
baseUrl: trimmedBaseUrl,
|
value: {
|
||||||
isEnabled: moonshotDetailAgentConfig.isEnabled,
|
enabled: moonshotDetailAgentConfig.isEnabled,
|
||||||
}
|
providerName: trimmedProviderName,
|
||||||
: {
|
|
||||||
name: trimmedProviderName,
|
|
||||||
type: "openai",
|
|
||||||
baseUrl: trimmedBaseUrl,
|
baseUrl: trimmedBaseUrl,
|
||||||
apiKey: moonshotDetailAgentConfig.apiKey.trim(),
|
apiKey: moonshotDetailAgentConfig.apiKey.trim(),
|
||||||
isEnabled: moonshotDetailAgentConfig.isEnabled,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (moonshotDetailAgentConfig.providerId && moonshotDetailAgentConfig.apiKey.trim()) {
|
|
||||||
providerPayload.apiKey = moonshotDetailAgentConfig.apiKey.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
const providerResponse = await fetch("/api/admin/ai-providers", {
|
|
||||||
method: moonshotDetailAgentConfig.providerId ? "PUT" : "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(providerPayload),
|
|
||||||
});
|
|
||||||
const providerResult = await providerResponse.json().catch(() => ({}));
|
|
||||||
if (!providerResponse.ok || !providerResult?.provider?.id) {
|
|
||||||
throw new Error(String(providerResult?.error || "保存 Moonshot 提供商失败"));
|
|
||||||
}
|
|
||||||
|
|
||||||
const providerId = String(providerResult.provider.id);
|
|
||||||
const existingModel =
|
|
||||||
(moonshotDetailAgentConfig.modelEntryId &&
|
|
||||||
modelsData?.models?.find((model) => model.id === moonshotDetailAgentConfig.modelEntryId)) ||
|
|
||||||
modelsData?.models?.find(
|
|
||||||
(model) => model.providerId === providerId && model.modelId === trimmedModelId,
|
|
||||||
) ||
|
|
||||||
null;
|
|
||||||
|
|
||||||
const modelPayload: Record<string, any> = existingModel
|
|
||||||
? {
|
|
||||||
id: existingModel.id,
|
|
||||||
providerId,
|
|
||||||
modelId: trimmedModelId,
|
modelId: trimmedModelId,
|
||||||
displayName: trimmedDisplayName,
|
displayName: trimmedDisplayName,
|
||||||
description: "Moonshot 官方 Kimi K2.5,专用于 AI详情生成 的规划与排版复审",
|
},
|
||||||
supportsVision: true,
|
}),
|
||||||
supportsImageGen: false,
|
|
||||||
isEnabled: moonshotDetailAgentConfig.isEnabled,
|
|
||||||
isHidden: false,
|
|
||||||
isDefault: false,
|
|
||||||
isAgentModel: false,
|
|
||||||
isMultimodal: true,
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
providerId,
|
|
||||||
modelId: trimmedModelId,
|
|
||||||
displayName: trimmedDisplayName,
|
|
||||||
description: "Moonshot 官方 Kimi K2.5,专用于 AI详情生成 的规划与排版复审",
|
|
||||||
supportsVision: true,
|
|
||||||
supportsImageGen: false,
|
|
||||||
isEnabled: moonshotDetailAgentConfig.isEnabled,
|
|
||||||
isHidden: false,
|
|
||||||
isDefault: false,
|
|
||||||
isAgentModel: false,
|
|
||||||
isMultimodal: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const modelResponse = await fetch("/api/admin/ai-models", {
|
|
||||||
method: existingModel ? "PUT" : "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(modelPayload),
|
|
||||||
});
|
});
|
||||||
const modelResult = await modelResponse.json().catch(() => ({}));
|
const settingResult = await settingResponse.json().catch(() => ({}));
|
||||||
const savedModel = modelResult?.model;
|
if (!settingResponse.ok) {
|
||||||
if (!modelResponse.ok || !savedModel?.id) {
|
throw new Error(String(settingResult?.message || "保存 Moonshot 本地AI配置失败"));
|
||||||
throw new Error(String(modelResult?.error || "保存 Kimi K2.5 模型失败"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsResponse = await fetch("/api/site-settings", {
|
const settingsResponse = await fetch("/api/site-settings", {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
canvasDetailAgentModelId: savedModel.id,
|
canvasDetailAgentModelId: DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (!settingsResponse.ok) {
|
if (!settingsResponse.ok) {
|
||||||
@ -2812,12 +2739,10 @@ export function AdminSettingsClient() {
|
|||||||
|
|
||||||
setSiteSettings((prev) => ({
|
setSiteSettings((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
canvasDetailAgentModelId: savedModel.id,
|
canvasDetailAgentModelId: DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID,
|
||||||
}));
|
}));
|
||||||
setMoonshotDetailAgentConfig((prev) => ({
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
providerId,
|
|
||||||
modelEntryId: String(savedModel.id),
|
|
||||||
providerName: trimmedProviderName,
|
providerName: trimmedProviderName,
|
||||||
baseUrl: trimmedBaseUrl,
|
baseUrl: trimmedBaseUrl,
|
||||||
modelId: trimmedModelId,
|
modelId: trimmedModelId,
|
||||||
@ -2825,8 +2750,7 @@ export function AdminSettingsClient() {
|
|||||||
apiKey: "",
|
apiKey: "",
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mutateProviders();
|
mutateMoonshotDetailAgentSetting();
|
||||||
mutateModels();
|
|
||||||
mutateSiteSettings();
|
mutateSiteSettings();
|
||||||
toast.success("Moonshot 官方 Kimi K2.5 已绑定为 AI详情 Agent 专用模型");
|
toast.success("Moonshot 官方 Kimi K2.5 已绑定为 AI详情 Agent 专用模型");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -5756,6 +5680,11 @@ export function AdminSettingsClient() {
|
|||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="agent-default">沿用Agent对话默认模型</SelectItem>
|
<SelectItem value="agent-default">沿用Agent对话默认模型</SelectItem>
|
||||||
|
{moonshotDetailAgentConfig.isEnabled ? (
|
||||||
|
<SelectItem value={DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID}>
|
||||||
|
{moonshotDetailAgentConfig.displayName} · 本地AI配置
|
||||||
|
</SelectItem>
|
||||||
|
) : null}
|
||||||
{detailAgentModelOptions.map((model) => (
|
{detailAgentModelOptions.map((model) => (
|
||||||
<SelectItem key={model.id} value={model.id}>
|
<SelectItem key={model.id} value={model.id}>
|
||||||
{model.displayName}
|
{model.displayName}
|
||||||
@ -8310,10 +8239,10 @@ export function AdminSettingsClient() {
|
|||||||
baseUrl: e.target.value,
|
baseUrl: e.target.value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
placeholder={MOONSHOT_OFFICIAL_BASE_URL}
|
placeholder={DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL}
|
||||||
/>
|
/>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
根据 Moonshot 官方文档,OpenAI 兼容接口地址为 `{MOONSHOT_OFFICIAL_BASE_URL}`
|
根据 Moonshot 官方文档,OpenAI 兼容接口地址为 `{DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL}`
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -8350,7 +8279,7 @@ export function AdminSettingsClient() {
|
|||||||
modelId: e.target.value,
|
modelId: e.target.value,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
placeholder={MOONSHOT_KIMI_K25_MODEL_ID}
|
placeholder={DETAIL_AGENT_LOCAL_MOONSHOT_MODEL_ID}
|
||||||
/>
|
/>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
已按你提供的 Moonshot 官方文档默认填写 `kimi-k2.5`;保存后系统会自动创建/更新对应模型记录。
|
已按你提供的 Moonshot 官方文档默认填写 `kimi-k2.5`;保存后系统会自动创建/更新对应模型记录。
|
||||||
|
|||||||
@ -1,9 +1,16 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { requireAdmin } from "@/lib/middleware/admin";
|
import { requireAdmin } from "@/lib/middleware/admin";
|
||||||
import {
|
import {
|
||||||
|
createAiModel,
|
||||||
|
createAiProvider,
|
||||||
|
getAiModelById,
|
||||||
|
getAiProviderById,
|
||||||
|
getAllAiModels,
|
||||||
getAllSystemSettings,
|
getAllSystemSettings,
|
||||||
getSystemSetting,
|
getSystemSetting,
|
||||||
upsertSystemSetting,
|
upsertSystemSetting,
|
||||||
|
updateAiModel,
|
||||||
|
updateAiProvider,
|
||||||
} from "@/lib/db/queries";
|
} from "@/lib/db/queries";
|
||||||
import { ChatSDKError } from "@/lib/errors";
|
import { ChatSDKError } from "@/lib/errors";
|
||||||
import { probeBflModelSupport } from "@/lib/realtime/bfl-capability";
|
import { probeBflModelSupport } from "@/lib/realtime/bfl-capability";
|
||||||
@ -12,12 +19,33 @@ import {
|
|||||||
normalizeRealtimeCanvasConfigForStorage,
|
normalizeRealtimeCanvasConfigForStorage,
|
||||||
resolveRealtimeProviders,
|
resolveRealtimeProviders,
|
||||||
} from "@/lib/realtime/config";
|
} from "@/lib/realtime/config";
|
||||||
|
import { db } from "@/lib/db";
|
||||||
|
import { siteSettings } from "@/lib/db/schema";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
const settingSchema = z.object({
|
const settingSchema = z.object({
|
||||||
key: z.string(),
|
key: z.string(),
|
||||||
value: z.any(),
|
value: z.any(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const moonshotDetailAgentConfigSchema = z.object({
|
||||||
|
providerId: z.string().uuid().optional(),
|
||||||
|
modelEntryId: z.string().uuid().optional(),
|
||||||
|
providerName: z.string().trim().min(1).max(128).optional(),
|
||||||
|
baseUrl: z.string().trim().min(1),
|
||||||
|
apiKey: z.string().optional(),
|
||||||
|
modelId: z.string().trim().min(1).max(128).optional(),
|
||||||
|
displayName: z.string().trim().min(1).max(128).optional(),
|
||||||
|
isEnabled: z.boolean().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const MOONSHOT_OFFICIAL_BASE_URL = "https://api.moonshot.cn/v1";
|
||||||
|
const MOONSHOT_KIMI_K25_MODEL_ID = "kimi-k2.5";
|
||||||
|
|
||||||
|
function isMoonshotOfficialBaseUrl(value: string) {
|
||||||
|
return String(value || "").trim().replace(/\/+$/, "") === MOONSHOT_OFFICIAL_BASE_URL;
|
||||||
|
}
|
||||||
|
|
||||||
function isObjectLike(value: unknown): value is Record<string, unknown> {
|
function isObjectLike(value: unknown): value is Record<string, unknown> {
|
||||||
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
||||||
}
|
}
|
||||||
@ -58,6 +86,132 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
let nextValue = value;
|
let nextValue = value;
|
||||||
|
|
||||||
|
if (key === "moonshot_detail_agent_config") {
|
||||||
|
const config = moonshotDetailAgentConfigSchema.parse(value || {});
|
||||||
|
const normalizedBaseUrl = String(config.baseUrl || "").trim() || MOONSHOT_OFFICIAL_BASE_URL;
|
||||||
|
const normalizedModelId = String(config.modelId || "").trim() || MOONSHOT_KIMI_K25_MODEL_ID;
|
||||||
|
const normalizedDisplayName = String(config.displayName || "").trim() || "官方 Kimi K2.5";
|
||||||
|
const normalizedProviderName =
|
||||||
|
String(config.providerName || "").trim() || "Moonshot AI (Official)";
|
||||||
|
const normalizedApiKey = String(config.apiKey || "").trim();
|
||||||
|
const enabled = config.isEnabled !== false;
|
||||||
|
|
||||||
|
if (!isMoonshotOfficialBaseUrl(normalizedBaseUrl)) {
|
||||||
|
return new ChatSDKError(
|
||||||
|
"bad_request:api",
|
||||||
|
`Moonshot 官方地址必须为 ${MOONSHOT_OFFICIAL_BASE_URL}`,
|
||||||
|
).toResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.providerId && !normalizedApiKey) {
|
||||||
|
return new ChatSDKError("bad_request:api", "缺少 Moonshot 官方 API Key").toResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
const [existingSiteSettings] = await db.select().from(siteSettings).limit(1);
|
||||||
|
|
||||||
|
const providerRows = config.providerId
|
||||||
|
? await updateAiProvider({
|
||||||
|
id: config.providerId,
|
||||||
|
name: normalizedProviderName,
|
||||||
|
type: "openai",
|
||||||
|
baseUrl: normalizedBaseUrl,
|
||||||
|
apiKey: normalizedApiKey || undefined,
|
||||||
|
isEnabled: enabled,
|
||||||
|
updatedBy: session.user.id,
|
||||||
|
})
|
||||||
|
: await createAiProvider({
|
||||||
|
name: normalizedProviderName,
|
||||||
|
type: "openai",
|
||||||
|
baseUrl: normalizedBaseUrl,
|
||||||
|
apiKey: normalizedApiKey,
|
||||||
|
isEnabled: enabled,
|
||||||
|
updatedBy: session.user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const provider = providerRows[0];
|
||||||
|
if (!provider) {
|
||||||
|
return new ChatSDKError("bad_request:database", "保存 Moonshot 提供商失败").toResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
let existingModel =
|
||||||
|
config.modelEntryId ? await getAiModelById({ id: config.modelEntryId }) : undefined;
|
||||||
|
|
||||||
|
if (!existingModel) {
|
||||||
|
const allModels = await getAllAiModels();
|
||||||
|
existingModel =
|
||||||
|
allModels.find(
|
||||||
|
(model) => model.providerId === provider.id && model.modelId === normalizedModelId,
|
||||||
|
) || undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modelRows = existingModel
|
||||||
|
? await updateAiModel({
|
||||||
|
id: existingModel.id,
|
||||||
|
providerId: provider.id,
|
||||||
|
modelId: normalizedModelId,
|
||||||
|
displayName: normalizedDisplayName,
|
||||||
|
description: "Moonshot 官方 Kimi K2.5,专用于 AI详情生成 的规划与排版复审",
|
||||||
|
supportsVision: true,
|
||||||
|
supportsImageGen: false,
|
||||||
|
isEnabled: enabled,
|
||||||
|
isHidden: false,
|
||||||
|
isDefault: false,
|
||||||
|
isAgentModel: false,
|
||||||
|
isMultimodal: true,
|
||||||
|
})
|
||||||
|
: await createAiModel({
|
||||||
|
providerId: provider.id,
|
||||||
|
modelId: normalizedModelId,
|
||||||
|
displayName: normalizedDisplayName,
|
||||||
|
description: "Moonshot 官方 Kimi K2.5,专用于 AI详情生成 的规划与排版复审",
|
||||||
|
supportsVision: true,
|
||||||
|
supportsImageGen: false,
|
||||||
|
isEnabled: enabled,
|
||||||
|
isHidden: false,
|
||||||
|
isDefault: false,
|
||||||
|
isAgentModel: false,
|
||||||
|
isMultimodal: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const model = modelRows[0];
|
||||||
|
if (!model) {
|
||||||
|
return new ChatSDKError("bad_request:database", "保存 Kimi K2.5 模型失败").toResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingSiteSettings) {
|
||||||
|
await db
|
||||||
|
.update(siteSettings)
|
||||||
|
.set({
|
||||||
|
canvasDetailAgentModelId: model.id,
|
||||||
|
updatedAt: new Date(),
|
||||||
|
updatedBy: session.user.id,
|
||||||
|
})
|
||||||
|
.where(eq(siteSettings.id, existingSiteSettings.id));
|
||||||
|
} else {
|
||||||
|
await db.insert(siteSettings).values({
|
||||||
|
siteName: "AI Chatbot",
|
||||||
|
canvasDetailAgentModelId: model.id,
|
||||||
|
updatedBy: session.user.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
setting: {
|
||||||
|
key: "moonshot_detail_agent_config",
|
||||||
|
value: {
|
||||||
|
providerId: provider.id,
|
||||||
|
modelEntryId: model.id,
|
||||||
|
providerName: provider.name,
|
||||||
|
baseUrl: provider.baseUrl,
|
||||||
|
modelId: model.modelId,
|
||||||
|
displayName: model.displayName,
|
||||||
|
isEnabled: model.isEnabled,
|
||||||
|
canvasDetailAgentModelId: model.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (key === "realtime_canvas_config") {
|
if (key === "realtime_canvas_config") {
|
||||||
if (!isObjectLike(value)) {
|
if (!isObjectLike(value)) {
|
||||||
return new ChatSDKError("bad_request:api", "realtime_canvas_config value 必须为对象").toResponse();
|
return new ChatSDKError("bad_request:api", "realtime_canvas_config value 必须为对象").toResponse();
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
isAiModelMultimodal,
|
isAiModelMultimodal,
|
||||||
} from "@/lib/db/ai-model-flags";
|
} from "@/lib/db/ai-model-flags";
|
||||||
import { getLanguageModelById } from "@/lib/ai/dynamic-provider";
|
import { getLanguageModelById } from "@/lib/ai/dynamic-provider";
|
||||||
|
import { resolveDetailAgentLocalMoonshotModel } from "@/lib/ai/detail-agent-model-runtime";
|
||||||
import { inlineVisionImages } from "@/lib/ai/vision-inline";
|
import { inlineVisionImages } from "@/lib/ai/vision-inline";
|
||||||
import { filterReachableRemoteUrls } from "@/lib/net/url-access";
|
import { filterReachableRemoteUrls } from "@/lib/net/url-access";
|
||||||
import {
|
import {
|
||||||
@ -1806,6 +1807,9 @@ export async function POST(request: Request) {
|
|||||||
getAgentAiModelWithProvider(),
|
getAgentAiModelWithProvider(),
|
||||||
getSiteSettings(),
|
getSiteSettings(),
|
||||||
]);
|
]);
|
||||||
|
const localDetailAgentResolved = await resolveDetailAgentLocalMoonshotModel({
|
||||||
|
siteSettingModelId: currentSiteSettings?.canvasDetailAgentModelId,
|
||||||
|
});
|
||||||
const reasoningCompatible = allModels.filter(
|
const reasoningCompatible = allModels.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
!item.model.isSubModel &&
|
!item.model.isSubModel &&
|
||||||
@ -1829,45 +1833,65 @@ export async function POST(request: Request) {
|
|||||||
const preferredAgentConfig = configuredDetailAgentConfig || fallbackAgentConfig;
|
const preferredAgentConfig = configuredDetailAgentConfig || fallbackAgentConfig;
|
||||||
let plannerConfig = preferredAgentConfig;
|
let plannerConfig = preferredAgentConfig;
|
||||||
|
|
||||||
if (referenceImages.length > 0) {
|
if (!localDetailAgentResolved && referenceImages.length > 0) {
|
||||||
if (!plannerConfig || !supportsVisionPlanning(plannerConfig.model)) {
|
if (!plannerConfig || !supportsVisionPlanning(plannerConfig.model)) {
|
||||||
plannerConfig = reasoningCompatible.find((item) => supportsVisionPlanning(item.model));
|
plannerConfig = reasoningCompatible.find((item) => supportsVisionPlanning(item.model));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!plannerConfig) {
|
if (!localDetailAgentResolved && !plannerConfig) {
|
||||||
plannerConfig = reasoningCompatible[0];
|
plannerConfig = reasoningCompatible[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!plannerConfig) {
|
if (!localDetailAgentResolved && !plannerConfig) {
|
||||||
return NextResponse.json({ success: true, data: fallback });
|
return NextResponse.json({ success: true, data: fallback });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedModelRef = localDetailAgentResolved
|
||||||
|
? localDetailAgentResolved.modelRef
|
||||||
|
: plannerConfig!.model;
|
||||||
|
const selectedProviderRef = localDetailAgentResolved
|
||||||
|
? localDetailAgentResolved.providerRef
|
||||||
|
: {
|
||||||
|
id: plannerConfig!.provider.id,
|
||||||
|
name: plannerConfig!.provider.name,
|
||||||
|
type: plannerConfig!.provider.type,
|
||||||
|
baseUrl: plannerConfig!.provider.baseUrl,
|
||||||
|
};
|
||||||
|
|
||||||
console.log("[Canvas Detail Plan] Planner model selected:", {
|
console.log("[Canvas Detail Plan] Planner model selected:", {
|
||||||
id: plannerConfig.model.id,
|
id: selectedModelRef.id,
|
||||||
displayName: plannerConfig.model.displayName,
|
displayName: selectedModelRef.displayName,
|
||||||
modelId: plannerConfig.model.modelId,
|
modelId: selectedModelRef.modelId,
|
||||||
codingModelId: getAiModelAgentCodingModelId(plannerConfig.model),
|
codingModelId: localDetailAgentResolved
|
||||||
providerType: plannerConfig.provider.type,
|
? null
|
||||||
supportsVision: plannerConfig.model.supportsVision,
|
: getAiModelAgentCodingModelId(plannerConfig!.model),
|
||||||
|
providerType: selectedProviderRef.type,
|
||||||
|
supportsVision: selectedModelRef.supportsVision,
|
||||||
detailAgentSettingModelId: currentSiteSettings?.canvasDetailAgentModelId || null,
|
detailAgentSettingModelId: currentSiteSettings?.canvasDetailAgentModelId || null,
|
||||||
selectionSource:
|
selectionSource:
|
||||||
configuredDetailAgentConfig?.model.id === plannerConfig.model.id
|
localDetailAgentResolved
|
||||||
|
? "detail-agent-local-config"
|
||||||
|
: configuredDetailAgentConfig?.model.id === plannerConfig?.model.id
|
||||||
? "detail-agent-setting"
|
? "detail-agent-setting"
|
||||||
: fallbackAgentConfig?.model.id === plannerConfig.model.id
|
: fallbackAgentConfig?.model.id === plannerConfig?.model.id
|
||||||
? "agent-default"
|
? "agent-default"
|
||||||
: "vision-fallback",
|
: "vision-fallback",
|
||||||
multimodalFlag: isAiModelMultimodal({
|
multimodalFlag: isAiModelMultimodal({
|
||||||
capabilities: plannerConfig.model.capabilities,
|
capabilities: selectedModelRef.capabilities,
|
||||||
defaultParams: plannerConfig.model.defaultParams,
|
defaultParams: selectedModelRef.defaultParams,
|
||||||
}),
|
}),
|
||||||
hasReferenceImages: referenceImages.length > 0,
|
hasReferenceImages: referenceImages.length > 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const codingModelId = getAiModelAgentCodingModelId(plannerConfig.model);
|
const codingModelId = localDetailAgentResolved
|
||||||
const model = await getLanguageModelById(
|
? null
|
||||||
plannerConfig.model.id,
|
: getAiModelAgentCodingModelId(plannerConfig!.model);
|
||||||
codingModelId
|
const model = localDetailAgentResolved
|
||||||
);
|
? localDetailAgentResolved.model
|
||||||
|
: await getLanguageModelById(
|
||||||
|
plannerConfig!.model.id,
|
||||||
|
codingModelId
|
||||||
|
);
|
||||||
if (!model) {
|
if (!model) {
|
||||||
return NextResponse.json({ success: true, data: fallback });
|
return NextResponse.json({ success: true, data: fallback });
|
||||||
}
|
}
|
||||||
@ -1898,9 +1922,12 @@ export async function POST(request: Request) {
|
|||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let imageInputs: Array<{ type: "image"; image: string }> = [];
|
let imageInputs: Array<{ type: "image"; image: string }> = [];
|
||||||
if (supportsVisionPlanning(plannerConfig.model) && referenceImages.length > 0) {
|
if (
|
||||||
|
(localDetailAgentResolved || supportsVisionPlanning(plannerConfig!.model)) &&
|
||||||
|
referenceImages.length > 0
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
if (isVolcanoResponsesPlanner(plannerConfig)) {
|
if (!localDetailAgentResolved && isVolcanoResponsesPlanner(plannerConfig!)) {
|
||||||
imageInputs = referenceImages
|
imageInputs = referenceImages
|
||||||
.slice(0, 6)
|
.slice(0, 6)
|
||||||
.map((url) => ({ type: "image" as const, image: url }));
|
.map((url) => ({ type: "image" as const, image: url }));
|
||||||
@ -2042,9 +2069,9 @@ export async function POST(request: Request) {
|
|||||||
issues: [fallbackReason],
|
issues: [fallbackReason],
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
id: plannerConfig.model.id,
|
id: selectedModelRef.id,
|
||||||
displayName: plannerConfig.model.displayName,
|
displayName: selectedModelRef.displayName,
|
||||||
providerType: plannerConfig.provider.type,
|
providerType: selectedProviderRef.type,
|
||||||
},
|
},
|
||||||
preflight: {
|
preflight: {
|
||||||
passed: false,
|
passed: false,
|
||||||
@ -2224,9 +2251,9 @@ export async function POST(request: Request) {
|
|||||||
})
|
})
|
||||||
: undefined,
|
: undefined,
|
||||||
model: {
|
model: {
|
||||||
id: plannerConfig.model.id,
|
id: selectedModelRef.id,
|
||||||
displayName: plannerConfig.model.displayName,
|
displayName: selectedModelRef.displayName,
|
||||||
providerType: plannerConfig.provider.type,
|
providerType: selectedProviderRef.type,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
isAiModelMultimodal,
|
isAiModelMultimodal,
|
||||||
} from "@/lib/db/ai-model-flags";
|
} from "@/lib/db/ai-model-flags";
|
||||||
import { getLanguageModelById } from "@/lib/ai/dynamic-provider";
|
import { getLanguageModelById } from "@/lib/ai/dynamic-provider";
|
||||||
|
import { resolveDetailAgentLocalMoonshotModel } from "@/lib/ai/detail-agent-model-runtime";
|
||||||
import { inlineVisionImages } from "@/lib/ai/vision-inline";
|
import { inlineVisionImages } from "@/lib/ai/vision-inline";
|
||||||
import {
|
import {
|
||||||
buildDetailUiProtocol,
|
buildDetailUiProtocol,
|
||||||
@ -195,6 +196,9 @@ export async function POST(request: Request) {
|
|||||||
getAgentAiModelWithProvider(),
|
getAgentAiModelWithProvider(),
|
||||||
getSiteSettings(),
|
getSiteSettings(),
|
||||||
]);
|
]);
|
||||||
|
const localDetailAgentResolved = await resolveDetailAgentLocalMoonshotModel({
|
||||||
|
siteSettingModelId: currentSiteSettings?.canvasDetailAgentModelId,
|
||||||
|
});
|
||||||
const reviewCompatible = allModels.filter(
|
const reviewCompatible = allModels.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
!item.model.isSubModel &&
|
!item.model.isSubModel &&
|
||||||
@ -217,7 +221,7 @@ export async function POST(request: Request) {
|
|||||||
? reviewCompatible.find((item) => item.model.id === agentModel.model.id)
|
? reviewCompatible.find((item) => item.model.id === agentModel.model.id)
|
||||||
: undefined;
|
: undefined;
|
||||||
let reviewerConfig = configuredDetailAgentConfig || fallbackAgentConfig;
|
let reviewerConfig = configuredDetailAgentConfig || fallbackAgentConfig;
|
||||||
if (!reviewerConfig || !supportsVisionReview(reviewerConfig.model)) {
|
if (!localDetailAgentResolved && (!reviewerConfig || !supportsVisionReview(reviewerConfig.model))) {
|
||||||
reviewerConfig = reviewCompatible.find((item) => supportsVisionReview(item.model));
|
reviewerConfig = reviewCompatible.find((item) => supportsVisionReview(item.model));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +257,7 @@ export async function POST(request: Request) {
|
|||||||
});
|
});
|
||||||
const currentUiValidation = validateDetailUiProtocol(currentUiProtocol);
|
const currentUiValidation = validateDetailUiProtocol(currentUiProtocol);
|
||||||
|
|
||||||
if (!reviewerConfig) {
|
if (!localDetailAgentResolved && !reviewerConfig) {
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
@ -272,23 +276,40 @@ export async function POST(request: Request) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const codingModelId = getAiModelAgentCodingModelId(reviewerConfig.model);
|
const selectedModelRef = localDetailAgentResolved
|
||||||
const model = await getLanguageModelById(
|
? localDetailAgentResolved.modelRef
|
||||||
reviewerConfig.model.id,
|
: reviewerConfig!.model;
|
||||||
codingModelId
|
const selectedProviderRef = localDetailAgentResolved
|
||||||
);
|
? localDetailAgentResolved.providerRef
|
||||||
|
: {
|
||||||
|
id: reviewerConfig!.provider.id,
|
||||||
|
name: reviewerConfig!.provider.name,
|
||||||
|
type: reviewerConfig!.provider.type,
|
||||||
|
baseUrl: reviewerConfig!.provider.baseUrl,
|
||||||
|
};
|
||||||
|
const codingModelId = localDetailAgentResolved
|
||||||
|
? null
|
||||||
|
: getAiModelAgentCodingModelId(reviewerConfig!.model);
|
||||||
|
const model = localDetailAgentResolved
|
||||||
|
? localDetailAgentResolved.model
|
||||||
|
: await getLanguageModelById(
|
||||||
|
reviewerConfig!.model.id,
|
||||||
|
codingModelId
|
||||||
|
);
|
||||||
console.log("[Canvas Detail Review] Reviewer model selected:", {
|
console.log("[Canvas Detail Review] Reviewer model selected:", {
|
||||||
id: reviewerConfig.model.id,
|
id: selectedModelRef.id,
|
||||||
displayName: reviewerConfig.model.displayName,
|
displayName: selectedModelRef.displayName,
|
||||||
modelId: reviewerConfig.model.modelId,
|
modelId: selectedModelRef.modelId,
|
||||||
codingModelId,
|
codingModelId,
|
||||||
providerType: reviewerConfig.provider.type,
|
providerType: selectedProviderRef.type,
|
||||||
supportsVision: reviewerConfig.model.supportsVision,
|
supportsVision: selectedModelRef.supportsVision,
|
||||||
detailAgentSettingModelId: currentSiteSettings?.canvasDetailAgentModelId || null,
|
detailAgentSettingModelId: currentSiteSettings?.canvasDetailAgentModelId || null,
|
||||||
selectionSource:
|
selectionSource:
|
||||||
configuredDetailAgentConfig?.model.id === reviewerConfig.model.id
|
localDetailAgentResolved
|
||||||
|
? "detail-agent-local-config"
|
||||||
|
: configuredDetailAgentConfig?.model.id === reviewerConfig?.model.id
|
||||||
? "detail-agent-setting"
|
? "detail-agent-setting"
|
||||||
: fallbackAgentConfig?.model.id === reviewerConfig.model.id
|
: fallbackAgentConfig?.model.id === reviewerConfig?.model.id
|
||||||
? "agent-default"
|
? "agent-default"
|
||||||
: "vision-fallback",
|
: "vision-fallback",
|
||||||
});
|
});
|
||||||
@ -355,7 +376,11 @@ export async function POST(request: Request) {
|
|||||||
|
|
||||||
const reviewImageCandidates = [imageUrl, ...referenceImages].slice(0, 6);
|
const reviewImageCandidates = [imageUrl, ...referenceImages].slice(0, 6);
|
||||||
let effectiveReviewImages = reviewImageCandidates;
|
let effectiveReviewImages = reviewImageCandidates;
|
||||||
if (!isVolcanoResponsesReviewer(reviewerConfig)) {
|
if (
|
||||||
|
!localDetailAgentResolved &&
|
||||||
|
reviewerConfig &&
|
||||||
|
!isVolcanoResponsesReviewer(reviewerConfig)
|
||||||
|
) {
|
||||||
const { inlineDataUrls } = await inlineVisionImages(reviewImageCandidates, {
|
const { inlineDataUrls } = await inlineVisionImages(reviewImageCandidates, {
|
||||||
maxImages: 6,
|
maxImages: 6,
|
||||||
maxSide: 900,
|
maxSide: 900,
|
||||||
@ -409,9 +434,9 @@ export async function POST(request: Request) {
|
|||||||
uiPatch: undefined,
|
uiPatch: undefined,
|
||||||
uiSurfacePatch: undefined,
|
uiSurfacePatch: undefined,
|
||||||
model: {
|
model: {
|
||||||
id: reviewerConfig.model.id,
|
id: selectedModelRef.id,
|
||||||
displayName: reviewerConfig.model.displayName,
|
displayName: selectedModelRef.displayName,
|
||||||
providerType: reviewerConfig.provider.type,
|
providerType: selectedProviderRef.type,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -565,9 +590,9 @@ export async function POST(request: Request) {
|
|||||||
issues: uiValidation.issues.map((item) => item.message),
|
issues: uiValidation.issues.map((item) => item.message),
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
id: reviewerConfig.model.id,
|
id: selectedModelRef.id,
|
||||||
displayName: reviewerConfig.model.displayName,
|
displayName: selectedModelRef.displayName,
|
||||||
providerType: reviewerConfig.provider.type,
|
providerType: selectedProviderRef.type,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
41
lib/ai/detail-agent-model-config.ts
Normal file
41
lib/ai/detail-agent-model-config.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
export const DETAIL_AGENT_LOCAL_MOONSHOT_SETTING_KEY =
|
||||||
|
"canvas_detail_agent_moonshot_config";
|
||||||
|
export const DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID =
|
||||||
|
"__moonshot_kimi_k25_local__";
|
||||||
|
export const DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL =
|
||||||
|
"https://api.moonshot.cn/v1";
|
||||||
|
export const DETAIL_AGENT_LOCAL_MOONSHOT_MODEL_ID = "kimi-k2.5";
|
||||||
|
|
||||||
|
export type DetailAgentLocalMoonshotConfig = {
|
||||||
|
enabled: boolean;
|
||||||
|
providerName: string;
|
||||||
|
baseUrl: string;
|
||||||
|
apiKey: string;
|
||||||
|
modelId: string;
|
||||||
|
displayName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isDetailAgentLocalMoonshotOption(value: unknown) {
|
||||||
|
return String(value || "").trim() === DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeDetailAgentLocalMoonshotConfig(
|
||||||
|
value: unknown,
|
||||||
|
): DetailAgentLocalMoonshotConfig {
|
||||||
|
const raw =
|
||||||
|
value && typeof value === "object" && !Array.isArray(value)
|
||||||
|
? (value as Record<string, unknown>)
|
||||||
|
: {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
enabled: raw.enabled !== false,
|
||||||
|
providerName:
|
||||||
|
String(raw.providerName || "").trim() || "Moonshot AI (Official)",
|
||||||
|
baseUrl:
|
||||||
|
String(raw.baseUrl || "").trim() || DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL,
|
||||||
|
apiKey: String(raw.apiKey || "").trim(),
|
||||||
|
modelId:
|
||||||
|
String(raw.modelId || "").trim() || DETAIL_AGENT_LOCAL_MOONSHOT_MODEL_ID,
|
||||||
|
displayName: String(raw.displayName || "").trim() || "官方 Kimi K2.5",
|
||||||
|
};
|
||||||
|
}
|
||||||
73
lib/ai/detail-agent-model-runtime.ts
Normal file
73
lib/ai/detail-agent-model-runtime.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import "server-only";
|
||||||
|
|
||||||
|
import { createOpenAI } from "@ai-sdk/openai";
|
||||||
|
import type { LanguageModel } from "ai";
|
||||||
|
import { getSystemSetting } from "@/lib/db/queries";
|
||||||
|
import {
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL,
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID,
|
||||||
|
DETAIL_AGENT_LOCAL_MOONSHOT_SETTING_KEY,
|
||||||
|
isDetailAgentLocalMoonshotOption,
|
||||||
|
normalizeDetailAgentLocalMoonshotConfig,
|
||||||
|
} from "@/lib/ai/detail-agent-model-config";
|
||||||
|
|
||||||
|
export async function resolveDetailAgentLocalMoonshotModel(params: {
|
||||||
|
siteSettingModelId?: string | null;
|
||||||
|
}) {
|
||||||
|
if (!isDetailAgentLocalMoonshotOption(params.siteSettingModelId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const setting = await getSystemSetting({
|
||||||
|
key: DETAIL_AGENT_LOCAL_MOONSHOT_SETTING_KEY,
|
||||||
|
}).catch(() => null);
|
||||||
|
const config = normalizeDetailAgentLocalMoonshotConfig(setting?.value);
|
||||||
|
|
||||||
|
if (!config.enabled || !config.apiKey.trim()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const provider = createOpenAI({
|
||||||
|
apiKey: config.apiKey,
|
||||||
|
baseURL: config.baseUrl || DETAIL_AGENT_LOCAL_MOONSHOT_BASE_URL,
|
||||||
|
});
|
||||||
|
const model = provider.chat(config.modelId);
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: "local_moonshot" as const,
|
||||||
|
model,
|
||||||
|
modelRef: {
|
||||||
|
id: DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID,
|
||||||
|
displayName: config.displayName,
|
||||||
|
modelId: config.modelId,
|
||||||
|
supportsVision: true,
|
||||||
|
capabilities: { multimodalModel: true },
|
||||||
|
defaultParams: null,
|
||||||
|
},
|
||||||
|
providerRef: {
|
||||||
|
id: DETAIL_AGENT_LOCAL_MOONSHOT_OPTION_ID,
|
||||||
|
name: config.providerName,
|
||||||
|
type: "openai",
|
||||||
|
baseUrl: config.baseUrl,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DetailAgentResolvedModel = {
|
||||||
|
kind: "local_moonshot" | "db_model";
|
||||||
|
model: LanguageModel;
|
||||||
|
modelRef: {
|
||||||
|
id: string;
|
||||||
|
displayName: string;
|
||||||
|
modelId: string;
|
||||||
|
supportsVision?: boolean | null;
|
||||||
|
capabilities?: unknown;
|
||||||
|
defaultParams?: unknown;
|
||||||
|
};
|
||||||
|
providerRef: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
baseUrl?: string | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user