模型能力

Text to Speech

只需一次 API 调用,即可将文本转换为语音。该 API 支持丰富且富有表现力的 voice、用于精细控制表达方式的 inline speech tag,以及从高保真 MP3 到针对电话优化的 μ-law 等多种输出格式。

快速入门

通过一次 API 调用生成语音:

curl -X POST https://api.x.ai/v1/tts \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Hello! Welcome to the xAI Text to Speech API.",
    "voice_id": "eve",
    "language": "en"
  }' \
  --output hello.mp3

Response body 包含 raw audio byte。可直接保存到文件,或通过 pipe 传给音频播放器。

Request Body

参数类型必需说明
textstring要转换为语音的文本。最多 15,000 个字符。支持 speech tag
voice_idstring用于合成的 voice。默认为 eve。请参阅 Voices
languagestringBCP-47 language code(例如 enzhpt-BR),或使用 auto 自动检测语言。请参阅 支持的语言
output_formatobject输出格式配置。默认为 24 kHz / 128 kbps 的 MP3。请参阅 输出格式
speednumber语速倍数。1.0 为正常语速。低于 1.0 会减慢语速,高于 1.0 会加快语速。范围:0.71.5。默认为 1.0
optimize_streaming_latencyintegerStreaming synthesis 的延迟优化级别。0(默认):不优化,音频质量最佳。1:减小首个 chunk,以降低 time-to-first-audio;chunk 边界处的质量会略有下降。2:进一步减小首个 chunk,以获得最低 time-to-first-audio;chunk 边界处的质量下降会更明显。
text_normalizationboolean在合成前启用 text normalization。设为 true 时,model 会在生成音频前将书面形式的文本(例如数字、缩写和符号)标准化为口语形式。默认为 false
with_timestampsboolean随音频返回 character-level timing metadata。设为 true 时,response 为 JSON envelope,其中包含 base64 编码的音频以及每个字符的开始/结束时间。合成后的 alignment pass 会增加延迟。默认为 false。请参阅 Character-level timestamp

包含全部选项的示例

JSON

{
  "text": "Hello! This is a high-fidelity text to speech example.",
  "voice_id": "ara",
  "language": "en",
  "output_format": {
    "codec": "mp3",
    "sample_rate": 44100,
    "bit_rate": 192000
  },
  "speed": 1.2
}

Voices

每种 voice 都有鲜明的个性。试听 sample,并选择最适合你使用场景的一种(eve 为默认值):

Voice音色与用例Sample
carina柔和、富有同理心且令人安心WellnessSupport
zagan有力、戏剧化且极具辨识度CharactersNarration
helix大胆、动感且充满冲劲CommentaryPodcast
orion浑厚、电影感且富有共鸣NarrationAudiobooks
luna温和、耐心且极具关怀感EducationAssistant
iris友好、积极且自然迷人SalesSupport
altair优雅、精致且具有高级感AdvertisingNarration
zenith敏锐、专注且充满驱动力SalesAdvertising
perseus坚定、自信且值得信赖AdvertisingNarration
helios活泼、充满能量且用途广泛AssistantWellness
lux沉稳、平静且含蓄睿智WellnessNarration
kepler创新、前瞻且富有魅力AdvertisingPodcast
rigel精准、专业且从容自信AssistantSupport
cosmo明快、好奇且易于理解EducationPodcast
celeste富有同理心、自信且令人安心SupportAssistant
ursa友好、温暖且坚定可靠AssistantPodcast
sirius机敏、聪慧且俏皮CommentaryCharacters
lumen温暖、表达清晰且富有感染力EducationAdvertising
castor富有魅力、亲切且随和SalesSupport
naksh温暖、周到且睿智AssistantSupport
atlas自信、有掌控力且令人安心SalesAssistant
ara温暖友好
eve充满活力且积极
leo权威有力
rex自信清晰
sal流畅均衡

