feat: add Bailian image generation support

This commit is contained in:
inman
2026-07-14 12:58:50 +08:00
parent 9a3070c749
commit f642b5e71f
18 changed files with 464 additions and 74 deletions

View File

@@ -8,6 +8,7 @@ import { accountManagementConfigured } from "@/lib/server/organization-client";
import { rootDir } from "@/lib/server/runtime";
import { shouldMockVisualApi } from "@/lib/volcengine/visual-client";
import type { EnabledImageCapability } from "@/lib/types";
import { getBailianConfig, shouldMockBailian } from "@/lib/bailian/client";
export type SettingsField = {
key: string;
@@ -133,6 +134,18 @@ const settingDefinitions: Array<{
{ key: "SEEDANCE_API_KEY", label: "方舟 API Key", secret: true, type: "password" }
]
},
{
id: "bailian",
title: "阿里云百炼 API",
description: "用于万相 2.7 文生图、参考图生图和图生视频。Base URL 会自动派生原生异步接口。",
fields: [
{ key: "BAILIAN_API_KEY", label: "百炼 API Key", secret: true, type: "password" },
{ key: "BAILIAN_BASE_URL", label: "Base URL", defaultValue: "https://llm-126wneubbdo6dbr5.cn-beijing.maas.aliyuncs.com/compatible-mode/v1" },
{ key: "BAILIAN_IMAGE_MODEL", label: "图片模型", defaultValue: "wan2.7-image-pro" },
{ key: "BAILIAN_VIDEO_MODEL", label: "视频模型", defaultValue: "wan2.7-i2v-2026-04-25" },
{ key: "BAILIAN_MOCK", label: "Mock 策略", type: "select", defaultValue: "auto", options: [{ label: "自动", value: "auto" }, { label: "总是 Mock", value: "true" }, { label: "总是真实接口", value: "false" }] }
]
},
{
id: "oss",
title: "OSS 资产存储",
@@ -155,7 +168,18 @@ const engineFieldDefinitions: FieldDefinition[] = [
defaultValue: "jimeng",
options: [
{ label: "即梦 / 火山视觉", value: "jimeng" },
{ label: "EvoLink GPT Image 2", value: "evolink" }
{ label: "EvoLink GPT Image 2", value: "evolink" },
{ label: "阿里云百炼 Wan 2.7", value: "bailian" }
]
},
{
key: "VIDEO_GENERATE_ENGINE",
label: "视频生成",
type: "select",
defaultValue: "seedance",
options: [
{ label: "Seedance", value: "seedance" },
{ label: "阿里云百炼 Wan 2.7", value: "bailian" }
]
},
{
@@ -197,6 +221,7 @@ export async function getApiSettings() {
visual: shouldMockVisualApi() ? "mock" : "real",
evolink: shouldMockEvolinkApi() ? "mock" : "real",
seedance: shouldMockSeedance() ? "mock" : "real",
bailian: shouldMockBailian() ? "mock" : getBailianConfig().apiKey ? "real" : "missing",
auth: authConfigSummary(auth),
organization: accountManagementConfigured() ? "configured" : "missing",
data: process.env.SUPABASE_SERVICE_ROLE_KEY ? "supabase" : "local"
@@ -215,10 +240,10 @@ export async function getApiSettings() {
}),
{
id: "video.generate",
label: "Seedance 视频生成",
reqKey: seedance.model,
engine: "seedance",
engineLabel: "Seedance",
label: "视频生成",
reqKey: engineAssignments.find((item) => item.id === "video.generate")?.reqKey || seedance.model,
engine: engineAssignments.find((item) => item.id === "video.generate")?.engine || "seedance",
engineLabel: engineAssignments.find((item) => item.id === "video.generate")?.engineLabel || "Seedance",
enabled: true
}
],
@@ -252,15 +277,16 @@ function buildEngineAssignments(fileEnv: Map<string, string>): EngineAssignment[
const capabilities = getJimengCapabilities();
const evolink = getEvolinkImageSettings();
const seedance = getSeedanceConfig();
const bailian = getBailianConfig();
const generateEngine = currentEngineValue(engineFieldDefinitions[0], fileEnv);
const inpaintEngine = currentEngineValue(engineFieldDefinitions[1], fileEnv);
const inpaintEngine = currentEngineValue(engineFieldDefinitions[2], fileEnv);
return [
imageEngineAssignment({
capability: "image.generate",
label: "图片生成",
engine: generateEngine,
reqKey: generateEngine === "evolink" ? evolink.model : capabilities["image.generate"].reqKey,
reqKey: generateEngine === "bailian" ? bailian.imageModel : generateEngine === "evolink" ? evolink.model : capabilities["image.generate"].reqKey,
field: engineFieldDefinitions[0],
fileEnv
}),
@@ -269,7 +295,7 @@ function buildEngineAssignments(fileEnv: Map<string, string>): EngineAssignment[
label: "局部重绘",
engine: inpaintEngine,
reqKey: inpaintEngine === "evolink" ? evolink.model : capabilities["image.inpaint"].reqKey,
field: engineFieldDefinitions[1],
field: engineFieldDefinitions[2],
fileEnv
}),
{
@@ -285,12 +311,13 @@ function buildEngineAssignments(fileEnv: Map<string, string>): EngineAssignment[
{
id: "video.generate",
label: "视频生成",
engine: "seedance",
engineLabel: "Seedance",
mode: shouldMockSeedance() ? "mock" : "seedance",
modeLabel: shouldMockSeedance() ? "Mock" : "Seedance 真实接口",
reqKey: seedance.model,
configurable: false
engine: currentVideoEngineValue(engineFieldDefinitions[1], fileEnv),
engineLabel: currentVideoEngineValue(engineFieldDefinitions[1], fileEnv) === "bailian" ? "阿里云百炼" : "Seedance",
mode: currentVideoEngineValue(engineFieldDefinitions[1], fileEnv) === "bailian" ? (shouldMockBailian() ? "mock" : "bailian") : (shouldMockSeedance() ? "mock" : "seedance"),
modeLabel: currentVideoEngineValue(engineFieldDefinitions[1], fileEnv) === "bailian" ? (shouldMockBailian() ? "Mock" : "百炼真实接口") : (shouldMockSeedance() ? "Mock" : "Seedance 真实接口"),
reqKey: currentVideoEngineValue(engineFieldDefinitions[1], fileEnv) === "bailian" ? bailian.videoModel : seedance.model,
configurable: true,
field: fieldWithValue(engineFieldDefinitions[1], currentVideoEngineValue(engineFieldDefinitions[1], fileEnv), isConfigured(engineFieldDefinitions[1].key, fileEnv))
}
];
}
@@ -336,21 +363,27 @@ function isConfigured(key: string, fileEnv: Map<string, string>) {
}
function normalizeImageEngine(value: string | undefined): ImageCreationEngine {
return value?.trim().toLowerCase() === "evolink" ? "evolink" : "jimeng";
const normalized = value?.trim().toLowerCase();
return normalized === "evolink" || normalized === "bailian" ? normalized : "jimeng";
}
function imageEngineLabel(engine: ImageCreationEngine) {
return engine === "evolink" ? "EvoLink" : "即梦";
return engine === "evolink" ? "EvoLink" : engine === "bailian" ? "阿里云百炼" : "即梦";
}
function modeForImageEngine(engine: ImageCreationEngine) {
if (engine === "evolink") return shouldMockEvolinkApi() ? "mock" : "evolink";
if (engine === "bailian") return shouldMockBailian() ? "mock" : "bailian";
return shouldMockVisualApi() ? "mock" : "volcengine";
}
function modeLabel(mode: string, engine: ImageCreationEngine) {
if (mode === "mock") return "Mock";
return engine === "evolink" ? "EvoLink 真实接口" : "即梦真实接口";
return engine === "evolink" ? "EvoLink 真实接口" : engine === "bailian" ? "百炼真实接口" : "即梦真实接口";
}
function currentVideoEngineValue(field: FieldDefinition, fileEnv: Map<string, string>) {
return (process.env[field.key] ?? fileEnv.get(field.key) ?? field.defaultValue) === "bailian" ? "bailian" : "seedance";
}
function envFilePath() {