Skip to main content
Services on Ace Data Cloud are divided into two categories based on response mode: This article focuses on the last two categories: TaskHandle polling for asynchronous tasks and the details, pitfalls, and cross-language differences of chat streaming responses.

I. TaskHandle — Unified Abstraction for Asynchronous Tasks

All three SDKs encapsulate asynchronous tasks into TaskHandle, providing the same four methods:

Two Calling Methods for Creating Tasks

Each asynchronous resource (images.generate / video.generate / audio.generate) has a wait parameter:
  • wait=False (default): Immediately returns TaskHandle, business code decides when to poll.
  • wait=True: The SDK internally calls handle.wait(), returning the response after completion. Only use this when you are sure the target API will return the status: succeeded field—a few providers do not adhere to this convention, causing wait to keep polling until max_wait throws a TimeoutError.

Unit Differences (⚠️ Must Read)

The units of poll_interval and max_wait differ across the three languages, which is a common pitfall during cross-language migration:
Treating TS’s { pollInterval: 3000 } as seconds and translating it to Python poll_interval=3000 will cause the SDK to wait 50 minutes before polling a second time.

Example: Python Explicit Polling for Midjourney

The entire code does the following:
  1. images.generate(..., wait=False) submits the prompt to the Midjourney API, immediately obtaining the handle without blocking.
  2. handle.wait(poll_interval=3.0, max_wait=180.0) internally POSTs to /midjourney/tasks every 3 seconds until status changes to succeeded or failed, or the total time exceeds 180 seconds, throwing a TimeoutError.
  3. After completion, result["response"]["data"] usually contains 4 images (Midjourney defaults to a 2x2 grid).

Example: TypeScript Explicit Polling

Trade-offs Between Synchronous Generation and Asynchronous Tasks

If your provider itself generates images synchronously (NanoBanana / Flux / Seedream), do not pass wait:
The judgment method is simple: if the target API documentation does not have the task_id + /tasks pair, it is synchronous generation; the response of synchronous generation already contains the final result in the data field.

Internal Protocol of TaskHandle

The TaskHandle.get() call is:
The response has a unified structure:
The SDK also supports older responses without the outer response wrapper—it directly reads the top-level status, so switching between old and new responses does not affect business code.

II. SSE Streaming Response (chat.completions)

chat.completions.create(stream=True) is currently the only streaming interface in the SDK (audio/video streams are not yet supported). The iterative styles of the three languages are native to each:

TypeScript

Actual running result:

Python

Actual running result:

Go

Actual running result:

Structure of Stream Chunks

Each chunk is an OpenAI compatible chat.completion.chunk:
  • The first chunk usually carries delta.role: "assistant" but has an empty content.
  • The middle chunks each carry delta.content, which can be concatenated directly.
  • The last chunk has an empty delta, and finish_reason is stop / length / content_filter.

Cancelling Midway

Tokens generated before cancellation are still billed — tokens generated before the cancellation moment will still be charged based on actual consumption.

III. Timeouts and Retries

The three SDKs share the same retry strategy: To disable retries: pass max_retries=0 / maxRetries: 0 / WithMaxRetries(0) when constructing the client. Polling of asynchronous tasks (TaskHandle) is not affected by max_retries — its loop is business-level rather than HTTP-level, controlled by max_wait for total duration.

IV. Common Pitfalls

  1. Do not pass wait for synchronous providers: NanoBanana / Flux / Seedream are synchronously generated, forcing wait=True will make the SDK poll a tasks interface that will not update at all.
  2. TaskHandle unit differences: Python is in seconds, TS is in milliseconds, make sure to convert when porting across languages.
  3. wait=True may still result in TimeoutError: The response must meet status in ('succeeded','failed') to exit the loop; if the provider uses different field names, the business code must handle handle.get() parsing itself.
  4. Streaming cancellation: Tokens generated before cancellation have already been billed.
  5. Reuse client within the same process: The SDK has a built-in connection pool, frequently calling new AceDataCloud() / AceDataCloud() will make TLS handshake a bottleneck.

Learn More