# 网关侧改动清单(worker 近期变更引发) > 给网关开发:以下是 worker/契约近期变化里**与网关有关**的点。标 ✅ 的我已在本仓库 `gateway/` > 改好(你 review/拉取即可);标 🔲 的是**建议你确认或改**。功能必需只有第 1 条。 --- ## 1. ✅【功能必需】base64→URL 落盘扩展名按内容嗅探(支持 JPG) **背景**:接口 **2/3/5 的返回图改成了 JPG**(体积约小 9×),**接口 1 标注图仍是 PNG**(含透明)。 网关把 `*_base64` 落盘时若**硬编码 `.png`**,JPG 会被存成 `.png`(内容是 JPG、扩展名错)。 **改动**(`gateway/forward.py` 的 `rewrite_base64_to_url`,已改): ```python # 原:filename = f"{uuid.uuid4().hex}.png" ext = "png" if img_bytes[:8] == b"\x89PNG\r\n\x1a\n" else "jpg" # 按内容嗅探 filename = f"{uuid.uuid4().hex}.{ext}" ``` > 这样接口1 存 `.png`、接口2/3/5 存 `.jpg`,对外 URL 后缀也就正确。**若你的网关是独立部署/独立代码,按上面这两行改一下即可。** --- ## 2. 🔲【建议】生发接口超时调大 接口 **2(一次 N 张 Flux,~18s)/ 3(~6s)** 经 ComfyUI 同步出图较慢。 `gateway/config.json` 的 `dispatch.request_timeout_seconds` 建议 **≥ 120**,否则网关会先超时换 worker 重试。 --- ## 3. 🔲【确认】base64→URL 通用改写仍覆盖这些场景 - **数组里的图片字段**:接口2 `results[].image_base64` / `results[].grown_image_base64`、 接口5 `hairline_images[].image_base64` 在数组元素内——改写要**递归进数组**(你现有的递归实现已覆盖)。 - **可空字段**:接口2/3 的生发图(ComfyUI 没起/失败时)`*_base64` 为 **null** → 保留 null、不落盘。 --- ## 4. 🔲【可选·仅影响 /docs】OpenAPI 表单声明 纯文档展示,不影响转发功能(网关是盲转发)。若想让网关 `/docs` 准确: - 接口2 `/hair/grow`、接口5 `/hairline/generate` 入参**新增必填 `gender`**(male/female)。 - 接口3 `/hair/grow-b` 入参**只剩 `marked_image_*`**(已去掉 `original_image_*`)。 - (`gateway/app.py` 里的 `_*_FORMS` 字典当前未被路由引用,所以不改也不影响实际行为。) --- ## 5. 🔲【新增】接口7 C端生发 v2(`/api/v1/hair/grow-v2`) **背景**:worker 侧已新增接口 7,功能与接口 2 完全一致,区别仅在于 ComfyUI 工作流使用 `add_hair2.json`(而非 `add_hair.json`)。 **网关需新增一个路由**,代理转发到 worker 的同路径: ```python # gateway/app.py @app.post("/api/v1/hair/grow-v2", tags=["生发"]) async def hair_grow_v2(request: Request): """接口7:C端生发 v2(add_hair2 工作流)""" return await _proxy(request, "/api/v1/hair/grow-v2") ``` **无需额外改动**: - 请求:multipart/form-data,参数与接口 2 完全相同(`image_file/url/base64` 三选一 + `gender` + `hair_style` + `beauty_enabled` + `use_mask` + `prompt`),网关盲转发即可 - 响应:结构与接口 2 完全一致,`results[].image_base64` / `results[].grown_image_base64` 经现有 `rewrite_base64_to_url` 自动改写为 URL - base64→URL:数组内图片字段递归改写已覆盖,无需修改 ### 入参(与接口 2 一致) | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | image_file / image_url / image_base64 | — | **三选一** | 用户正面照 | | gender | string | **是** | `male` / `female` | | hair_style | string | **是** | 发型序号,**逗号分隔多选**(如 `1,2,3`)。female: 1–5,male: 1–4 | | beauty_enabled | bool | 否 | 美颜开关(本期不生效) | | use_mask | bool | 否 | 默认 `true`,`false` 跳过遮罩 | | prompt | string | 否 | ComfyUI 提示词 | ### 出参(与接口 2 一致) ```json { "code": 0, "message": "success", "request_id": "gw-xxxxxxxx", "data": { "results": [ { "image_url": "https://hair.xiangsilian.com/static/annotations/xxx.jpg", "grown_image_url": "https://hair.xiangsilian.com/static/annotations/xxx.jpg", "hairline_type": "ellipse", "order": 1 } ] } } ``` ### worker 侧信息 | 项目 | 值 | |------|-----| | worker 路径 | `/api/v1/hair/grow-v2` | | 工作流文件 | `add_hair2.json` | | 输入/遮罩节点 | 26(LoadImage) | | 输出节点 | 75(SaveImage,自动检测) | | 提示词节点 | 60(JjkText) | ### 🔲【可选·仅影响 /docs】OpenAPI 表单声明 若想让网关 `/docs` 展示准确,在 `gateway/app.py` 新增 `_GROW_V2_FORMS`(或复用 `_GROW_FORMS` 并补充 `gender`/`hair_style` 字段),然后将路由函数签名改为显式声明 Form 参数(参考接口 4 的写法)。不改也不影响实际转发。 --- ## 6. 🔲【新增】接口6 四庭七眼测量 v2(`/api/v1/face/measure-v2`) **背景**:worker 侧已新增接口 6,功能与接口 1 完全一致(复刻),共用同一实现。 **网关需新增一个路由**: ```python # gateway/app.py @app.post("/api/v1/face/measure-v2", tags=["人脸分析"]) async def face_measure_v2(request: Request): """接口6:四庭七眼测量 v2(复刻接口1)""" return await _proxy(request, "/api/v1/face/measure-v2") ``` **无需额外改动**: - 入参:与接口 1 完全相同(image_file/url/base64 三选一) - 出参:`annotated_image_base64` → 经现有 `rewrite_base64_to_url` 自动改写为 `annotated_image_url` - worker 侧与接口 1 共用 `_face_measure_impl()`,逻辑零差异 --- ## 已经做好、无需再动的 - **接口4 在网关本机实现**(调豆包,不转发 worker)——已完成;config 里配 `ark`。 - **接口4 业务错误 HTTP 状态**已统一为 200(与其余接口一致)。 - **接口3 去 original / best_hairline**——网关盲转发,无需改(映射表里也没有 best_hairline)。 --- > 对外字段映射总表见 [`实现说明.md`](实现说明.md) §1;契约以 [`接口文档.md`](接口文档.md) 为准。