Voice ID 不区分大小写eveEveEVE 均可使用。在 Playground 中试听所有 voice →

Custom voice

使用 Custom Voices API 从简短的参考 clip 克隆任意 voice,或在 Console 中免费创建 voice。要在 Console 中找到 custom voice ID,请点击 voice card 上的三点菜单,然后选择 Copy Voice ID。然后将其作为 voice_id

# Replace YOUR_VOICE_ID with your custom voice ID from the console
# or the GET /v1/custom-voices endpoint.
curl -X POST https://api.x.ai/v1/tts \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Hello! This is my custom voice.",
    "voice_id": "YOUR_VOICE_ID",
    "language": "en"
  }' \
  --output hello.mp3

你也可以通过 Text to speech - List voices endpoint 以编程方式列出 voice:

curl -s https://api.x.ai/v1/tts/voices \
  -H "Authorization: Bearer $XAI_API_KEY"

支持的语言

TTS API 通过 BCP-47 language code 支持 20 种语言。使用 auto 自动检测语言,或明确指定 language code 以获得一致结果。

Language code 验证不区分大小写enENEn 均可使用。

语言Language Code
自动检测auto
英语en
阿拉伯语(埃及)ar-EG
阿拉伯语(沙特阿拉伯)ar-SA
阿拉伯语(阿拉伯联合酋长国)ar-AE
孟加拉语bn
中文(简体)zh
法语fr
德语de
印地语hi
印度尼西亚语id
意大利语it
日语ja
韩语ko
葡萄牙语(巴西)pt-BR
葡萄牙语(葡萄牙)pt-PT
俄语ru
西班牙语(墨西哥)es-MX
西班牙语(西班牙)es-ES
土耳其语tr
越南语vi

该 model 还能够生成上述列表之外的其他语言,准确度因语言而异。

Speech Tag

示例: 我走进去,[pause] 它就在那里。[laugh] 我简直不敢相信!<whisper>原来它一直是个秘密。</whisper> 很酷,对吧?

在文本中添加 inline speech tag,以获得更富表现力的表达。Tag 分为两种:

  • Inline tag [tag],放在文本中的特定位置,用于产生声音表达(例如笑声或停顿)

  • Wrapping tag <tag>text</tag>,包裹一段文本以改变其表达方式(例如耳语或歌唱)

Inline Tag

将这些 tag 插入需要产生相应表达的位置。点击任意 tag 即可试听示例:

类别Tag
停顿
笑声与哭声
口腔音
呼吸

Wrapping Tag

包裹文本以改变表达风格。请使用 opening tag 和匹配的 closing tag。点击任意 tag 即可试听示例:

类别Tag
音量与强度
音高与速度
声音风格

示例

# Inline tags
curl -X POST https://api.x.ai/v1/tts \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "So I walked in and [pause] there it was. [laugh] I honestly could not believe it!",
    "voice_id": "eve",
    "language": "en"
  }' \
  --output expressive.mp3

# Wrapping tags
curl -X POST https://api.x.ai/v1/tts \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "I need to tell you something. <whisper>It is a secret.</whisper> Pretty cool, right?",
    "voice_id": "eve",
    "language": "en"
  }' \
  --output whisper.mp3

Speech tag 使用建议:

  • 将 inline tag 放在对话中自然出现相应表达的位置

  • 将 tag 与标点结合使用,"Really? [laugh] That's incredible!" 的效果比堆叠 tag 更自然

  • 使用 [pause][long-pause] 加入戏剧化的节奏,或让一句话有停顿沉淀的空间

  • Wrapping tag 包裹完整短语时效果最佳,<whisper>It is a secret.</whisper> 比只包裹单个词读起来更自然

  • 组合多种风格以获得所需效果,<slow><soft>Goodnight, sleep well.</soft></slow>

输出格式

使用 output_format object 控制 audio codec、sample rate 和 bit rate。省略时默认为 24 kHz / 128 kbps 的 MP3

