实时音频(Realtime)
通过 WebSocket 或 WebRTC 与实时语音模型建立低延迟会话。适用于语音助手、边说边听交互。文本补全请使用 创建对话补全,不要对本页模型调用 /chat/completions。
支持模型示例:gpt-realtime-2.1、gpt-realtime-2.1-mini(以 获取模型 modality=audio 为准)。gemini-3.5-live-translate-preview 见 Gemini Live 同声传译。
Endpoint
| Method | URL | 说明 |
|---|---|---|
POST | {TRINITY_BASE_URL}/realtime/client_secrets | 签发浏览器用临时凭证(WebRTC) |
POST | {TRINITY_BASE_URL}/realtime/calls | WebRTC SDP 建连 |
POST | {TRINITY_BASE_URL}/realtime/usage | WebRTC 上报 response.done 结算 |
| WebSocket | {TRINITY_WS_BASE_URL}/realtime?model={model} | 服务端 WebSocket 代理 |
Base URL
| 项 | 值 |
|---|---|
| Base URL | https://api.trinitydesk.ai/v1 |
| WebSocket | wss://api.trinitydesk.ai/v1 |
| 协议 | HTTPS / WSS |
bash
export TRINITY_BASE_URL="https://api.trinitydesk.ai/v1"
export TRINITY_WS_BASE_URL="wss://api.trinitydesk.ai/v1"
export TRINITY_API_KEY="xh-..."Headers
| Header | 必填 | 说明 |
|---|---|---|
Authorization | 是 | Bearer <TRINITY_API_KEY> |
Content-Type | HTTP 时 | application/json |
WebSocket 也可在查询参数中携带 api_key=<TRINITY_API_KEY>(与 Header 二选一即可)。
请求示例
签发 client_secrets
bash
curl -sS "${TRINITY_BASE_URL}/realtime/client_secrets" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TRINITY_API_KEY}" \
-d '{
"model": "gpt-realtime-2.1-mini",
"session": {
"type": "realtime",
"output_modalities": ["text"]
}
}'WebSocket(文本一轮)
连接:
text
${TRINITY_WS_BASE_URL}/realtime?model=gpt-realtime-2.1-mini&api_key=${TRINITY_API_KEY}连接成功后可发送:
json
{
"type": "conversation.item.create",
"item": {
"type": "message",
"role": "user",
"content": [{ "type": "input_text", "text": "Say hello in one short sentence." }]
}
}json
{ "type": "response.create" }服务端会转发 Realtime 事件流;计费以 response.done 中的 usage 为准。
请求体字段(client_secrets)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | string | 是 | 音频模型 ID |
session | object | 否 | 会话配置(如 type、output_modalities、instructions) |
返回示例(client_secrets)
json
{
"value": "ek_...",
"expires_at": 1710000000,
"session": {
"type": "realtime",
"model": "gpt-realtime-2.1-mini"
}
}字段名以上游返回为准;平台会保证会话侧 model 与选路部署一致。
上报 usage(WebRTC)
浏览器 WebRTC 会话无法被服务端代理主动掐断,需由客户端在收到 response.done 后调用本接口上报 usage,用于按实际用量结算。每个 response 仅上报一次即可,重复上报同一 response 不会重复扣费。
上报原始 response.done 事件,或 response 对象,或仅含 usage 的对象均可。
bash
curl -sS "${TRINITY_BASE_URL}/realtime/usage" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${TRINITY_API_KEY}" \
-d '{
"model": "gpt-realtime-2.1-mini",
"event": {
"type": "response.done",
"response": {
"id": "resp_xxx",
"usage": {
"input_tokens": 120,
"output_tokens": 80,
"total_tokens": 200,
"input_token_details": {
"text_tokens": 40,
"audio_tokens": 80,
"cached_tokens": 0
},
"output_token_details": {
"text_tokens": 30,
"audio_tokens": 50
}
}
}
}
}'请求体字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model | string | 是 | 音频模型 ID(也可放 session.model) |
event | object | 条件 | response.done 事件;与直接平铺 response / usage 二选一 |
response | object | 条件 | Realtime 响应对象,含 id 与 usage |
usage | object | 条件 | 用量对象(当请求体只含 usage 时) |
计费以 response.id 为幂等依据:相同 response.id 重复上报不重复扣费。
返回字段
| 字段 | 类型 | 说明 |
|---|---|---|
settled | boolean | 是否已结算 |
response_id | string | 上报的响应 ID |
prompt_tokens | integer | 折算后的输入 token 数 |
completion_tokens | integer | 折算后的输出 token 数 |
total_tokens | integer | 折算后的总 token 数 |
audio_tokens | integer | 其中音频 token 数 |
usage | object | 折算后的 usage(含原始 realtime_usage) |
返回示例
json
{
"settled": true,
"response_id": "resp_xxx",
"prompt_tokens": 120,
"completion_tokens": 80,
"total_tokens": 200,
"audio_tokens": 130,
"usage": {
"prompt_tokens": 120,
"completion_tokens": 80,
"total_tokens": 200,
"prompt_tokens_details": {
"text_tokens": 40,
"audio_tokens": 80
},
"completion_tokens_details": {
"text_tokens": 30,
"audio_tokens": 50
},
"realtime_usage": {
"input_tokens": 120,
"output_tokens": 80,
"total_tokens": 200
}
}
}Python 示例
python
import json
import os
import urllib.request
from urllib.parse import quote
import websocket # pip install websocket-client
BASE = os.environ.get("TRINITY_BASE_URL", "https://api.trinitydesk.ai/v1")
KEY = os.environ["TRINITY_API_KEY"]
MODEL = "gpt-realtime-2.1-mini"
body = json.dumps({
"model": MODEL,
"session": {"type": "realtime", "output_modalities": ["text"]},
}).encode()
req = urllib.request.Request(
f"{BASE}/realtime/client_secrets",
data=body,
headers={
"Authorization": f"Bearer {KEY}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=30) as resp:
print("client_secrets", resp.status, resp.read()[:200])
ws_url = (
BASE.replace("https://", "wss://").replace("http://", "ws://")
+ f"/realtime?model={quote(MODEL)}&api_key={quote(KEY)}"
)
ws = websocket.create_connection(ws_url, timeout=60)
print(ws.recv()[:200])
ws.send(json.dumps({
"type": "conversation.item.create",
"item": {
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "Hi"}],
},
}))
ws.send(json.dumps({"type": "response.create"}))
for _ in range(20):
raw = ws.recv()
ev = json.loads(raw)
print(ev.get("type"))
if ev.get("type") == "response.done":
break
ws.close()