dsh-image-tool
v0.4.0
Published
DSH 生图工具:把 OpenAI 兼容的图片生成/改图接口包装成 agent 工具,结果落盘并作为图片内容块回灌模型与 Web UI;插件页可配置接口地址与 API key。
Maintainers
Readme
dsh-image-tool
DSH(DeepSeek Harness)生图工具:把 OpenAI Images API 包装成一个 agent 工具 generate_image。文本生图走 /images/generations,带参考图时自动切到 /images/edits;结果落盘,并以图片内容块回灌会话——能看图的模型直接看到图,Web UI 在工具卡里渲染。
能力
- 一个工具,两种模式:
generate_image({ prompt })生图;再多传reference_images就是改图(参考图按视觉输入理解,比文字描述可靠)。 - 多图:
n一次最多 10 张,每张单独落盘、单独建 attachment。 - 结果双通道:工具结果里带绝对路径(纯文字模型也能用),同时是
image内容块(视觉模型能看、UI 能显示)。 - 错误可操作:内容审核拒绝会明确告诉模型「改 prompt/换参考图,不要原样重试」;HTTP 410 提示模型已下线;429 区分配额与限流;401 指向
DSH_IMAGE_API_KEY;403 提示组织验证。 - 可控落点:
dest可给目录或文件路径,相对路径按会话目录解析;也能在配置里固定输出目录。 - relay 兼容:改图默认用 JSON + base64 data URL(新版官方形态),可切 multipart,并可选
image/image[]两种表单字段名。 - 配置界面:在 Plugins 页
dsh-image-tool的详情页里直接改接口地址、模型和 API key;地址与模型存进 profile 配置(volatile,保存即生效),key 单独写进密钥文件(0600),都不需要重启。
结构
src/
index.ts 工具注册 + 设置路由挂载:参数 schema、执行、结果渲染、调用卡片
api.ts Images API 客户端:请求体构造、JSON/multipart 传输、错误映射
config.ts volatile 配置(接口地址)+ 环境变量合并 + 优先级
settings-api.ts 配置界面的宿主路由:读写接口地址、存取 API key
key-store.ts 密钥文件读写(每次调用重读,0600)
paths.ts 输出路径规划(目录/文件名、多图编号)
client/main.js 浏览器半:Plugins 页里的配置卡片
build.mjs esbuild 打包 browser 半 → lib/client.cjs
scripts/
smoke.mjs 本地假 Images API + 假 Host 服务,端到端跑构建产物安装
作为 DSH profile 的 bundle 挂载。先停掉正在运行的 dsh,再装 —— 服务运行期间改它的 node_modules 会让正在跑的进程读到残缺依赖树。
cd ~/.dsh/profiles/<你的 profile> # 例如 web
dsh plugin --profile <你的 profile> add dsh-image-tool装完把包名加进该 profile package.json 的 dsh.profile.bundles(顺序放最后即可):
{
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "dsh-image-tool"]
}
}
}重启 dsh 后 generate_image 出现在工具列表。验证:
dsh --profile <你的 profile> --dump-config | grep dsh-image-tool密钥
最省事的是界面里填:Plugins → 已安装插件 → dsh-image-tool → 详情页的配置卡片,填入 接口地址、模型 和 API key 后保存。key 会写进 ~/.dsh/image-tool.key(0600),插件每次调用都重读该文件,所以换 key 即时生效。
也可以手工给 key,按优先级从高到低:
配置文件里的
apiKey:能用,但明文进 profile 配置,只建议临时排查。密钥文件(界面写的就是这个):
printf %s 'sk-...' > ~/.dsh/image-tool.key && chmod 600 ~/.dsh/image-tool.key路径可用
apiKeyFile/DSH_IMAGE_API_KEY_FILE改。环境变量:
DSH_IMAGE_API_KEY,或OPENAI_API_KEY。
缺密钥时不会静默失败:模型会收到一条明确说明该配哪里的错误。
配置
日常用界面改即可;要落到文件或做部署,profile 自己的 cordis.patch.yml 里按 id 覆盖 config(同 DSH 其他插件):
- id: dsh-image-tool
name: dsh-image-tool
config:
model: gpt-image-2.5-sunburst
outputDir: ~/Pictures/dsh
timeoutMs: 300000| 字段 | 默认 | 说明 |
| --- | --- | --- |
| enabled | true | false 时不注册工具 |
| apiKey | 空 | 明文密钥,优先级最高,不建议 |
| apiKeyFile | ~/.dsh/image-tool.key | 存放密钥的文件;每次调用重读,可运行中更换 |
| baseUrl | 空(回落 https://api.openai.com/v1) | volatile 字段,界面保存只写这一项;末尾斜杠会去掉 |
| model | 空(回落 gpt-image-2.5-flare) | volatile 字段,界面可改;每调用也可用 model 参数覆盖 |
| outputDir | 空(会话目录) | 未给 dest 时的落盘目录 |
| timeoutMs | 300000 | 单次请求预算,复杂高质量生图可达 2 分钟 |
| editsTransport | json | json 或 multipart |
| multipartImageField | image | multipart 时文件名段字段,部分 relay 要 image[] |
优先级:接口地址与模型是 配置(界面保存) > 环境变量(DSH_IMAGE_BASE_URL / DSH_IMAGE_MODEL) > 内置默认;密钥是 配置 apiKey > 密钥文件 > 环境变量。界面会把「当前来源」标出来。
环境变量(key 与接口地址都是兜底;其余字段仍以环境变量覆盖 config):
| 变量 | 作用 |
| --- | --- |
| DSH_IMAGE_ENABLED | 0 禁用 |
| DSH_IMAGE_API_KEY | API Key(优先于 config 与 OPENAI_API_KEY) |
| DSH_IMAGE_API_KEY_FILE | 密钥文件路径(优先于 config 的 apiKeyFile) |
| DSH_IMAGE_BASE_URL | API base URL |
| DSH_IMAGE_MODEL | 默认模型 |
| DSH_IMAGE_OUTPUT_DIR | 默认输出目录 |
| DSH_IMAGE_TIMEOUT_MS | 单次请求超时 |
| DSH_IMAGE_EDITS_TRANSPORT | json / multipart |
| DSH_IMAGE_MULTIPART_FIELD | image / image[] |
模型
默认 gpt-image-2.5-flare(OpenAI 文档称为多数应用的默认选择)。其他可选:
gpt-image-2.5-sunburst:编辑精度最高,但更慢;gpt-image-2:高分辨率与宽幅输出(如2048x2048、3840x2160);- 尺寸除
auto/1024x1024/1536x1024/1024x1536外,2.x 模型支持任意WIDTHxHEIGHT(双方皆为 16 的倍数,受比例与像素总量限制); quality支持auto/low/medium/high,2.5 另有xhigh/max。
不要再用 gpt-image-1 或 dall-e-3:前者 2026-10-23 关停,后者 2026-05-12 退役,调用返回 HTTP 410(工具会把这条写进反馈)。GPT Image 系列只返回 b64_json,没有 URL 形态。
生图模型还要求组织完成 API Organization Verification;未完成时典型表现是 403,错误信息里会提示。
工具形态(LLM 视角)
generate_image prompt:"黄昏时分的沿海公路,胶片颗粒质感,广角" n:2 size:"1536x1024" quality:"high"
generate_image prompt:"把背景换成雪山,保留人物姿势" reference_images:["/work/portrait.png"]
generate_image prompt:"给这张图加透明底" reference_images:["in.png"] background:"transparent" dest:"out/cutout.png"模型拿到的是:一段带绝对路径、尺寸、字节数的文字,加上对应数量的图片内容块。纯文字模型只会看到(被替换成占位说明的)文字部分,图片文件仍在磁盘上。
开发
本地联调可以直接打包安装(*.tgz 已 gitignore,profile 以 file: 引用):
pnpm --filter dsh-image-tool build
cd packages/image-tool && pnpm pack --pack-destination ../..
dsh plugin --profile <profile> add <repo>/dsh-image-tool-0.3.0.tgzpnpm --filter dsh-image-tool build # tsc(host)→ lib/,esbuild(client)→ lib/client.cjs
pnpm --filter dsh-image-tool check # 类型检查(host;client 是纯 JS)
pnpm --filter dsh-image-tool test # bun test
pnpm --filter dsh-image-tool smoke # 构建后跑 scripts/smoke.mjs(本地假 API,不发真实请求)smoke 会注册工具和设置路由,打一次 generations、一次 JSON edits,并校验落盘字节、attachment 投影、渲染内容、配置读写、密钥热替换与错误映射;不消耗额度。
首次给已运行的插件加上 browser 半边后,DSH 需要重扫包清单:重启,或在 Plugins 页把插件禁用再启用一次。之后改 lib/client.cjs 会被前端 HMR 自动替换。
已知边界
- 不支持
mask/markup/ 流式partial_images:局部编辑先用 prompt + 参考图表达。 - 单次请求无重试:429/5xx 由模型决定是否重试;审核拒绝明确禁止原样重试。
- 落盘与 attachment 都受 attachment 的大小/尺寸上限约束,超大图会先被拒绝,需要换更小尺寸。
- 不解析
usage:token 计费看 OpenAI 后台。 - 环境变量代理不会被自动使用;需要走代理时用
baseUrl指到网关。 - 落盘由 host 进程直接写文件,不受工具级 sandbox 策略约束(和 browser-tool 截图一致);给
dest传绝对路径时注意别写到工作区外。
许可
MIT
