news 2026/9/19 17:07:18

Cherry Studio 图像生成参数化架构:从注册表数据到厂商请求体的零重复管线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cherry Studio 图像生成参数化架构:从注册表数据到厂商请求体的零重复管线
  • 人工智能
  • 大模型
  • AI 应用
  • 交互助手
  • 本地部署

【免费下载链接】cherry-studio

🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端

项目地址:https://gitcode.com/CherryHQ/cherry-studio
点击查看免费下载

本篇技术指南围绕 Cherry Studio 的绘图(Paintings)页面展开,系统讲解其图像生成参数(image-generation parameters)如何由注册表 + 参数目录(catalog)数据驱动,实现「一次声明、处处投影」的完整链路:从渲染进程按模型渲染参数表单,到收集用户输入,再到主进程将其转换为各家 LLM 厂商格式正确的图像生成请求。读完本文,你将掌握IMAGE_PARAM_CATALOGWireProfilesplitParamValues等核心构件的职责划分,并能独立完成「给模型加参数」「加模型」「加厂商」三类扩展——全程无需编写任何 per-vendor UI 代码,canonical→wire 的重命名只声明一次。

设计目标:新增模型等于改数据

Cherry Studio 的图像生成参数系统遵循一条核心原则:为每个模型声明支持的参数及其约束(options/range/default),并为每个厂商声明 wire 格式的差异,二者各只有一处声明。其收益是:

  • 新增图像模型(甚至整个新厂商)是一次数据变更,而不是代码变更;
  • 多个模型共用的新参数,只需在目录(catalog)中增加一行,即可同时流向表单、校验、wire 请求体与静态类型;
  • 厂商 wire 格式的怪癖(字段名、嵌套、双键、透传等)各自只存在于一个声明式位置。

数据链路总览:一个 canonical 袋子,两个交付适配器

整条管线以一个规范参数集paramValues(canonical camelCase 袋子)为主干,只在末端分叉为两条交付路径。无论哪条路径,canonical 袋子本身完全相同,不同的只是适配器将其转换为 wire 请求的方式——SDK 交付走WireProfile生成的providerOptions请求体,transport 交付则由运输层自行构造专属信封。

┌─ RENDERER ────────────────────────────────────────────────────────────────────┐ │ registry per-model `supports` ──useImageGenerationSupport──▶ form │ │ imageGenerationToFields: SupportSpec.type → control (one map) │ │ user edits → painting.params (canonical camelCase bag) │ │ canonicalGenerate: buildParamsSchema(support,mode) validate / coerce │ │ → paramValues (pure canonical bag; blanks dropped, customSize composed) │ └──────────────────────────── ipcApi.request('ai.image.generate') ───────────────┘ │ { uniqueModelId, prompt, mode, paramValues, inputImages?, mask? } ▼ (model routing is dynamic; paramValues validated + coerced by the catalog imageParamsSchema) ┌─ MAIN · AiService.generateImage ───────────────────────────────────────────────┐ │ splitParamValues(paramValues) ──via AI_SDK_NATIVE_BINDINGS──▶ │ │ structured = { n, size, seed, aspectRatio, … } (typed ParamValues & {n})│ │ vendorBag = { the non-native canonical params, camelCase } │ │ │ │ resolveImageTransport(provider, model)? │ │ ├─ NO → SDK delivery ├─ YES → transport delivery │ │ ▼ ▼ │ │ buildVendorProviderOptions getImageGenerationSupport(provider, │ │ (WireProfile + wireName) model).modes[mode].vendorTransport │ │ → providerOptions[id] = WIRE body → modelDescriptor (typed routing) │ │ native → imageParams (n/size/seed) generateImageViaJob(structured, │ │ vendorBag, modelDescriptor) │ └────────┼────────────────────────────────────────────┼──────────────────────────┘ ▼ ▼ JobManager → imageGenerationJobHandler AI SDK image model transport.submit(input) (SiliconImageModel / @ai-sdk/openai-compatible reads canonical camelCase params / @ai-sdk/google / aihubmix custom) + input.{n,size,seed} + input.modelDescriptor spreads providerOptions[id] → HTTP body builds its own envelope (input.* / parameters.* / messages[]), POST, poll │ │ ▼ parse response ▼ poll → parse response image data URLs ◀────────────── back through IPC ──────────▶ renderer

