diff --git a/docs/development/providers/volcengine-seedance.md b/docs/development/providers/volcengine-seedance.md index 529c9c6..f0edf65 100644 --- a/docs/development/providers/volcengine-seedance.md +++ b/docs/development/providers/volcengine-seedance.md @@ -2,7 +2,8 @@ > 取证日期:2026-09-20 来源:用户提供的三份官方文档(`doc_temp/火山视频请求.md`、 > `doc_temp/火山错误码.md`、`doc_temp/火山查询.md`) -> 实现位置:`patbond-ai` 的 `app/providers/seedance.py` 与 `app/providers/errors.py` +> 实现位置:`patbond-ai` 的 `app/providers/volcengine/`(`client.py` 传输 / `errors.py` 错误映射 / +> `models.py` 厂商 DTO / `video.py` 视频业务 / `capabilities.py` 模型限制) 本页记录**从官方文档实际读到的事实**,供实现与排查引用。凡本页与代码不一致, 以代码为准并回改本页;凡本页与厂商文档不一致,以厂商文档为准并回改两处。 @@ -131,7 +132,8 @@ ## 4. 错误码映射 -实现在 `app/providers/errors.py`,测试在 `tests/test_errors.py`(32 项)。 +实现在 `app/providers/volcengine/errors.py`,测试在 +`tests/providers/volcengine/test_errors.py`(32 项)。 ### 🔴 `QuotaExceeded` 一码两义 @@ -167,7 +169,7 @@ **403 的欠费/未开通归入配额族**:HTTP 状态码不同(403 而非 429),但用户要做的 事一样——去充值。按状态码分族会把它们错分。 -## 5. 模型与能力矩阵(供 V7 目录种子取用) +## 5. 模型与能力矩阵 | 模型 | 文生视频 | 图生视频-首帧 | 首尾帧 | 全模态参考 | 有声 | | --- | --- | --- | --- | --- | --- | @@ -185,17 +187,55 @@ `doubao-seedance-2-0-260128`、`doubao-seedance-1-0-pro-250528`、 `doubao-seedance-1-0-lite-i2v-250428`。 -> **V7 种子待定项**:实际可用的 model ID 取决于账号开通与推理接入点配置, -> 文档示例值不能直接当种子。需确认账号实际开通的模型后再写 V7,否则种子里 -> 的 `provider_model_name` 会指向一个不存在或无权访问的模型 -> (对应错误码 `InvalidEndpointOrModel.NotFound` / `ModelNotOpen`)。 +### 账号实际开通的模型(2026-09-20 确认) + +| model ID | 目录 code | 关键限制 | +| --- | --- | --- | +| `doubao-seedance-2-5-260628` | `seedance-2-5` | 时长 `[4,30]`;分辨率 480p/720p/1080p(**无 4k**);**图生视频时 `ratio` 只支持 `adaptive`**;支持 `output_format` mp4/mov;1080p 为 10bit + H.265/HEVC | +| `doubao-seedance-2-0-260128` | `seedance-2-0` | 时长 `[4,15]`;分辨率 480p/720p/1080p/**4k**;`ratio` 可指定;不支持 `output_format`;4k 为 10bit + H.265/HEVC | + +已由 **V7 目录种子**播种(`provider_code = 'volcengine-seedance'`)。注意与目标 +模型 `patbond_postgresql.sql:1495` 的种子**有意不同**——正典那份是服务商未定时 +写下的 `provider_code='fixture'` 占位。 + +**两个模型的限制差异不进目录表**(正典的 `generation_models` 没有能力列,本迭代 +不发明超出已评审模型的列),而是由 `patbond-ai` 的 +`app/providers/volcengine/capabilities.py` 承载并在提交前**本地预校验**。这样 +超范围的请求不必花一次往返去换一个厂商 400——而那一次往返还会计入 RPM 并表现为 +一个失败的任务。按**模型族前缀**匹配而非完整 ID,故一次小版本升级(如 +`2-5-261231`)不需要改表;**未知模型一律放行**,使「账号开通即可用」不退化为 +「开通还得改代码发版」。 + +同一组参数在两个模型上一个合法一个不合法(如 20 秒时长、4k 分辨率),这正是 +需要这张表的理由,测试里两个方向都钉住了。 + +**风格表(`generation_styles`)刻意未播种**:风格是产品内容决策(提供哪些艺术 +风格、叫什么、配图是什么),无法从服务商文档推导;正典那 4 条风格均为 `image` +类且其 `preview_asset_id` 指向 demo 资产,而 V1..V7 链路不含该 demo 种子,照抄 +会违反外键。`generation_jobs.style_id` 可空,故空风格表不阻塞任何功能。 + +**图片模型同样未播种**:gpt-image-2 的适配层尚未落地,播种一个没有适配器的模型 +会让客户端渲染出一个点了没反应的入口(目录端点按 `media_kind` 聚合可用模型, +故当前只返回 `video`)。 ## 6. 实现状态 +`patbond-ai` 已按高内聚低耦合分层(`config → assembly → providers → domain`), +分层约束由 `tests/test_layering.py` 用 AST 自动守着。以后接火山的图片/语音接口 +应新建兄弟模块复用同一个 `ArkClient`,而不是再抄一遍鉴权与错误解包。 + | 项 | 状态 | | --- | --- | -| 错误分类(`errors.py`) | ✅ 32 测试 | -| 提交与查询客户端(`seedance.py`) | ✅ 26 测试,全部离线(MockTransport) | +| 错误分类(`providers/volcengine/errors.py`) | ✅ 32 测试 | +| HTTP 传输与鉴权(`providers/volcengine/client.py`) | ✅ 15 测试,全部离线(MockTransport) | +| 视频业务(`providers/volcengine/video.py`) | ✅ 提交与查询 + 状态映射 | +| 模型限制预校验(`providers/volcengine/capabilities.py`) | ✅ 23 测试 | +| V7 目录种子(两个已开通模型) | ✅ 6 测试(含 `provider_code` 跨语言一致性) | | 产物转存 | ⏸ 阻塞于存储决策(ADR-023 §7)。注意 §2-④ 的 24 小时硬约束 | -| V7 目录种子 | ⏸ 待确认账号实际开通的 model ID | -| Java 侧 provider 适配 | ⏸ 待本服务的 HTTP 端点与 `openapi-internal.yaml` 落地 | +| HTTP 端点与 `openapi-internal.yaml` | ⏸ 未开始 | +| Java 侧 provider 适配 | ⏸ 待上一项落地 | + +**一处跨语言约定,两侧都有测试钉住**:目录表的 `provider_code` 必须与 +`patbond-ai` 适配器的 `PROVIDER_CODE` **逐字一致**(`volcengine-seedance`)。 +对不上时 Worker 按任务行冻结的快照查不到适配器,任务会永远停在排队——而那看起来 +像队列坏了,不像两个字符串不一致。