API 參考

非同步任務

圖片、影片和音樂任務共用一套持久化生命週期:只提交一次、儲存 task_id、等待終態,然後取得結果。只需實作一個任務執行器,再依模型選擇端點分類。

#所有非同步模型共用一套生命週期

  1. 1

    只提交一次

    使用唯一的 Idempotency-Key,並立即儲存回應。

    POST /v1/{capability}
  2. 2

    持久儲存 task_id

    在 worker 確認任務之前先完成儲存。

    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、端點分類、模型、建立時間和你自己的業務識別碼。worker 重啟後可以繼續輪詢,無需再次提交並產生重複計費。

#結果留存

僅在成功後取得 /content。把平台回傳的網址儲存到業務記錄中;如留存要求需要獨立儲存,再下載一份私有副本。

還是沒解決?

把請求 ID 或錯誤訊息傳給我們,我們來查。