"use client"; import { Database, Eye, EyeOff, Image as ImageIcon, Loader2, Paintbrush, Save, Sparkles, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { DEFAULT_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS, DEFAULT_CANVAS_IMAGE_TASK_CONCURRENCY, MAX_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS, MAX_CANVAS_IMAGE_TASK_CONCURRENCY, MIN_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS, MIN_CANVAS_IMAGE_TASK_CONCURRENCY, normalizeCanvasImageTaskQueueConfig, } from "@/lib/canvas/image-task-config"; import type { AdminAiModel, AdminAiProvider, LocalAiRuntimeSectionsProps, } from "../../_lib/types"; import { LocalAiGroupHeading, LocalAiSectionShell, LocalAiSectionSummary, } from "../local-ai-section-shell"; export function LocalAiRuntimeSections({ thirdPartySqlServerConfig, setThirdPartySqlServerConfig, isThirdPartySqlServerFieldsVisible, setIsThirdPartySqlServerFieldsVisible, isTestingThirdPartySqlServer, realtimeCanvasConfig, setRealtimeCanvasConfig, canvasImageTaskQueueConfig, setCanvasImageTaskQueueConfig, sceneFusionConfig, setSceneFusionConfig, hasThirdPartySqlServerConfiguredValue, modelsData, providersData, isLocalAiSectionExpanded, toggleLocalAiSection, DEFAULT_BFL_BASE_URL, DEFAULT_BFL_REALTIME_MODEL, handleSaveThirdPartySqlServerConfig, handleTestThirdPartySqlServerConfig, handleSaveRealtimeCanvasConfig, handleSaveCanvasImageTaskQueueConfig, handleSaveSceneFusionConfig, }: LocalAiRuntimeSectionsProps) { return ( <> 配置后可供后续业务功能直接访问外部 SQL Server 数据库。当前仅提供配置保存与连通性测试。 } expanded={isLocalAiSectionExpanded("third-party-sqlserver")} id="local-ai-section-third-party-sqlserver" onToggle={() => toggleLocalAiSection("third-party-sqlserver")} summary={ } title={ 第三方 SQL Server 数据库连接 } >
关闭后保留配置但不作为后续功能默认外部数据源。
setThirdPartySqlServerConfig((prev) => ({ ...prev, enabled: checked, })) } /> {thirdPartySqlServerConfig.enabled ? "已开启" : "已关闭"}
数据库连接信息默认隐藏
{hasThirdPartySqlServerConfiguredValue ? "已保存的主机地址、数据库、账号和密码不会默认显示,只有主动点击后才会回显并允许编辑。" : "当前未展开连接字段。需要新增或编辑配置时,再手动显示连接信息。"}
{isThirdPartySqlServerFieldsVisible ? ( <>
setThirdPartySqlServerConfig((prev) => ({ ...prev, host: e.target.value, })) } placeholder="例如:db.example.com 或 10.0.0.8" value={thirdPartySqlServerConfig.host} />
setThirdPartySqlServerConfig((prev) => ({ ...prev, port: Math.max( 1, Math.min(65_535, Number(e.target.value) || 1433) ), })) } placeholder="1433" type="number" value={thirdPartySqlServerConfig.port} />
setThirdPartySqlServerConfig((prev) => ({ ...prev, database: e.target.value, })) } placeholder="请输入数据库名称" value={thirdPartySqlServerConfig.database} />
setThirdPartySqlServerConfig((prev) => ({ ...prev, username: e.target.value, })) } placeholder="请输入数据库账号" value={thirdPartySqlServerConfig.username} />
setThirdPartySqlServerConfig((prev) => ({ ...prev, password: e.target.value, })) } placeholder="请输入 SQL Server 密码" type="password" value={thirdPartySqlServerConfig.password} />
setThirdPartySqlServerConfig((prev) => ({ ...prev, connectionTimeoutMs: Math.max( 1000, Math.min(120_000, Number(e.target.value) || 15_000) ), })) } step={1000} type="number" value={thirdPartySqlServerConfig.connectionTimeoutMs} />
setThirdPartySqlServerConfig((prev) => ({ ...prev, requestTimeoutMs: Math.max( 1000, Math.min(120_000, Number(e.target.value) || 30_000) ), })) } step={1000} type="number" value={thirdPartySqlServerConfig.requestTimeoutMs} />
现代云数据库通常需要开启;传统内网 SQL Server 可关闭。
setThirdPartySqlServerConfig((prev) => ({ ...prev, encrypt: checked, })) } />
对自签名证书或传统 SQL Server 更友好。
setThirdPartySqlServerConfig((prev) => ({ ...prev, trustServerCertificate: checked, })) } />
) : (
{hasThirdPartySqlServerConfiguredValue ? "当前已保存的 SQL Server 连接字段处于隐藏状态。点击上方按钮后才会展示主机、数据库、账号和密码。" : "当前未展示 SQL Server 连接字段。点击上方按钮开始填写连接信息。"}
)}