Codec

CodecContent-Type最适合
mp3audio/mpeg通用场景,兼容性广、压缩效果好
wavaudio/wav无损音频,适用于编辑和后期制作
pcmaudio/pcmRaw audio,适用于实时处理 pipeline
mulawaudio/basic电话音频(G.711 μ-law)
alawaudio/alaw电话音频(G.711 A-law)

Sample Rate

采样率说明
8000Narrowband,适用于电话音频
16000Wideband,适用于语音识别
22050标准,质量均衡
24000高质量,默认值,推荐用于大多数场景
44100CD 质量,适用于媒体制作
48000专业级,适用于录音室级音频

Bit Rate(仅 MP3)

采样率质量
32000低,文件最小
64000中,适合语音
96000标准,效果均衡
128000高,默认值,推荐
192000最高,保真度最高

示例:高保真 MP3

JSON

{
  "text": "Crystal clear audio at maximum quality.",
  "voice_id": "rex",
  "language": "en",
  "output_format": {
    "codec": "mp3",
    "sample_rate": 44100,
    "bit_rate": 192000
  }
}

示例:电话音频(μ-law)

JSON

{
  "text": "Hello, thank you for calling. How can I help you today?",
  "voice_id": "ara",
  "language": "en",
  "output_format": {
    "codec": "mulaw",
    "sample_rate": 8000
  }
}

Character-level timestamp

设置 with_timestampstrue,即可接收每个字符的开始和结束 timestamp。非常适合字幕同步、卡拉 OK 高亮、实时口型同步以及其他需要时间对齐的应用。

此时 response 会从 raw audio byte 变为 JSON envelope(Content-Type: application/json),其中包含 base64 编码的音频和字符 timing。

请求 timestamp

在普通请求中加入该 flag。音频将包含在 JSON body 中返回,而不是以 raw byte 返回:

curl -X POST https://api.x.ai/v1/tts \
  -H "Authorization: Bearer $XAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Hello world.",
    "voice_id": "eve",
    "language": "en",
    "with_timestamps": true
  }' \
  --output response.json

Response 结构

JSON

