개발자

TypeScript SDK에서 Sume Job·실행이 끝날 때까지 기다리기

waitForJob, waitForRun, subscribeFormatRun은 Sume Job이나 실행이 끝날 때까지 폴링합니다. 기본 타임아웃, throw하는 오류, 재시도를 정리했습니다.

읽는 시간 6분Sume
전체 글

TypeScript에서 Sume Job이나 실행이 끝날 때까지 기다리려면, 생성 Job에는 waitForJob(jobId, { client })를, Format, Action, Agent 실행에는 waitForRun(runId, { client, family })를 호출하세요. Format 실행을 만들고 기다리는 일을 호출 한 번으로 끝내려면 subscribeFormatRun을 쓰세요. SSE 스트림이 없으므로 세 헬퍼 모두 폴링하며, 실패를 포함한 종료 상태에서 resolve하므로 반환값의 status를 확인하세요.

아래 기본값과 오류는 Sume의 실행 기다리기 (영문) 페이지에서, 실행 기한은 실행과 결과 (영문)에서 가져왔으며, 모두 2026-09-26에 확인했습니다. 패키지 설치는 Sume TypeScript SDK 빠른 시작에서 다룹니다.

어떤 대기 헬퍼를 써야 하나요?

어떤 ID가 있느냐에 따라 고르세요. Job ID와 실행 ID는 서로 바꿔 쓸 수 없습니다. /v1/videos 같은 생성 엔드포인트와 Avatar 경로는 /v1/jobs/:id에 Job을 만들고, Format, Action, Agent Completions는 실행을 만듭니다. 둘의 차이는 Sume Job과 실행의 차이에서 비교합니다.

  • waitForRun에는 family가 필요합니다. 값은 "format", "action", "agent" 중 하나입니다. 실행 ID만으로는 세 URL 프리픽스 중 어디에 속하는지 알 수 없기 때문입니다.
  • waitForJob은 pollInterval을 하한으로 취급합니다. 상태 페이로드의 next_poll_after_seconds가 더 긴 간격을 요청하면 그 값이 우선합니다.
  • 세 헬퍼 모두 대기와 진행 중인 요청을 중단하는 signal과, 상태를 읽을 때마다 호출되는 onStatus(status, snapshot) 콜백을 받습니다.
실행 기다리기 (영문) 기준 기본값, 2026-09-26 확인.
헬퍼대기 대상기본 타임아웃throw하는 오류
subscribeFormatRun헬퍼가 직접 만드는 Format 실행20분SumeRunRequestError, SumeRunTimeoutError
waitForRun이미 있는 Format, Action, Agent 실행10분SumeRunRequestError, SumeRunTimeoutError
waitForJob/v1/jobs/:id의 생성 Job20분SumeJobRequestError, SumeJobTimeoutError

영상 생성 Job은 어떻게 기다리나요?

제출하고 error를 확인한 뒤, Job ID를 waitForJob에 넘기세요. Sume API 레퍼런스에 따르면 POST /v1/videos는 Sume의 { data } 봉투가 아니라 봉투 없는 객체를 반환합니다. 그 id가 Job ID이며, GET /v1/jobs/{id}/status에서도 읽을 수 있습니다.

waitForJob은 /v1/jobs/:id의 Job 레코드로 resolve합니다. /result는 실패하거나 취소된 Job에 409 job_not_completed로 응답하기 때문입니다. 레코드에서 status, result, error를 읽으세요. 제출 경로가 mode를 받는다면 async를 보내거나 생략하세요. sync와 subscribe는 둘 다 최대 30초로 제한된 같은 서버 측 대기입니다.

import { createSumeClient, createVideoGeneration, waitForJob } from "@sume-com/sdk";

const client = createSumeClient({ apiKey: process.env.SUME_API_KEY! });

const { data: video, error } = await createVideoGeneration({
  client,
  headers: { "idempotency-key": "mug-teaser-v1" },
  body: { model: "sume/auto", prompt: "Slow push-in on a ceramic mug" },
});
if (error) throw new Error(JSON.stringify(error));

const job = await waitForJob(video!.id, { client });
if (job.status === "completed") console.log(job.result.artifacts);
else console.error(job.status, job.error);

실행이 기본 타임아웃보다 오래 걸리면 어떻게 하나요?

