资讯动态

Cherry Studio v2 绘画图像生成链路剖析:AI SDK 补丁如何让 generateImage 兼容 HTTP URL 与 gpt-image 系列模型

发布时间:2026/9/19 1:50:05 来源:尧图企业网站定制
Cherry Studio v2 绘画图像生成链路剖析AI SDK 补丁如何让 generateImage 兼容 HTTP URL 与 gpt-image 系列模型【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址: https://gitcode.com/CherryHQ/cherry-studio导读Cherry Studio 的绘画paintings功能需要让不同提供商的图像生成网关OpenAI 兼容网关、Google Gemini/Imagen 网关统一收敛到 AI SDK 的generateImage调用上。但上游 AI SDK 及其 Provider 包对图像响应形态b64_json与url、对response_format参数的支持并不一致。cherrystudio/ai-core与cherrystudio/ai-sdk-provider通过一组针对性补丁patch补齐了这条链路ai6.0.185补丁为generateImage增加experimental_download选项让 SDK 能识别并下载 HTTP(S) 图像输出ai-sdk/openai-compatible2.0.72补丁扩展了对url字段图像响应和gpt-image-*模型的处理ai-sdk/google3.0.113补丁修正了 Gemini/Imagen 模型的路径前缀解析。读完本文你将理解这组补丁逐行做了什么、它们如何在 Cherry Studio 的绘画主流程中生效以及如何用仓库内测试验证补丁行为。背景绘画功能的调用链与补丁的作用位置从用户指令到图像落盘的主链路Cherry Studio 的绘画功能并不直接调用 AI SDK而是经过一条完整的调用链渲染进程发出ai.image.generateIPC 请求携带经 catalogimageParamsSchema校验的paramValues主进程AiService.generateImageAiService.ts负责 provider/model 解析、vendor 参数映射WireProfile 引擎、同步与异步任务两种传输方式以及 FileEntry 持久化对于支持异步提交/轮询的自定义 providerppio / dashscope / modelscope / dmxapi-bespoke会走generateImageViaJobAiService.ts进入任务系统由 imageGenerationJobHandler.ts 完成 submit → poll → download → persist对于走 AI SDK 直接调用的路径最终落到aiCoreGenerateImageruntime/index.ts它创建 RuntimeExecutor 并转发给 AI SDK 的generateImage。generate_image内置工具与 Claude Code 进程内 MCP 桥都是generateImageFromPromptpainting.ts的薄封装绘画模型由feature.paintings.default_model_id偏好解析。为什么需要补丁上游 SDK 的三个缺口上游 AI SDK 的generateImage默认只接收模型直接返回的 base64 数据而 Cherry Studio 的绘画提供商会返回三种形态的输出b64_json字段OpenAI 风格url字段如 DMXAPI 的flux-1会显式请求response_format: url见 dmxapi.boundary.test.ts.snap 中的请求快照Gemini/Imagen 网关的models/{modelId}:predict端点与models/{modelId}前缀的模型路径。补丁的作用就是把这三类差异收敛在 SDK 层保证上层绘画代码无需感知。补丁一ai6.0.185 的 experimental_download 与图像分类下载类型层generateImage 新增 experimental_download 选项补丁在dist/index.d.ts与dist/index.d.mts的generateImage声明中新增了可选参数ai6.0.185.patchdeclare function generateImage({ model, prompt, n, maxImagesPerCall, size, aspectRatio, seed, providerOptions, maxRetries, abortSignal, headers, experimental_download: download, }: { // ... /** * Custom download function to use for URLs. * * By default, files are downloaded if the model returns URLs instead of binary data. */ experimental_download?: DownloadFunction | undefined; }): PromiseGenerateImageResult;experimental_download接收一个下载函数入参是[{ url, isUrlSupportedByModel }]列表返回{ data, mediaType }列表。这与packages/aiCore中 runtime/types.ts 的generateImageParams类型定义保持一致——aiCore 已将experimental_download?: Experimental_DownloadFunction纳入自己的参数类型。实现层图像分类、下载与容错补丁在generate-image.ts的运行时实现中加入了一个关键工具函数function toDownloadableImageUrl(value) { try { const url new URL(value); return url.protocol http: || url.protocol https: ? url : void 0; } catch (invalidUrl) { return void 0; } }随后对每个图像结果依次处理data:URL 原样解析若结果以data:开头直接splitDataUrl拆出 mediaType 与 base64 内容构造DefaultGeneratedFile不做任何下载HTTP(S) URL 交由下载函数toDownloadableImageUrl只接受http:/https:协议大小写不敏感下载成功则包装成DefaultGeneratedFilemediaType 缺失时兜底为image/png失败即丢弃而非落库下载抛错或返回空时返回null最终通过filter剔除。若整批有图像被丢弃会向warnings追加N of M generated images could not be downloaded and were dropped警告坏数据容错既不是可下载 URL 也不是可解码 base64 的结果会被丢弃而不是让单个畸形条目使整批生成失败patch 注释明确写道dropping it beats persisting the url string as if it were the base64 bytes。最后如果所有图像都下载失败generateImage会抛出NoImageGeneratedError与 aiCore 的 generateImage.test.ts 中对该错误的处理一致。专项测试generateImageDownloadPatch.test.ts仓库为这半个补丁准备了专门的守卫测试 generateImageDownloadPatch.test.ts覆盖五种典型场景测试场景断言要点下载失败只丢对应图images长度 1且warnings包含1 of 2 ... dropped下载函数抛错只影响该图抛错的 URL 被丢弃其余图像保留大写 scheme 可下载、不可解析 URL 被丢弃HTTPS://img/upper.png可下载https://exa mple.com/x.png被丢弃全部下载失败抛出NoImageGeneratedErrorb64_json结果不做下载下载函数不被调用base64原样透传在 AiService 中的实际接线AiService.generateImage为experimental_download提供了真实实现AiService.tsexperimental_download: async (downloads) { return Promise.all( downloads.map(async ({ url }) { if (signal?.aborted) return null const downloaded await downloadImageAsBase64(url.toString()) if (signal?.aborted) return null if (!downloaded) return null return { data: Buffer.from(downloaded.data, base64), mediaType: downloaded.media_type } }) ) }注意两点工程细节下载期间再次检查signal.aborted确保取消优先数据以二进制 Buffer 返回交由 SDK 侧统一构造文件对象。补丁丢弃失败下载后AiService 侧AiService.ts还会过滤掉没有base64的结果并记录Filtered invalid generated images警告形成双重保险。补丁二ai-sdk/openai-compatible 的 url 字段与 gpt-image-* 兼容响应解析从只认 b64_json 到 b64_json / url 双通道上游openaiCompatibleImageResponseSchema只允许data[]中的b64_json字符串字段补丁将其扩展为二选一可空字段var openaiCompatibleImageResponseSchema z8.object({ data: z8.array(z8.object({ b64_json: z8.string().nullish(), url: z8.string().nullish() })) });解析逻辑也相应改为flatMap双通道输出openai-compatible patchimages: response.data.flatMap((item) { if (typeof item.b64_json string) return [item.b64_json]; if (typeof item.url string) return [item.url]; return []; })response_format 的按模型条件发送与 400/422 重试上游实现无条件发送response_format: b64_json。补丁引入defaultResponseFormatPrefixes列表var defaultResponseFormatPrefixes [ chatgpt-image-, gpt-image-1-mini, gpt-image-1.5, gpt-image-1, gpt-image-2 ]; function hasDefaultResponseFormat(modelId) { return defaultResponseFormatPrefixes.some((prefix) modelId.startsWith(prefix)); }当modelId命中这些前缀或调用方已显式传入response_format时不再强制附加response_format否则默认带b64_json。同时对不支持response_format的模型拒绝时返回 400 或 422补丁实现了单次无参重试第一次带response_format被拒后去掉该参数重发一次若重试仍失败则抛出原始拒绝错误避免掩盖真正原因。const defaultResponseFormat hasDefaultResponseFormat(this.modelId) || args.response_format ! void 0 ? null : b64_json; let posted; try { posted await postImageRequest(defaultResponseFormat); } catch (error) { const isRejectedRequest !!error (error.statusCode 400 || error.statusCode 422); if (defaultResponseFormat null || !isRejectedRequest) throw error; try { posted await postImageRequest(null); } catch (retryError) { throw error; // 保留原始拒绝 } }为什么绘画需要它真实网关的响应形态DMXAPI 边界测试快照显示flux-1的请求体为{ model: flux-1, n: 2, prompt: a fox, response_format: url, size: 1328x1328 }dmxapi.boundary.test.ts.snap即该网关以 URL 而非 base64 返回图像。没有 url 通道这类输出会在 schema 校验阶段直接被丢弃。此外该补丁还顺带修复了 embeddingusage中prompt_tokens缺失时回退total_tokens的问题以及思维链reasoning_content在工具调用轮次被丢弃导致 DeepSeek/GLM/Kimi/MiniMax 等方言拒绝请求的问题——这些都属于同一包的非图像兼容性加固。补丁三ai-sdk/google 的模型路径与 isGeminiModel 前缀处理getModelPath 的路径拼接修正上游getModelPath的逻辑是「modelId 包含/则原样使用否则加models/前缀」// 上游 return modelId.includes(/) ? modelId : models/${modelId}; // 补丁后 return modelId.includes(models/) ? modelId : models/${modelId};修正点在于当 modelId 本身已包含models/前缀例如models/gemini-2.0-flash时上游会因为其中包含/而原样返回——但若调用方传的是models/gemini-2.0-flash这种已带前缀的 id旧逻辑会直接透传导致后续:predict端点拼出重复前缀。补丁改为检查models/是否存在避免models/models/...双重拼接。该函数同时作用于图像生成的:predict端点url: ${this.config.baseURL}/${getModelPath(this.modelId)}:predict,isGeminiModel 的前缀剥离上游判断 Gemini 模型只用modelId.startsWith(gemini-)对google/gemini-...或models/gemini-...这类带前缀的 id 会误判为「非 Gemini」。补丁先剥离google/与models/前缀再判断function isGeminiModel(modelId) { return modelId.replace(/^(google\/|models\/)/i, ).startsWith(gemini-); }这让 Imagen/Gemini 图像模型在带 provider 前缀的 id 下也能正确走到图像响应 schemagoogleImageResponseSchema与对应参数分支。注意dist/internal/index.js与dist/internal/index.mjs也同步应用了 getModelPath 的修正保证内部入口与公开入口行为一致。补丁的版本定位与变更管理变更记录changesetpaintings-image-gen-patches.md 标注了三个包cherrystudio/ai-core与cherrystudio/ai-sdk-provider均为patch级别变更属于向后兼容的缺陷修复。changeset 撰写时ai补丁目标版本为6.0.143当前仓库已随依赖升级到ai6.0.185见 package.json补丁文件 ai6.0.185.patch 在同一基础上持续应用ai-sdk/openai-compatible2.0.72、ai-sdk/google3.0.113与 pnpm-workspace.yaml 中的patchedDependencies映射一一对应。仓库通过 pnpm 的patchedDependencies机制管理全部补丁pnpm-workspace.yaml每个 patch 均锁定精确依赖版本避免依赖漂移导致补丁失配。兼容性边界与设计取舍非图像调用不受影响changeset 明确指出这组补丁是针对 OpenAI 兼容 / Google 图像网关的定向 shimtargeted shims非图像的 OpenAI 兼容调用不受影响——openai-compatible 补丁中的images字段扩展只作用于 chat 响应里的image_url内容块与delta.images流式块。失败语义清晰补丁在「宁可丢弃也不要存坏数据」与「不能静默吞掉付费生成」之间取了平衡——单图失败丢弃并告警整批失败抛NoImageGeneratedError异步任务路径的 imageGenerationJobHandler.ts 同样在「成功但零 URL」时抛错避免把已计费的生成报告为静默成功。提供商侧自有实现并行存在除 AI SDK 补丁外仓库还维护了各自的图像模型实现如 SiliconFlow 的 SiliconImageModel.ts同样支持url/b64_json双通道 flatMap 解析见其doGenerate返回值。补丁与自研模型各司其职补丁解决 SDK 默认路径自研模型解决需要定制 body 形状的提供商。总结这组补丁以极小的 diff 面积解决了绘画功能落地的三个真实痛点AI SDK 无法消费 URL 形态的图像输出experimental_download、OpenAI 兼容网关对gpt-image-*等模型拒绝response_format参数且返回url字段双通道解析 条件发送 400/422 重试、Google 网关模型路径前缀与 Gemini 判定错误getModelPath/isGeminiModel。它们与AiService.generateImage、任务系统、自研图像模型共同构成 Cherry Studio v2 绘画功能的完整底座并且每一处行为都有仓库内测试与边界快照守护是理解该项目图像生成链路的最佳切入点。【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址: https://gitcode.com/CherryHQ/cherry-studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价