# Voice（Grok）

Clomio 将 xAI Voice 能力以 Grok 分组 API 暴露出来，包含文字转语音、语音转文字和实时双向语音会话。

| 能力 | 方法 | 路径 | 返回 | 适用分组 |
|---|---|---|---|---|
| Text to Speech | `POST` | `/v1/tts` | 音频字节（默认 `audio/mpeg`） | Grok |
| Speech to Text | `POST` | `/v1/stt` | JSON 转写结果 | Grok |
| Realtime Voice | `GET` + WebSocket Upgrade | `/v1/realtime` | Realtime 事件流 | Grok |
| Custom Voices | `POST` | `/v1/custom-voices` | 自定义声音对象 | Grok |

> 文档以 `/v1/...` 作为规范写法；网关也提供省略 `/v1` 的 Voice 兼容入口。请确保 Base URL 与客户端的 endpoint 拼接方式一致，并确认响应是音频、JSON 或 WebSocket，而不是站点 HTML。

## 通用鉴权

```text
Authorization: Bearer 你的 Grok 分组密钥
```

这些端点仅使用 Key 所在的 Grok 分组。用 OpenAI、Claude、Gemini 或 Antigravity 分组会返回 `404 Voice API is not supported for this platform`。

## TTS：文字转语音

```text
POST https://api.clomio.ai/v1/tts
Content-Type: application/json
```

### 请求体

| 字段 | 类型 | 必填 | 说明 |
|---|---|:---:|---|
| `text` | string | 是 | 要合成的文字 |
| `voice_id` | string | 是 | 内置或自定义声音 ID，例如 `eve`、`ara` |
| `language` | string | 否 | 语言提示，例如 `en`、`zh` |
| `output_format` | object | 否 | 按 xAI Voice 文档指定音频编码/采样率时使用 |

### cURL

```bash
curl https://api.clomio.ai/v1/tts \
  -H "Authorization: Bearer 你的 Grok 分组密钥" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Hello from Grok",
    "voice_id": "eve",
    "language": "en"
  }' \
  --output hello.mp3
```

成功响应是音频二进制，不是 JSON。网关会保留上游 `Content-Type`，通常为 `audio/mpeg`。不要对 TTS 响应调用 `.json()`；应直接写入文件或音频播放器。

## STT：语音转文字

```text
POST https://api.clomio.ai/v1/stt
Content-Type: multipart/form-data
```

### 请求字段

| 字段 | 类型 | 必填 | 说明 |
|---|---|:---:|---|
| `file` | file | 是 | 音频文件，例如 WAV、MP3 |
| `model` | string | 否 | Voice 模型提示；可使用 `grok-voice-latest` |

### cURL

```bash
curl https://api.clomio.ai/v1/stt \
  -H "Authorization: Bearer 你的 Grok 分组密钥" \
  -F "file=@recording.mp3" \
  -F "model=grok-voice-latest"
```

成功响应示例：

```json
{
  "text": "Hello from Grok",
  "language": "en",
  "duration": 1.72
}
```

静音文件返回空 `text` 是正常现象。STT 是批处理 HTTP 接口；Realtime 会话中的音频不应再重复按 STT 接口计费。

## Realtime：实时双向语音

Realtime 不是普通 HTTP POST，而是 WebSocket：

```text
wss://api.clomio.ai/v1/realtime?model=grok-voice-latest
```

客户端必须发送标准 WebSocket Upgrade，并带 `Authorization`。连接成功后首先会收到 `session.created`，随后可能收到 `conversation.created`、`ping` 等事件。

### 最小 JavaScript 示例

```javascript
import WebSocket from "ws";

const ws = new WebSocket(
  "wss://api.clomio.ai/v1/realtime?model=grok-voice-latest",
  { headers: { Authorization: `Bearer ${process.env.GROK_API_KEY}` } },
);

ws.on("message", (raw) => {
  const event = JSON.parse(raw.toString());
  if (event.type === "response.output_audio.delta") {
    const audio = Buffer.from(event.delta, "base64");
    // 把 audio 写入播放器或音频缓冲区
  }
  if (event.type === "response.output_audio_transcript.done") {
    console.log("assistant:", event.transcript);
  }
});

ws.on("open", () => {
  ws.send(JSON.stringify({
    type: "response.create",
    response: { modalities: ["audio"], instructions: "Say hello" },
  }));
});
```

### 常见事件

| 事件 | 方向 | 用途 |
|---|---|---|
| `session.created` | 服务端 → 客户端 | 会话建立成功，包含模型、默认声音和能力 |
| `conversation.created` | 服务端 → 客户端 | 会话容器创建完成 |
| `response.create` | 客户端 → 服务端 | 请求模型生成文字/音频回复 |
| `response.output_audio.delta` | 服务端 → 客户端 | Base64 音频增量，需按顺序播放/拼接 |
| `response.output_audio_transcript.done` | 服务端 → 客户端 | 音频回复的完整文字转写 |
| `response.done` | 服务端 → 客户端 | 本轮响应结束 |
| `error` | 双向 | 协议、参数或上游错误 |

Realtime 音频回复是增量事件，不要等待一个完整 MP3 文件再播放。客户端应缓存 `delta`，并依据 xAI Voice 文档的音频格式解码。

## Custom Voices：自定义声音

```text
POST https://api.clomio.ai/v1/custom-voices
Content-Type: multipart/form-data
```

自定义声音通常需要参考音频和声音名称/语言字段。字段形态由 xAI Voice 当前版本定义，网关会保留 multipart 内容并透传；请按 xAI 文档上传参考音频，成功后保存返回的 `voice_id`，再将它用于 `/v1/tts` 或 Realtime 会话。

## 错误与排查

| 状态 | 常见原因 | 处理 |
|---:|---|---|
| `400` | JSON、multipart 或 WebSocket Upgrade 格式错误 | 检查 `Content-Type`、`file` 字段和 Upgrade 头 |
| `401` | Key 无效或已过期 | 使用 Grok 分组 Key，并确认使用 `Authorization` 头 |
| `404` | Key 不属于 Grok 分组，或路径缺少 `/v1` | 检查分组和最终 URL |
| `503` `No available Grok accounts` | 当前暂时没有可用的 Grok Voice 服务资源 | 稍后重试；持续出现时提交 `x-request-id` |
| `502` / 上游 4xx/5xx | xAI Voice 参数、凭证、代理或上游状态异常 | 保留完整错误体和 `x-request-id` 排查 |

每次请求都应记录响应头里的 `x-request-id`。不要在工单或日志中提交完整 API Key。

## 参数与格式边界

| 接口 | 必填 | 可选值/范围 | 成功内容 |
|---|---|---|---|
| `/v1/tts` | `text`、`voice_id` | `language` 使用 BCP-47/常见语言代码；`voice_id` 为 `eve`、`ara` 或已创建的自定义 ID | 音频二进制 |
| `/v1/stt` | multipart `file` | `model` 可填 `grok-voice-latest`；文件格式由 xAI Voice 支持列表决定 | `text`、`language`、`duration` |
| `/v1/realtime` | 有效 WS Upgrade | `model` 默认 `grok-voice-latest`；事件字段按 Realtime 协议 | 事件流和 Base64 音频增量 |
| `/v1/custom-voices` | 参考音频/声音信息 | 参考音频最长时长、编码和名称限制以 xAI Voice 当前版本为准 | `voice_id` 等 JSON |

不要把 `/v1/tts` 的音频响应当 JSON；不要把 Realtime 的 `delta` 当完整音频文件；不要把 Realtime 的音频时长再次按 `/v1/stt` 计费。
