API 参考

异步任务

图片、视频和音乐任务共用一套持久化生命周期:只提交一次、保存 task_id、等待终态,然后获取结果。只需实现一个任务执行器,再按模型选择接口分类。

#所有异步模型共用一套生命周期

  1. 1

    只提交一次

    使用唯一的 Idempotency-Key,并立即保存响应。

    POST /v1/{capability}
  2. 2

    持久保存 task_id

    工作线程确认任务前先完成保存。

    task_id
  3. 3

    轮询或接收 Webhook

    状态比较忽略大小写,并在进入终态时停止。

    GET /{task_id}
  4. 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_STARTprocessingqueued否网关已受理,尚未分发到渠道。
SUBMITTEDqueuedqueued否已提交上游并被厂商确认。
QUEUEDqueuedqueued否在厂商队列中排队。
IN_PROGRESSprocessingin_progress否生成中。progress 是形如 "60%" 的百分比字符串。
SUCCESSsucceededcompleted是已完成。通过 /content 获取结果。
FAILUREfailedfailed是已确定失败。fail_reason 说明原因,预扣额度会退回。
UNKNOWNunknownunknown否厂商不再返回网关能识别的状态。请带上截止时间继续轮询,超时后转对账——不要当成成功或失败处理。

判断终态集合,不要去判断“还没跑完”

只把 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 或报错信息发给我们,我们来查。