feat(ai): speechSession() for streaming text-to-speech over a live socket
维护者通常 1 天内回复
@AlemTuzlak 已经在做这个了。
开始于 2026年11月5日。
评估
这个 Issue 还没有评估数据。
描述
Problem
generateSpeech() returns a whole clip. The stream: true option only wraps the finished clip in a StreamChunk iterable; the ElevenLabs adapter still buffers the full response (packages/ai-elevenlabs/src/adapters/speech.ts:200,260). A voice agent waits 1 to 2 s before the first byte of audio plays, so a phone call feels like an IVR, not a conversation.
#1944 added transcriptionSession() for audio in and deferred audio out to a separate issue. This is that issue. The call-centre example built on #1944 works around it by splitting the reply into sentences and calling generateSpeech() per sentence. That gets first audio to about 500 ms but still pays a round trip per sentence and can't stream a long sentence.
Proposal
A speechSession() activity that mirrors transcriptionSession(): text in over a provider WebSocket, audio chunks out as the provider makes them.
import { speechSession } from '@tanstack/ai'
import { elevenlabsLiveSpeech } from '@tanstack/ai-elevenlabs'
const session = await speechSession({
adapter: elevenlabsLiveSpeech({ voice: 'rachel' }),
audio: { encoding: 'mulaw', sampleRate: 8000 }, // what the carrier wants
})
for await (const chunk of chat({ ... })) {
if (chunk.type === 'TEXT_MESSAGE_CONTENT') session.send(chunk.delta)
}
session.flush() // end of turn
for await (const event of session) {
// { type: 'audio', data: Uint8Array } play or forward to the carrier
// { type: 'done' } the provider finished the flushed text
// { type: 'error', error }
}
await session.close()
Barge-in: session.cancel() drops queued text and in-flight audio so the app can send Twilio clear and start a new turn.
Goals
- Server only, same runtimes as
transcriptionSession(). - Same adapter shape: open, send text, receive events, close. Reuse
openLiveSocketfrom@tanstack/ai/adapter-internals. - Telephony formats out of the box: μ-law and A-law at 8 kHz, PCM at 16/24/48 kHz. Adapters declare what they emit; the shared
createAudioConverterresamples and encodes when the provider can't. - Interim text is cheap. Sending partial sentences must not cost a request per token.
Adapters
| Provider | Package | Socket API |
|---|---|---|
| ElevenLabs | @tanstack/ai-elevenlabs |
wss://api.elevenlabs.io/v1/text-to-speech/{voice}/stream-input |
| OpenAI realtime | @tanstack/ai-openai |
realtime session, text-only input |
| Deepgram Aura | @tanstack/ai-deepgram |
wss://api.deepgram.com/v1/speak |
| Cartesia | new or community | wss://api.cartesia.ai/tts/websocket |
Check live-streaming support and ids before building.
Out of scope
- A browser hook. The browser already has
useRealtimeChat. - Carrier helpers. Document the pattern, as #1944 did.
- A full turn-taking loop. The call-centre example shows one.
Follow-up to
#1944, #1964.
- 主要语言
- TypeScript
- 星标
- 3.3k
- 派生
- 378
- 平均合并
- 1 天 2 小时
- 30 天内合并 PR
- 254
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
TanStack/ai 的其他 Issue
-
openai-base: a Responses max_output_tokens cutoff ends as RUN_ERROR without usage instead of finishReason 'length'可能已有人在做 @tombeckenham 于 8 天前认领。 未关闭has-pr waiting-on: maintainer
难度 2/5 1-3 小时 新手友好度 74/100
TanStack/ai#1916 · 1 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
-
AG-UI TOOL_CALL_RESULT metadata is dropped in the live stream reducer可能已有人在做 @c446984928 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 35/100
维护者通常 1 天内回复
-
ai-sandbox: encrypt snapshot blobs at rest with an encrypting blob store wrapper可能已有人在做 @AlemTuzlak 于 1 天前认领。 未关闭waiting-on: maintainer
维护者通常 1 天内回复
-
Sandbox takeover: the saved transcript loses everything the first host delivered before the restart可能已有人在做 @tombeckenham 于 1 天前认领。 未关闭waiting-on: maintainer
维护者通常 1 天内回复
-
withSandbox: each run's save drops the harness tool calls of earlier runs可能已有人在做 @tombeckenham 于 1 天前认领。 未关闭has-pr waiting-on: maintainer
维护者通常 1 天内回复
相似的 Issue
-
bug
难度 2/5 1-3 小时 新手友好度 78/100
Yeraze/meshmonitor#6239 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 82/100
better-auth/better-auth#12292 · 1 个 reaction ·
维护者通常 1 天内回复
-
The sample email preview's ResizeObserver is never disconnected and keeps raising errors after leaving Settings可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭bug
难度 2/5 1-3 小时 新手友好度 82/100
维护者通常 1 天内回复
-
area:ui bug effort:S priority:P2
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复
-
Typo in WSL project description on Projects page可能已有人在做 @akhil9tiet 今天认领。 未关闭
难度 1/5 1 小时以内 新手友好度 75/100
microsoft/opensource.microsoft.com#1066 · 1 条评论 ·