- 人工智能
- 大模型
- AI 应用
- 交互助手
- 本地部署
【免费下载链接】cherry-studio
🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端
本篇技术指南围绕 Cherry Studio 的绘图(Paintings)页面展开,系统讲解其图像生成参数(image-generation parameters)如何由注册表 + 参数目录(catalog)数据驱动,实现「一次声明、处处投影」的完整链路:从渲染进程按模型渲染参数表单,到收集用户输入,再到主进程将其转换为各家 LLM 厂商格式正确的图像生成请求。读完本文,你将掌握IMAGE_PARAM_CATALOG、WireProfile、splitParamValues等核心构件的职责划分,并能独立完成「给模型加参数」「加模型」「加厂商」三类扩展——全程无需编写任何 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)——
paramValues→splitParamValues→WireProfile引擎 / transport → 厂商 wire 请求体。
两个半场之间的契约就是目录(catalog):canonical 键集合 + 每个键的值类型与 wire 名称。表单、校验、参数分区、静态类型全部由它投影而来,因此永远不会漂移。
单一事实来源:每项事实只声明一次
| 事实 | 声明位置 | 被谁消费 |
|---|---|---|
| 参数值类型 + wire 名称 | IMAGE_PARAM_CATALOG(ParamValues、wireName) | 表单、校验、wire 引擎、structured类型 |
| 每个模型:支持哪些参数 + 约束(options/range/default) | 注册表supports | 表单 +buildParamsSchema |
canonical → AI-SDK 原生(numImages→n、aspectRatio归一化…) | AI_SDK_NATIVE_BINDINGS | splitParamValues |
canonical → 厂商 wire 名称(negativePrompt→negative_prompt) | wireName()(目录wire或自动 snake_case) | WireProfile+ aihubmix DEFAULT |
| 每个提供商的交付方式(双键 / 透传 / 兄弟键 / 信封) | WIRE_REGISTRY+ 各 transport | 两个适配器 |
| 每个模型的端点路由(endpoint / sync / 响应族) | 注册表vendorTransport→modelDescriptor | transports |
这份清单中没有任何一项被重复:一个 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 键覆盖了主流厂商图像模型所需:negativePrompt、numInferenceSteps、guidanceScale、cfg、quality、seed、size、aspectRatio、imageResolution、addWatermark、outputFormat、outputCompression、background、moderation、style、styleType、personGeneration、safetyTolerance、promptEnhancement、promptExtend、refMode/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/aspectRatio)不走wireName——它们由AI_SDK_NATIVE_BINDINGS(应用层,见下文)路由,因为其目标是 AI SDK 调用选项,而不是请求体字段名。
注册表 schema:模型级supports与路由
来源是packages/provider-registry/src/schemas/model.ts的ImageGenerationSupportSchema:
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.getImageGenerationSupport(src/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。
读半场:注册表 + 目录 → 参数表单
渲染进程的三个步骤:
拉取——
useImageGenerationSupport(providerId, modelId)(src/renderer/pages/paintings/hooks/useImageGenerationSupport.ts)请求GET /providers/:providerId/models/:modelId*/image-generation-support(DataApi → 主进程同一个ProviderRegistryService,SWR 缓存)。映射——
imageGenerationToFields(support, { mode })(src/renderer/pages/paintings/form/imageGenerationToFields.ts)遍历modes[mode].supports,按spec.type分发到specToField——没有 per-vendor 分支:SupportSpec.type控件 switch开关(toggle) enum(render:'chips')chip 行(size / aspectRatio / imageResolution) enum(默认)下拉选择框 range滑块 size自定义宽×高输入(以 pairedEnumKey === 'custom'为门槛)text输入框 / 多行文本域( multiline)标签——
KEY_LABELS(同一文件)将每个CanonicalParamKey映射到 i18n 标题/提示(对键集合穷尽,缺标签即编译错误);另有OPTION_LABELS对枚举选项值做本地化(如auto、quality的 low/medium/high、personGeneration的 DONT_ALLOW/ALLOW_ADULT/ALLOW_ALL、style的'<photography>'等选项值原样透传、仅本地化显示文案)。
表单编辑写入painting.params——一个扁平、以 canonical 键为 key 的袋子。默认值在模型被选中时由computeModelFieldReset(src/renderer/pages/paintings/utils/computeModelFieldReset.ts)提交。
写半场:paramValues→ 厂商请求
1. 校验并收拢为一个 IPC 袋子(canonicalGenerate)
src/renderer/pages/paintings/model/canonicalGenerate.ts用buildParamsSchema(support, mode)校验painting.params(坏值软失败回退原始值)、丢弃空白、把customSize_*组合为size,然后通过 IPC(ai.image.generate,schema 见src/shared/ipc/schemas/ai.ts)发送一个 canonicalparamValues袋子 +mode(mode是请求属性而非参数,见 §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.ts的generateImage调用splitParamValues(src/main/ai/utils/imageOptions.ts),后者依据AI_SDK_NATIVE_BINDINGS(src/main/ai/utils/aiSdkNativeBindings.ts)把袋子分成两半:
- 原生(
numImages→n、size、seed、aspectRatio归一化一次)→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四个;其余(negativePrompt、numInferenceSteps、guidanceScale、quality、background、moderation、style、personGeneration…)不是 SDK 的类型化选项,其唯一通道是providerOptions(即厂商请求体),因此一律流经vendorBag→ WireProfile 引擎(SDK 交付)或 transports(job 交付),绝不进入本表。
3. WireProfile 引擎(SDK 交付)
对走 SDK 交付的提供商,buildVendorProviderOptions(src/main/ai/provider/custom/wire/buildImageRequest.ts)先经wireName把 canonical 袋子映射为 wire 请求体,再按该提供商在WIRE_REGISTRY(src/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 的,也绝无重复重命名。buildImageRequest的skipValue会丢弃undefined/''/null/'auto',与旧compact()保持字节一致;mergeContribution对contribute()结果做嵌套深合并并剔除空叶(保证未设置的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.ts、src/main/ai/provider/custom/aihubmix/aihubmixImageModel.ts)、@ai-sdk/openai-compatible或@ai-sdk/google。它读取wire 请求体——不会二次重命名。
WIRE_REGISTRY源码中已确认的注册条目(节选):
| provider id | profile | 投递特性 |
|---|---|---|
openai/openai-chat/azure/azure-responses/huggingface/newapi | OPENAI_WIRE_PROFILE(quality/background/moderation/style) | dualOpenAI |
cherryin/cherryin-chat | OPENAI_WIRE_PROFILE | dualOpenAI+passthrough(cherryin-chat另以key: 'cherryin'交付,因为 wrapper 读取固定键providerOptions['cherryin']) |
google | GOOGLE_WIRE_PROFILE | 默认键 |
google-vertex | GOOGLE_WIRE_PROFILE | key: 'vertex'(SDK 读取providerOptions.vertex) |
dashscope | DASHSCOPE_WIRE_PROFILE | passthrough(透传modelDescriptor/sourceLang/targetLang等给 transport) |
doubao | DOUBAO_WIRE_PROFILE | key: 'bytedance'+passthrough(@ai-sdk/bytedance的选项 schema 是 camelCase,自行完成厂商命名;'auto'对 Ark 是启用组图的真值,因此必须透传保活) |
aihubmix | AIHUBMIX_WIRE_PROFILE(OpenAI body +seed) | dualOpenAI+passthrough(openai 镜像保持干净,厂商键下挂完整袋子) |
dmxapi | DMXAPI_WIRE_PROFILE | also: [{ key: 'google', profile: DMXAPI_GOOGLE_PROFILE }](多后端网关向 google 适配器投递imageConfig块) |
ollama | OLLAMA_WIRE_PROFILE | 默认键 |
minimax | MINIMAX_WIRE_PROFILE | 默认键 |
openrouter | OPENROUTER_WIRE_PROFILE(含outputCompression的contribute:仅当outputFormat为 jpeg/webp 时输出output_compression) | 默认键 |
| 未列出 | DEFAULT_DIFFUSION_REGISTRATION | passthrough |
4. Transport 投递(异步 / 定制 wire 形态)
当resolveImageTransport(provider, model, settings)(src/main/ai/provider/custom/imageTransportRegistry.ts)返回 transport(DashScope / PPIO / ModelScope / OVMS / DMXAPI 定制家族)时,请求改走任务系统:generateImageViaJob→JobManager→imageGenerationJobHandler(src/main/ai/provider/custom/tasks/imageGenerationJobHandler.ts),由 handler 独占 submit/poll 循环、排队与取消。
该 job刻意不重启持久化(recovery: 'abandon'):其唯一消费者是generateImageViaJob中的进程内 awaiter(handle.finished),payload 未记录消费者身份,重启后恢复的任务无人接收结果;非终止态 job 在启动时被取消而非恢复。handler 还设置了defaultConcurrency: 2、defaultRetryPolicy: { 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.negativePrompt、params.promptExtend… 放入自己的结构(input.*/parameters.*/messages[])。契约见src/main/ai/provider/custom/imageGenerationModel.ts的ImageGenerationTransport(submit/poll/cancel)与各<vendor>/<vendor>Transport.ts;createImageGenerationModel将 transport 包成ImageModelV3(maxImagesPerCall: 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 是纯派生值,而非参数。
扩展配方
给模型加一个参数
- 选取(或复用)canonical 键。若是新键,在
IMAGE_PARAM_CATALOG增加一行{ schema, wire? }——仅当厂商名称不是自动 snake_case 形式时才设置wire;再补一行KEY_LABELS,并按 i18n 工作流完成各语言环境与校验步骤。 - 在模型的
supports中以正确的SupportSpec(options/range/default)声明它。 - 对扁平请求体厂商,到此为止——表单、校验、类型、wire 名称全部从目录行流出。只有当参数需要定制摆放(嵌套块、兄弟厂商键、transport 信封)时才需要加
WireProfile字段 / transport 行。
加一个模型
- 厂商无关的官方模型 → 在
models.json写imageGeneration块; - 厂商口味 / 独占 → 在
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 |
| 每模型校验 schema | packages/provider-registry/src/utils/buildParamsSchema.ts |
注册表 schema(supports/vendorTransport) | packages/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 拉取 hook | src/renderer/pages/paintings/hooks/useImageGenerationSupport.ts |
注册表 → 表单字段 +KEY_LABELS | src/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 schema | src/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 |
| 异步任务 handler | src/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 提供商的桌面客户端
相关推荐
Cherry Studio 图像生成参数化架构:从注册表驱动表单到厂商 Wire 请求的完整数据链路
Cherry Studio 图像生成参数化架构:从注册表驱动表单到厂商 Wire 请求的完整数据链路 导读 :本文深入解析 Cherry Studio 中"绘画
AI 应用大模型桌面应用本地部署RAGCherry Studio 文件树架构解析:从视图注册、数据模型到虚拟化渲染的完整实现指南
Cherry Studio 文件树架构解析:从视图注册、数据模型到虚拟化渲染的完整实现指南 导读 本文以 CherryHQ/cherry studio 仓库中的
人工智能大模型AI 应用交互助手本地部署Cherry Studio 参数流水线(Params Pipeline)深度解析:从请求元组到 Agent.stream() 的组装全流程
Cherry Studio 参数流水线(Params Pipeline)深度解析:从请求元组到 Agent.stream 的组装全流程 导读 Cherry St
AI 应用大模型桌面应用本地部署RAG
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考