• 当前支持 SQL Server 标准 TCP 连接。

• 配置会保存到系统设置,供后续外部数据查询/同步功能复用。

• 测试连接会尝试建立连接并执行基础查询,不会修改外部数据库数据。

{/* 实时画布API配置 */} 配置实时画布功能的供应商、模型与 API Key。当前已接入 BFL / Replicate / FAL 供应商;FAL 的 WebSocket 真流式实时属于第二阶段,不是这次上线内容。 } expanded={isLocalAiSectionExpanded("realtime-canvas")} id="local-ai-section-realtime-canvas" onToggle={() => toggleLocalAiSection("realtime-canvas")} summary={ } title={ 实时画布配置 } >
关闭后前端“实时画布”入口仍可显示,但会提示服务未启用。
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, enabled: checked, }) } /> {realtimeCanvasConfig.enabled ? "已开启" : "已关闭"}
{realtimeCanvasConfig.providerType === "replicate" ? ( <>
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, replicateApiToken: e.target.value, }) } placeholder="r8_xxxxxxxxxxxxx" type="password" value={realtimeCanvasConfig.replicateApiToken} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, replicateModel: e.target.value, }) } placeholder="black-forest-labs/flux-2-klein-4b" value={realtimeCanvasConfig.replicateModel} />
) : realtimeCanvasConfig.providerType === "fal" ? ( <>
当前 FAL 接入的是“供应商能力接入阶段”,会通过现有 `/api/realtime/generate` 服务端链路调用 FAL。 前端直连 FAL WebSocket 的“真流式实时”属于第二阶段,当前尚未启用。
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, falApiKey: e.target.value, }) } placeholder="FAL_KEY_ID:FAL_KEY_SECRET 或 token" type="password" value={realtimeCanvasConfig.falApiKey} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, falModel: e.target.value, }) } placeholder="fal-ai/your-image-edit-model" value={realtimeCanvasConfig.falModel} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, falBaseUrl: e.target.value, }) } placeholder="https://fal.run" value={realtimeCanvasConfig.falBaseUrl} />
) : ( <>
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, bflApiKey: e.target.value, }) } placeholder="bfl_xxxxxxxxxxxxx" type="password" value={realtimeCanvasConfig.bflApiKey} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, bflBaseUrl: e.target.value, }) } placeholder={DEFAULT_BFL_BASE_URL} value={realtimeCanvasConfig.bflBaseUrl} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, bflModel: e.target.value, }) } placeholder={DEFAULT_BFL_REALTIME_MODEL} value={realtimeCanvasConfig.bflModel} />
)}
主实时服务失败时自动切换到备用 BFL 服务。
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackEnabled: checked, }) } />
{realtimeCanvasConfig.fallbackProviderType === "replicate" ? ( <>
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackReplicateApiToken: e.target.value, }) } placeholder="r8_xxxxxxxxxxxxx" type="password" value={realtimeCanvasConfig.fallbackReplicateApiToken} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackReplicateModel: e.target.value, }) } placeholder="black-forest-labs/flux-2-klein-4b" value={realtimeCanvasConfig.fallbackReplicateModel} />
) : realtimeCanvasConfig.fallbackProviderType === "fal" ? ( <>
备用 FAL 目前同样走现有服务端生成链路,不是前端直连 WebSocket 真流式实时。
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackFalApiKey: e.target.value, }) } placeholder="FAL_KEY_ID:FAL_KEY_SECRET 或 token" type="password" value={realtimeCanvasConfig.fallbackFalApiKey} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackFalModel: e.target.value, }) } placeholder="fal-ai/your-image-edit-model" value={realtimeCanvasConfig.fallbackFalModel} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackFalBaseUrl: e.target.value, }) } placeholder="https://fal.run" value={realtimeCanvasConfig.fallbackFalBaseUrl} />
) : ( <>
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackBflApiKey: e.target.value, }) } placeholder="bfl_xxxxxxxxxxxxx" type="password" value={realtimeCanvasConfig.fallbackBflApiKey} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackBflBaseUrl: e.target.value, }) } placeholder={DEFAULT_BFL_BASE_URL} value={realtimeCanvasConfig.fallbackBflBaseUrl} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, fallbackBflModel: e.target.value, }) } placeholder={DEFAULT_BFL_REALTIME_MODEL} value={realtimeCanvasConfig.fallbackBflModel} />
)}

推荐:BFL 实时草图模型使用 `flux-2-klein-4b`;FAL 需填写支持图像编辑输入的模型 ID。

