API 参考
异步任务
图片、视频和音乐任务共用一套持久化生命周期:只提交一次、保存 task_id、等待终态,然后获取结果。只需实现一个任务执行器,再按模型选择接口分类。
#所有异步模型共用一套生命周期
- 1
只提交一次
使用唯一的 Idempotency-Key,并立即保存响应。
POST /v1/{capability} - 2
持久保存 task_id
工作线程确认任务前先完成保存。
task_id - 3
轮询或接收 Webhook
状态比较忽略大小写,并在进入终态时停止。
GET /{task_id} - 4
获取并保存结果
成功后调用 /content,并保存平台返回的地址。
GET /{task_id}/content
#接口分类
| 能力 | 提交 | 轮询 | 结果内容 |
|---|---|---|---|
| 图片 | /v1/images/tasks | /v1/images/tasks/{task_id} | /v1/images/tasks/{task_id}/content |
| 视频 | /v1/videos | /v1/videos/{task_id} | /v1/videos/{task_id}/content |
| 音乐 | /v1/music/generations | /v1/music/tasks/{task_id} | /v1/music/tasks/{task_id}/content |
#状态取值
同一套生命周期,对外有两套状态词汇:图片、音乐、3D 任务用 OpenAI 任务那一套;视频以及所有 webhook 负载用 OpenAI 视频那一套。为其中一类写的状态判断,在另一类里永远不会命中、而且不会报错——本表就是两者的对照。
| 内部状态 | 图片 · 音乐 · 3D | 视频 · webhook | 终态 | 含义 |
|---|---|---|---|---|
| NOT_START | processing | queued | 否 | 网关已受理,尚未分发到渠道。 |
| SUBMITTED | queued | queued | 否 | 已提交上游并被厂商确认。 |
| QUEUED | queued | queued | 否 | 在厂商队列中排队。 |
| IN_PROGRESS | processing | in_progress | 否 | 生成中。progress 是形如 "60%" 的百分比字符串。 |
| SUCCESS | succeeded | completed | 是 | 已完成。通过 /content 获取结果。 |
| FAILURE | failed | failed | 是 | 已确定失败。fail_reason 说明原因,预扣额度会退回。 |
| UNKNOWN | unknown | unknown | 否 | 厂商不再返回网关能识别的状态。请带上截止时间继续轮询,超时后转对账——不要当成成功或失败处理。 |
判断终态集合,不要去判断“还没跑完”
只把 succeeded / completed 和 failed 当作停止条件,其余一切——包括本表没列出的取值——都按“仍在进行”处理。中间状态未来可能新增;终态不会,新增终态属于破坏性变更。
UNKNOWN 不等于失败
当厂商不再返回网关能识别的状态时,任务会停留在 UNKNOWN。请带上截止时间继续轮询,超时后交给人工或对账任务——当成失败会因重试而重复扣费,当成成功则会把空结果发给用户。
#安全轮询循环
按你所轮询的那一类的终态词停止;其余状态都要有一个真实时间上的截止时间,并做退避,避免卡住的任务变成死循环。
poll.py
import time, requests
# The two vocabularies. Pick the one that matches the endpoint you poll.
TASK_DONE, TASK_FAILED = {"succeeded"}, {"failed"} # images / music / 3D
VIDEO_DONE, VIDEO_FAILED = {"completed"}, {"failed"} # videos + webhooks
DEADLINE_SECONDS = 15 * 60
def wait_for_image_task(task_id, api_key):
started = time.monotonic()
delay = 2
while True:
response = requests.get(
f"https://www.aisplice.store/v1/images/tasks/{task_id}",
headers={"Authorization": f"Bearer {api_key}"},
timeout=30,
)
response.raise_for_status()
task = response.json()
status = task.get("status", "")
if status in TASK_DONE:
return task
if status in TASK_FAILED:
raise RuntimeError((task.get("error") or {}).get("message", "task failed"))
# queued / processing / unknown all mean "not finished". Wait — but
# never forever: hand an overdue task to reconciliation.
if time.monotonic() - started > DEADLINE_SECONDS:
raise TimeoutError(f"task {task_id} still {status} after the deadline")
time.sleep(delay)
delay = min(delay * 1.5, 15)重启后继续执行
保存 task_id、接口分类、模型、创建时间和业务标识。工作线程重启后可以继续轮询,无需再次提交并产生重复计费。
#结果留存
仅在成功后获取 /content。把平台返回的地址保存到业务记录中;如留存要求需要独立存储,再下载一份私有副本。
还是没解决?
把请求 ID 或报错信息发给我们,我们来查。