c6e8dddc8df3ed8730b2356cb92f61b08cde8bf1
CI / ai-gates (push) Successful in 31s
按用户提供的三份官方文档适配 Seedance 视频生成:提交
(POST /contents/generations/tasks)与查询(GET .../tasks/{id})。
取证记录见 patbond-doc docs/development/providers/volcengine-seedance.md。
**开工前问的五项,两项与原假设不同:**
1. **不支持幂等键**。请求参数全集里没有任何幂等字段。故 ADR-023 §4 设想的
「把我方 idempotency_key 透传为厂商幂等头」对本供应商不成立,「提交超时后
不知道有没有扣费」只能由 max_attempts 兜住——如实登记,不假装解决。
两点减轻:提交是异步的(只入队即返回,超时概率低);且厂商支持 callback_url,
孤儿任务会通过回调把 ID 送回来,故回调在此供应商上还是**孤儿回收通道**,
不只是延迟优化——这是 ADR-023 §4「回调仅预留」之外的新论据,值得复评。
2. **产物是有效期 24 小时的 URL**(2.5 另有 100 次下载上限)。因此厂商 URL
**不能当存储用**:存进 media.assets.external_url 会在 24h 后变死链。
**这对挂起中的存储决策是硬约束**——无论选本地还是云,都必须在 24 小时内
把文件复制到我们自己的存储。
其余三项:查询接口只保留 7 天记录;无明确频率数值,但官方 SDK 示例用 10s 轮询
(我们的 3s/10s/30s 阶梯与之兼容)、客户端截止 30 分钟(建议据此把 ADR-023 §10
的视频客户端超时从 15~20 分钟对齐到 30 分钟);execution_expires_after 范围
[3600, 259200],实现取下限 1 小时(厂商默认 48h 对交互式应用过长,且无法更短)。
**两个文档自身的问题,实现按宽的那份:**
- **状态枚举不一致**:查询文档列 5 个(queued/running/cancelled/succeeded/
failed),创建文档的回调一节额外列 **expired**。只按查询文档实现会让超时
任务掉进未知分支。按 6 个实现,expired 视为**可重试**失败(是超时不是内容
问题),且厂商 expired 时不一定给 error.code 故单独分类。
- **QuotaExceeded 一码两义**:同一 code 既表示「排队任务数超限」(等几秒)
又表示「额度耗尽」(等到明天或充值)。按 code 一刀切必然有一半场景的文案
和出口是错的;只能按 message 判别,两条用例都在测试里钉住。
**顺带拿到两个免费收益**:last_frame_url 可直接作视频封面(iteration-4/03 R13
把缩略图列为视频成本项,实际免费);查询返回 ratio/resolution/framespersecond/
duration,**Worker 写产物时维度与时长都在手里、无需解码**——widthPx/heightPx
债在 AI 产物上可零成本修掉。
一处精度陷阱已处理:返回的 duration 是「帧数/24 向下取整」的约数(133 帧实际
5.54s 返回 5),且 duration 与 frames 只返回其一。故优先按 frames/fps 精确换算,
只有 duration 时退回整数秒并标注为约数,缺失时返回 None 而非编 0。
取消语义已查明:厂商**只支持取消 queued 状态**的任务,故我方取消已 running
的任务无法向厂商传导,**仍会计费**。ADR-023 §6 关于取消的说明据此可定稿。
测试 58 项(错误分类 32 + 客户端 26),全部离线用 httpx MockTransport,
夹具响应体逐字取自官方文档示例以免字段名对不上。
过程中防泄漏钩子拦下了测试夹具里的 `api_key="test-key"`——规则是对的(这正是
真实密钥最常见的泄漏形态),故改用 ALLOW 清单认可的占位词,未放宽规则。
未实现:产物转存(阻塞于存储决策)、HTTP 服务端点与 openapi-internal.yaml。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
patbond-ai
Patbond 的 AI 生成服务(Python)。负责调用 AI 服务商 API 完成图片与视频生成, 是四仓中唯一直接持有 AI 服务商凭证的仓库。
架构与边界的完整决策见 patbond-doc docs/architecture/decisions.md ADR-023。
以下为要点,改动前请以 ADR 原文为准。
职责边界
- 只负责调用 AI 服务商 API。 不访问数据库——生成任务状态的唯一事实来源是
Java 侧的
creation.generation_jobs。 - 不持有对象存储凭证。 产物由 Java 下发预签名 PUT 地址,本服务直传后回报,
再由 Java 核实并建
media.assets行(即客户端两步上传,本服务站在客户端位置)。 - 对上游同步或异步一律对外呈现为异步:
submit立即返回任务 id,同步型 服务商(长连接等待返回)在本服务内部以后台任务承载。这样 Java 侧无长连接占线程, 且provider_request_id立即可得,超时不再产生「是否已扣费」的歧义。
与 Java 侧的接口
- 传输为 REST(JSON),非 gRPC(理由见 ADR-023 §4:避免引入第二套无门禁契约体系)。
- 契约为本仓内独立的
openapi-internal.yaml,独立版本号, 不进 patbond-api 的四模块快照锁,也不写入面向客户端的openapi.yaml。 - 鉴权为 服务身份 JWT(独立密钥对,非用户 token 转发):本服务强制校验
aud=patbond-ai与typ=service。 - Java 主动轮询本服务查询进度;回调是将来的可选优化,当前不启用。
- 请求体不含
userId(数据最小化):完成工作只需jobId、生成参数与预签名上传地址。
本地门禁
与 CI 同一套命令(.gitea/workflows/ci.yml):
sh scripts/check-secrets.sh --all # 凭证防泄漏全量自查
ruff format --check .
ruff check .
pytest -q
每人每仓启用一次 pre-commit 防泄漏钩子:
git config core.hooksPath scripts/hooks
scripts/check-secrets.sh 是四仓同构副本,调整规则时四仓同步提交。
它覆盖 AI 服务商密钥形态(sk-ant-、sk-/sk-proj-/sk-svcacct-)与
api_key/auth_token 实值赋值——本仓正是这些规则的主要适用对象。
凭证纪律
真实密钥只允许存在于被 gitignore 的文件中(.env),占位样例进 .env.sample。
拦下真实凭证时的第一动作是去服务商控制台轮换/禁用该密钥,然后才是清理提交。
分支与发布
dev 直推 + CI 绿,main 经 PR 合并。本仓纳入四仓 tag 同步与发布回归
(ADR-023 §12)。发布流程见 patbond-doc docs/development/releases.md。
Description