整条链路可以切分为两个半场,中间以同一套规范词汇表衔接:

  • 读半场(Read half)——注册表supports⊗ 目录 → 表单字段 →painting.params
  • 写半场(Write half)——paramValuessplitParamValuesWireProfile引擎 / transport → 厂商 wire 请求体。

两个半场之间的契约就是目录(catalog):canonical 键集合 + 每个键的值类型与 wire 名称。表单、校验、参数分区、静态类型全部由它投影而来,因此永远不会漂移。

单一事实来源:每项事实只声明一次

事实声明位置被谁消费
参数值类型 + wire 名称IMAGE_PARAM_CATALOGParamValueswireName表单、校验、wire 引擎、structured类型
每个模型:支持哪些参数 + 约束(options/range/default)注册表supports表单 +buildParamsSchema
canonical → AI-SDK 原生(numImages→naspectRatio归一化…)AI_SDK_NATIVE_BINDINGSsplitParamValues
canonical → 厂商 wire 名称(negativePrompt→negative_promptwireName()(目录wire或自动 snake_case)WireProfile+ aihubmix DEFAULT
每个提供商的交付方式(双键 / 透传 / 兄弟键 / 信封)WIRE_REGISTRY+ 各 transport两个适配器
每个模型的端点路由(endpoint / sync / 响应族)注册表vendorTransportmodelDescriptortransports

这份清单中没有任何一项被重复:一个 canonical 参数就是一行目录条目;一个提供商的 wire 形态就是一个 profile / transport

第一层:参数目录(原子层)

单一事实来源是packages/provider-registry/src/schemas/imageParamCatalog.ts。每个CanonicalParamKey只声明一次,并携带管线投影所需的全部信息:

interface ImageParamCatalogEntry { schema: z.ZodTypeAny // 值类型的单一来源 → 表单校验 + ParamValues 类型 wire?: string // 厂商 wire 名称覆盖;缺省时走自动 camelCase→snake_case } const IMAGE_PARAM_CATALOG = { negativePrompt: { schema: optString }, // → 'negative_prompt' (auto) numInferenceSteps: { schema: optInt }, // → 'num_inference_steps' (auto) imageResolution: { schema: optString, wire: 'size' }, // 不规则 → 显式声明 addWatermark: { schema: optBool, wire: 'watermark' }, // … 覆盖全部 CanonicalParamKey(缺失/多余键均为编译错误) } as const satisfies Record<CanonicalParamKey, ImageParamCatalogEntry> type ParamValues = { [K in CanonicalParamKey]?: ParamValue<K> } // 每个 schema 的 z.infer

从源码实现看,该文件还内置了两条强不变量:

  • 穷尽性约束as const satisfies Record<CanonicalParamKey, …>让缺失键和未知键都成为编译错误;同时导出的IMAGE_PARAM_CATALOG_KEYS配合运行时测试锁定键集合与CANONICAL_PARAM_KEY的一致性。
  • 值类型规范化normalizeImageParamNumber只把「有限数字与数字字符串」规范化为数字,布尔值与数组保持无效,避免在提交时被静默转换成0/1之类的数字(optNumber/optInt均通过z.preprocess接入)。

目录中已声明的 canonical 键覆盖了主流厂商图像模型所需:negativePromptnumInferenceStepsguidanceScalecfgqualityseedsizeaspectRatioimageResolutionaddWatermarkoutputFormatoutputCompressionbackgroundmoderationstylestyleTypepersonGenerationsafetyTolerancepromptEnhancementpromptExtendrefMode/refStrength(参考图)、upscaleFactor/resemblance/detail(放大)、DashScope 系列的sourceLang/targetLang/function/strength/isSketch、DMXAPI 的sequentialImageGeneration/maxImages等。

从原子层投影出三样东西,且永不漂移:

  • buildParamsSchema(support, mode)packages/provider-registry/src/utils/buildParamsSchema.ts)——目录值 schema ⊗ 模型级supports约束(enum 成员 / range 边界)→ 校验某个模型表单袋子的 zod schema。实现要点是「目录负责类型强转,SupportSpec.refine追加约束」,绝不从 spec 重建 zod(那会丢失目录的强转能力);每个字段.catch(undefined)且对象.loose(),保证单个坏值/遗留值不会让整个提交失败(迁移期软失败)。
  • ParamValues——目录的z.infer,类型化的 canonical 袋子,不存在手写的参数形状类型。
  • wireName(key)——catalog.wire ?? autoSnakeCase(key)唯一的 canonical→wire 重命名。所有扁平请求体厂商的字段名都从这里派生,没有任何厂商重复写negativePrompt → negative_prompt

原生参数(numImages/size/seed/aspectRatiowireName——它们由AI_SDK_NATIVE_BINDINGS(应用层,见下文)路由,因为其目标是 AI SDK 调用选项,而不是请求体字段名。

注册表 schema:模型级supports与路由

来源是packages/provider-registry/src/schemas/model.tsImageGenerationSupportSchema

interface ImageGenerationSupport { modes: Partial<Record<ImageGenerationMode, ModeDef>> // generate | edit | remix | upscale | merge } interface ModeDef { supports: Partial<Record<CanonicalParamKey, SupportSpec>> // 支持哪些参数 + 每模型约束 vendorTransport?: { endpoint: string; isSync?: boolean } // 异步 / 非 OpenAI 路由 requirePrompt?: boolean // 默认 true;qwen-mt-image、upscalers 为 false } type SupportSpec = | { type: 'switch'; default?: boolean } | { type: 'enum'; options: string[]; default?: string; render?: 'select' | 'chips'; columns?: number } | { type: 'range'; min: number; max: number; default?: number; step?: number } | { type: 'size'; minSide: number; maxSide: number; pairedEnumKey?: string } | { type: 'text'; multiline?: boolean }

supports只携带每模型的约束(支持哪些参数、options/range/default);值类型与 wire 名称在目录中,控件种类由表单层的imageGenerationToFields依据SupportSpec.type派生。此外ModeDef还支持maxInputImages(整数、正数、可选)用于限定该模式的输入图数量。从 schema 实现可看到vendorTransport.endpoint被约束为根相对路径/^\/(?!\/)/),即/xxx而非完整 URL。

模型块的解析遵循覆盖优先规则,实现在ProviderRegistryService.getImageGenerationSupportsrc/main/data/services/ProviderRegistryService.ts):

registryOverride.imageGeneration ?? presetModel.imageGeneration ?? null // null → 空表单
  • 基础条目 →packages/provider-registry/data/models.json(厂商无关的官方契约);
  • 厂商覆盖 →packages/provider-registry/data/provider-models.json,以{ providerId, modelId }为键(厂商口味参数 / 厂商独占 SKU),id 规范化容忍带点号与净化后的 id。

读半场:注册表 + 目录 → 参数表单

渲染进程的三个步骤:

  1. 拉取——useImageGenerationSupport(providerId, modelId)src/renderer/pages/paintings/hooks/useImageGenerationSupport.ts)请求GET /providers/:providerId/models/:modelId*/image-generation-support(DataApi → 主进程同一个ProviderRegistryService,SWR 缓存)。

  2. 映射——imageGenerationToFields(support, { mode })src/renderer/pages/paintings/form/imageGenerationToFields.ts)遍历modes[mode].supports,按spec.type分发到specToField——没有 per-vendor 分支:

    SupportSpec.type控件
    switch开关(toggle)
    enumrender:'chips'chip 行(size / aspectRatio / imageResolution)
    enum(默认)下拉选择框
    range滑块
    size自定义宽×高输入(以pairedEnumKey === 'custom'为门槛)
    text输入框 / 多行文本域(multiline
  3. 标签——KEY_LABELS(同一文件)将每个CanonicalParamKey映射到 i18n 标题/提示(对键集合穷尽,缺标签即编译错误);另有OPTION_LABELS对枚举选项值做本地化(如autoquality的 low/medium/high、personGeneration的 DONT_ALLOW/ALLOW_ADULT/ALLOW_ALL、style'<photography>'等选项值原样透传、仅本地化显示文案)。

表单编辑写入painting.params——一个扁平、以 canonical 键为 key 的袋子。默认值在模型被选中时由computeModelFieldResetsrc/renderer/pages/paintings/utils/computeModelFieldReset.ts)提交。

