跳至主要內容

Deepgram 即時(/listen)WebSocket 透傳

透過 LiteLLM proxy 串流即時音訊到 Deepgram 的即時語音轉文字 API(wss://api.deepgram.com/v1/listen)。proxy 會用 LiteLLM 虛擬金鑰驗證呼叫端,在伺服器端注入 Deepgram 憑證,雙向原樣轉送音訊與轉錄框架,並在 socket 關閉時將該工作階段的音訊時長記錄為支出

功能支援備註
成本追蹤✅在 socket 關閉時依據 Metadata.duration 框架計費,乘以通道數,使用 deepgram/streaming/<model> 每秒價格
記錄✅可跨所有整合運作,每個 WebSocket 工作階段一筆 SpendLogs 資料列
串流✅Deepgram 傳送的中繼與最終 Results 框架都會被轉送
防護欄❌音訊框架是透明位元組;不會對其執行請求或回應防護欄

端點​

ws://<proxy>/deepgram/v1/listen 及其別名 ws://<proxy>/deepgram/listen。兩者都會轉送至 <DEEPGRAM_API_BASE>/listen(預設 wss://api.deepgram.com/v1/listen)

設定​

請將 Deepgram 憑證設定在兩個位置之一。proxy 會先檢查已設定的透傳部署,然後再回退到 DEEPGRAM_API_KEY 環境變數

export DEEPGRAM_API_KEY="your-deepgram-key"
export LITELLM_MASTER_KEY="${LITELLM_MASTER_KEY:-sk-$(openssl rand -hex 16)}"
litellm --config config.yaml --port 4000

DEEPGRAM_API_BASE(選用)會覆寫上游 base URL,例如自架或區域性的 Deepgram 部署。https:// 與 http:// 的 base 會轉換為 wss:// 和 ws://。base URL 只會從伺服器端讀取;客戶端無法選擇 proxy 的 Deepgram 金鑰要送往的目的地

驗證​

WebSocket 握手接受與其他 WebSocket 路由相同的 LiteLLM 金鑰機制:Authorization: Bearer <litellm-key> 標頭、api-key: <litellm-key> 標頭,或供無法設定標頭的瀏覽器使用的 Sec-WebSocket-Protocol: openai-insecure-api-key.<litellm-key> 子協定。當金鑰無效時,握手會被拒絕;若 proxy 上未設定 Deepgram 憑證,socket 會以代碼 1011 關閉

查詢參數​

? 之後的所有內容都會原樣轉送給 Deepgram(encoding、sample_rate、channels、language、interim_results、smart_format、punctuate 等,詳見 Deepgram listen 參考文件)。如果 model 缺少或為空,proxy 會附加 model=nova-3

使用方式​

import asyncio
import json
import os

import websockets

PROXY_URL = "ws://localhost:4000/deepgram/v1/listen?model=nova-3&encoding=linear16&sample_rate=16000&interim_results=true"
LITELLM_KEY = os.environ["LITELLM_API_KEY"]


async def main() -> None:
async with websockets.connect(
PROXY_URL,
additional_headers={"Authorization": f"Bearer {LITELLM_KEY}"},
) as ws:

async def send_audio() -> None:
with open("audio.raw", "rb") as f: # 16 kHz, 16-bit, mono PCM
while chunk := f.read(8000):
await ws.send(chunk)
await asyncio.sleep(0.25)
await ws.send(json.dumps({"type": "CloseStream"}))

async def read_frames() -> None:
async for frame in ws:
data = json.loads(frame)
if data.get("type") == "Results":
alt = data["channel"]["alternatives"][0]
label = "final" if data.get("is_final") else "interim"
print(f"[{label}] {alt['transcript']}")
elif data.get("type") == "Metadata":
print(f"[metadata] duration={data['duration']}s")

await asyncio.gather(send_audio(), read_frames())


asyncio.run(main())

會與不會轉送的內容​

從客戶端到 Deepgram:二進位框架(音訊)會以二進位形式送出,文字框架(KeepAlive、Finalize、CloseStream 控制訊息)會以文字形式送出,查詢字串則維持原樣。呼叫端的 Authorization、api-key 與其他請求標頭不會被轉送;proxy 只會將 Authorization: Token <DEEPGRAM_API_KEY> 傳送至上游

從 Deepgram 到客戶端:每個框架都會依接收順序逐位元組原樣轉送,包括中繼 Results、最終 Results、UtteranceEnd、SpeechStarted,以及關閉的 Metadata 框架。proxy 不會重塑、合併或過濾轉錄。當 Deepgram 以非正常代碼關閉連線時(例如 1008,表示請求無效),該代碼與原因都會傳遞給客戶端

成本追蹤​

當 socket 關閉時,proxy 會讀取最後一個 Metadata 框架的 duration(Deepgram 已處理的音訊秒數),並將其乘以 model_prices_and_context_window.json 中 deepgram/streaming/<model> 項目的 input_cost_per_second(language=multi 時為 deepgram/streaming/<model>-multilingual)。預錄的 deepgram/<model> 項目絕不會被用作替代。如果 Deepgram 從未傳送 Metadata(例如客戶端中斷連線),proxy 會回退到在 Results 框架中看到的最遠 start + duration。Deepgram 會按其處理的每個通道計費,因此一個 multichannel=true&channels=2 的立體聲工作階段,費用是其實際時長的兩倍。proxy 會以 Metadata.channels 中的通道數乘上時長,若無則回退到在 Results 框架中看到的最寬 channel_index,再無則回退到 channels 查詢參數,最後預設為一。支出會以 call_type: pass_through_endpoint 寫入 SpendLogs,並像任何其他路由一樣歸屬於呼叫端金鑰、團隊與使用者

curl -s "http://localhost:4000/spend/logs?api_key=$LITELLM_API_KEY" -H "Authorization: Bearer $LITELLM_MASTER_KEY"

已知限制​

費用會在 socket 關閉時一次性計費,因此仍開啟中的工作階段尚未有支出資料列,且配額僅在連線時檢查一次。音訊框架不會被檢查,因此請求與回應防護欄不適用於此路由。中繼結果會原樣轉送;伺服器端不會對中繼與最終轉錄做去重。Deepgram 的 /listen 不會回報 token 數,因此 SpendLogs 會將音訊時長顯示為使用量指標,而 prompt_tokens/completion_tokens 會維持為零。若成本對應表中沒有某個模型的精確 deepgram/streaming/<model> 項目,該模型會在任何內容轉送之前被拒絕:socket 會以代碼 1008 關閉,原因為 No streaming price for 'deepgram/streaming/<model>': add it to the model cost map to enable it,因此請先新增該項目再使用該模型。Deepgram 的 callback 與 callback_method 查詢參數會以關閉代碼 1008 拒絕,因為 callback 傳送會將轉錄框架送往您的 URL,而不是透過此 socket 下行,這會讓該工作階段無法計量

LiteLLM Enterprise
為正式環境打造的 SSO/SAML、稽核記錄、支出追蹤、多團隊管理與防護欄。
深入瞭解 →