dsh-image-generate
# dsh-image-generate
面向 DeepSeek Harness(DSH)的**图片 & 视频生成 MCP 服务器**,工具接口对齐 **OpenAI Images API 语义**,
后端接入阿里云百炼(DashScope):
| 能力 | 模型 | 传输方式 |
|---|---|---|
| 文生图 | `z-image-turbo` | DashScope 原生同步接口 |
| 文生图 / 图生图 / 图像编辑 | `qwen-image-3.0`、`qwen-image-3.0-pro` | DashScope 原生同步接口(1-3 张参考图) |
| 文生图 / 图生图 / 编辑 | `qwen-image-3(-edit)` 及任意 OpenAI 兼容后端 | OpenAI 兼容接口 `/images/generations`、`/images/edits` |
| 文生视频 / 图生视频 | `wan2.5` / `wan2.6` / `wan2.7` 系列 | DashScope 异步任务(video-synthesis + 任务轮询) |
生成结果自动下载到本地输出目录,同时返回远端 URL(有效期约 24 小时)与 `local_path`。
## 快速开始
```powershell
# 1. 安装依赖(uv 会自动创建 .venv)
uv sync
# 2. 配置凭据(参考 .env.example)
Copy-Item .env.example .env # 然后填入 DASHSCOPE_API_KEY
# 或在 ~/.dsh/.env 中配置,DSH 启动时会自动加载
# 3. 本地以 stdio 启动(MCP)
uv run dsh-image-generate --transport stdio
# 4. 或启动 streamable-http 服务(默认 127.0.0.1:8900/mcp)
uv run dsh-image-generate --transport streamable-http --port 8900
# 5. 跑测试
uv run pytest -q
# 6. 真实 API 冒烟测试(消耗少量额度,视频需数分钟)
uv run python scripts/smoke_test.py
```
## MCP 工具
挂载到 DSH 后,模型看到的工具名为 `mcp__dsh-image-generate__<工具名>`。
### `generate_image` — 文生图
参数语义对齐 [OpenAI Images API](https://platform.openai.com/docs/api-reference/images/create):
| 参数 | 说明 |
|---|---|
| `prompt` | 提示词(中英文均可,必填) |
| `model` | 默认 `z-image-turbo`;`qwen-image-3.0` / `qwen-image-3.0-pro` 或 OpenAI 兼容模型 |
| `size` | `"1024x1024"`(OpenAI 写法,DashScope 下自动转为 `1024*1024`) |
| `n` | 生成张数。z-image 每张单独调用;qwen-image-3.0 单次最多 6 张 |
| `quality` / `style` | OpenAI 语义透传(DashScope 原生接口忽略) |
| `response_format` | `url`(默认)或 `b64_json` |
| `seed` | 随机种子,用于结果复现 |
| `negative_prompt` | 反向提示词(仅 qwen-image-3.0 支持) |
| `prompt_extend` | 提示词智能改写(改写结果会作为 `revised_prompt` 返回) |
| `watermark` | 添加"AI 生成"水印(仅 qwen-image-3.0 支持) |
| `output_dir` | 本地保存目录(相对路径相对于全局输出目录) |
返回(OpenAI 风格 + 本地增强):
```json
{
"created": 1789...,
"provider": "dashscope-native",
"model": "z-image-turbo",
"data": [
{"url": "https://.../xx.png", "revised_prompt": null,
"local_path": "C:\\Users\\...\\outputs\\dsh-image-generate\\img-1-....png",
"width": 1024, "height": 1024}
],
"usage": {},
"output_dir": "C:\\Users\\...\\outputs\\dsh-image-generate"
}
```
### `edit_image` — 图生图 / 图像编辑
- `image`(必填)+ 可选 `image2`、`image3`:本地路径 / http(s) URL / `data:` URL。
- DashScope 原生接口(qwen-image-3.0):1-3 张参考图 + 编辑指令;
- OpenAI 兼容接口:上传为 `/images/edits` 的 multipart 文件。
- `mask`:蒙版,仅 OpenAI 兼容后端支持(DashScope 原生接口会报错说明)。
- `strength`:编辑强度,仅 OpenAI 兼容后端。
- 其余参数同 `generate_image`。
### `generate_video` — 文生视频 / 图生视频(异步任务)
| 参数 | 说明 |
|---|---|
| `prompt` | 视频描述,支持分镜头写法(`第1个镜头... 第2个镜头...`) |
| `model` | 默认 `wan2.6-t2v`;图生视频用 `wan2.5-i2v-preview` / `wan2.6-i2v` |
| `image` | 图生视频的首帧图(本地路径 / URL / data: URL) |
| `duration` | 2-15 秒 |
| `size` | `"1280x720"`(wan2.5/2.6);或 `resolution`(480P/720P/1080P)+ `ratio`(wan2.7) |
| `shot_type` | `multi` 多镜头(wan2.6+) |
| `wait` | 默认 `true`:阻塞等待完成(数分钟);`false` 立即返回 `task_id` |
| 其余 | `seed`、`negative_prompt`、`prompt_extend`、`watermark` |
`wait=true` 超时或中断后,可用 `get_video_status(task_id=...)` 继续查询并下载。
### `get_video_status`
查询视频任务状态;`SUCCEEDED` 时默认下载到本地并返回 `local_path`。
任务 ID 24 小时内有效。
### `list_models`
返回支持的模型目录、能力说明与当前默认配置。
## 配置(环境变量)
变量清单见 [.env.example](.env.example)。关键项:
| 变量 | 默认 | 说明 |
|---|---|---|
| `DASHSCOPE_API_KEY` | — | 必填,百炼 API Key(可放 `.env`) |
| `IMG_DASHSCOPE_BASE_URL` | `https://dashscope.aliyuncs.com/api/v1` | DashScope 原生接口根 |
| `IMG_OPENAI_BASE_URL` | `https://dashscope.aliyuncs.com/compatible-mode/v1` | OpenAI 兼容接口根 |
| `IMG_OUTPUT_DIR` | `~/.dsh/outputs/dsh-image-generate` | 输出目录 |
| `IMG_DEFAULT_IMAGE_MODEL` | `z-image-turbo` | 默认文生图模型 |
| `IMG_DEFAULT_VIDEO_MODEL` | `wan2.6-t2v` | 默认视频模型 |
| `IMG_VIDEO_POLL_TIMEOUT` | `900` | 视频任务最长等待(秒) |
> ℹ️ **命名说明**:早期版本曾用 `DSH_IMG_*` 命名,但 Harness 的 dsh-app-boot 禁止任何
> `.env` 文件声明 `DSH_*`、`XDG_*`、`DYLD_*`、`BASH_FUNC_*` 前缀及 PATH/HOME/HTTP_PROXY
> 等启动期变量(违者启动直接失败,报 "... which only the launching environment may set")。
> 因此本项目统一改用 **`IMG_*`** 前缀,`.env` 与导出环境变量两种方式都可用:
```powershell
# 方式 A:写入 ~/.dsh/.env(IMG_* 不在保留名单内,安全;Harness 启动时自动加载)
# DASHSCOPE_API_KEY=sk-xxx
# IMG_DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/api/v1
# IMG_OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
# 方式 B:导出为系统/用户环境变量(DSH 进程重启后生效)
setx DASHSCOPE_API_KEY "sk-xxx"
setx IMG_DASHSCOPE_BASE_URL "https://dashscope.aliyuncs.com/api/v1"
setx IMG_OPENAI_BASE_URL "https://dashscope.aliyuncs.com/compatible-mode/v1"
setx IMG_OUTPUT_DIR "C:\Users\YOURNAME\.dsh\outputs\dsh-image-generate"
```
> 也可以把这些(非机密)值直接写成 cordis.patch.yml 中 MCP 行 `env:` 块的字面量。
> 业务空间(Workspace)专属域名(`https://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/...`)
> 同样受支持:把上述两个 BASE_URL 指向它即可,需保证 API Key 与域名同地域。
## 模型路由规则
- 已知 DashScope 原生模型(`z-image-turbo`、`qwen-image-3.0*`)→ 原生同步接口;
- 已知 OpenAI 兼容模型(`qwen-image-3*` 等)→ `/images/generations`、`/images/edits`;
- 其他模型名 → 走 `IMG_DEFAULT_TRANSPORT`(默认 `dashscope-native`)。
因此也可对接**任意 OpenAI 兼容图片后端**(包括自托管的 Z-Image OpenAI 兼容服务),
只需设置 `IMG_OPENAI_BASE_URL` 与 `IMG_OPENAI_API_KEY`。
> 注意:部分业务空间(Workspace)专属域名的 compatible-mode 端点可能不提供
> `/images/generations` 路由(实测返回 404)。此时请改用 DashScope 原生模型
> (z-image-turbo / qwen-image-3.0)或标准域名
> `https://dashscope.aliyuncs.com/compatible-mode/v1`。
## 接入 DeepSeek Harness
在 `$DSH_HOME/cordis.patch.yml`(所有 profile 生效,热加载,无需重启)中插入:
```yaml
- insert:
- id: mcp-dsh-image-generate
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: dsh-image-generate
transport: stdio
command: D:/py/dsh-imageGenerate/.venv/Scripts/dsh-image-generate.exe
args: ['--transport', 'stdio']
cwd: D:/py/dsh-imageGenerate
env:
DASHSCOPE_API_KEY: !!js process.env.DASHSCOPE_API_KEY
IMG_DASHSCOPE_BASE_URL: !!js process.env.IMG_DASHSCOPE_BASE_URL
IMG_OPENAI_BASE_URL: !!js process.env.IMG_OPENAI_BASE_URL
IMG_OUTPUT_DIR: !!js process.env.IMG_OUTPUT_DIR
failOnStartupError: true
toolCallTimeoutMs: 900000 # 视频生成需数分钟
```
凭据与端点配置应**导出为环境变量**(`setx`,见上文"配置"一节)后由 `!!js process.env.*` 间接引用;
**不要**把 `IMG_*` 写进任何 `.env`(DSH 启动层会拒绝启动),也**不要**在
`cordis.patch.yml` 里写字面量密钥(端点 URL 等非机密值可以写成 env 块字面量)。
## 项目结构
```
src/dsh_image_generate/
├── server.py # MCP 工具层(generate_image / edit_image / generate_video / ...)
├── config.py # 环境变量配置
├── storage.py # 下载落盘与 .meta.json
├── utils.py # size 语法、图片输入归一化等
└── providers/
├── openai_images.py # OpenAI 兼容 transport(/images/generations、/images/edits)
├── dashscope_native.py # DashScope 原生同步接口(z-image / qwen-image-3.0)
└── dashscope_video.py # DashScope 视频异步任务(提交 + 轮询)
tests/ # 离线 mock 测试(httpx.MockTransport,无需 Key)
scripts/smoke_test.py # 真实 API 冒烟测试
```
## 注意事项
- **费用**:按生成张数/视频时长计费,`n`、`duration` 直接影响费用;测试建议 `n=1`。
- **URL 有效期**:DashScope 返回的图片/视频 URL 仅保留 **24 小时**,务必使用返回的 `local_path`。
- **内容审核**:违规提示词或输出会报 `DataInspectionFailed` / `IPInfringementSuspect`;提示词改写开启时可能引入版权内容触发审核,可关闭 `prompt_extend` 重试。
- **跨地域**:模型、endpoint、API Key 必须同地域,否则鉴权失败。
## 开发
```powershell
uv sync # 安装依赖(含 dev)
uv run pytest -q # 离线测试
uv run python scripts/smoke_test.py # 真实 API 冒烟(需 Key 与额度)
```
TDQS
Scored across 5 tools
Each tool targets a distinct operation: text-to-image, image editing, model listing, video generation, and status query. The overlapping media types are clearly differentiated by purpose and parameters, leaving no ambiguity.
All tool names follow a consistent verb_noun pattern in snake_case (generate_image, edit_image, list_models, generate_video, get_video_status). The style is uniform and predictable across the set.
With 5 tools covering image and video generation plus supporting operations, the count is well-scoped for the server's purpose. Each tool earns its place without redundancy or bloat.
The core workflows are covered: text-to-image, image editing, text/video-to-video, model listing, and async status polling. A minor gap is the absence of cancellation or task history management for video generation, but the essential lifecycle is present.