写半场:paramValues→ 厂商请求

1. 校验并收拢为一个 IPC 袋子(canonicalGenerate

src/renderer/pages/paintings/model/canonicalGenerate.tsbuildParamsSchema(support, mode)校验painting.params(坏值软失败回退原始值)、丢弃空白、把customSize_*组合为size,然后通过 IPC(ai.image.generate,schema 见src/shared/ipc/schemas/ai.ts)发送一个 canonicalparamValues袋子 +modemode是请求属性而非参数,见 §5)。IPC schema 把paramValues类型化为目录的imageParamsSchema——路由的safeParse产出严格、已强转的ParamValues(非目录键被剥离)。模型级 options/range 约束已在渲染进程的buildParamsSchema中执行过,这里做的是值类型闸门

该文件还包含几个与表单强相关的守卫逻辑:仅图片文件计入输入(inputImages过滤);mode在「有输入图 →edit,否则 →generate」间自动推导;编辑类模式必须携带输入图(否则抛EDIT_IMAGE_REQUIRED);maxInputImages超限抛INPUT_IMAGE_LIMIT_EXCEEDED;输入图被编码为data:URL 随 IPC 单独携带(它们是文件而不是表单参数)。

2. 主进程分区(splitParamValues+AI_SDK_NATIVE_BINDINGS

src/main/ai/AiService.tsgenerateImage调用splitParamValuessrc/main/ai/utils/imageOptions.ts),后者依据AI_SDK_NATIVE_BINDINGSsrc/main/ai/utils/aiSdkNativeBindings.ts)把袋子分成两半:

  • 原生numImages→nsizeseedaspectRatio归一化一次)→structured(类型化为ParamValues & { n?: number }),即 AI SDK 调用选项;
  • 其余全部vendorBag(canonical camelCase)。

