feat(ai): speechSession() for streaming text-to-speech over a live socket
I maintainer di solito rispondono entro 1 giorno
@AlemTuzlak ci sta già lavorando.
Dal 5/11/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
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.
- Lingua principale
- TypeScript
- Stelle
- 3.3k
- Fork
- 378
- Merge medio
- 1g 2h
- PR unite (30g)
- 254
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di TanStack/ai
-
openai-base: a Responses max_output_tokens cutoff ends as RUN_ERROR without usage instead of finishReason 'length'Forse già presa @tombeckenham l’ha presa 8 giorni fa. Apertahas-pr waiting-on: maintainer
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
TanStack/ai#1916 · 1 commento · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
-
AG-UI TOOL_CALL_RESULT metadata is dropped in the live stream reducerForse già presa @c446984928 l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 35/100
I maintainer di solito rispondono entro 1 giorno
-
ai-sandbox: encrypt snapshot blobs at rest with an encrypting blob store wrapperForse già presa @AlemTuzlak l’ha presa 1 giorno fa. Apertawaiting-on: maintainer
TanStack/ai#2062 · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
-
Sandbox takeover: the saved transcript loses everything the first host delivered before the restartForse già presa @tombeckenham l’ha presa 1 giorno fa. Apertawaiting-on: maintainer
TanStack/ai#2061 · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
-
withSandbox: each run's save drops the harness tool calls of earlier runsForse già presa @tombeckenham l’ha presa 1 giorno fa. Apertahas-pr waiting-on: maintainer
TanStack/ai#2059 · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
Issue simili
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
Yeraze/meshmonitor#6239 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
better-auth/better-auth#12292 · 1 reazione ·
I maintainer di solito rispondono entro 1 giorno
-
The sample email preview's ResizeObserver is never disconnected and keeps raising errors after leaving SettingsForse già presa Una pull request collegata a questa issue è aperta o già unita. Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
I maintainer di solito rispondono entro 1 giorno
-
area:ui bug effort:S priority:P2
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
I maintainer di solito rispondono entro 1 giorno
-
Typo in WSL project description on Projects pageForse già presa @akhil9tiet l’ha presa oggi. Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 75/100
microsoft/opensource.microsoft.com#1066 · 1 commento ·