{
  "audio": "<base64-encoded audio in the requested codec>",
  "content_type": "audio/mpeg",
  "duration": 0.92,
  "audio_timestamps": {
    "graph_chars": ["H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "."],
    "graph_times": [
      [0.00, 0.06],
      [0.06, 0.12],
      [0.12, 0.18],
      [0.18, 0.24],
      [0.24, 0.34],
      [0.34, 0.40],
      [0.40, 0.48],
      [0.48, 0.54],
      [0.54, 0.62],
      [0.62, 0.68],
      [0.68, 0.78],
      [0.78, 0.92]
    ]
  }
}
字段类型说明
audiostring使用请求 codec 的 base64 编码音频。解码后可像普通 response 一样播放。
content_typestring解码后音频的 MIME type(例如 audio/mpegaudio/wav)。
durationnumber音频总时长,单位为秒。
audio_timestamps.graph_charsstring[]按顺序排列的每个输入字符,包括空格、标点和 speech tag。
audio_timestamps.graph_timesnumber[][][start, end] pair 组成的并行 array,单位为秒。

graph_charsgraph_times 按 index 对齐,两者逐项对应。因此 graph_chars[i] 是在 graph_times[i] 时间区间内读出的字符。对于 "Hello world."

Text

char:    H     e     l     l     o     ␣     w     o     r     l     d     .
start:  0.00  0.06  0.12  0.18  0.24  0.34  0.40  0.48  0.54  0.62  0.68  0.78
        └──────────── "Hello" ───────────┘     └──────────── "world." ──────────┘
0s ─────────────────────────────────────────────────────────────────────▶ 0.92s

特殊字符

graph_chars 与输入逐字符对应,包括空格、标点和 speech tag。当一个书写 token 被读成多个单词时,其 timing 会分配给第一个字符,其余字符则在同一时间范围内插值。

这种情况主要发生在启用 text_normalization 时,该功能会把符号和数字展开为单词。启用 normalization 后,$5 会被读作 "five dollars",但仍然只有两个字符:$ 会获得 "five dollars" 的完整时间范围,而 5 会获得该范围内的插值时间。因此应始终按顺序遍历 graph_chars,不要按 index 切分输入文本。

最佳实践

以下建议可帮助你从 TTS API 获得最高质量的输出。

编写有效文本

  • 使用自然的标点。逗号、句号和问号会引导节奏与语调。"Wait, really?" 听起来比 "Wait really"

  • 加入情绪语境。感叹号和问号会影响表达方式,"That's amazing!" 听起来热情,而 "That's amazing." 则平实直接。

  • 将长内容拆分成段落。段落分隔会产生自然停顿,并帮助 model 在较长文本中保持一致质量。

  • 将 unary request 控制在 15,000 个字符以内。对于更长的内容,请使用没有文本长度限制的 双向 WebSocket endpoint,或者按段落或句子拆分成逻辑片段,再拼接音频输出。

与 AI coding assistant 集成

Cloud Console Playground 提供了现成的 Agent instructions,可复制粘贴到 Cursor、GitHub Copilot 或 Windsurf 等工具中。这些 instructions 已预先配置当前 voice 和格式设置。打开 Playground、调整设置并复制 prompt,即可获得针对 coding agent 定制的集成指南。

针对生产环境进行优化

  • 在服务端代理请求。绝不要在 client-side code 中暴露 API key。请通过 backend 转发 TTS 请求。

  • 缓存生成的音频。如果同一文本被重复请求,请缓存 audio byte,以减少 API 调用并降低延迟。

  • 根据使用场景选择格式。请使用 mulawalaw 8 kHz 处理电话音频;使用 mp3 24 kHz 处理 Web 音频;使用 wav 44.1 kHz 或更高采样率进行后期制作。

  • 遵守 concurrent session 限制。Streaming WebSocket endpoint 最多允许 每个 team 50 个 concurrent session。对于高吞吐量服务,请使用连接池或对请求排队,以保持在该限制内。

浏览器播放

要在浏览器中播放 TTS 音频,请通过 backend 代理请求,并使用 Web Audio API 或 <audio> 元素:

JavaScript

// Client-side: fetch from your backend proxy, then play
async function speakText(text, voiceId = "eve") {
  const response = await fetch("/api/tts", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ text, voice_id: voiceId }),
  });

  if (!response.ok) throw new Error("TTS request failed");

  const blob = await response.blob();
  const url = URL.createObjectURL(blob);

  const audio = new Audio(url);
  audio.addEventListener("ended", () => URL.revokeObjectURL(url));
  await audio.play();
}

// Usage
await speakText("Hello from the browser!");

浏览器注意事项

Safari 在 blob URL 上返回 Infinity,作为 audio.duration 的值。其中 loadedmetadata event 会触发,但 audio.durationInfinity,导致 seek bar 和时间显示失效。请改用 AudioContext.decodeAudioData()

JavaScript

async function getAudioDuration(arrayBuffer) {
  const AudioCtx = window.AudioContext || window.webkitAudioContext;
  const ctx = new AudioCtx();
  // Clone the buffer - decodeAudioData detaches the original
  const decoded = await ctx.decodeAudioData(arrayBuffer.slice(0));
  const durationMs = Math.round(decoded.duration * 1000);
  await ctx.close();
  return durationMs;
}

AudioContext 在 Safari 中必须在 user gesture 期间创建。Safari 会永久挂起在 click/tap handler 外创建的 AudioContext,且无法恢复。Chrome 对此更宽松。请始终在按钮的 click handler 中创建或恢复 context,并在此之后再执行任何 await

JavaScript