空白 /null/undefined在此被丢弃(字节一致 wire 守卫);'auto'哨兵值保留到 wire 层再解析为「省略该字段」。

AI_SDK_NATIVE_BINDINGS的完整定义(源码确认):

export const AI_SDK_NATIVE_BINDINGS = { numImages: { option: 'n' }, // 唯一的重命名:numImages → n size: { option: 'size' }, seed: { option: 'seed' }, aspectRatio: { option: 'aspectRatio', map: (v: unknown) => normalizeAspectRatio(typeof v === 'string' ? v : undefined) } } as const satisfies Partial<Record<CanonicalParamKey, NativeBinding>>

其中normalizeAspectRatio将表单的ASPECT_X_Y枚举(或已归一化的X:Y)转为 AI SDK 图像选项与 Google/Imagen 接受的${number}:${number}形状,幂等(X:Y → X:Y),空白/不匹配返回undefined从而省略该字段。真正属于 AI SDKImageModelV3CallOptions的只有n/size/aspectRatio/seed四个;其余(negativePromptnumInferenceStepsguidanceScalequalitybackgroundmoderationstylepersonGeneration…)不是 SDK 的类型化选项,其唯一通道是providerOptions(即厂商请求体),因此一律流经vendorBag→ WireProfile 引擎(SDK 交付)或 transports(job 交付),绝不进入本表。

3. WireProfile 引擎(SDK 交付)

对走 SDK 交付的提供商,buildVendorProviderOptionssrc/main/ai/provider/custom/wire/buildImageRequest.ts)先经wireName把 canonical 袋子映射为 wire 请求体,再按该提供商在WIRE_REGISTRYsrc/main/ai/provider/custom/wire/wireProfile.ts)中的注册方式打包:

interface WireProfile { fields: Partial<Record<CanonicalParamKey, WireRule>> } // forward / map / contribute interface WireRegistration { profile: WireProfile dualOpenAI?: boolean // 在 openai 键下镜像干净请求体(gpt-image 家族) passthrough?: boolean // 透传 profile 未命名的 vendor-bag 字段(silicon cfg…) also?: { key; profile }[] // 兄弟提供商键(dmxapi → google.imageConfig) }

