feat: add moonshot detail agent model config
This commit is contained in:
parent
9a96824e2b
commit
656aacbd01
@ -258,6 +258,7 @@ type SiteSettings = {
|
|||||||
canvasDefaultModelId: string | null;
|
canvasDefaultModelId: string | null;
|
||||||
canvasAutoModeDefaultModelId: string | null;
|
canvasAutoModeDefaultModelId: string | null;
|
||||||
canvasDetailDefaultModelId: string | null;
|
canvasDetailDefaultModelId: string | null;
|
||||||
|
canvasDetailAgentModelId: string | null;
|
||||||
lovartComposerPrefix: string;
|
lovartComposerPrefix: string;
|
||||||
lovartComposerPreviewTexts: string[];
|
lovartComposerPreviewTexts: string[];
|
||||||
lovartPromptTagVisibility: LovartPromptTagVisibility;
|
lovartPromptTagVisibility: LovartPromptTagVisibility;
|
||||||
@ -510,6 +511,8 @@ 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",
|
||||||
@ -541,6 +544,10 @@ function isLemonApiBaseUrl(value?: string | null) {
|
|||||||
return String(value || "").trim().replace(/\/+$/, "") === LEMON_OPENAI_BASE_URL;
|
return String(value || "").trim().replace(/\/+$/, "") === LEMON_OPENAI_BASE_URL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isMoonshotOfficialBaseUrl(value?: string | null) {
|
||||||
|
return String(value || "").trim().replace(/\/+$/, "") === MOONSHOT_OFFICIAL_BASE_URL;
|
||||||
|
}
|
||||||
|
|
||||||
export function AdminSettingsClient() {
|
export function AdminSettingsClient() {
|
||||||
const [activeTab, setActiveTab] = useState("users");
|
const [activeTab, setActiveTab] = useState("users");
|
||||||
|
|
||||||
@ -625,6 +632,14 @@ export function AdminSettingsClient() {
|
|||||||
: null;
|
: null;
|
||||||
const { data: analyticsData } = useSWR<ImageAnalytics>(analyticsUrl, fetcher);
|
const { data: analyticsData } = useSWR<ImageAnalytics>(analyticsUrl, fetcher);
|
||||||
|
|
||||||
|
const detailAgentModelOptions = useMemo(
|
||||||
|
() =>
|
||||||
|
(modelsData?.models || []).filter(
|
||||||
|
(model) => !model.supportsImageGen && model.isEnabled && !model.isHidden,
|
||||||
|
),
|
||||||
|
[modelsData?.models],
|
||||||
|
);
|
||||||
|
|
||||||
// 网站设置状态
|
// 网站设置状态
|
||||||
const [siteSettings, setSiteSettings] = useState<SiteSettings>({
|
const [siteSettings, setSiteSettings] = useState<SiteSettings>({
|
||||||
siteName: "AI Chatbot",
|
siteName: "AI Chatbot",
|
||||||
@ -666,6 +681,7 @@ export function AdminSettingsClient() {
|
|||||||
canvasDefaultModelId: null,
|
canvasDefaultModelId: null,
|
||||||
canvasAutoModeDefaultModelId: null,
|
canvasAutoModeDefaultModelId: null,
|
||||||
canvasDetailDefaultModelId: null,
|
canvasDetailDefaultModelId: null,
|
||||||
|
canvasDetailAgentModelId: null,
|
||||||
lovartComposerPrefix: "让Buyi-ai",
|
lovartComposerPrefix: "让Buyi-ai",
|
||||||
lovartComposerPreviewTexts: [
|
lovartComposerPreviewTexts: [
|
||||||
"打造引人注目的社交媒体视觉",
|
"打造引人注目的社交媒体视觉",
|
||||||
@ -766,6 +782,7 @@ export function AdminSettingsClient() {
|
|||||||
canvasDefaultModelId: siteSettingsData.canvasDefaultModelId || null,
|
canvasDefaultModelId: siteSettingsData.canvasDefaultModelId || null,
|
||||||
canvasAutoModeDefaultModelId: siteSettingsData.canvasAutoModeDefaultModelId || null,
|
canvasAutoModeDefaultModelId: siteSettingsData.canvasAutoModeDefaultModelId || null,
|
||||||
canvasDetailDefaultModelId: siteSettingsData.canvasDetailDefaultModelId || null,
|
canvasDetailDefaultModelId: siteSettingsData.canvasDetailDefaultModelId || null,
|
||||||
|
canvasDetailAgentModelId: (siteSettingsData as any).canvasDetailAgentModelId || null,
|
||||||
lovartComposerPrefix:
|
lovartComposerPrefix:
|
||||||
String((siteSettingsData as any).lovartComposerPrefix || "").trim() ||
|
String((siteSettingsData as any).lovartComposerPrefix || "").trim() ||
|
||||||
"让Buyi-ai",
|
"让Buyi-ai",
|
||||||
@ -852,6 +869,50 @@ export function AdminSettingsClient() {
|
|||||||
}
|
}
|
||||||
}, [siteSettingsData]);
|
}, [siteSettingsData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const configuredModelId = siteSettings.canvasDetailAgentModelId;
|
||||||
|
const providerList = providersData?.providers || [];
|
||||||
|
const modelList = modelsData?.models || [];
|
||||||
|
|
||||||
|
if (!configuredModelId) {
|
||||||
|
const moonshotProvider = providerList.find(
|
||||||
|
(provider) => provider.type === "openai" && isMoonshotOfficialBaseUrl(provider.baseUrl),
|
||||||
|
);
|
||||||
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
providerId: moonshotProvider?.id || "",
|
||||||
|
providerName: moonshotProvider?.name || "Moonshot AI (Official)",
|
||||||
|
baseUrl: moonshotProvider?.baseUrl || MOONSHOT_OFFICIAL_BASE_URL,
|
||||||
|
modelId: prev.modelId || MOONSHOT_KIMI_K25_MODEL_ID,
|
||||||
|
}));
|
||||||
|
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 || [])
|
||||||
.map((item) => String(item || "").trim())
|
.map((item) => String(item || "").trim())
|
||||||
@ -957,6 +1018,16 @@ export function AdminSettingsClient() {
|
|||||||
apiKey: "",
|
apiKey: "",
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
});
|
});
|
||||||
|
const [moonshotDetailAgentConfig, setMoonshotDetailAgentConfig] = useState({
|
||||||
|
providerId: "",
|
||||||
|
modelEntryId: "",
|
||||||
|
providerName: "Moonshot AI (Official)",
|
||||||
|
baseUrl: MOONSHOT_OFFICIAL_BASE_URL,
|
||||||
|
apiKey: "",
|
||||||
|
modelId: MOONSHOT_KIMI_K25_MODEL_ID,
|
||||||
|
displayName: "官方 Kimi K2.5",
|
||||||
|
isEnabled: true,
|
||||||
|
});
|
||||||
|
|
||||||
// 加载抠图API配置
|
// 加载抠图API配置
|
||||||
const { data: rmbgConfigData } = useSWR("/api/admin/settings?key=rmbg_api_config", fetcher);
|
const { data: rmbgConfigData } = useSWR("/api/admin/settings?key=rmbg_api_config", fetcher);
|
||||||
@ -2623,6 +2694,147 @@ export function AdminSettingsClient() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSaveMoonshotDetailAgentConfig = async () => {
|
||||||
|
try {
|
||||||
|
const trimmedBaseUrl =
|
||||||
|
moonshotDetailAgentConfig.baseUrl.trim() || MOONSHOT_OFFICIAL_BASE_URL;
|
||||||
|
const trimmedModelId =
|
||||||
|
moonshotDetailAgentConfig.modelId.trim() || MOONSHOT_KIMI_K25_MODEL_ID;
|
||||||
|
const trimmedDisplayName =
|
||||||
|
moonshotDetailAgentConfig.displayName.trim() || "官方 Kimi K2.5";
|
||||||
|
const trimmedProviderName =
|
||||||
|
moonshotDetailAgentConfig.providerName.trim() || "Moonshot AI (Official)";
|
||||||
|
|
||||||
|
if (!isMoonshotOfficialBaseUrl(trimmedBaseUrl)) {
|
||||||
|
toast.error("请使用 Moonshot 官方 API 地址:https://api.moonshot.cn/v1");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trimmedModelId) {
|
||||||
|
toast.error("请填写官方 Kimi K2.5 的 API 模型 ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!moonshotDetailAgentConfig.providerId && !moonshotDetailAgentConfig.apiKey.trim()) {
|
||||||
|
toast.error("请填写 Moonshot 官方 API Key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerPayload: Record<string, any> = moonshotDetailAgentConfig.providerId
|
||||||
|
? {
|
||||||
|
id: moonshotDetailAgentConfig.providerId,
|
||||||
|
name: trimmedProviderName,
|
||||||
|
type: "openai",
|
||||||
|
baseUrl: trimmedBaseUrl,
|
||||||
|
isEnabled: moonshotDetailAgentConfig.isEnabled,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: trimmedProviderName,
|
||||||
|
type: "openai",
|
||||||
|
baseUrl: trimmedBaseUrl,
|
||||||
|
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,
|
||||||
|
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 savedModel = modelResult?.model;
|
||||||
|
if (!modelResponse.ok || !savedModel?.id) {
|
||||||
|
throw new Error(String(modelResult?.error || "保存 Kimi K2.5 模型失败"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingsResponse = await fetch("/api/site-settings", {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
canvasDetailAgentModelId: savedModel.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!settingsResponse.ok) {
|
||||||
|
throw new Error("写入 AI详情 Agent 专用模型设置失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
setSiteSettings((prev) => ({
|
||||||
|
...prev,
|
||||||
|
canvasDetailAgentModelId: savedModel.id,
|
||||||
|
}));
|
||||||
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
providerId,
|
||||||
|
modelEntryId: String(savedModel.id),
|
||||||
|
providerName: trimmedProviderName,
|
||||||
|
baseUrl: trimmedBaseUrl,
|
||||||
|
modelId: trimmedModelId,
|
||||||
|
displayName: trimmedDisplayName,
|
||||||
|
apiKey: "",
|
||||||
|
}));
|
||||||
|
|
||||||
|
mutateProviders();
|
||||||
|
mutateModels();
|
||||||
|
mutateSiteSettings();
|
||||||
|
toast.success("Moonshot 官方 Kimi K2.5 已绑定为 AI详情 Agent 专用模型");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("[Moonshot Detail Agent Config] Error:", error);
|
||||||
|
toast.error(error instanceof Error ? error.message : "保存 Moonshot Kimi 配置失败");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 快速添加火山引擎视频预设模型
|
// 快速添加火山引擎视频预设模型
|
||||||
const handleQuickAddVolcanoModels = async (providerId: string) => {
|
const handleQuickAddVolcanoModels = async (providerId: string) => {
|
||||||
if (!confirm("将为此服务商添加4个预设模型:\n- Seedance Pro (全功能)\n- Seedance Pro Fast (快速)\n- Seedance Lite I2V (图生视频)\n- Seedance Lite T2V (文生视频)\n\n继续吗?")) {
|
if (!confirm("将为此服务商添加4个预设模型:\n- Seedance Pro (全功能)\n- Seedance Pro Fast (快速)\n- Seedance Lite I2V (图生视频)\n- Seedance Lite T2V (文生视频)\n\n继续吗?")) {
|
||||||
@ -5526,6 +5738,38 @@ export function AdminSettingsClient() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2 mt-4">
|
||||||
|
<Label htmlFor="canvasDetailAgentModelId">
|
||||||
|
AI详情生成 - Agent专用模型
|
||||||
|
</Label>
|
||||||
|
<Select
|
||||||
|
value={siteSettings.canvasDetailAgentModelId || "agent-default"}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setSiteSettings({
|
||||||
|
...siteSettings,
|
||||||
|
canvasDetailAgentModelId: value === "agent-default" ? null : value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger id="canvasDetailAgentModelId">
|
||||||
|
<SelectValue placeholder="选择详情专用Agent模型" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="agent-default">沿用Agent对话默认模型</SelectItem>
|
||||||
|
{detailAgentModelOptions.map((model) => (
|
||||||
|
<SelectItem key={model.id} value={model.id}>
|
||||||
|
{model.displayName}
|
||||||
|
{model.supportsVision ? " · 多模态" : ""}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
用于 AI详情生成 的分镜规划与排版复审。未设置时沿用 Agent 对话默认模型;建议选择官方
|
||||||
|
Kimi K2.5 多模态模型。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2 mt-4">
|
<div className="space-y-2 mt-4">
|
||||||
<Label htmlFor="canvasDetailDefaultModelId">AI详情生成 - 默认生图模型</Label>
|
<Label htmlFor="canvasDetailDefaultModelId">AI详情生成 - 默认生图模型</Label>
|
||||||
<Select
|
<Select
|
||||||
@ -8032,6 +8276,140 @@ export function AdminSettingsClient() {
|
|||||||
|
|
||||||
{/* 本地AI设置 */}
|
{/* 本地AI设置 */}
|
||||||
<TabsContent value="rmbg" className="mt-6 space-y-6">
|
<TabsContent value="rmbg" className="mt-6 space-y-6">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-xl">Moonshot 官方 Kimi K2.5 配置</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
为 AI详情生成 单独配置 Moonshot 官方 Kimi K2.5 模型。保存后会自动绑定到“AI详情生成 - Agent专用模型”。
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-6 pt-6">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="moonshotProviderName">提供商名称</Label>
|
||||||
|
<Input
|
||||||
|
id="moonshotProviderName"
|
||||||
|
value={moonshotDetailAgentConfig.providerName}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
providerName: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
placeholder="Moonshot AI (Official)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="moonshotBaseUrl">官方 API 地址</Label>
|
||||||
|
<Input
|
||||||
|
id="moonshotBaseUrl"
|
||||||
|
value={moonshotDetailAgentConfig.baseUrl}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
baseUrl: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
placeholder={MOONSHOT_OFFICIAL_BASE_URL}
|
||||||
|
/>
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
根据 Moonshot 官方文档,OpenAI 兼容接口地址为 `{MOONSHOT_OFFICIAL_BASE_URL}`
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="moonshotApiKey">
|
||||||
|
API Key {moonshotDetailAgentConfig.providerId ? "(留空则不修改)" : "*"}
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="moonshotApiKey"
|
||||||
|
type="password"
|
||||||
|
value={moonshotDetailAgentConfig.apiKey}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
apiKey: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
placeholder={
|
||||||
|
moonshotDetailAgentConfig.providerId
|
||||||
|
? "留空则不修改当前 Key"
|
||||||
|
: "请输入 Moonshot 官方 API Key"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="moonshotModelId">Kimi K2.5 API 模型 ID *</Label>
|
||||||
|
<Input
|
||||||
|
id="moonshotModelId"
|
||||||
|
value={moonshotDetailAgentConfig.modelId}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
modelId: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
placeholder={MOONSHOT_KIMI_K25_MODEL_ID}
|
||||||
|
/>
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
已按你提供的 Moonshot 官方文档默认填写 `kimi-k2.5`;保存后系统会自动创建/更新对应模型记录。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="moonshotDisplayName">模型显示名称</Label>
|
||||||
|
<Input
|
||||||
|
id="moonshotDisplayName"
|
||||||
|
value={moonshotDetailAgentConfig.displayName}
|
||||||
|
onChange={(e) =>
|
||||||
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
displayName: e.target.value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
placeholder="官方 Kimi K2.5"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between rounded-lg border p-3">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<Label className="text-sm">启用该详情 Agent 模型</Label>
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
关闭后仍会保留记录,但不会被 AI详情生成 选作专用 Agent 模型。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Switch
|
||||||
|
checked={moonshotDetailAgentConfig.isEnabled}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
setMoonshotDetailAgentConfig((prev) => ({
|
||||||
|
...prev,
|
||||||
|
isEnabled: checked,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<span className="text-sm font-medium text-muted-foreground min-w-[3rem]">
|
||||||
|
{moonshotDetailAgentConfig.isEnabled ? "已开启" : "已关闭"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-lg border border-blue-200 bg-blue-50 p-4 text-xs text-blue-800 space-y-1">
|
||||||
|
<p>• 专用于 AI详情生成 的分镜规划与排版复审,不影响普通 Agent 对话。</p>
|
||||||
|
<p>• 系统会按多模态 Agent 模型写入:支持视觉、支持结构化规划、支持协议复审。</p>
|
||||||
|
<p>• 保存成功后会自动写入“AI详情生成 - Agent专用模型”。</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button onClick={handleSaveMoonshotDetailAgentConfig}>
|
||||||
|
<Save className="mr-2 h-4 w-4" />
|
||||||
|
保存 Moonshot Kimi K2.5 配置
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-xl">Tripo3D API 配置</CardTitle>
|
<CardTitle className="text-xl">Tripo3D API 配置</CardTitle>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { auth } from "@/app/(auth)/auth";
|
|||||||
import {
|
import {
|
||||||
getAgentAiModelWithProvider,
|
getAgentAiModelWithProvider,
|
||||||
getEnabledAiModelsWithProvider,
|
getEnabledAiModelsWithProvider,
|
||||||
|
getSiteSettings,
|
||||||
} from "@/lib/db/queries";
|
} from "@/lib/db/queries";
|
||||||
import {
|
import {
|
||||||
getAiModelAgentCodingModelId,
|
getAiModelAgentCodingModelId,
|
||||||
@ -1800,19 +1801,32 @@ export async function POST(request: Request) {
|
|||||||
bundleImageCount: bundleImageUrls.length,
|
bundleImageCount: bundleImageUrls.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
const allModels = await getEnabledAiModelsWithProvider(false);
|
const [allModels, agentModel, currentSiteSettings] = await Promise.all([
|
||||||
const agentModel = await getAgentAiModelWithProvider();
|
getEnabledAiModelsWithProvider(false),
|
||||||
|
getAgentAiModelWithProvider(),
|
||||||
|
getSiteSettings(),
|
||||||
|
]);
|
||||||
const reasoningCompatible = allModels.filter(
|
const reasoningCompatible = allModels.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
!item.model.isSubModel &&
|
!item.model.isSubModel &&
|
||||||
item.model.isEnabled &&
|
item.model.isEnabled &&
|
||||||
["zhipu", "alibaba", "openai", "custom", "local"].includes(item.provider.type),
|
["zhipu", "alibaba", "openai", "custom", "local"].includes(item.provider.type),
|
||||||
);
|
);
|
||||||
const preferredAgentConfig =
|
const configuredDetailAgentConfig =
|
||||||
|
currentSiteSettings?.canvasDetailAgentModelId &&
|
||||||
|
reasoningCompatible.some(
|
||||||
|
(item) => item.model.id === currentSiteSettings.canvasDetailAgentModelId,
|
||||||
|
)
|
||||||
|
? reasoningCompatible.find(
|
||||||
|
(item) => item.model.id === currentSiteSettings.canvasDetailAgentModelId,
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
const fallbackAgentConfig =
|
||||||
agentModel &&
|
agentModel &&
|
||||||
reasoningCompatible.some((item) => item.model.id === agentModel.model.id)
|
reasoningCompatible.some((item) => item.model.id === agentModel.model.id)
|
||||||
? reasoningCompatible.find((item) => item.model.id === agentModel.model.id)
|
? reasoningCompatible.find((item) => item.model.id === agentModel.model.id)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
const preferredAgentConfig = configuredDetailAgentConfig || fallbackAgentConfig;
|
||||||
let plannerConfig = preferredAgentConfig;
|
let plannerConfig = preferredAgentConfig;
|
||||||
|
|
||||||
if (referenceImages.length > 0) {
|
if (referenceImages.length > 0) {
|
||||||
@ -1835,6 +1849,13 @@ export async function POST(request: Request) {
|
|||||||
codingModelId: getAiModelAgentCodingModelId(plannerConfig.model),
|
codingModelId: getAiModelAgentCodingModelId(plannerConfig.model),
|
||||||
providerType: plannerConfig.provider.type,
|
providerType: plannerConfig.provider.type,
|
||||||
supportsVision: plannerConfig.model.supportsVision,
|
supportsVision: plannerConfig.model.supportsVision,
|
||||||
|
detailAgentSettingModelId: currentSiteSettings?.canvasDetailAgentModelId || null,
|
||||||
|
selectionSource:
|
||||||
|
configuredDetailAgentConfig?.model.id === plannerConfig.model.id
|
||||||
|
? "detail-agent-setting"
|
||||||
|
: fallbackAgentConfig?.model.id === plannerConfig.model.id
|
||||||
|
? "agent-default"
|
||||||
|
: "vision-fallback",
|
||||||
multimodalFlag: isAiModelMultimodal({
|
multimodalFlag: isAiModelMultimodal({
|
||||||
capabilities: plannerConfig.model.capabilities,
|
capabilities: plannerConfig.model.capabilities,
|
||||||
defaultParams: plannerConfig.model.defaultParams,
|
defaultParams: plannerConfig.model.defaultParams,
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { auth } from "@/app/(auth)/auth";
|
|||||||
import {
|
import {
|
||||||
getAgentAiModelWithProvider,
|
getAgentAiModelWithProvider,
|
||||||
getEnabledAiModelsWithProvider,
|
getEnabledAiModelsWithProvider,
|
||||||
|
getSiteSettings,
|
||||||
} from "@/lib/db/queries";
|
} from "@/lib/db/queries";
|
||||||
import {
|
import {
|
||||||
getAiModelAgentCodingModelId,
|
getAiModelAgentCodingModelId,
|
||||||
@ -189,20 +190,33 @@ export async function POST(request: Request) {
|
|||||||
const screenPrompt = String(body.screen?.visualPrompt || "").trim();
|
const screenPrompt = String(body.screen?.visualPrompt || "").trim();
|
||||||
const layoutNote = String(body.screen?.layoutNote || "").trim();
|
const layoutNote = String(body.screen?.layoutNote || "").trim();
|
||||||
const referenceImages = sanitizeImageUrls(body.referenceImages);
|
const referenceImages = sanitizeImageUrls(body.referenceImages);
|
||||||
const allModels = await getEnabledAiModelsWithProvider(false);
|
const [allModels, agentModel, currentSiteSettings] = await Promise.all([
|
||||||
const agentModel = await getAgentAiModelWithProvider();
|
getEnabledAiModelsWithProvider(false),
|
||||||
|
getAgentAiModelWithProvider(),
|
||||||
|
getSiteSettings(),
|
||||||
|
]);
|
||||||
const reviewCompatible = allModels.filter(
|
const reviewCompatible = allModels.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
!item.model.isSubModel &&
|
!item.model.isSubModel &&
|
||||||
item.model.isEnabled &&
|
item.model.isEnabled &&
|
||||||
["zhipu", "alibaba", "openai", "custom", "local"].includes(item.provider.type),
|
["zhipu", "alibaba", "openai", "custom", "local"].includes(item.provider.type),
|
||||||
);
|
);
|
||||||
|
const configuredDetailAgentConfig =
|
||||||
|
currentSiteSettings?.canvasDetailAgentModelId &&
|
||||||
|
reviewCompatible.some(
|
||||||
|
(item) => item.model.id === currentSiteSettings.canvasDetailAgentModelId,
|
||||||
|
)
|
||||||
|
? reviewCompatible.find(
|
||||||
|
(item) => item.model.id === currentSiteSettings.canvasDetailAgentModelId,
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
let reviewerConfig =
|
const fallbackAgentConfig =
|
||||||
agentModel &&
|
agentModel &&
|
||||||
reviewCompatible.some((item) => item.model.id === agentModel.model.id)
|
reviewCompatible.some((item) => item.model.id === agentModel.model.id)
|
||||||
? reviewCompatible.find((item) => item.model.id === agentModel.model.id)
|
? reviewCompatible.find((item) => item.model.id === agentModel.model.id)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
let reviewerConfig = configuredDetailAgentConfig || fallbackAgentConfig;
|
||||||
if (!reviewerConfig || !supportsVisionReview(reviewerConfig.model)) {
|
if (!reviewerConfig || !supportsVisionReview(reviewerConfig.model)) {
|
||||||
reviewerConfig = reviewCompatible.find((item) => supportsVisionReview(item.model));
|
reviewerConfig = reviewCompatible.find((item) => supportsVisionReview(item.model));
|
||||||
}
|
}
|
||||||
@ -263,6 +277,21 @@ export async function POST(request: Request) {
|
|||||||
reviewerConfig.model.id,
|
reviewerConfig.model.id,
|
||||||
codingModelId
|
codingModelId
|
||||||
);
|
);
|
||||||
|
console.log("[Canvas Detail Review] Reviewer model selected:", {
|
||||||
|
id: reviewerConfig.model.id,
|
||||||
|
displayName: reviewerConfig.model.displayName,
|
||||||
|
modelId: reviewerConfig.model.modelId,
|
||||||
|
codingModelId,
|
||||||
|
providerType: reviewerConfig.provider.type,
|
||||||
|
supportsVision: reviewerConfig.model.supportsVision,
|
||||||
|
detailAgentSettingModelId: currentSiteSettings?.canvasDetailAgentModelId || null,
|
||||||
|
selectionSource:
|
||||||
|
configuredDetailAgentConfig?.model.id === reviewerConfig.model.id
|
||||||
|
? "detail-agent-setting"
|
||||||
|
: fallbackAgentConfig?.model.id === reviewerConfig.model.id
|
||||||
|
? "agent-default"
|
||||||
|
: "vision-fallback",
|
||||||
|
});
|
||||||
if (!model) {
|
if (!model) {
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@ -484,6 +484,7 @@ export async function GET() {
|
|||||||
canvasDefaultModelId: null,
|
canvasDefaultModelId: null,
|
||||||
canvasAutoModeDefaultModelId: null,
|
canvasAutoModeDefaultModelId: null,
|
||||||
canvasDetailDefaultModelId: null,
|
canvasDetailDefaultModelId: null,
|
||||||
|
canvasDetailAgentModelId: null,
|
||||||
lovartPromoText: "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠",
|
lovartPromoText: "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠",
|
||||||
lovartPromoActionText: "立即升级",
|
lovartPromoActionText: "立即升级",
|
||||||
lovartHeroTitle: "由壹点AI 让设计更简单",
|
lovartHeroTitle: "由壹点AI 让设计更简单",
|
||||||
@ -735,6 +736,7 @@ const updateSchema = z.object({
|
|||||||
canvasDefaultModelId: z.string().nullable().optional(),
|
canvasDefaultModelId: z.string().nullable().optional(),
|
||||||
canvasAutoModeDefaultModelId: z.string().nullable().optional(),
|
canvasAutoModeDefaultModelId: z.string().nullable().optional(),
|
||||||
canvasDetailDefaultModelId: z.string().nullable().optional(),
|
canvasDetailDefaultModelId: z.string().nullable().optional(),
|
||||||
|
canvasDetailAgentModelId: z.string().nullable().optional(),
|
||||||
lovartPromoText: z.string().max(300).optional(),
|
lovartPromoText: z.string().max(300).optional(),
|
||||||
lovartPromoActionText: z.string().max(32).optional(),
|
lovartPromoActionText: z.string().max(32).optional(),
|
||||||
lovartHeroTitle: z.string().max(300).optional(),
|
lovartHeroTitle: z.string().max(300).optional(),
|
||||||
@ -1389,6 +1391,7 @@ export async function PUT(request: NextRequest) {
|
|||||||
canvasDefaultModelId: siteSettingsPayload.canvasDefaultModelId,
|
canvasDefaultModelId: siteSettingsPayload.canvasDefaultModelId,
|
||||||
canvasAutoModeDefaultModelId: siteSettingsPayload.canvasAutoModeDefaultModelId,
|
canvasAutoModeDefaultModelId: siteSettingsPayload.canvasAutoModeDefaultModelId,
|
||||||
canvasDetailDefaultModelId: siteSettingsPayload.canvasDetailDefaultModelId,
|
canvasDetailDefaultModelId: siteSettingsPayload.canvasDetailDefaultModelId,
|
||||||
|
canvasDetailAgentModelId: siteSettingsPayload.canvasDetailAgentModelId,
|
||||||
lovartPromoText:
|
lovartPromoText:
|
||||||
siteSettingsPayload.lovartPromoText ??
|
siteSettingsPayload.lovartPromoText ??
|
||||||
"现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠",
|
"现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠",
|
||||||
|
|||||||
2
lib/db/migrations/0060_add_canvas_detail_agent_model.sql
Normal file
2
lib/db/migrations/0060_add_canvas_detail_agent_model.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE "SiteSettings"
|
||||||
|
ADD COLUMN IF NOT EXISTS "canvasDetailAgentModelId" varchar(128);
|
||||||
@ -323,6 +323,13 @@
|
|||||||
"when": 1774070400000,
|
"when": 1774070400000,
|
||||||
"tag": "0059_add_canvas_detail_surface_snapshot",
|
"tag": "0059_add_canvas_detail_surface_snapshot",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 46,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1774074000000,
|
||||||
|
"tag": "0060_add_canvas_detail_agent_model",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4082,6 +4082,7 @@ export async function getSiteSettings() {
|
|||||||
tutorialEnabled: false,
|
tutorialEnabled: false,
|
||||||
rankingEnabled: true,
|
rankingEnabled: true,
|
||||||
agentEnabled: true,
|
agentEnabled: true,
|
||||||
|
canvasDetailAgentModelId: null,
|
||||||
lovartPromoText: "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠",
|
lovartPromoText: "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠",
|
||||||
lovartPromoActionText: "立即升级",
|
lovartPromoActionText: "立即升级",
|
||||||
lovartHeroTitle: "由壹点AI 让设计更简单",
|
lovartHeroTitle: "由壹点AI 让设计更简单",
|
||||||
|
|||||||
@ -381,6 +381,7 @@ export const siteSettings = pgTable("SiteSettings", {
|
|||||||
canvasDefaultModelId: varchar("canvasDefaultModelId", { length: 128 }), // 画布默认选择的模型ID(null表示智能自动)
|
canvasDefaultModelId: varchar("canvasDefaultModelId", { length: 128 }), // 画布默认选择的模型ID(null表示智能自动)
|
||||||
canvasAutoModeDefaultModelId: varchar("canvasAutoModeDefaultModelId", { length: 128 }), // 智能自动模式下实际使用的默认生图模型ID
|
canvasAutoModeDefaultModelId: varchar("canvasAutoModeDefaultModelId", { length: 128 }), // 智能自动模式下实际使用的默认生图模型ID
|
||||||
canvasDetailDefaultModelId: varchar("canvasDetailDefaultModelId", { length: 128 }), // AI详情生成专用默认生图模型ID
|
canvasDetailDefaultModelId: varchar("canvasDetailDefaultModelId", { length: 128 }), // AI详情生成专用默认生图模型ID
|
||||||
|
canvasDetailAgentModelId: varchar("canvasDetailAgentModelId", { length: 128 }), // AI详情生成专用Agent模型ID(规划/复审)
|
||||||
lovartPromoText: text("lovartPromoText").default(
|
lovartPromoText: text("lovartPromoText").default(
|
||||||
"现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠"
|
"现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠"
|
||||||
),
|
),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user