# Breeze TTS Agent 接入指南

Base URL: https://tts.heang.top
POST /v1/audio/speech
GET /health

所有请求需携带 CF-Access-Client-Id 和 CF-Access-Client-Secret，密钥由服务提供方另行提供。设置 User-Agent: BreezeAgent/1.0。密钥保存在环境变量或 Secret 管理器中，禁止写入公开前端。

请求为 UTF-8 JSON，字段 input 必填，instructions 描述音色，response_format 建议 pcm，cfg_scale 建议 1，max_new_tokens 可设 500。不要传入尚未注册的 voice 名称。

PCM: 24000 Hz，mono，signed 16-bit little-endian，无文件头，每秒48000字节。持续读取并交给播放队列；缓存未对齐到2字节的尾部。不要等待整个响应结束才播放。wav、mp3 亦可用。

并发上限1；429时等待约5秒并加入抖动后重试。403检查认证及User-Agent；404检查路径；413请求超过1MiB；5xx检查健康接口后退避重试。先验证状态与Content-Type再解码音频，认证错误可能返回HTML。

仅提供流式TTS，不包括全双工、识别、Live2D、WebSocket或音色管理。服务依赖提供方设备在线。模型与输出受上游研究与非商业许可约束。

## cURL（Bash）
```bash
export TTS_CLIENT_ID="由服务提供方单独提供"
export TTS_CLIENT_SECRET="由服务提供方单独提供"

curl --fail-with-body --no-buffer https://tts.heang.top/v1/audio/speech \
  -H "CF-Access-Client-Id: $TTS_CLIENT_ID" \
  -H "CF-Access-Client-Secret: $TTS_CLIENT_SECRET" \
  -H "User-Agent: BreezeAgent/1.0" \
  -H "Content-Type: application/json" \
  -d '{"input":"你好，欢迎来到我的直播间。","instructions":"温柔自然的成年女性，用普通话亲切地说话。","response_format":"pcm","cfg_scale":1,"max_new_tokens":500}' \
  --output speech.pcm

# 可选：使用 ffplay 边接收边播放时，将 --output speech.pcm 替换为：
# --output - | ffplay -nodisp -autoexit -f s16le -ar 24000 -ac 1 -i -
```

## Python
```python
# Python 3.10+，仅使用标准库。密钥先设置到环境变量。
import json, os, urllib.request, wave

request = urllib.request.Request(
    "https://tts.heang.top/v1/audio/speech",
    data=json.dumps({
        "input": "你好，这是流式语音合成测试。",
        "instructions": "自然、清晰、温暖的成年女声。",
        "response_format": "pcm",
        "cfg_scale": 1,
        "max_new_tokens": 500,
    }).encode("utf-8"),
    headers={
        "Content-Type": "application/json",
        "User-Agent": "BreezeAgent/1.0",
        "CF-Access-Client-Id": os.environ["TTS_CLIENT_ID"],
        "CF-Access-Client-Secret": os.environ["TTS_CLIENT_SECRET"],
    },
)
with urllib.request.urlopen(request, timeout=180) as response:
    if "audio" not in response.headers.get("Content-Type", ""):
        raise RuntimeError("服务未返回音频，请检查认证和响应内容")
    with wave.open("speech.wav", "wb") as audio:
        audio.setnchannels(1)
        audio.setsampwidth(2)
        audio.setframerate(24000)
        while chunk := response.read1(4096):
            # 实时播放：将 chunk 交给 PCM 播放队列，勿等待整个请求结束。
            audio.writeframesraw(chunk)
# wave 自动写入正确的文件长度；不要把 .pcm 直接改名为 .wav。
```

## Node.js
```javascript
// Node.js 22+；保存为 speech.mjs 后执行 node speech.mjs。
import { createWriteStream } from "node:fs";
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";

const response = await fetch("https://tts.heang.top/v1/audio/speech", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "User-Agent": "BreezeAgent/1.0",
    "CF-Access-Client-Id": process.env.TTS_CLIENT_ID,
    "CF-Access-Client-Secret": process.env.TTS_CLIENT_SECRET,
  },
  body: JSON.stringify({
    input: "你好，我正在实时生成语音。",
    instructions: "温暖、自然的成年男声。",
    response_format: "pcm", cfg_scale: 1, max_new_tokens: 500,
  }),
  signal: AbortSignal.timeout(180000),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
if (!response.headers.get("content-type")?.includes("audio")) {
  throw new Error("Expected audio response");
}
await pipeline(Readable.fromWeb(response.body), createWriteStream("speech.pcm"));
// 实时应用可逐块读取 response.body，并交给播放器。
```