WireRule提供三种字段处理方式:

  • to?: string——显式 wire 字段名(覆盖wireName(key)),例如 Ollama 的numInferenceSteps: { to: 'steps' }、MiniMax 的addWatermark: { to: 'aigc_watermark' }
  • map?: (value, all) => JSONValue——值变换,例如 Google 的personGeneration转小写(ALLOW_ALL → allow_all,因为@ai-sdk/google的选项 schema 只接受小写);
  • contribute?: (value, all) => Record<string, JSONValue>——一对多 / 嵌套逃生门,例如 Google 的aspectRatio/size组装进imageConfig嵌套块(aspectRatioImageConfigRule/imageResolutionImageConfigRule被 google、google-vertex 与 dmxapi 的 google 路由块共享)。

profile 声明哪些canonical 参数进入请求体;名称来自wireName投递方式(哪个键、是否透传、兄弟键、嵌套)是 registration 的职责——不是 profile 的,也绝无重复重命名。buildImageRequestskipValue会丢弃undefined/''/null/'auto',与旧compact()保持字节一致;mergeContributioncontribute()结果做嵌套深合并并剔除空叶(保证未设置的aspectRatio不会留下imageConfig.aspectRatio)。未出现在WIRE_REGISTRY的提供商回退到DEFAULT_DIFFUSION_REGISTRATION(OpenAI 兼容 diffusion 家族,passthrough: true)。最终产出providerOptions[id],AI SDK 图像模型将其展开进请求体;structured则成为类型化调用选项(imageParams)。

SDK 图像模型为以下之一:自定义ImageModelV3(如src/main/ai/provider/custom/silicon/SiliconImageModel.tssrc/main/ai/provider/custom/aihubmix/aihubmixImageModel.ts)、@ai-sdk/openai-compatible@ai-sdk/google。它读取wire 请求体——不会二次重命名。

WIRE_REGISTRY源码中已确认的注册条目(节选):

provider idprofile投递特性
openai/openai-chat/azure/azure-responses/huggingface/newapiOPENAI_WIRE_PROFILE(quality/background/moderation/style)dualOpenAI
cherryin/cherryin-chatOPENAI_WIRE_PROFILEdualOpenAI+passthroughcherryin-chat另以key: 'cherryin'交付,因为 wrapper 读取固定键providerOptions['cherryin']
googleGOOGLE_WIRE_PROFILE默认键
google-vertexGOOGLE_WIRE_PROFILEkey: 'vertex'(SDK 读取providerOptions.vertex
dashscopeDASHSCOPE_WIRE_PROFILEpassthrough(透传modelDescriptor/sourceLang/targetLang等给 transport)
doubaoDOUBAO_WIRE_PROFILEkey: 'bytedance'+passthrough@ai-sdk/bytedance的选项 schema 是 camelCase,自行完成厂商命名;'auto'对 Ark 是启用组图的真值,因此必须透传保活)
aihubmixAIHUBMIX_WIRE_PROFILE(OpenAI body +seeddualOpenAI+passthrough(openai 镜像保持干净,厂商键下挂完整袋子)
dmxapiDMXAPI_WIRE_PROFILEalso: [{ key: 'google', profile: DMXAPI_GOOGLE_PROFILE }](多后端网关向 google 适配器投递imageConfig块)
ollamaOLLAMA_WIRE_PROFILE默认键
minimaxMINIMAX_WIRE_PROFILE默认键
openrouterOPENROUTER_WIRE_PROFILE(含outputCompressioncontribute:仅当outputFormat为 jpeg/webp 时输出output_compression默认键
未列出DEFAULT_DIFFUSION_REGISTRATIONpassthrough

4. Transport 投递(异步 / 定制 wire 形态)

resolveImageTransport(provider, model, settings)src/main/ai/provider/custom/imageTransportRegistry.ts)返回 transport(DashScope / PPIO / ModelScope / OVMS / DMXAPI 定制家族)时,请求改走任务系统:generateImageViaJobJobManagerimageGenerationJobHandlersrc/main/ai/provider/custom/tasks/imageGenerationJobHandler.ts),由 handler 独占 submit/poll 循环、排队与取消。