setRealtimeCanvasConfig({ ...realtimeCanvasConfig, throttleInterval: Number.parseInt(e.target.value, 10) || 100, }) } type="number" value={realtimeCanvasConfig.throttleInterval} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, maxBuffering: Number.parseInt(e.target.value, 10) || 2, }) } type="number" value={realtimeCanvasConfig.maxBuffering} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, maxInputResolution: Number.parseInt(e.target.value, 10) || 1024, }) } type="number" value={realtimeCanvasConfig.maxInputResolution} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, jpegQuality: Number.parseFloat(e.target.value) || 0.55, }) } step={0.01} type="number" value={realtimeCanvasConfig.jpegQuality} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, previewMegapixels: Number.parseFloat(e.target.value) || 0.4, }) } step={0.01} type="number" value={realtimeCanvasConfig.previewMegapixels} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, detailMegapixels: Number.parseFloat(e.target.value) || 1, }) } step={0.01} type="number" value={realtimeCanvasConfig.detailMegapixels} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, outputJpegQuality: Number.parseInt(e.target.value, 10) || 62, }) } step={1} type="number" value={realtimeCanvasConfig.outputJpegQuality} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, steps: Number.parseInt(e.target.value, 10) || 3, }) } type="number" value={realtimeCanvasConfig.steps} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, feedback: Number.parseFloat(e.target.value) || 0.95, }) } step={0.01} type="number" value={realtimeCanvasConfig.feedback} />
setRealtimeCanvasConfig({ ...realtimeCanvasConfig, scheduleMu: Number.parseFloat(e.target.value) || 2.3, }) } step={0.1} type="number" value={realtimeCanvasConfig.scheduleMu} />
{/* 全站生图单模型并发 */} 控制全站生图链路中,同一个模型允许同时运行的任务数,以及排队后下一批任务的发起间隔。配置保存到数据库,异步队列和直调生成都会自动读取,无需重启服务。 } expanded={isLocalAiSectionExpanded("canvas-image-task-queue")} id="local-ai-section-canvas-image-task-queue" onToggle={() => toggleLocalAiSection("canvas-image-task-queue")} summary={ } title={ 全站生图单模型并发与批次间隔 } >
生效范围

• 影响画布异步生图、智能体直调生图、视频分镜高清预览等全站生图入口。

• 多用户共享同一套并发限制,但会按模型分别计数,不同模型之间不再互相阻塞。

• 当前配置的是单模型并发,不是全站总并发,也不是单用户并发。

• 批次间隔只控制排队补位的“下一批”发起节奏,不影响首批任务即时启动。

• 数值过高可能触发上游模型 429、超时或 504,建议配合模型 Key 池继续细分。

setCanvasImageTaskQueueConfig( normalizeCanvasImageTaskQueueConfig({ ...canvasImageTaskQueueConfig, concurrency: e.target.value, }) ) } type="number" value={canvasImageTaskQueueConfig.concurrency} />
可配置范围 {MIN_CANVAS_IMAGE_TASK_CONCURRENCY}- {MAX_CANVAS_IMAGE_TASK_CONCURRENCY},当前为{" "} {canvasImageTaskQueueConfig.concurrency} 并发,默认值为{" "} {DEFAULT_CANVAS_IMAGE_TASK_CONCURRENCY}。
setCanvasImageTaskQueueConfig( normalizeCanvasImageTaskQueueConfig({ ...canvasImageTaskQueueConfig, batchDispatchIntervalMs: Math.round( Number.parseFloat(e.target.value || "0") * 1000 ), }) ) } step="0.5" type="number" value={String( canvasImageTaskQueueConfig.batchDispatchIntervalMs / 1000 )} />
可配置范围 {MIN_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS / 1000}- {MAX_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS / 1000} 秒,当前为{" "} {( canvasImageTaskQueueConfig.batchDispatchIntervalMs / 1000 ).toFixed(1)}{" "} 秒,默认值为{" "} {( DEFAULT_CANVAS_IMAGE_BATCH_DISPATCH_INTERVAL_MS / 1000 ).toFixed(1)}{" "} 秒。
{/* 批量处理配置 */} 配置批量处理功能的默认提示词和参数。批量处理可将产品图批量融合到多个场景中。 } expanded={isLocalAiSectionExpanded("batch-process")} id="local-ai-section-batch-process" onToggle={() => toggleLocalAiSection("batch-process")} summary={ } title={ 批量处理配置 } > {/* 功能开关 */}
开启后用户可在图片编辑器中使用批量处理功能
setSceneFusionConfig({ ...sceneFusionConfig, enabled: checked, }) } /> {sceneFusionConfig.enabled ? "已开启" : "已关闭"}
先对产品图做抠图预处理,再进行裂变融合;抠图失败将自动降级为原图继续生成
setSceneFusionConfig({ ...sceneFusionConfig, preRmbgEnabled: checked, }) } /> {sceneFusionConfig.preRmbgEnabled ? "已开启" : "已关闭"}
{/* 默认提示词 */}