> ## 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.

# 流式补全

> 完整的 SSE 事件模式、传输格式与错误语义。

在任意 `POST /api/v1/completions` 请求中设置 `stream: true`，即可获得 Server-Sent Events（SSE）流，而非单个 JSON 响应。

## 请求

```bash theme={null}
curl -N -X POST https://www.hitheo.ai/api/v1/completions \
  -H "Authorization: Bearer $THEO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Write a haiku about code", "stream": true}'
```

需要通过 Bearer token 进行身份验证。参见[身份验证](/api-reference/authentication)。

## 传输格式

每个事件由两行组成，后跟一个空行：

```
event: <event_name>
data: <json_payload>
```

调用方必须缓冲部分数据块并按 `\n\n` 进行拆分——令牌事件每条可能只有几十字节，且传输层不保证与事件边界对齐。

## 事件类型（按到达顺序）

服务器以固定顺序发出事件：`thinking` → `meta` → (`skills`? / `genui_meta`?) → `tool`\* → `artifact`\* → `token`\* → `done`。若流中途出现失败，将发出 `error` 事件并关闭该流。

| 事件 | 说明 |
| - | - |
| `thinking` | 心跳——单个字节，用于让代理立即刷新响应。无 JSON 负载 |
| `meta` | 引擎信息、解析后的模式、工件、路由、`conversation_id`、`request_id` |
| `skills` | 本轮生效的技能（仅当应用了一个或多个技能时出现） |
| `genui_meta` | GenUI 组件库信息（仅当 `resolved_mode === "genui"` 时出现） |
| `tool` | 表示一次工具调用（可能多次触发） |
| `artifact` | 一个生成的文件（图片、文档、代码、视频）（可能多次触发） |
| `token` | 一个文本片段（每个令牌触发一次） |
| `done` | 最终负载：完整内容、后续建议、用量、`conversation_id`、`request_id` |
| `error` | 发生错误——负载与 REST 错误信封格式一致 |

## 负载模式

### `meta`

```typescript theme={null}
interface StreamMetaData {
  id: string;                       // completion id (cmpl_...)
  mode: ChatMode;                   // mode you requested
  resolved_mode: ChatMode;          // mode after intent classification
  model: {
    id: string;                     // theo-branded id (e.g. "theo-1-flash")
    label: string;                  // human-friendly label
    engine: string;                 // engine subsystem (e.g. "theo-core")
  };
  tools: Array<{ name: string; status: string; description?: string }>;
  artifacts: unknown[];             // pre-populated artifacts (images, docs)
  brand?: Record<string, unknown>;  // optional brand-soul overlay
  routing?: Record<string, unknown>;// routing-engine telemetry
  conversation_id: string | null;   // null for stateless callers
  request_id: string;               // mirror of X-Request-Id header
}
```

### `token`

```typescript theme={null}
interface StreamTokenData {
  token: string; // text chunk; concatenate in order to reconstruct the full content
}
```

### `tool`

```typescript theme={null}
interface StreamToolData {
  name: string;        // tool name (e.g. "Intent classifier", "browser_navigate")
  status: string;      // "pending" | "running" | "complete" | "error"
  description?: string;// short human-readable description
}
```

### `artifact`

负载取决于 `artifact.type`。通用字段：`id`、`type`、`title`、`providerId`、`modelId`。图片类工件包含 `imageUrl`；视频类工件包含 `videoUrl`；文档类工件包含 `downloadUrl` 和 `sizeBytes`；代码类工件包含 `code` 和 `language`。

### `genui_meta`

```typescript theme={null}
interface GenUIMetaEvent {
  library: string; // component library id (e.g. "theo")
  tools: string[]; // tool names the Renderer's toolProvider should expose
}
```

### `skills`

```typescript theme={null}
interface StreamSkillsData {
  active: Array<{
    id: string;
    slug: string;
    name: string;
    intensity?: number; // 0–100 when the skill supports intensity
  }>;
}
```

### `done`