subscribeFormatRun은 기본적으로 20분을 기다립니다. 영상 Format은 보통 10~20분 걸리기 때문입니다. waitForRun은 10분을 기다립니다. 롱폼 영상은 15~30분짜리 작업이므로, 정상적인 실행이 아직 진행 중인데 기본 타임아웃이 지나 버릴 수 있습니다.

더 긴 timeout을 넘기세요. 문서는 상한을 영수증의 expires_at에서 정하라고 권합니다. expires_at은 실행이 failed로 강제 종료되는 기한으로, created_at부터 90분이며, 25분을 넘긴 실행이 10분 동안 아무 활동이 없으면 그보다 앞당겨집니다.

  • 타임아웃이 나면 runId와 lastStatus를 담은 SumeRunTimeoutError나, jobId를 담은 SumeJobTimeoutError가 throw됩니다.
  • 타임아웃은 아무것도 취소하지 않습니다. 실행이나 Job은 계속 진행되고 계속 과금됩니다. 나중에 getFormatRun이나 getApiJob으로 읽으세요. 취소하려면 cancelFormatRun을 쓰고, Job이라면 생성이 시작되기 전에 cancelApiJob을 쓰세요.
import { SumeRunTimeoutError, waitForRun } from "@sume-com/sdk";

// created: the 202 receipt from a Format run create
try {
  const run = await waitForRun(created.id, {
    client,
    family: "format",
    timeout: Date.parse(created.expires_at) - Date.now(),
  });
} catch (error) {
  if (!(error instanceof SumeRunTimeoutError)) throw error;
  await markPending(error.runId, error.lastStatus); // still running, still billing
}

종료 상태면 실행이 성공한 건가요?

아닙니다. 실행 헬퍼는 completed, failed, canceled, skipped에서 resolve하고, Job의 종료 상태는 completed, failed, canceled입니다. 실패한 실행은 예외가 아니라 직접 요청한 결과이므로, 웹훅 핸들러가 하듯이 status와 error를 읽으세요. skipped는 이미 진행 중인 실행이 있었고 on_active_run: "skip"을 보냈다는 뜻입니다. Format 실행은 기본적으로 동시 실행을 허용합니다.

헬퍼가 throw하는 경우는 세 가지입니다. SumeRunRequestError는 생성이 거부됐거나(예: 개인 키로 팀 Format을 호출했을 때의 403 workspace_key_required) 상태 조회가 일시적이지 않은 오류로 실패했다는 뜻이며, runId나 "(not created)"를 담습니다. waitForJob은 그 대신 jobId를 담은 SumeJobRequestError를 throw합니다. 타임아웃이 나면 타임아웃 오류를 throw하고, 중단하면 넘겨준 signal의 사유로 reject합니다.

재시도하면 subscribeFormatRun이 두 번째 실행을 시작하나요?

호출 한 번 안에서는 그렇지 않습니다. idempotencyKey의 기본값은 자동 생성된 UUID이며 Idempotency-Key로 전송됩니다. 클라이언트는 키가 있는 POST만 재시도하므로, 생성 요청을 헬퍼가 스스로 재시도해도 안전합니다. 이미 끝난 실행을 다시 보내면 즉시 반환됩니다. 키를 보내지 않으려면 null을 넘기세요.

애플리케이션 코드가 같은 호출을 다시 하는 경우까지 막아 주지는 않습니다. Format 문서는 요청마다 만드는 UUID를 장식에 불과하다고 설명합니다. 주문 ID에 의도적으로 다시 실행할 때 올리는 버전을 더한 값처럼, 만들고 있는 대상에서 유도한 키를 넘기세요. 자세한 내용은 AI 영상 API 멱등성 키를 참고하세요.

기다리는 동안 429나 5xx가 나면 어떻게 되나요?

상태 조회에서 받은 429나 5xx는 실행이 아니라 조회가 실패했다는 뜻이며, 실행은 여전히 진행 중이고 비용도 계속 쓰고 있습니다. 클라이언트는 408, 429, 5xx와 전송 실패를 retry-after를 따르며 두 번 재시도하고, 그다음에는 waitForRun이 일시적인 조회 실패를 연속 여섯 번까지 견딥니다(maxTransientFailures, onTransientError).

  • 폴링은 진행 중인 실행마다 타이머 하나와 열린 요청 하나를 씁니다. 가능하면 Run 웹훅 (영문)을 받고, 헬퍼는 대비책으로 남겨 두세요.

출처

관련 글

작성자 Sume