# 작업 수명주기

Tripo API의 모든 세대 API는 비동기 작업 패턴을 사용합니다. 요청을 제출하면 API가 `task_id`를 반환합니다. 그런 다음 작업 상태와 결과를 폴링할 수 있습니다.

## 상태 머신

```
queued -> running -> success
                 -> failed
                 -> cancelled
```

## 상태 설명

| 상태 | 설명 | 진행 |
| :-: | :-: | :-: |
| `queued` | 작업이 생성되었으며 대기열에서 대기 중입니다. | 0 |
| `running` | 작업을 처리하는 중입니다. | 0-100 |
| `success` | 작업이 완료되었으며 `output` 필드를 사용할 수 있습니다. | 100 |
| `failed` | 일반적으로 서버 측 문제로 인해 작업이 실패했습니다. 지원팀에 문의 | - |
| `banned` | 입력 콘텐츠가 콘텐츠 정책을 위반합니다. | - |
| `expired` | 작업이 만료되어 출력 파일을 더 이상 사용할 수 없습니다. | - |
| `cancelled` | 작업이 취소되었습니다. | - |

## 작업 흐름

```
1. Create a task (POST /v3/generation/*)
   |
   v
2. Return task_id
   |
   v
3. Poll status (GET /v3/tasks/{task_id})
   |
   v
4a. status=success -> Get download links from output
4b. status=failed  -> Check error details and contact support
4c. status=banned  -> Modify the input content and submit again
```

## 신용 동결 모델

Tripo는 크레딧 "동결 후 공제" 모델을 사용하여 실패한 작업에 대해 사용자에게 비용이 청구되지 않도록 합니다.

| 무대 | 신용 변동 | 설명 |
| :-: | :-: | :-: |
| 작업 생성 | 필수 크레딧 동결 | 사용 가능한 잔액에서 크레딧이 사전 승인되어 동결 상태로 이동됩니다. |
| 작업 성공 | 동결된 크레딧은 소비된 크레딧이 됩니다. | 크레딧은 공식적으로 차감됩니다. |
| 작업 실패 | 동결된 크레딧이 출시되었습니다. | 크레딧은 사용 가능한 잔액으로 반환됩니다. |
| 작업이 취소되었습니다. | 동결된 크레딧이 출시되었습니다. | 크레딧은 사용 가능한 잔액으로 반환됩니다. |

현재 잔액 및 동결된 크레딧을 쿼리합니다.

```bash
curl -X GET https://openapi.tripo3d.ai/v3/account/balance \
  -H "Authorization: Bearer {api_key}"
```

```json
{
  "code": 0,
  "data": {
    "balance": 10000,
    "frozen": 200
  }
}
```

## 여론조사 권장사항

- **폴링 간격**: 1~2초마다 한 번씩 쿼리
- **진행률을 표시하려면 `progress` 필드를 사용하세요**: 값은 0에서 100 사이의 정수입니다.
- **시간 초과 설정**: 최대 폴링 기간(예: 5분)을 설정하는 것이 좋습니다. 시간이 초과되면 사용자에게 나중에 다시 확인하라고 안내하세요.
- **일괄 작업에는 `POST /v3/tasks/list` 사용**: 요청 수 줄이기

### Python 폴링 예

```python
import time
import requests

def wait_for_task(task_id, api_key, interval=2, timeout=300):
    url = f"https://openapi.tripo3d.ai/v3/tasks/{task_id}"
    headers = {"Authorization": f"Bearer {api_key}"}
    start = time.time()

    while time.time() - start < timeout:
        response = requests.get(url, headers=headers)
        task = response.json()["data"]
        status = task["status"]
        progress = task.get("progress", 0)

        print(f"[{task_id}] {status} ({progress}%)")

        if status == "success":
            return task["output"]
        if status in ("failed", "cancelled", "banned"):
            raise Exception(f"Task terminated: {status}")

        time.sleep(interval)

    raise TimeoutError(f"Task {task_id} timed out")
```

### JavaScript 폴링 예

```javascript
async function waitForTask(taskId, apiKey, interval = 2000, timeout = 300000) {
  const url = `https://openapi.tripo3d.ai/v3/tasks/${taskId}`;
  const headers = { "Authorization": `Bearer ${apiKey}` };
  const start = Date.now();

  while (Date.now() - start < timeout) {
    const response = await fetch(url, { headers });
    const { data: task } = await response.json();
    const { status, progress = 0 } = task;

    console.log(`[${taskId}] ${status} (${progress}%)`);

    if (status === "success") return task.output;
    if (["failed", "cancelled", "banned"].includes(status)) {
      throw new Error(`Task terminated: ${status}`);
    }

    await new Promise(resolve => setTimeout(resolve, interval));
  }

  throw new Error(`Task ${taskId} timed out`);
}
```
