feat: support default canvas text edit image model
This commit is contained in:
parent
8b9d9d42e0
commit
1f1ffd2211
@ -771,6 +771,7 @@ type AiModel = {
|
||||
isEnabled: boolean;
|
||||
isHidden: boolean;
|
||||
isDefault: boolean;
|
||||
isDefaultTextEditModel?: boolean;
|
||||
isSubModel?: boolean;
|
||||
parentModelId?: string | null;
|
||||
sortOrder: number;
|
||||
@ -3260,6 +3261,7 @@ export function AdminSettingsClient() {
|
||||
isEnabled: true,
|
||||
isHidden: false,
|
||||
isDefault: false,
|
||||
isDefaultTextEditModel: false,
|
||||
isAgentModel: false,
|
||||
agentCodingModelId: "",
|
||||
isMultimodal: false,
|
||||
@ -6499,6 +6501,7 @@ export function AdminSettingsClient() {
|
||||
isEnabled: true,
|
||||
isHidden: false,
|
||||
isDefault: false,
|
||||
isDefaultTextEditModel: false,
|
||||
isAgentModel: false,
|
||||
agentCodingModelId: "",
|
||||
isMultimodal: false,
|
||||
@ -14194,6 +14197,10 @@ export function AdminSettingsClient() {
|
||||
setModelForm({
|
||||
...modelForm,
|
||||
supportsImageGen: checked,
|
||||
isDefaultTextEditModel:
|
||||
checked
|
||||
? modelForm.isDefaultTextEditModel
|
||||
: false,
|
||||
})
|
||||
}
|
||||
/>
|
||||
@ -14231,6 +14238,30 @@ export function AdminSettingsClient() {
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={modelForm.isDefaultTextEditModel}
|
||||
disabled={!modelForm.supportsImageGen}
|
||||
onCheckedChange={(checked) =>
|
||||
setModelForm({
|
||||
...modelForm,
|
||||
isDefaultTextEditModel: checked,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Label
|
||||
className={
|
||||
modelForm.supportsImageGen
|
||||
? ""
|
||||
: "text-muted-foreground"
|
||||
}
|
||||
>
|
||||
默认文字编辑生图模型(无限画布文字编辑使用)
|
||||
</Label>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
建议给可用的火山生图模型开启。未配置时,系统仍会回退到当前可用的火山生图模型。
|
||||
</p>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={modelForm.isHidden}
|
||||
@ -14981,6 +15012,11 @@ export function AdminSettingsClient() {
|
||||
默认
|
||||
</span>
|
||||
)}
|
||||
{model.isDefaultTextEditModel && (
|
||||
<span className="text-xs bg-fuchsia-100 text-fuchsia-800 px-2 py-0.5 rounded">
|
||||
文字编辑默认
|
||||
</span>
|
||||
)}
|
||||
{model.isAgentModel && (
|
||||
<span className="text-xs bg-violet-100 text-violet-800 px-2 py-0.5 rounded">
|
||||
Agent
|
||||
@ -15040,6 +15076,8 @@ export function AdminSettingsClient() {
|
||||
isEnabled: model.isEnabled,
|
||||
isHidden: model.isHidden || false,
|
||||
isDefault: model.isDefault,
|
||||
isDefaultTextEditModel:
|
||||
model.isDefaultTextEditModel || false,
|
||||
isAgentModel: model.isAgentModel || false,
|
||||
agentCodingModelId:
|
||||
model.agentCodingModelId || "",
|
||||
|
||||
@ -74,6 +74,7 @@ const createModelSchema = z.object({
|
||||
isEnabled: z.boolean().optional(),
|
||||
isHidden: z.boolean().optional(),
|
||||
isDefault: z.boolean().optional(),
|
||||
isDefaultTextEditModel: z.boolean().optional(),
|
||||
sortOrder: z.number().optional(),
|
||||
fallbackModelId1: z.string().max(128).optional(),
|
||||
fallbackModelId2: z.string().max(128).optional(),
|
||||
@ -97,6 +98,7 @@ const updateModelSchema = z.object({
|
||||
isEnabled: z.boolean().optional(),
|
||||
isHidden: z.boolean().optional(),
|
||||
isDefault: z.boolean().optional(),
|
||||
isDefaultTextEditModel: z.boolean().optional(),
|
||||
sortOrder: z.number().optional(),
|
||||
fallbackModelId1: z.string().max(128).optional(),
|
||||
fallbackModelId2: z.string().max(128).optional(),
|
||||
|
||||
@ -19,15 +19,17 @@ const requestSchema = z.object({
|
||||
items: z.array(textItemSchema).min(1, "至少需要一条文字修改"),
|
||||
});
|
||||
|
||||
function pickVolcanoModel(models: Awaited<ReturnType<typeof getEnabledAiModelsWithProvider>>) {
|
||||
function pickTextEditModel(models: Awaited<ReturnType<typeof getEnabledAiModelsWithProvider>>) {
|
||||
const candidates = models
|
||||
.filter((item) => item.model.supportsImageGen && item.provider.type === "volcano-vision")
|
||||
.sort((a, b) => {
|
||||
const aScore =
|
||||
(a.model.isDefaultTextEditModel ? 100 : 0) +
|
||||
(a.model.isDefault ? 10 : 0) +
|
||||
(a.model.modelId?.toLowerCase().includes("seedream") ? 5 : 0) +
|
||||
(a.model.displayName?.includes("火山") ? 3 : 0);
|
||||
const bScore =
|
||||
(b.model.isDefaultTextEditModel ? 100 : 0) +
|
||||
(b.model.isDefault ? 10 : 0) +
|
||||
(b.model.modelId?.toLowerCase().includes("seedream") ? 5 : 0) +
|
||||
(b.model.displayName?.includes("火山") ? 3 : 0);
|
||||
@ -148,12 +150,27 @@ export async function POST(request: Request) {
|
||||
}
|
||||
|
||||
const models = await getEnabledAiModelsWithProvider();
|
||||
const selected = pickVolcanoModel(models);
|
||||
const selected = pickTextEditModel(models);
|
||||
|
||||
if (!selected) {
|
||||
return NextResponse.json({ success: false, error: "未配置可用的火山生图模型" }, { status: 500 });
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: "未配置可用的文字编辑生图模型,请在系统设置-AI 模型中启用一个火山生图模型并设为默认文字编辑生图模型",
|
||||
},
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
console.info("[Canvas Text Edit] selected model:", {
|
||||
providerType: selected.provider.type,
|
||||
providerName: selected.provider.name,
|
||||
modelId: selected.model.modelId,
|
||||
displayName: selected.model.displayName,
|
||||
isDefault: selected.model.isDefault,
|
||||
isDefaultTextEditModel: selected.model.isDefaultTextEditModel,
|
||||
});
|
||||
|
||||
const provider = createVolcanoVision({
|
||||
baseURL: selected.provider.baseUrl,
|
||||
apiKey: selected.provider.apiKey,
|
||||
|
||||
@ -105,6 +105,10 @@ const runMigrate = async () => {
|
||||
ALTER TABLE "AiModel"
|
||||
ADD COLUMN IF NOT EXISTS "excludeFromFailover" boolean NOT NULL DEFAULT false;
|
||||
`);
|
||||
await db.execute(sql`
|
||||
ALTER TABLE "AiModel"
|
||||
ADD COLUMN IF NOT EXISTS "isDefaultTextEditModel" boolean NOT NULL DEFAULT false;
|
||||
`);
|
||||
for (const item of providerBaseUrlDefaults) {
|
||||
await db.execute(sql`
|
||||
UPDATE "AiProvider"
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
ALTER TABLE "AiModel"
|
||||
ADD COLUMN IF NOT EXISTS "isDefaultTextEditModel" boolean NOT NULL DEFAULT false;
|
||||
@ -435,6 +435,13 @@
|
||||
"when": 1777276800000,
|
||||
"tag": "0075_add_agent_workflow_default_model",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 62,
|
||||
"version": "7",
|
||||
"when": 1777305600000,
|
||||
"tag": "0076_add_ai_model_text_edit_default",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -3941,6 +3941,7 @@ export async function createAiModel({
|
||||
isEnabled,
|
||||
isHidden,
|
||||
isDefault,
|
||||
isDefaultTextEditModel,
|
||||
sortOrder,
|
||||
fallbackModelId1,
|
||||
fallbackModelId2,
|
||||
@ -3961,6 +3962,7 @@ export async function createAiModel({
|
||||
isEnabled?: boolean;
|
||||
isHidden?: boolean;
|
||||
isDefault?: boolean;
|
||||
isDefaultTextEditModel?: boolean;
|
||||
sortOrder?: number;
|
||||
fallbackModelId1?: string;
|
||||
fallbackModelId2?: string;
|
||||
@ -3980,6 +3982,13 @@ export async function createAiModel({
|
||||
.where(eq(aiModel.isDefault, true));
|
||||
}
|
||||
|
||||
if (isDefaultTextEditModel) {
|
||||
await db
|
||||
.update(aiModel)
|
||||
.set({ isDefaultTextEditModel: false })
|
||||
.where(eq(aiModel.isDefaultTextEditModel, true));
|
||||
}
|
||||
|
||||
const defaultParams =
|
||||
agentCodingModelId !== undefined
|
||||
? withAgentCodingModelId(
|
||||
@ -4003,6 +4012,7 @@ export async function createAiModel({
|
||||
isEnabled: isEnabled ?? true,
|
||||
isHidden: isHidden ?? false,
|
||||
isDefault: isDefault ?? false,
|
||||
isDefaultTextEditModel: isDefaultTextEditModel ?? false,
|
||||
sortOrder: sortOrder ?? 0,
|
||||
fallbackModelId1: fallbackModelId1 || null,
|
||||
fallbackModelId2: fallbackModelId2 || null,
|
||||
@ -4035,6 +4045,7 @@ export async function updateAiModel({
|
||||
isEnabled,
|
||||
isHidden,
|
||||
isDefault,
|
||||
isDefaultTextEditModel,
|
||||
sortOrder,
|
||||
fallbackModelId1,
|
||||
fallbackModelId2,
|
||||
@ -4056,6 +4067,7 @@ export async function updateAiModel({
|
||||
isEnabled?: boolean;
|
||||
isHidden?: boolean;
|
||||
isDefault?: boolean;
|
||||
isDefaultTextEditModel?: boolean;
|
||||
sortOrder?: number;
|
||||
fallbackModelId1?: string;
|
||||
fallbackModelId2?: string;
|
||||
@ -4075,6 +4087,13 @@ export async function updateAiModel({
|
||||
.where(eq(aiModel.isDefault, true));
|
||||
}
|
||||
|
||||
if (isDefaultTextEditModel) {
|
||||
await db
|
||||
.update(aiModel)
|
||||
.set({ isDefaultTextEditModel: false })
|
||||
.where(eq(aiModel.isDefaultTextEditModel, true));
|
||||
}
|
||||
|
||||
const updateData: any = { updatedAt: new Date() };
|
||||
if (providerId !== undefined) updateData.providerId = providerId;
|
||||
if (modelId !== undefined) updateData.modelId = modelId;
|
||||
@ -4087,6 +4106,9 @@ export async function updateAiModel({
|
||||
if (isEnabled !== undefined) updateData.isEnabled = isEnabled;
|
||||
if (isHidden !== undefined) updateData.isHidden = isHidden;
|
||||
if (isDefault !== undefined) updateData.isDefault = isDefault;
|
||||
if (isDefaultTextEditModel !== undefined) {
|
||||
updateData.isDefaultTextEditModel = isDefaultTextEditModel;
|
||||
}
|
||||
if (sortOrder !== undefined) updateData.sortOrder = sortOrder;
|
||||
// 备用模型 ID(空字符串转为 null)
|
||||
if (fallbackModelId1 !== undefined) updateData.fallbackModelId1 = fallbackModelId1 || null;
|
||||
|
||||
@ -304,6 +304,7 @@ export const aiModel = pgTable("AiModel", {
|
||||
isEnabled: boolean("isEnabled").notNull().default(true), // 是否启用
|
||||
isHidden: boolean("isHidden").notNull().default(false), // 是否在前端模型选择中隐藏
|
||||
isDefault: boolean("isDefault").notNull().default(false), // 是否为默认模型
|
||||
isDefaultTextEditModel: boolean("isDefaultTextEditModel").notNull().default(false), // 是否为默认文字编辑生图模型
|
||||
sortOrder: integer("sortOrder").notNull().default(0), // 排序顺序
|
||||
capabilities: jsonb("capabilities"), // 🆕 模型能力配置(用于视频模型)
|
||||
defaultParams: jsonb("defaultParams"), // 🆕 默认参数配置
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user