该 job刻意不重启持久化recovery: 'abandon'):其唯一消费者是generateImageViaJob中的进程内 awaiter(handle.finished),payload 未记录消费者身份,重启后恢复的任务无人接收结果;非终止态 job 在启动时被取消而非恢复。handler 还设置了defaultConcurrency: 2defaultRetryPolicy: { maxAttempts: 1 }(transport 内部已重试瞬时轮询错误,job 级重试会导致重复提交并烧掉用户配额)、defaultTimeoutMs: 30 * 60_000;密钥永不持久化,每次执行都通过resolveProviderAiSdkConfig重读 apiKey;输入图按 FileEntry id 引用、从 FileManager 读回,保证 payload 低于 1MB job 上限。

与 SDK 路径(其袋子就是请求体)不同,transport 构建自己的per-model 信封,因此它直接接收 canonical camelCase 参数——原生n/size/seed走类型化的ImageGenerationSubmitInput.{n,size,seed},其余经providerParams(即vendorBag)。没有 wire 命名、没有大小写探测:每个 transport 直接读params.negativePromptparams.promptExtend… 放入自己的结构(input.*/parameters.*/messages[])。契约见src/main/ai/provider/custom/imageGenerationModel.tsImageGenerationTransportsubmit/poll/cancel)与各<vendor>/<vendor>Transport.tscreateImageGenerationModel将 transport 包成ImageModelV3maxImagesPerCall: 1),doGenerate执行 submit→可选 poll→返回 URL,图片 URL 由打过补丁的aiSDK 自动下载为GeneratedFile

5. 路由——modelDescriptor(主进程派生)

transport 依据modelDescriptor = { id, endpoint, isSync, mode }路由:POST 到哪个端点、是否轮询、用哪个响应族解析器。

这是后端路由数据,在路由发生处派生:AiService持有解析后的(providerId, modelId, mode),调用providerRegistryService.getImageGenerationSupport(...)modes[mode].vendorTransport构建 descriptor,并以类型化字段挂在 job payload /ImageGenerationSubmitInput.modelDescriptor上——绝不混入参数袋子。注册表是vendorTransport的来源,且主进程本就托管它(渲染进程的 support 拉取只是到同一服务的 IPC 往返),所以 descriptor 是纯派生值,而非参数。

扩展配方

给模型加一个参数

  1. 选取(或复用)canonical 键。若是新键,在IMAGE_PARAM_CATALOG增加一行{ schema, wire? }——仅当厂商名称不是自动 snake_case 形式时才设置wire;再补一行KEY_LABELS,并按 i18n 工作流完成各语言环境与校验步骤。
  2. 在模型的supports中以正确的SupportSpec(options/range/default)声明它。
  3. 对扁平请求体厂商,到此为止——表单、校验、类型、wire 名称全部从目录行流出。只有当参数需要定制摆放(嵌套块、兄弟厂商键、transport 信封)时才需要加WireProfile字段 / transport 行。

加一个模型

  • 厂商无关的官方模型 → 在models.jsonimageGeneration块;
  • 厂商口味 / 独占 → 在provider-models.json{ providerId, modelId, imageGeneration }覆盖;
  • 异步 / 非 OpenAI wire 形态 → 加vendorTransport.endpoint(+isSync),并确保对应厂商 transport 认识该模型族。

加一个厂商

  • OpenAI 兼容 → 无需定制:DEFAULT_DIFFUSION_REGISTRATION引擎 +@ai-sdk/openai-compatible直接覆盖;
  • 原生 SDK(OpenAI/Google)→ 以正确的投递标记(dualOpenAI/also/passthrough)注册一个WireProfile
  • 定制 / 异步 wire 形态 → 实现ImageGenerationTransport,在提供商的imageModel(...)上注册,并读取 canonical camelCase 参数 +input.modelDescriptor

关键文件索引