// Create the AudioContext once, in a click handler
let audioCtx;
button.addEventListener("click", async () => {
  // This MUST happen synchronously in the click handler for Safari
  if (!audioCtx) audioCtx = new AudioContext();
  if (audioCtx.state === "suspended") await audioCtx.resume();

  // Now it's safe to fetch and play audio asynchronously
  const response = await fetch("/api/tts", { /* ... */ });
  const arrayBuffer = await response.arrayBuffer();
  const decoded = await audioCtx.decodeAudioData(arrayBuffer);
  const source = audioCtx.createBufferSource();
  source.buffer = decoded;
  source.connect(audioCtx.destination);
  source.start();
});

Raw codec(pcm、mulaw、alaw)无法在浏览器中播放。 AudioContext.decodeAudioData()<audio> 元素只支持 MP3 和 WAV 等 container 格式。浏览器播放请使用 mp3wav。如果在服务端处理 raw 格式(例如通过 pipe 传给电话系统),可根据 byte 数估算时长:

JavaScript

// PCM = 16-bit LE (2 bytes/sample), mulaw/alaw = 8-bit (1 byte/sample)
const bytesPerSample = codec === "pcm" ? 2 : 1;
const durationMs = Math.round((byteLength / bytesPerSample / sampleRate) * 1000);

撤销 blob URL 以避免内存泄漏。每次 URL.createObjectURL() 调用都会分配内存,直到明确释放才会回收。播放结束时请撤销 URL。对于下载,请延迟撤销,以便浏览器完成文件保存:

JavaScript

// Playback: revoke when done
const url = URL.createObjectURL(blob);
const audio = new Audio(url);
audio.addEventListener("ended", () => URL.revokeObjectURL(url));

// Downloads: delay revocation
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "speech.mp3";
a.click();
setTimeout(() => URL.revokeObjectURL(downloadUrl), 10_000);

错误处理

状态含义处理方式
200成功Response body 中的 audio byte
400错误请求检查:text 非空且少于 15,000 个字符;codec 和 sample rate 有效
401未授权API key 缺失或无效
404未找到未知的 voice_id,请通过 GET /v1/tts/voices(内置)或 GET /v1/custom-voices(custom)进行验证
429触发速率限制退避并按指数延迟重试
503服务不可用TTS 服务暂时不可用,请重试
500Server 错误使用 exponential backoff 重试

使用 backoff 重试

import os
import time
import requests

def generate_speech(text, language="en", voice_id="eve", max_retries=3):
    for attempt in range(max_retries):
        response = requests.post(
            "https://api.x.ai/v1/tts",
            headers={
                "Authorization": f"Bearer {os.environ['XAI_API_KEY']}",
                "Content-Type": "application/json",
            },
            json={"text": text, "language": language, "voice_id": voice_id},
        )
        if response.ok:
            return response.content
        if response.status_code in (429, 500, 503):
            wait = 2 ** attempt
            time.sleep(wait)
            continue
        response.raise_for_status()  # Non-retryable error
    raise RuntimeError("Max retries exceeded")

限制

Unary/server-streamed endpoint 与双向 WebSocket endpoint 的限制不同:

