Skip to main content
Ace Data Cloud의 서비스는 응답 모드에 따라 두 가지로 나뉩니다: 이 문서에서는 후자의 두 가지에 중점을 둡니다: 비동기 작업의 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 }을 초로 잘못 해석하여 Python poll_interval=3000으로 변환하면 SDK가 두 번째 폴링을 위해 50분을 기다리게 됩니다.

예시: Python 명시적 폴링 Midjourney

전체 코드는 다음과 같은 작업을 수행합니다:
  1. images.generate(..., wait=False)가 prompt를 Midjourney API에 제출하고 즉시 handle을 가져오며, 블로킹되지 않습니다.
  2. handle.wait(poll_interval=3.0, max_wait=180.0)는 내부적으로 3초마다 /midjourney/tasks에 POST 요청을 보내며, status가 succeeded 또는 failed로 변할 때까지 또는 총 소요 시간이 180초를 초과할 때까지 진행됩니다. TimeoutError가 발생합니다.
  3. 완료 후 result["response"]["data"]는 일반적으로 4개의 이미지를 포함합니다(기본적으로 Midjourney는 2x2 그리드).

예시: TypeScript 명시적 폴링

동기 생성 vs 비동기 작업의 선택

만약 당신의 공급자가 본래 동기적으로 이미지를 생성하는 경우(NanoBanana / Flux / Seedream), wait를 전달하지 마세요:
판별 방법은 간단합니다: 목표 API 문서에 task_id + /tasks 쌍이 없다면, 동기 생성입니다; 동기 생성의 응답에서 data 필드는 이미 최종 결과를 포함하고 있습니다.

TaskHandle 내부 프로토콜

TaskHandle.get() 호출은 다음과 같습니다:
응답은 통일된 구조입니다:
SDK는 또한 외부 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로 총 기간을 제어합니다.

사, 일반적인 함정

  1. 동기 제공자는 wait를 전달하지 마세요: NanoBanana / Flux / Seedream은 동기 생성이며, 강제로 wait=True를 설정하면 SDK가 업데이트되지 않는 tasks 인터페이스를 폴링하게 됩니다.
  2. TaskHandle 단위 차이: Python은 초, TS는 밀리초로, 언어 간 이식 시 반드시 변환해야 합니다.
  3. wait=True는 여전히 TimeoutError를 발생시킬 수 있습니다: 응답은 status in ('succeeded','failed')를 충족해야 루프를 종료합니다; 제공자가 다른 필드 이름을 사용한 경우, 비즈니스 코드는 직접 handle.get()으로 파싱해야 합니다.
  4. 스트리밍 취소: 취소 전에 생성된 토큰은 이미 청구됩니다.
  5. 동일한 프로세스 내에서 클라이언트 재사용: SDK는 자체 연결 풀을 제공하므로, 빈번한 new AceDataCloud() / AceDataCloud() 호출은 TLS 핸드셰이크를 병목으로 만듭니다.

더 알아보기