Text-to-Speech
以 OpenAI Audio Speech 規格呼叫 OpenAI、Gemini、xAI 與 Azure 的文字轉語音
Hakila 提供 OpenAI 相容的 POST /v1/audio/speech,把文字合成語音。OpenAI 的 TTS 模型原樣轉發;
Gemini、xAI 與 Azure AI Speech 由 Gateway 翻譯成各自的原生 API,呼叫端只需要認識 OpenAI 一種格式。
回應本體就是音訊位元組(audio/mpeg、audio/wav …),可以直接寫檔或播放。
TTS 模型與文字生成模型在目錄中是不同類型:GET /v1/models 的每筆回傳帶 type,
tts 走 /v1/audio/speech;用錯端點會回 400 並提示正確端點。
支援的模型
種子目錄目前包含(單價以 Console「系統模型」為準):
| 模型 id | 供應商 | 輸入上限 | 計費 | 單價 |
|---|---|---|---|---|
openai/gpt-4o-mini-tts | OpenAI | 2,000 tokens | 字元 | ≈ $15 / 1M 字元(OpenAI 實際以 tokens 計,約 $0.015 / 分鐘) |
openai/tts-1 | OpenAI | 4,096 字元 | 字元 | $15 / 1M 字元 |
openai/tts-1-hd | OpenAI | 4,096 字元 | 字元 | $30 / 1M 字元 |
google/gemini-3.1-flash-tts-preview | Gemini | 8,192 tokens | tokens | $1 / 1M 文字輸入 tokens、$20 / 1M 音訊輸出 tokens |
google/gemini-2.5-flash-preview-tts | Gemini | 8,192 tokens | tokens | $0.50 / $10 |
google/gemini-2.5-pro-preview-tts | Gemini | 8,192 tokens | tokens | $1 / $20 |
x-ai/tts | xAI | 60,000 字元 | 字元 | $15 / 1M 字元 |
azure/neural-tts | Azure AI Speech | 20,000 字元 | 字元 | $16 / 1M 字元(標準 Neural 語音) |
azure/neural-hd-tts | Azure AI Speech | 20,000 字元 | 字元 | $22 / 1M 字元(Dragon/DragonHD 語音) |
azure/mai-voice-2 | Azure AI Speech | 20,000 字元 | 字元 | $22 / 1M 字元(MAI-Voice-2/2.1 語音) |
azure/mai-voice-2-flash | Azure AI Speech | 20,000 字元 | 字元 | $15 / 1M 字元(MAI-Voice-2/2.1 Flash 語音) |
TTS 模型的 context_length 代表單次輸入的上限(參考值)。平台可在 Console 新增其他 TTS 模型,
包含後台新增的 OpenAI 相容供應商(例如支援 /audio/speech 的自架服務);
Anthropic 沒有 TTS API,無法設為 tts 類型。
快速開始
官方 OpenAI SDK 只要把 base_url 指到 Hakila、api_key 換成 Hakila API Key 就能使用。
裸模型名稱(tts-1)會在已啟用的 tts 模型中比對名稱並補上開發商前綴;有多個同名模型時回 400,找不到時回 404(見不帶前綴的模型名稱)。
curl https://api.hakila.ai/v1/audio/speech \
-H "Authorization: Bearer $HAKILA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o-mini-tts",
"input": "今天的天氣真好,適合出門走走。",
"voice": "coral",
"instructions": "用溫暖、友善的語氣說話。",
"response_format": "mp3"
}' \
--output speech.mp3成功時回 200,本體為音訊;Content-Type 依 response_format(audio/mpeg、audio/wav、audio/pcm、audio/opus、audio/ogg …)。
請求欄位
| 欄位 | OpenAI | Gemini | xAI | Azure | 說明 |
|---|---|---|---|---|---|
model | 必填 | 必填 | 必填 | 必填 | 目錄中 type: tts 的 id;不支援 hakila/auto 與 Preset |
input | ≤ 4,096 字元 | 建議 ≤ 8k tokens | ≤ 60,000 字元 | ≤ 20,000 字元;可直接傳 <speak> 開頭的 SSML | Hakila 統一擋 100,000 字元以上 |
voice | alloy、ash、coral、nova…(預設 alloy) | Kore、Puck、Zephyr…(預設 Kore) | eve、ara、rex、sal、leo(預設 eve) | 完整語音名稱,例如 zh-TW-HsiaoChenNeural;須屬於模型的語音家族(預設依模型,見下方) | |
instructions | gpt-4o-mini-tts 支援 | 併入提示({instructions}\n\nTRANSCRIPT:\n{input}) | 忽略(用文字內的 speech tags) | 忽略 | 語氣/情緒指示 |
response_format | mp3(預設)/opus/aac/flac/wav/pcm | wav(預設)/pcm | mp3(預設)/wav/pcm/mulaw/alaw | mp3(預設)/wav/pcm/opus | 不支援的格式回 400 |
speed | 0.25–4.0 | 忽略 | 轉發 | 轉成 SSML prosody rate | 語速 |
stream_format | audio(預設)/sse | 只接受 audio | 只接受 audio | 只接受 audio | sse 只有 OpenAI 支援 |
language | 忽略 | languageCode(例如 zh-TW) | language(預設 auto) | SSML xml:lang(預設由語音名稱推得) | Hakila 延伸欄位 |
sample_rate | 忽略 | 忽略 | output_format.sample_rate | 忽略 | Hakila 延伸欄位 |
speakers | 忽略 | 多人對話 [{ "speaker", "voice" }] | 忽略 | 忽略 | Hakila 延伸欄位,見下方 |
style | 忽略 | 忽略 | 忽略 | SSML mstts:express-as style,例如 cheerful、happy | Hakila 延伸欄位,可用值依語音而定 |
style_degree | 忽略 | 忽略 | 忽略 | styledegree,0.01–2(預設 1) | Hakila 延伸欄位,需同時帶 style |
role | 忽略 | 忽略 | 忽略 | role,例如 Girl、YoungAdultFemale | Hakila 延伸欄位,少數語音支援 |
其他欄位對 OpenAI 相容上游原樣透傳,對其他供應商會被忽略。
Gemini 多人對話
Gemini 支援最多兩位說話者,在 input 裡以名稱標出對白,並用 speakers 指定每位的語音:
curl https://api.hakila.ai/v1/audio/speech \
-H "Authorization: Bearer $HAKILA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "google/gemini-2.5-flash-preview-tts",
"input": "Joe: 嗨,今天過得如何?\nJane: 很好啊,剛跑完步。",
"speakers": [
{ "speaker": "Joe", "voice": "Kore" },
{ "speaker": "Jane", "voice": "Puck" }
],
"response_format": "wav"
}' --output dialogue.wavGemini TTS 只回 24kHz 16-bit mono PCM,Hakila 在 response_format: wav 時補上 WAV 檔頭;
要 mp3/opus 請改用其他供應商,或自行轉碼。
Gemini 偶發沒有音訊
google/gemini-2.5-flash-preview-tts 對很短、或讀起來像指令的文字(例如「要朗讀的文字」),偶爾會回 200
卻沒有音訊、finishReason 為 OTHER。Hakila 遇到這種回應會原樣自動重送,最多 2 次,呼叫端不需要另外處理;
重送的請求不計費,總延遲會包含重送時間。三次都失敗才回 502。
改寫提示詞(例如加上「Say:」)無法穩定避免這種情況。較完整的句子幾乎不會遇到;
新專案也可以改用 3.x 系列的 Gemini TTS 模型。被安全政策擋下(blockReason)的回應不會重送。
Azure 語音、風格與 SSML
Azure 的 voice 必須是完整語音名稱,可在 Hakila Models 依語言挑選,
或到 Azure 的語音庫查詢。四個 Azure 模型共用同一個端點,
差別在語音家族與單價,每個模型只接受自己家族的語音,帶到其他家族會回 400 並指出該用哪個模型:
| 模型 | 語音家族 | 語音名稱範例 | 未帶 voice 時的預設 |
|---|---|---|---|
azure/neural-tts | 標準 Neural | zh-TW-HsiaoChenNeural、ja-JP-NanamiNeural | en-US-AvaMultilingualNeural |
azure/neural-hd-tts | Dragon/DragonHD | zh-CN-Xiaomo:DragonHDFlashLatestNeural、en-US-Ava:DragonLatestNeural | en-US-Tiana:DragonHDFlashLatestNeural |
azure/mai-voice-2 | MAI-Voice-2/2.1 | zh-CN-Bo:MAI-Voice-2、en-US-Harper:MAI-Voice-2.1 | en-US-Harper:MAI-Voice-2 |
azure/mai-voice-2-flash | MAI-Voice-2/2.1 Flash | zh-CN-Bo:MAI-Voice-2-Flash、en-US-Harper:MAI-Voice-2.1-Flash | en-US-Harper:MAI-Voice-2-Flash |
台灣中文(zh-TW)目前只有標準 Neural 語音;MAI-Voice 涵蓋英文、簡體中文、韓文、法文、德文等二十多種語言。
許多語音支援說話風格,以 style(可加 style_degree)與 role 指定,Hakila 會轉成 SSML 的 mstts:express-as:
curl https://api.hakila.ai/v1/audio/speech \
-H "Authorization: Bearer $HAKILA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "azure/mai-voice-2",
"input": "太好了!我們終於完成這個專案了。",
"voice": "zh-CN-Bo:MAI-Voice-2",
"style": "happy",
"style_degree": 1.5
}' --output happy.mp3可用的風格依語音而定(Hakila Models 的「說話風格」選單只列該語音支援的值)。傳統 Neural 語音遇到不支援的風格會改用預設語氣;
MAI-Voice 語音則會直接回 400。style_degree 目前只對簡體中文語音與 MAI-Voice 有效。
input 若以 <speak 開頭會視為完整 SSML 原樣送出,voice/speed/language/style 不再套用;
SSML 裡每個 <voice name> 仍須屬於模型的語音家族。
路由與允許清單
model必須是這把 API Key 在GET /v1/models中看得到、且type為tts的 id; 不在目錄、未啟用、未被組織或 API Key 允許,一律回400。API Key 若是自訂允許清單,之後新上線的 TTS 模型要另外勾選(見允許清單)- 把
chat/embedding模型送進/v1/audio/speech回400並指向正確端點;反過來把tts模型送進 Chat/Embeddings 也一樣 hakila/auto不支援 TTS(回400),候選也只會落在chat模型- 金鑰解析順序與 Chat 相同:BYOK → 平台金鑰;沒有金鑰回
502 - 上游位址:OpenAI 相容上游
{BaseUrl}/audio/speech;Gemini 原生models/{model}:generateContent(responseModalities: ["AUDIO"],金鑰以x-goog-api-key帶入); xAI{BaseUrl}/tts;Azurehttps://{region}.tts.speech.microsoft.com/cognitiveservices/v1(金鑰以Ocp-Apim-Subscription-Key帶入)
計費
- OpenAI/xAI/Azure:上游不回 usage,Hakila 以
input的字元數(Unicode code point)記在prompt_tokens, 費用 = 字元數 × 模型輸入單價(單價表的「輸入」欄即 USD / 1M 字元);沒有輸出與 Prompt Cache 單價 - Gemini:使用 API 回傳的真實 usage,
prompt_tokens為文字輸入 tokens、completion_tokens為音訊輸出 tokens, 分別乘以模型的輸入/輸出單價 - 上游回錯誤、或 Gemini 沒有回音訊(安全政策擋下、模型回文字、
finishReason: OTHER)時不計費;OTHER自動重送後成功的請求,只依最後成功那次的 usage 計費 - 不支援 Response Cache(音訊不進快取);預算與組織儲值的
402檢查與 Chat 相同 - 用量紀錄的 response body 只保留
Content-Type、位元組數與計費依據,不保存音訊
錯誤
| 狀態 | 情境 |
|---|---|
400 | 缺少 model/input、speed/sample_rate/style/style_degree/role 不合法、模型不是 tts 類型、不在允許清單、供應商沒有 TTS API、供應商不支援該 response_format/stream_format、Azure 模型與語音家族不符、MAI-Voice 語音不支援指定的風格 |
401 | API Key 缺少、無效或過期 |
402 | 儲值用盡或預算超標 |
502 | 供應商金鑰缺失、上游失敗,或 Gemini 沒有回傳音訊(finishReason: OTHER 會先自動重送 2 次) |
504 | 上游在首字元逾時(預設 30 秒)內沒有回應 |
錯誤為 JSON,形狀與 Chat Completions 相同(error.message/error.code);OpenAI/xAI 的 4xx/5xx 原樣轉出,
Gemini 與 Azure 的錯誤會轉成同一形狀。完整欄位請見 API Reference。