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;
|
||||
canvasAutoModeDefaultModelId: string | null;
|
||||
canvasDetailDefaultModelId: string | null;
|
||||
canvasDetailAgentModelId: string | null;
|
||||
lovartComposerPrefix: string;
|
||||
lovartComposerPreviewTexts: string[];
|
||||
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 MOONSHOT_OFFICIAL_BASE_URL = "https://api.moonshot.cn/v1";
|
||||
const MOONSHOT_KIMI_K25_MODEL_ID = "kimi-k2.5";
|
||||
|
||||
const LEMON_PROVIDER_PRESET = {
|
||||
name: "LemonAPI Gemini",
|
||||
@ -541,6 +544,10 @@ function isLemonApiBaseUrl(value?: string | null) {
|
||||
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() {
|
||||
const [activeTab, setActiveTab] = useState("users");
|
||||
|
||||
@ -625,6 +632,14 @@ export function AdminSettingsClient() {
|
||||
: null;
|
||||
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>({
|
||||
siteName: "AI Chatbot",
|
||||
@ -666,6 +681,7 @@ export function AdminSettingsClient() {
|
||||
canvasDefaultModelId: null,
|
||||
canvasAutoModeDefaultModelId: null,
|
||||
canvasDetailDefaultModelId: null,
|
||||
canvasDetailAgentModelId: null,
|
||||
lovartComposerPrefix: "让Buyi-ai",
|
||||
lovartComposerPreviewTexts: [
|
||||
"打造引人注目的社交媒体视觉",
|
||||
@ -766,6 +782,7 @@ export function AdminSettingsClient() {
|
||||
canvasDefaultModelId: siteSettingsData.canvasDefaultModelId || null,
|
||||
canvasAutoModeDefaultModelId: siteSettingsData.canvasAutoModeDefaultModelId || null,
|
||||
canvasDetailDefaultModelId: siteSettingsData.canvasDetailDefaultModelId || null,
|
||||
canvasDetailAgentModelId: (siteSettingsData as any).canvasDetailAgentModelId || null,
|
||||
lovartComposerPrefix:
|
||||
String((siteSettingsData as any).lovartComposerPrefix || "").trim() ||
|
||||
"让Buyi-ai",
|
||||
@ -852,6 +869,50 @@ export function AdminSettingsClient() {
|
||||
}
|
||||
}, [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 values = (siteSettings.surpriseCategories || [])
|
||||
.map((item) => String(item || "").trim())
|
||||
@ -957,6 +1018,16 @@ export function AdminSettingsClient() {
|
||||
apiKey: "",
|
||||
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配置
|
||||
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) => {
|
||||
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 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">
|
||||
<Label htmlFor="canvasDetailDefaultModelId">AI详情生成 - 默认生图模型</Label>
|
||||
<Select
|
||||
@ -8032,6 +8276,140 @@ export function AdminSettingsClient() {
|
||||
|
||||
{/* 本地AI设置 */}
|
||||
<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>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-xl">Tripo3D API 配置</CardTitle>
|
||||
|
||||
@ -4,6 +4,7 @@ import { auth } from "@/app/(auth)/auth";
|
||||
import {
|
||||
getAgentAiModelWithProvider,
|
||||
getEnabledAiModelsWithProvider,
|
||||
getSiteSettings,
|
||||
} from "@/lib/db/queries";
|
||||
import {
|
||||
getAiModelAgentCodingModelId,
|
||||
@ -1800,19 +1801,32 @@ export async function POST(request: Request) {
|
||||
bundleImageCount: bundleImageUrls.length,
|
||||
});
|
||||
|
||||
const allModels = await getEnabledAiModelsWithProvider(false);
|
||||
const agentModel = await getAgentAiModelWithProvider();
|
||||
const [allModels, agentModel, currentSiteSettings] = await Promise.all([
|
||||
getEnabledAiModelsWithProvider(false),
|
||||
getAgentAiModelWithProvider(),
|
||||
getSiteSettings(),
|
||||
]);
|
||||
const reasoningCompatible = allModels.filter(
|
||||
(item) =>
|
||||
!item.model.isSubModel &&
|
||||
item.model.isEnabled &&
|
||||
["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 &&
|
||||
reasoningCompatible.some((item) => item.model.id === agentModel.model.id)
|
||||
? reasoningCompatible.find((item) => item.model.id === agentModel.model.id)
|
||||
: undefined;
|
||||
const preferredAgentConfig = configuredDetailAgentConfig || fallbackAgentConfig;
|
||||
let plannerConfig = preferredAgentConfig;
|
||||
|
||||
if (referenceImages.length > 0) {
|
||||
@ -1835,6 +1849,13 @@ export async function POST(request: Request) {
|
||||
codingModelId: getAiModelAgentCodingModelId(plannerConfig.model),
|
||||
providerType: plannerConfig.provider.type,
|
||||
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({
|
||||
capabilities: plannerConfig.model.capabilities,
|
||||
defaultParams: plannerConfig.model.defaultParams,
|
||||
|
||||
@ -4,6 +4,7 @@ import { auth } from "@/app/(auth)/auth";
|
||||
import {
|
||||
getAgentAiModelWithProvider,
|
||||
getEnabledAiModelsWithProvider,
|
||||
getSiteSettings,
|
||||
} from "@/lib/db/queries";
|
||||
import {
|
||||
getAiModelAgentCodingModelId,
|
||||
@ -189,20 +190,33 @@ export async function POST(request: Request) {
|
||||
const screenPrompt = String(body.screen?.visualPrompt || "").trim();
|
||||
const layoutNote = String(body.screen?.layoutNote || "").trim();
|
||||
const referenceImages = sanitizeImageUrls(body.referenceImages);
|
||||
const allModels = await getEnabledAiModelsWithProvider(false);
|
||||
const agentModel = await getAgentAiModelWithProvider();
|
||||
const [allModels, agentModel, currentSiteSettings] = await Promise.all([
|
||||
getEnabledAiModelsWithProvider(false),
|
||||
getAgentAiModelWithProvider(),
|
||||
getSiteSettings(),
|
||||
]);
|
||||
const reviewCompatible = allModels.filter(
|
||||
(item) =>
|
||||
!item.model.isSubModel &&
|
||||
item.model.isEnabled &&
|
||||
["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 &&
|
||||
reviewCompatible.some((item) => item.model.id === agentModel.model.id)
|
||||
? reviewCompatible.find((item) => item.model.id === agentModel.model.id)
|
||||
: undefined;
|
||||
let reviewerConfig = configuredDetailAgentConfig || fallbackAgentConfig;
|
||||
if (!reviewerConfig || !supportsVisionReview(reviewerConfig.model)) {
|
||||
reviewerConfig = reviewCompatible.find((item) => supportsVisionReview(item.model));
|
||||
}
|
||||
@ -263,6 +277,21 @@ export async function POST(request: Request) {
|
||||
reviewerConfig.model.id,
|
||||
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) {
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
|
||||
@ -484,6 +484,7 @@ export async function GET() {
|
||||
canvasDefaultModelId: null,
|
||||
canvasAutoModeDefaultModelId: null,
|
||||
canvasDetailDefaultModelId: null,
|
||||
canvasDetailAgentModelId: null,
|
||||
lovartPromoText: "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠",
|
||||
lovartPromoActionText: "立即升级",
|
||||
lovartHeroTitle: "由壹点AI 让设计更简单",
|
||||
@ -735,6 +736,7 @@ const updateSchema = z.object({
|
||||
canvasDefaultModelId: z.string().nullable().optional(),
|
||||
canvasAutoModeDefaultModelId: z.string().nullable().optional(),
|
||||
canvasDetailDefaultModelId: z.string().nullable().optional(),
|
||||
canvasDetailAgentModelId: z.string().nullable().optional(),
|
||||
lovartPromoText: z.string().max(300).optional(),
|
||||
lovartPromoActionText: z.string().max(32).optional(),
|
||||
lovartHeroTitle: z.string().max(300).optional(),
|
||||
@ -1389,6 +1391,7 @@ export async function PUT(request: NextRequest) {
|
||||
canvasDefaultModelId: siteSettingsPayload.canvasDefaultModelId,
|
||||
canvasAutoModeDefaultModelId: siteSettingsPayload.canvasAutoModeDefaultModelId,
|
||||
canvasDetailDefaultModelId: siteSettingsPayload.canvasDetailDefaultModelId,
|
||||
canvasDetailAgentModelId: siteSettingsPayload.canvasDetailAgentModelId,
|
||||
lovartPromoText:
|
||||
siteSettingsPayload.lovartPromoText ??
|
||||
"现在升级,立享 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,
|
||||
"tag": "0059_add_canvas_detail_surface_snapshot",
|
||||
"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,
|
||||
rankingEnabled: true,
|
||||
agentEnabled: true,
|
||||
canvasDetailAgentModelId: null,
|
||||
lovartPromoText: "现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠",
|
||||
lovartPromoActionText: "立即升级",
|
||||
lovartHeroTitle: "由壹点AI 让设计更简单",
|
||||
|
||||
@ -381,6 +381,7 @@ export const siteSettings = pgTable("SiteSettings", {
|
||||
canvasDefaultModelId: varchar("canvasDefaultModelId", { length: 128 }), // 画布默认选择的模型ID(null表示智能自动)
|
||||
canvasAutoModeDefaultModelId: varchar("canvasAutoModeDefaultModelId", { length: 128 }), // 智能自动模式下实际使用的默认生图模型ID
|
||||
canvasDetailDefaultModelId: varchar("canvasDetailDefaultModelId", { length: 128 }), // AI详情生成专用默认生图模型ID
|
||||
canvasDetailAgentModelId: varchar("canvasDetailAgentModelId", { length: 128 }), // AI详情生成专用Agent模型ID(规划/复审)
|
||||
lovartPromoText: text("lovartPromoText").default(
|
||||
"现在升级,立享 Kling 3.0 与 Nano Banana Pro,最高 5 折优惠"
|
||||
),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user