From d5737b70c013dde371501739b1bedcda894154ad Mon Sep 17 00:00:00 2001 From: 134 Date: Fri, 31 Jul 2026 18:17:29 +0800 Subject: [PATCH] feat(canvas): add layout replace and vip98k provider --- app/(admin)/settings/_lib/types.ts | 1 + .../settings/admin-settings-client.tsx | 82 ++- app/(chat)/api/chat/_lib/image-failover.ts | 2 + app/(chat)/api/chat/_lib/image-media.ts | 1 + app/(chat)/api/chat/_lib/stream.ts | 1 + app/(chat)/api/chat/route.ts | 63 +- app/api/admin/ai-models/route.ts | 6 + app/api/admin/ai-providers/route.ts | 13 +- app/api/admin/vip98k-models/route.ts | 185 +++++ app/api/canvas/[id]/route.ts | 60 +- app/api/canvas/generate/image/route.ts | 10 + app/api/canvas/layout-replace/route.ts | 116 +++ app/api/canvas/local-edit/route.ts | 2 + app/api/canvas/text-edit/route.ts | 15 +- app/api/canvas/world/tasks/[taskId]/route.ts | 398 ++++++++++- app/api/canvas/world/tasks/route.ts | 250 +++++-- app/api/canvas/world/upload/route.ts | 117 ++- app/api/scene-fusion/route.ts | 52 ++ .../[id]/CanvasEditorLegacyPageClient.tsx | 200 +++++- app/canvas/[id]/model3d/page.tsx | 24 +- components/canvas/CanvasWorkspace.tsx | 32 + components/canvas/nodes/ImageNode.tsx | 561 ++++++++++++++- .../nodes/image-node/image-node-local-edit.ts | 11 + components/image-generation-loading.tsx | 1 + components/image-params-bar.tsx | 1 + docs/worldlabs-proxy-provider-requirements.md | 343 ++------- lib/ai/dynamic-provider.ts | 16 + lib/ai/model-classification.ts | 7 + lib/ai/model-router.ts | 37 +- lib/ai/provider-defaults.ts | 3 +- lib/ai/providers/vip98k.ts | 674 ++++++++++++++++++ lib/ai/tools/generate-video.ts | 156 +++- lib/ai/vip98k-models.ts | 626 ++++++++++++++++ lib/canvas-toolbar-config.ts | 8 +- lib/canvas/generate-image.ts | 28 +- lib/canvas/image-task-queue.ts | 27 +- lib/canvas/layout-replace-task.ts | 633 ++++++++++++++++ lib/db/migrate.ts | 5 +- .../0093_update_worldlabs_vip98k_base_url.sql | 32 + .../0094_add_vip98k_provider_type.sql | 41 ++ lib/db/migrations/meta/_journal.json | 14 + lib/db/queries.ts | 9 +- lib/db/schema.ts | 2 +- lib/external-image-api/service.ts | 1 + lib/model3d/model-alias-config.ts | 8 +- lib/openclaw-chat/config.ts | 2 +- lib/scene-fusion/output-capabilities.ts | 2 + lib/worldlabs/client.ts | 20 +- lib/worldlabs/key-pool-config-shared.ts | 2 +- lib/worldlabs/server.ts | 3 +- lib/worldlabs/types.ts | 42 +- next-env.d.ts | 2 +- services/canvas/index.ts | 45 ++ store/canvas/index.ts | 6 +- 54 files changed, 4538 insertions(+), 460 deletions(-) create mode 100644 app/api/admin/vip98k-models/route.ts create mode 100644 app/api/canvas/layout-replace/route.ts create mode 100644 lib/ai/providers/vip98k.ts create mode 100644 lib/ai/vip98k-models.ts create mode 100644 lib/canvas/layout-replace-task.ts create mode 100644 lib/db/migrations/0093_update_worldlabs_vip98k_base_url.sql create mode 100644 lib/db/migrations/0094_add_vip98k_provider_type.sql diff --git a/app/(admin)/settings/_lib/types.ts b/app/(admin)/settings/_lib/types.ts index 82578cbf6..03db7244a 100644 --- a/app/(admin)/settings/_lib/types.ts +++ b/app/(admin)/settings/_lib/types.ts @@ -92,6 +92,7 @@ export type AdminAiProvider = { | "bananaimage" | "visionary" | "atlascloud" + | "vip98k" | "stepfun-image" | "midjourney" | "fal-video" diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx index bab8ac724..dba9be50b 100644 --- a/app/(admin)/settings/admin-settings-client.tsx +++ b/app/(admin)/settings/admin-settings-client.tsx @@ -36,6 +36,7 @@ import { ImagePlus, Key, Layers, + LayoutTemplate, Loader2, type LucideIcon, Maximize2, @@ -625,6 +626,7 @@ type AiProvider = { | "bananaimage" | "visionary" | "atlascloud" + | "vip98k" | "stepfun-image" | "midjourney" | "fal-video" @@ -654,6 +656,9 @@ type ImageGenConfig = { supportsSeed?: boolean; // 是否支持随机种子 supportedAspectRatios?: string[]; // 支持的图片比例 supportedImageSizes?: string[]; // 支持的分辨率/K 数 + maxReferenceImages?: number; // 最大参考图数量 + maxVideoReferences?: number; // 最大参考视频数量 + supportsQuality?: boolean; // 是否支持质量档位 creditCostPerImage?: number; // 每张图片积分消耗 creditCostMultiplierBySize?: Record; // 各图片尺寸积分倍率 creditDiscountRate?: number; // 积分折扣,按“折”配置,如 5 = 5折 @@ -663,6 +668,7 @@ type ImageGenConfig = { creditCostPerVideoByResolution?: Record; // 各分辨率积分 enableLoadingPlaceholder?: boolean; // 是否启用加载占位图 loadingColor?: string; // 占位图主题色,如 "purple", "blue", "green" + vip98k?: Record; // VIP98K 目录元数据 }; function createDefaultImageGenConfig(): ImageGenConfig { @@ -1037,6 +1043,7 @@ const CANVAS_TOOLBAR_FALLBACK_ICONS: Record = imageRemoveBackground: Scissors, imageBoxCutout: Scan, imageLocalEdit: Wand2, + imageLayoutReplace: LayoutTemplate, imageRemoveWatermark: Eraser, imageResize: Maximize2, imageTextEdit: Type, @@ -1585,6 +1592,7 @@ const PROVIDER_TYPES = [ { value: "bananaimage", label: "BananaImage 生图" }, { value: "visionary", label: "Visionary 生图" }, { value: "atlascloud", label: "Atlas Cloud 图片 / 3D 模型" }, + { value: "vip98k", label: "VIP98K 混合模型服务" }, { value: "stepfun-image", label: "阶跃星辰 Step Image Edit" }, { value: "bltcy-video", label: "柏拉图统一视频" }, { value: "grsai-gpt-image", label: "Grsai GPT Image" }, @@ -1622,6 +1630,7 @@ const PROVIDER_DEFAULT_BASE_URL: Record = { bananaimage: "https://bananaimage.aigenmedia.art", visionary: "https://api.visionary.beer", atlascloud: "https://api.atlascloud.ai", + vip98k: "https://api.vip98k.com", "stepfun-image": "https://api.stepfun.com", "bltcy-video": "https://api.bltcy.ai", "grsai-gpt-image": "https://grsai.dakka.com.cn", @@ -1629,7 +1638,7 @@ const PROVIDER_DEFAULT_BASE_URL: Record = { midjourney: "https://your-midjourney-api.example.com", "fal-video": "https://fal.run", "tripo-3d": "https://api.tripo3d.ai/v2/openapi", - "worldlabs-3d": "https://api.worldlabs.ai", + "worldlabs-3d": "https://api.vip98k.com", "kittycad-cad": "https://api.zoo.dev", runway: "https://api.dev.runwayml.com", krea: "https://api.krea.ai", @@ -4055,6 +4064,7 @@ export function AdminSettingsClient() { | "bananaimage" | "visionary" | "atlascloud" + | "vip98k" | "stepfun-image" | "midjourney" | "fal-video" @@ -6329,6 +6339,7 @@ export function AdminSettingsClient() { const isBananaImageProvider = providerForm.type === "bananaimage"; const isVisionaryProvider = providerForm.type === "visionary"; const isAtlasCloudProvider = providerForm.type === "atlascloud"; + const isVip98kProvider = providerForm.type === "vip98k"; const isBltcyVideoProvider = providerForm.type === "bltcy-video"; const isLemonProvider = providerForm.type === "openai" && @@ -6504,6 +6515,17 @@ export function AdminSettingsClient() { } } + if (isNewProvider && isVip98kProvider && providerId) { + try { + await handleQuickAddVip98kModels(providerId, { skipConfirm: true }); + } catch (modelError) { + console.error("[Auto Sync VIP98K Models] Error:", modelError); + toast.warning( + 'VIP98K 模型同步失败,请手动点击"同步 VIP98K 全部模型"按钮' + ); + } + } + if (isNewProvider && isBltcyVideoProvider && providerId) { try { await handleQuickAddBltcyVideoModels(providerId, { @@ -7591,6 +7613,46 @@ export function AdminSettingsClient() { } }; + const handleQuickAddVip98kModels = async ( + providerId: string, + options?: { skipConfirm?: boolean } + ) => { + if ( + !options?.skipConfirm && + !confirm( + "将从 VIP98K 模型目录同步全部模型,并按图片、视频、3D 世界、文本、图片工具、OCR、音频工具分类。图片模型会配置比例、1K/2K/4K 和多参考图能力。\n\n继续吗?" + ) + ) { + return; + } + + try { + toast.info("正在读取 VIP98K 模型目录..."); + const response = await fetch("/api/admin/vip98k-models", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ providerId }), + }); + const data = await response.json(); + if (!response.ok) throw new Error(data.error || "同步失败"); + + const totalSynced = + Number(data.addedCount || 0) + Number(data.updatedCount || 0); + const totalCatalog = Number(data.counts?.total || totalSynced); + toast.success( + `已同步 ${totalSynced} 个 VIP98K 模型(目录共 ${totalCatalog} 个)` + ); + mutateModels(); + if (data.errors?.length) { + toast.warning(`${data.errors.length} 个 VIP98K 模型同步失败`); + } + } catch (error: any) { + console.error("[Quick Add VIP98K Models] Error:", error); + toast.error(error.message || "同步 VIP98K 模型失败"); + throw error; + } + }; + const handleUseFalImageProviderPreset = () => { setProviderForm({ id: "", @@ -15800,6 +15862,13 @@ export function AdminSettingsClient() { 并按每个模型自己的 Schema 配置多图数量、比例及 1K/2K/4K 档位。

)} + {providerForm.type === "vip98k" && ( +

+ VIP98K 使用 Bearer API Key 认证,Base URL 只填写 + https://api.vip98k.com。保存后会自动同步目录中的图片、视频、3D 世界、 + 文本和工具模型;图片模型会配置比例、1K/2K/4K 与多参考图能力。 +

+ )} {providerForm.type === "bfl-image" && isBltcyBflBaseUrl(providerForm.baseUrl) && (

@@ -15957,6 +16026,17 @@ export function AdminSettingsClient() { 同步 Atlas Cloud 图片 / 3D 模型 )} + {provider.type === "vip98k" && ( + + )} {provider.type === "bltcy-video" && ( ) : null} + {selectedImageLayoutReplaceEnabled && selectedImageNodes.length === 1 ? ( + + ) : null}