feat: add moonshot detail agent model config
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-21 21:50:08 +08:00
parent 9a96824e2b
commit 656aacbd01
8 changed files with 448 additions and 6 deletions

View File

@ -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>

View File

@ -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,

View File

@ -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,

View File

@ -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 折优惠",

View File

@ -0,0 +1,2 @@
ALTER TABLE "SiteSettings"
ADD COLUMN IF NOT EXISTS "canvasDetailAgentModelId" varchar(128);

View File

@ -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
}
]
}

View File

@ -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 让设计更简单",

View File

@ -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 折优惠"
),