本文重點講後兩類:異步任務的 TaskHandle 輪詢 和 chat 串流回應 的細節、陷阱和跨語言差異。
一、TaskHandle —— 異步任務的統一抽象
三種 SDK 都把異步任務封裝成TaskHandle,提供同樣的 4 個方法:
創建任務的兩種呼叫方式
每個異步資源(images.generate / video.generate / audio.generate)都有 wait 參數:
wait=False(預設):立即返回TaskHandle,業務代碼自己決定何時輪詢。wait=True:SDK 內部直接調handle.wait(),函數返回完成後的回應。僅當你確定目標 API 一定會返回status: succeeded字段時再用——少數 provider 沒遵守這個約定,會讓wait一直轉到max_wait才拋TimeoutError。
單位差異(⚠️ 必看)
poll_interval 和 max_wait 的單位在三種語言裡不一樣,跨語言遷移時是常見踩坑點:
把 TS 的{ pollInterval: 3000 }當成秒翻成 Pythonpoll_interval=3000,會讓 SDK 等 50 分鐘才輪詢第二次。
示例:Python 明確輪詢 Midjourney
images.generate(..., wait=False)把prompt提交給 Midjourney API,立即拿到handle,不阻塞。handle.wait(poll_interval=3.0, max_wait=180.0)內部每 3 秒 POST 一次/midjourney/tasks,直到status變成succeeded或failed,或總耗時超過 180 秒拋TimeoutError。- 完成後
result["response"]["data"]通常包含 4 張圖(Midjourney 預設 2x2 grid)。
示例:TypeScript 明確輪詢
同步生成 vs 異步任務的取捨
如果你的 provider 本身就是同步出圖(NanoBanana / Flux / Seedream),不要傳wait:
task_id + /tasks 這一對,就是同步生成;同步生成的回應裡 data 字段已經包含最終結果。
TaskHandle 內部協議
TaskHandle.get() 呼叫的是:
response 包裹的舊版回應——直接讀取頂層 status,所以新舊版回應切換不影響業務代碼。
二、SSE 串流回應(chat.completions)
chat.completions.create(stream=True) 是目前 SDK 裡唯一的串流介面(音頻 / 視頻串流尚未支持)。三種語言的迭代風格各自原生:
TypeScript
Python
Go
流式 chunk 的結構
每一幀 chunk 都是一个 OpenAI 兼容的chat.completion.chunk:
- 第一个 chunk 通常带
delta.role: "assistant"但content为空。 - 中间的 chunk 每个带
delta.content,可以直接拼接。 - 最后一个 chunk
delta为空、finish_reason是stop/length/content_filter。
中途取消
提前取消已经计费的 token —— 取消时刻之前生成的 token 还是会按实际消耗扣费。
三、超时和重试
三种 SDK 共享同一套重试策略:
要禁用重试:构造客户端时传
max_retries=0 / maxRetries: 0 / WithMaxRetries(0)。
异步任务(TaskHandle)的轮询本身不受 max_retries 影响——它的循环是业务级的而不是 HTTP 级的,靠 max_wait 控制总时长。
四、常见陷阱
- 同步 provider 不要传
wait:NanoBanana / Flux / Seedream 都是同步生成,强行wait=True会让 SDK 去轮询一个根本不会更新的tasks接口。 - TaskHandle 单位差异:Python 是秒、TS 是毫秒,跨语言移植时一定换算。
wait=True仍可能TimeoutError:响应必须满足status in ('succeeded','failed')才会退出循环;如果 provider 用了别的字段名,需要业务代码自己handle.get()解析。- 流式取消:取消前生成的 token 已经计费。
- 同一 process 内复用 client:SDK 自带连接池,频繁
new AceDataCloud()/AceDataCloud()会让 TLS 握手成为瓶颈。