关注点文件
参数目录(值 + wire)packages/provider-registry/src/schemas/imageParamCatalog.ts
每模型校验 schemapackages/provider-registry/src/utils/buildParamsSchema.ts
注册表 schema(supports/vendorTransportpackages/provider-registry/src/schemas/model.ts
基础 / 覆盖模型数据packages/provider-registry/data/models.json、packages/provider-registry/data/provider-models.json
解析器(override ?? base)src/main/data/services/ProviderRegistryService.ts
Support 拉取 hooksrc/renderer/pages/paintings/hooks/useImageGenerationSupport.ts
注册表 → 表单字段 +KEY_LABELSsrc/renderer/pages/paintings/form/imageGenerationToFields.ts
切换模型时的默认值填充src/renderer/pages/paintings/utils/computeModelFieldReset.ts
校验 + 构建 IPCparamValues袋子src/renderer/pages/paintings/model/canonicalGenerate.ts
Transport 路由提示(→ 后端,见 §5)src/renderer/pages/paintings/model/paintingPipeline.ts
IPC payload schemasrc/shared/ipc/schemas/ai.ts
主进程入口 + 原生分区 + job 分发src/main/ai/AiService.ts
splitParamValues(native vs vendorBag)src/main/ai/utils/imageOptions.ts
AI-SDK 原生绑定表src/main/ai/utils/aiSdkNativeBindings.ts
WireProfile 引擎 + 投递注册表src/main/ai/provider/custom/wire/buildImageRequest.ts、src/main/ai/provider/custom/wire/wireProfile.ts
自定义 transport 契约src/main/ai/provider/custom/imageGenerationModel.ts
异步任务 handlersrc/main/ai/provider/custom/tasks/imageGenerationJobHandler.ts
共享 transport 工具src/main/ai/provider/custom/transportUtils.ts

小结

Cherry Studio 的图像生成参数系统把「表单渲染」「值校验」「请求体组装」「静态类型」全部收敛到两个声明式事实之上:参数目录(canonical 键的值类型与 wire 名称)与注册表数据(每模型的supports与每模式的vendorTransport)。canonicalparamValues袋子贯穿渲染进程与主进程,只在最后分叉为WireProfile(SDK 交付)与ImageGenerationTransport(异步 job 交付)两条适配路径。任何一层都看不到逐厂商手写的 UI 分支或重复的字段重命名——这正是「新增图像模型等于一次数据变更」这一设计目标的落地形态。

  • 人工智能
  • 大模型
  • AI 应用
  • 交互助手
  • 本地部署

【免费下载链接】cherry-studio

🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端

项目地址:https://gitcode.com/CherryHQ/cherry-studio
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 17:04:42

2023数据中台建设方案:DataOps驱动的MVP落地实践

简介&#xff1a;本资源为一份面向企业数字化转型实践者、数据架构师与中台建设团队的2023年数据中台项目建设方案&#xff0c;聚焦解决多源数据分散、治理低效、指标口径不一、资产价值难量化等典型痛点。方案全文以Word文档&#xff08;.docx&#xff09;形式呈现&#xff0c…

作者头像 李华
网站建设 2026/9/19 17:02:19

DeepSeek 降重版测评,论文改写的 Base URL 填 TaoToken 的 API 地址

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 17:00:20

社区健康志愿服务APP开发:FastAPI订单状态机与GPS签到实践

简介&#xff1a;“乐助”社区健康志愿服务APP平台的设计与构建是一篇聚焦社区志愿管理数字化的学术论文&#xff0c;面向健康服务、护理信息化及APP开发方向的从业者和研究者。论文以沈阳市5个社区180名居民为调查对象&#xff0c;采用自研问卷得出居民参与意愿达80.9%、对志愿…

作者头像 李华
网站建设 2026/9/19 16:59:57

STM32 ADC片内信号采集实战:从温度传感器到VREFINT的完整链路

1. 为什么我要用片内信号做ADC演示而不是外接传感器很多人第一次接触STM32的ADC&#xff0c;第一反应是找个电位器或者光敏电阻接上去&#xff0c;拧一拧、遮一遮&#xff0c;看串口输出的数字跳来跳去&#xff0c;觉得“跑通了”。但真到了项目里&#xff0c;比如做数字电源的…

作者头像 李华
网站建设 2026/9/19 16:59:47

MindSpore Transformers训练在线监控:实时曲线可视化方案从0到1

在深度学习训练里&#xff0c;我吃过最大的亏就是“闷头跑实验”。模型扔上去&#xff0c;loss 一开始降得挺漂亮&#xff0c;我就放心去忙别的了&#xff0c;等第二天回来看日志&#xff0c;发现 loss 早就发散成一条冲天直线&#xff0c;而且是在好几个小时之前就爆掉了——那…

作者头像 李华