Unary 与 server-streamed(POST /v1/tts双向 WebSocket(wss://api.x.ai/v1/tts
最大文本长度每个请求 15,000 个字符无限制,但每条 text.delta message 最多 15,000 个字符
请求超时15 分钟无超时(连接保持打开)
Concurrent session每个 team 50 个

对于超过 15,000 个字符的内容,请使用 双向 WebSocket endpoint,该 endpoint 没有文本长度限制。

Streaming TTS(WebSocket)

要实时生成音频,请与 streaming TTS endpoint 建立 WebSocket 连接。文本以 delta 形式 streamed in,音频则以 base64 编码的 chunk streamed back;这非常适合希望在完整文本就绪前就开始播放音频的交互式应用。

Endpoint: wss://api.x.ai/v1/tts

连接

建立 WebSocket 连接,并通过 query parameter 配置语言、voice 和音频格式:

Text

GET /v1/tts?language=en&voice=eve&codec=mp3&sample_rate=24000&bit_rate=128000
Upgrade: websocket
Authorization: Bearer $XAI_API_KEY
参数必需默认值可接受的值
voice任意内置 voice ID(请参阅 Voices)或 custom voice ID
languageauto 或 BCP-47 code(例如 enzhpt-BR)。请参阅 支持的语言
codecmp3mp3wavpcmmulaw(或 ulaw)、alaw
sample_rate2400080001600022050240004410048000
bit_rate128000320006400096000128000192000(仅 MP3)
speed1.0语速倍数(0.71.5
optimize_streaming_latency00(关闭,质量最佳)、1(中等,降低 time-to-first-audio)、2(激进,time-to-first-audio 最低)
text_normalizationfalsetruefalse
with_timestampsfalsetruefalse。设为 true 时,每个 audio.delta 都会携带 audio_timestampsaudio_duration,用于描述该 chunk 中字符的 timing。

无效的 voicelanguagecodecsample_rate 会在 WebSocket upgrade 之前被拒绝,并返回 HTTP 400 或 404。

Client → Server Message

以 JSON text frame 将文本发送到 server。将文本拆分到多条 text.delta message 中,然后使用 text.done

JSON

{"type": "text.delta", "delta": "Here is some text. "}
{"type": "text.delta", "delta": "More text follows."}
{"type": "text.done"}
Event说明
text.delta要合成的文本 chunk。每条 delta 最多 15,000 个字符
text.done表示当前 utterance 结束。Server 将完成音频生成并发送 audio.done
text.clear取消当前 utterance。Server 会停止生成音频、丢弃所有 buffered data,并返回 audio.clear

Server → Client Message

Server 返回 base64 编码的 audio chunk 和 completion event:

JSON

{"type": "audio.delta", "delta": "<base64-encoded audio bytes>"}
{"type": "audio.done", "trace_id": "uuid"}
{"type": "audio.clear"}
{"type": "error", "message": "description"}
Event说明
audio.delta使用建立连接时所指定 codec 的 base64 编码 audio chunk。解码后加入播放队列。当连接以 with_timestamps=true 建立时,还会携带 audio_timestampsgraph_chars + graph_times)和 audio_duration,用于描述该 chunk 中的字符。请参阅 Character-level timestamp
audio.done当前 utterance 的全部音频均已发送。包含用于调试的 trace_id
audio.clear确认已响应 text.clear 取消当前 utterance。连接已准备好处理下一个 utterance。
error发生错误。message 字段包含便于阅读的说明。

Multi-Utterance Session

收到 audio.done 后连接会保持打开。你可以立即再发送一轮 text.deltatext.done message 来合成更多文本,无需重新连接。这适用于按顺序为每个 assistant response 生成音频的对话式 UI。

Multi-turn session 流程:

  1. Turn 1:Client 发送 text.deltatext.done

  2. Server 返回 audio.delta chunk → audio.done

  3. 连接保持打开

  4. Turn 2:Client 发送 text.deltatext.done

  5. Server 返回 audio.delta chunk → audio.done

  6. 按需重复

每条 text.done 都会 flush 累积的文本以生成音频。收到 audio.done 后,即可发送更多文本用于下一个 turn。每个 turn 的音频彼此独立,turn 1 的内容不会混入 turn 2。

import asyncio
import base64
import json
import os

import websockets  # pip install websockets

XAI_API_KEY = os.environ["XAI_API_KEY"]

async def multi_turn_tts(language: str = "en", voice: str = "eve", codec: str = "mp3"):
    uri = f"wss://api.x.ai/v1/tts?language={language}&voice={voice}&codec={codec}"

    async with websockets.connect(
        uri,
        additional_headers={"Authorization": f"Bearer {XAI_API_KEY}"},
    ) as ws:
        # Turn 1
        await ws.send(json.dumps({"type": "text.delta", "delta": "Hello from turn one."}))
        await ws.send(json.dumps({"type": "text.done"}))

        turn1_audio = bytearray()
        async for msg in ws:
            event = json.loads(msg)
            if event["type"] == "audio.delta":
                turn1_audio.extend(base64.b64decode(event["delta"]))
            elif event["type"] == "audio.done":
                print(f"Turn 1: {len(turn1_audio):,} bytes")
                break
            elif event["type"] == "error":
                raise RuntimeError(event["message"])

        # Connection is still open — send turn 2
        await ws.send(json.dumps({"type": "text.delta", "delta": "And hello from turn two."}))
        await ws.send(json.dumps({"type": "text.done"}))

        turn2_audio = bytearray()
        async for msg in ws:
            event = json.loads(msg)
            if event["type"] == "audio.delta":
                turn2_audio.extend(base64.b64decode(event["delta"]))
            elif event["type"] == "audio.done":
                print(f"Turn 2: {len(turn2_audio):,} bytes")
                break
            elif event["type"] == "error":
                raise RuntimeError(event["message"])

asyncio.run(multi_turn_tts())

取消(Barge-in)

发送 text.clear 可取消当前 utterance,并在同一连接上开始新的 utterance,无需重新连接。这可消除每次中断时的 WebSocket handshake 延迟(远距离 client 约为 600ms)。

Barge-in 流程:

  1. Client 发送 text.deltatext.done

  2. Server 开始 streaming audio.delta chunk

  3. 用户中断,client 发送 text.clear

  4. Server 返回 audio.clear

  5. Client 发送新的 text.deltatext.done

  6. Server streaming 新的 audio.delta chunk → audio.done

text.clear 可随时安全发送;如果没有正在进行的 utterance,server 会立即返回 audio.clear。收到 audio.clear 时,请清空本地 audio playback buffer,防止播放过期音频。

import asyncio
import base64
import json
import os

import websockets  # pip install websockets

XAI_API_KEY = os.environ["XAI_API_KEY"]

async def tts_with_barge_in(language: str = "en", voice: str = "eve", codec: str = "mp3"):
    uri = f"wss://api.x.ai/v1/tts?language={language}&voice={voice}&codec={codec}"

    async with websockets.connect(
        uri,
        additional_headers={"Authorization": f"Bearer {XAI_API_KEY}"},
    ) as ws:
        # Start first utterance
        await ws.send(json.dumps({"type": "text.delta", "delta": "The answer to your question is a long explanation..."}))
        await ws.send(json.dumps({"type": "text.done"}))

        # Wait for audio to start, then cancel
        event = json.loads(await ws.recv())
        print(f"Got {event['type']} — cancelling")
        await ws.send(json.dumps({"type": "text.clear"}))

        async for msg in ws:
            if json.loads(msg)["type"] == "audio.clear":
                break

        # New utterance on the same connection
        await ws.send(json.dumps({"type": "text.delta", "delta": "Actually, let me start over."}))
        await ws.send(json.dumps({"type": "text.done"}))

        audio = bytearray()
        async for msg in ws:
            event = json.loads(msg)
            if event["type"] == "audio.delta":
                audio.extend(base64.b64decode(event["delta"]))
            elif event["type"] == "audio.done":
                print(f"New utterance: {len(audio):,} bytes")
                break

asyncio.run(tts_with_barge_in())

限制与行为

属性
文本总长度无限制,可按需发送任意数量的 text.delta message
Delta 大小每条 text.delta message 最多 15,000 个字符
Concurrent session每个 team 50 个
Session permit TTL600 秒
Moderation在音频发送后,对累积文本异步执行(fail-open)
计费按 session 根据输入字符总数记录