```typescript theme={null}
interface StreamDoneData {
  id: string;
  content: string;                  // full text output (same as concatenated tokens)
  follow_ups: Array<{ label: string; prompt: string }>;
  structured_output?: {
    type: string;
    data: unknown;
    parse_error?: string;
  };
  skills_active?: Array<{ id: string; slug: string; name: string; intensity?: number }>;
  routing?: Record<string, unknown>;
  usage: {
    cost_cents: number;
    prompt_tokens: number;    // always 0 for non-text modes
    completion_tokens: number;// always 0 for non-text modes
    total_tokens: number;
    cached?: boolean;         // true when served from the semantic cache
  };
  conversation_id: string | null;
  request_id: string;
}
```

### `error`

`error` 事件的负载与 REST 错误信封格式一致（参见[概览](/api-reference/overview)）：

```typescript theme={null}
interface StreamErrorData {
  error: {
    message: string;
    type: string;            // e.g. "rate_limit_error", "server_error"
    code: string;            // e.g. "stream_interrupted", "orchestration_failed"
    request_id: string | null;
  };
}
```

## 流示例

```
event: thinking
data: {"status":"processing"}

event: meta
data: {"id":"cmpl_abc123","mode":"auto","resolved_mode":"fast","model":{"id":"theo-1-flash","label":"Theo Flash","engine":"theo-core"},"tools":[],"artifacts":[],"conversation_id":null,"request_id":"req_9f2e1a"}

event: token
data: {"token":"Lines"}

event: token
data: {"token":" of"}

event: token
data: {"token":" code"}

event: done
data: {"id":"cmpl_abc123","content":"Lines of code","follow_ups":[],"usage":{"cost_cents":0.01,"prompt_tokens":8,"completion_tokens":3,"total_tokens":11},"conversation_id":null,"request_id":"req_9f2e1a"}
```

## 提取 `conversation_id`

`conversation_id` 同时出现在 `meta` 和 `done` 中。对于无状态调用方（请求中未包含 `conversation_id`），其值为 `null`。对于在请求中传入了 `conversation_id` 的调用方，该值会原样回传。SDK 在流结束后将其暴露为 `stream.conversationId`。

```typescript theme={null}
const stream = theo.stream({ prompt: "..." });
for await (const event of stream) {
  // consume events
}
console.log(stream.conversationId); // populated from meta / done
```

## 流中错误

当编排在 `meta` 已发出之后失败时（例如上游 LLM 提供方超时，或触发了某个模式下的速率限制），服务器会发出带 REST 信封格式的 `error` 事件并关闭该流：

```
event: meta
data: {"id":"cmpl_abc123","mode":"auto","resolved_mode":"fast","model":{"id":"theo-1-flash","label":"Theo Flash","engine":"theo-core"},"tools":[],"artifacts":[],"conversation_id":null,"request_id":"req_9f2e1a"}

event: token
data: {"token":"Lines"}

event: error
data: {"error":{"message":"Rate limit exceeded for fast mode (120 RPM on 'free' tier).","type":"rate_limit_error","code":"mode_rate_limit_exceeded","request_id":"req_9f2e1a"}}
```

请使用与 REST 错误处理相同的代码路径：

```typescript theme={null}
for await (const event of stream) {
  if (event.type === "error") {
    const { message, type, code, request_id } = event.data.error;
    throw new Error(`[${type}/${code}] ${message} (request_id=${request_id})`);
  }
  // ...
}
```

## SDK 用法

SDK 返回一个 `TheoStream`——一个可异步迭代的对象，带有用于中途取消的 `cancel()` 方法，以及最终的元数据属性。

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

const theo = new Theo({ apiKey: process.env.THEO_API_KEY! });
const stream = theo.stream({ prompt: "Explain DNS" });

// Graceful cancel after 3s (e.g. user clicked "Stop generating")
setTimeout(() => stream.cancel(), 3000);

for await (const event of stream) {
  switch (event.type) {
    case "meta":     console.log("Mode:", event.data.resolved_mode); break;
    case "token":    process.stdout.write(event.token); break;
    case "tool":     console.log("Tool:", event.data.name, event.data.status); break;
    case "artifact": console.log("Artifact:", event.data); break;
    case "error":    console.error("Stream error:", event.data.error); break;
    case "done":     console.log("\nCost:", event.data.usage.cost_cents); break;
  }
}

console.log("conversation_id:", stream.conversationId);
console.log("usage:", stream.usage);
console.log("request_id:", stream.requestId);
```

参见[流式](/core-concepts/streaming)获取概念性指南。


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