이 문서에서는 후자의 두 가지에 중점을 둡니다: 비동기 작업의 TaskHandle 폴링 및 채팅 스트리밍 응답의 세부 사항, 함정 및 언어 간 차이.
1. TaskHandle — 비동기 작업의 통합 추상화
세 가지 SDK는 비동기 작업을TaskHandle로 캡슐화하여 동일한 4개의 메서드를 제공합니다:
작업 생성의 두 가지 호출 방식
각 비동기 리소스(images.generate / video.generate / audio.generate)에는 wait 매개변수가 있습니다:
wait=False(기본값): 즉시TaskHandle을 반환하며, 비즈니스 코드는 언제 폴링할지 결정합니다.wait=True: SDK 내부에서 직접handle.wait()를 호출하여 함수가 완료된 후 응답을 반환합니다. 목표 API가 반드시status: succeeded필드를 반환할 것이라고 확신할 때만 사용하세요 — 일부 공급자는 이 약속을 지키지 않아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초마다/midjourney/tasks에 POST 요청을 보내며,status가succeeded또는failed로 변할 때까지 또는 총 소요 시간이 180초를 초과할 때까지 진행됩니다.TimeoutError가 발생합니다.- 완료 후
result["response"]["data"]는 일반적으로 4개의 이미지를 포함합니다(기본적으로 Midjourney는 2x2 그리드).
예시: TypeScript 명시적 폴링
동기 생성 vs 비동기 작업의 선택
만약 당신의 공급자가 본래 동기적으로 이미지를 생성하는 경우(NanoBanana / Flux / Seedream),wait를 전달하지 마세요:
task_id + /tasks 쌍이 없다면, 동기 생성입니다; 동기 생성의 응답에서 data 필드는 이미 최종 결과를 포함하고 있습니다.
TaskHandle 내부 프로토콜
TaskHandle.get() 호출은 다음과 같습니다:
response로 감싸지지 않은 구형 응답을 호환합니다 — 최상위 status를 직접 읽을 수 있으므로 신구형 응답 전환이 비즈니스 코드에 영향을 미치지 않습니다.
2. 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입니다.
중간 취소
미리 취소된 토큰은 이미 청구됩니다 — 취소 시점 이전에 생성된 토큰은 실제 소비에 따라 청구됩니다.
삼, 타임아웃 및 재시도
세 가지 SDK는 동일한 재시도 전략을 공유합니다:
재시도를 비활성화하려면: 클라이언트를 구성할 때
max_retries=0 / maxRetries: 0 / WithMaxRetries(0)를 전달합니다.
비동기 작업(TaskHandle)의 폴링 자체는 max_retries의 영향을 받지 않습니다 — 그 루프는 비즈니스 수준의 것이며 HTTP 수준의 것이 아니며, max_wait로 총 기간을 제어합니다.
사, 일반적인 함정
- 동기 제공자는
wait를 전달하지 마세요: NanoBanana / Flux / Seedream은 동기 생성이며, 강제로wait=True를 설정하면 SDK가 업데이트되지 않는tasks인터페이스를 폴링하게 됩니다. - TaskHandle 단위 차이: Python은 초, TS는 밀리초로, 언어 간 이식 시 반드시 변환해야 합니다.
wait=True는 여전히TimeoutError를 발생시킬 수 있습니다: 응답은status in ('succeeded','failed')를 충족해야 루프를 종료합니다; 제공자가 다른 필드 이름을 사용한 경우, 비즈니스 코드는 직접handle.get()으로 파싱해야 합니다.- 스트리밍 취소: 취소 전에 생성된 토큰은 이미 청구됩니다.
- 동일한 프로세스 내에서 클라이언트 재사용: SDK는 자체 연결 풀을 제공하므로, 빈번한
new AceDataCloud()/AceDataCloud()호출은 TLS 핸드셰이크를 병목으로 만듭니다.

