> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hitheo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 異步任務（研究與視頻）

> 以正確的方式運行長任務：使用 research()/video() 入隊，然後用 waitForJob() 輪詢任務。

部分 Theo 能力**太慢，無法內聯返回**。`research`（規劃 → 多源網絡搜索 → 綜合）和 `video`（模型生成）以後台任務的形式運行：你先將其入隊，立即獲得一個 `job_id`，然後輪詢直到任務完成。

<Warning>
  **不要**向 `/completions` 或 `theo.stream()` 發送 `mode: "research"` 或 `mode: "video"`。那樣會讓任務**內聯**運行，從而超出請求超時時間。如果你這樣做，SDK 會立即拋出 `TheoUsageError`。請使用下方的專用方法。
</Warning>

## 生命週期

1. **入隊** — `POST /api/v1/research` 或 `/api/v1/video` 會返回 `202`，並帶有 `{ id, job_id, status: "queued", poll_url }`。
2. **輪詢** — `GET /api/v1/jobs/{job_id}` 返回當前 `status`：`queued` → `active` → `completed`（或 `failed`）。
3. **讀取結果** — 當 `status === "completed"` 時，`result` 中保存着類型化的負載數據。

## SDK 用法

`waitForJob()` 會為你執行輪詢循環，並支持可選的 `onProgress` 回調和 `AbortSignal`。

```typescript theme={null}
import { Theo, type ResearchJobResult } from "@hitheo/sdk";

const theo = new Theo({ apiKey: process.env.THEO_API_KEY! });

const job = await theo.research({
  prompt: "Compare React, Vue, and Svelte for enterprise apps in 2026",
  depth: "advanced",
  max_sources: 10,
});

const final = await theo.waitForJob<ResearchJobResult>(job.job_id, {
  intervalMs: 3000,                       // poll cadence (default 2s)
  maxWaitMs: 180_000,                      // give up after 3 min (default 5 min)
  onProgress: (s) => console.log(`${s.status} ${s.progress}%`),
  signal: AbortSignal.timeout(180_000),   // optional hard cancel
});

if (final.status === "completed") {
  console.log(final.result?.report);
  console.log(`${final.result?.sourceCount} sources`);
}
```

Video 的用法與之相同，使用 `theo.video()` + `waitForJob<VideoJobResult>()`。

### 輪詢間隔與限制

* 默認輪詢間隔為 **2 秒**；`waitForJob` 默認在 **5 分鐘**後放棄（`maxWaitMs`），並拋出 `TheoTimeoutError`。對於較長的視頻渲染，可以調高 `maxWaitMs`。
* 不要以快於約 1 秒的頻率輪詢——這隻會消耗速率限制額度，並不會讓任務更快完成。

### 取消

傳入一個 `AbortSignal`。當它觸發時，`waitForJob` 會停止輪詢，並以 `TheoCancelledError` 拒絕（它**不會**取消服務器端的任務——只是讓你的客戶端停止等待）。

```typescript theme={null}
const controller = new AbortController();
// e.g. user navigates away
document.addEventListener("stop", () => controller.abort());

await theo.waitForJob(job.job_id, { signal: controller.signal });
```

## 結果模式

`result` 通過 `job<T>()` / `waitForJob<T>()` 上的泛型進行類型化。這些結構與 REST 負載一致（參見[獲取任務狀態](/api-reference/jobs/get)）：

* **`ResearchJobResult`** — `{ report, sources: { title, url }[], queries, sourceCount }`
* **`VideoJobResult`** — `{ videoUrl, model, engine, durationMs }`
* **`ImageJobResult`** — `{ imageUrl, model, engine }`
* **`DocumentJobResult`** — `{ title, format, downloadUrl, sizeBytes }`
  任何 `model` / `engine` 字段都會以 Theo 品牌呈現——原始的上游標識符永遠不會出現。

## 成本與用量

任務結果目前尚不攜帶按任務計算的 `cost_cents` 字段。要歸因 research/video 任務的支出，請查看聚合的[用量報告](/api-reference/usage/get)（`theo.usage()`），其中包含覆蓋 `research` 和 `video` 的 `by_mode` 明細。

## 失敗

當 `status === "failed"` 時，`result` 為 `null`，`error` 中保存着失敗原因字符串。入隊調用本身也可能在任務隊列暫時不可用時返回 `503 queue_unavailable`——請稍後重試。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.