Files
patbond-ai/.env.sample
T
lixi 8275a04903
CI / ai-gates (push) Successful in 38s
feat(ai): 接入 gpt-image-2 图片生成(文生图 + 图生图)
新增 app/providers/openai/:client 传输(JSON + multipart)、errors 错误映射、
models 厂商 DTO、capabilities 尺寸算术约束、tasks 同步→异步桥、image 图片业务。
装配与配置同步接入,缺 OPENAI_API_KEY 时跳过装配、服务仍可启动。

与火山视频的三处根本差异及其处置:
- 接口同步(无任务 ID、无查询端点)→ SyncTaskRegistry 在本服务内部吸收,
  对外仍是 ADR-023 §4 的两段式提交/轮询。代价:本服务从此持有状态,进程重启
  丢在途任务,且横向扩容前须先解决任务 ID 只在签发进程内有效的问题。
- 产物是 base64 字节而非 URL → 无过期问题,但字节只在内存里。asset_url 放
  data URI 仅为满足领域不变式,绝不能原样写进 media.assets.external_url。
- 图生图走 multipart → 先下载参考图再上传;下载失败与超限按参数错误同步拒绝,
  不变成注定失败的后台任务。

两处需要记住的判断:
- 尺寸上限「3840x2160」文档未说清是逐边还是按像素。逐边理解会禁掉所有高于
  2160 的竖图,而 1024x1536 是文档明示的标准竖图,故必然过严。采用最长边
  ≤3840 且总像素 ≤3840×2160,已在 capabilities.py 与取证页标为推断。
- 两份文档都没有错误码表,故 errors.py 以 HTTP 状态码为主、type/code 为辅,
  顶部标注「未经文档证实」。429 无线索时按限流而非配额耗尽(猜成配额会把可
  重试的限流判死);400 无线索时归服务端错误而非内容被拒(错报会让用户去改
  一个其实没问题的提示词)。

一处死锁已修并回归钉住:SyncTaskRegistry 原用 add_done_callback 记录完成时刻,
而快速完成的任务在注册时已 done,回调便同步地在持有非可重入锁的线程里执行。
改为首次被观察到时惰性记录。

测试 166 → 271(+105)。四道门禁逐条读退出码,全绿。
产物转存仍阻塞于存储决策(ADR-023 §7),图片模型因此未播种进 V7 目录。
2026-09-21 12:10:07 +08:00

55 lines
3.1 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# patbond-ai 配置样例。复制为 .env 后填入实际值——.env 已被 gitignore。
#
# 凭证纪律(ADR-021):真实密钥只允许存在于被忽略的文件中,本样例只放占位值。
# 被拦下时的第一动作是去服务商控制台轮换/禁用该密钥,然后才是清理提交。
# ---- 火山方舟(Ark)----
# 密钥无默认值。缺失时该适配器不装配,服务仍可启动并提供健康检查。
ARK_API_KEY=your-ark-api-key
# 北京区官方地址,一般无需改。
# ARK_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
# 单次 HTTP 往返超时(秒)。**不是生成耗时**——提交接口是异步的(只入队即返回),
# 调大成「等出图」会重新引入长连接占用与计费歧义。
# ARK_TIMEOUT_SEC=10
# 视频模型 ID,仅作兜底。正常路径由 Java 侧传入任务行冻结的模型快照——
# 适配层不自行选模型,否则任务记录与实际调用会不一致、事后无法对账。
ARK_VIDEO_MODEL=
# 厂商 execution_expires_after(秒),区间 [3600, 259200]。
# 默认取下限 1 小时:厂商默认 48 小时对交互式应用过长,被放弃的任务会挂两天。
# ARK_TASK_EXPIRY_SEC=3600
# ---- OpenAI 图片(gpt-image-2)----
# 密钥无默认值。缺失时该适配器不装配,服务仍可启动并提供健康检查。
OPENAI_API_KEY=your-openai-api-key
# OPENAI_BASE_URL=https://api.openai.com/v1
# 属于多个组织时才需要指定。
# OPENAI_ORGANIZATION=
# ⚠️ 与 ARK_TIMEOUT_SEC 语义不同:OpenAI 图片接口是**同步**的,一次请求里等到
# 图片生成完才返回,故这个值**确实是生成耗时的上限**,不是短往返的上限。
# OPENAI_TIMEOUT_SEC=180
# 图片模型 ID,仅作兜底。同 ARK_VIDEO_MODEL:正常由 Java 侧传入任务行的快照。
OPENAI_IMAGE_MODEL=
# 同时执行的同步调用数上限。这同时也是「同时有多少钱在花」的上界——超出的提交
# 排队,而不是一起打到服务商那里。
# OPENAI_MAX_CONCURRENT=4
# 已完成任务在内存注册表里的保留时长(秒)。**必须大于 Java 侧最长的轮询间隔**
# (当前阶梯最长 30 秒),否则结果会在被取走前被清掉——症状是「任务成功了但查
# 不到」,极难查。
# OPENAI_COMPLETED_TTL_SEC=900
# 注册表条目上限。满了以后新提交被拒(503),**不挤掉在途任务**——挤掉一个在途
# 任务会让一次可能正在计费的调用变成无人认领的开销。
# OPENAI_MAX_INFLIGHT=256
# ---- 服务身份 JWT(ADR-023 §5)----
# 与 patbond-creation 持有的私钥配对的 RS256 公钥。注意这是一对**独立于用户
# token 的**密钥:现有 JwtVerifier 只验签名、不校验 aud/iss,复用同一密钥对会
# 让服务票被四个 Java 模块当成合法用户 token 接受。
PATBOND_SERVICE_JWT_PUBLIC_KEY=
# PATBOND_SERVICE_JWT_ISSUER=patbond-api
# audience 必须强制校验,否则独立密钥对失去意义。
# PATBOND_SERVICE_JWT_AUDIENCE=patbond-ai
# PATBOND_SERVICE_JWT_TYPE=service
# ---- 其他 ----
# PATBOND_AI_LOG_LEVEL=INFO