feat: live speech-to-text sessions for voice and telephony agents
Les mainteneurs répondent en général sous 1 jour
Évaluation
Cette issue n'a pas encore été évaluée.
Description
Problem
TanStack AI can't turn live audio into text on the server. That blocks the whole voice and telephony agent space: phone agents, call centres, IVR replacement, meeting bots, and voice agents behind an SFU.
generateTranscription()only takes a finished file.- Realtime voice (
useRealtimeChat) connects the browser straight to the provider. The connection reads the microphone itself (startAudioCapture), so a server can't feed it audio from a phone call.
A server-owned voice agent needs to feed audio as it arrives, and get text back while the caller talks. The audio comes from Twilio Media Streams, Telnyx, Plivo, Vonage, LiveKit, an SFU, or a browser WebSocket. Today teams stitch this together from vendor SDKs, then hand the text to chat().
This came up from a user who runs a voice agent on Cloudflare agents with withVoice. Their reply text already comes from chat(). Speech-to-text is the piece TanStack can't provide. See Transcriber in agents/voice for one pipeline's view of the shape.
Proposal
A new activity, a live transcription session, with one provider-neutral adapter contract.
import { transcriptionSession } from '@tanstack/ai'
import { deepgramLiveTranscription } from '@tanstack/ai-deepgram'
const session = await transcriptionSession({
adapter: deepgramLiveTranscription({ model: 'nova-3' }),
audio: { encoding: 'mulaw', sampleRate: 8000, channels: 1 }, // a Twilio call
language: 'en',
})
twilioSocket.on('media', (m) => session.send(m.payload)) // raw audio chunks
for await (const event of session) {
// { type: 'interim', text } text that may still change
// { type: 'final', text } stable text for a segment
// { type: 'turn-end' } the provider thinks the caller finished talking
// { type: 'error', error }
}
await session.close()
Swap the adapter to change provider. Nothing else changes.
Goals
- Runs on the server: Node, Bun, Workers and Durable Objects. No browser APIs.
- One small adapter contract (open, send audio, receive events, close), so providers are cheap to add, including community adapters.
- Telephony audio is first-class: G.711 μ-law and A-law at 8 kHz, plus linear PCM at 8, 16, 24 and 48 kHz. Each adapter declares what it accepts. A shared helper converts formats when the provider can't take the input as is.
- Optional
turn-endsignal, when the provider has endpointing. - Barge-in support: the caller's speech during agent playback must reach the app fast, as interim events.
- Small enough to wrap for other voice pipelines, for example an
agents/voiceTranscriber, in a few lines.
Providers
The first PR ships at least two providers from different vendors. That proves the contract isn't shaped around one API.
| Provider | Package | Notes |
|---|---|---|
| Deepgram (Nova-3, Flux) | new @tanstack/ai-deepgram |
Most common STT in telephony stacks. Flux has turn detection |
| AssemblyAI (Universal-Streaming) | new @tanstack/ai-assemblyai |
Turn detection built in |
| ElevenLabs Scribe realtime | @tanstack/ai-elevenlabs |
Package exists |
| OpenAI realtime transcription | @tanstack/ai-openai |
Package exists |
Workers AI (Deepgram models over the AI binding) |
@tanstack/ai-cloudflare |
Package exists. The user who asked |
| Gladia, Speechmatics, Soniox, Google, Azure | community or later |
I haven't confirmed live-streaming support or exact model ids for each row. Check before you build.
Out of scope for the first PR
- A browser hook (
useLiveTranscription). Add it later if people ask. - Streaming text-to-speech audio. Telephony needs it too (μ-law out, sent as it's made), but it's a separate issue.
generateSpeech()returns whole clips today. - Carrier helpers (Twilio, Telnyx and similar message parsing). Document the pattern first.
- A full server voice loop (turn-taking, barge-in, interruption). This issue is the building block for that.
Open questions
- An async iterator of events, or
on(event, handler)callbacks likeRealtimeConnection? - Should events follow AG-UI (
CUSTOMevents), or stay a plain transcription event union? - Where does format conversion live: in
@tanstack/ai, or in a small@tanstack/ai-utilsaudio helper? - E2E: aimock has no live-audio WebSocket mocking. Use unit tests with a fake adapter until it does?
Done when
- Activity and adapter contract in
@tanstack/ai, plus two or more providers from different vendors. - A docs page under
docs/media/, with a telephony example (a Twilio Media Streams call intochat()). - A
media-generationskill update. - Changesets.
- Langage dominant
- TypeScript
- Étoiles
- 3.3k
- Forks
- 378
- Merge moyen
- 1 j 1 h
- PR mergées (30 j)
- 214
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de TanStack/ai
-
openai-base: a Responses max_output_tokens cutoff ends as RUN_ERROR without usage instead of finishReason 'length'Peut-être pris @tombeckenham l’a pris il y a 3 jours. Ouvertehas-pr waiting-on: maintainer
Difficulté 2/5 1-3 heures Accessibilité débutants 74/100
TanStack/ai#1916 · 1 commentaire · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
-
ai-harness: view.dispose() doesn't cancel snapshot, transcript or describe requestsPeut-être pris @AlemTuzlak l’a pris aujourd’hui. Ouverteneeds-repro waiting-on: maintainer
TanStack/ai#1960 · 1 commentaire · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
-
ai-anthropic: 400 on signed thinking blocks after compaction rewrites history (missing block_binding drop_block)Peut-être pris @tombeckenham l’a pris il y a 1 jour. Ouvertewaiting-on: maintainer
TanStack/ai#1959 · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
-
Chat UI: a fallback widget for tool calls whose names aren't known when the kit is createdPeut-être pris @AlemTuzlak l’a pris il y a 2 jours. Ouvertewaiting-on: maintainer
Difficulté 4/5 1-2 jours Accessibilité débutants 58/100
TanStack/ai#1953 · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
-
`ChatEngine.handleTextMessageContentEvent` is O(n²) in answer length: `startsWith(before)` copies the whole accumulated text on every chunkPeut-être pris @tombeckenham l’a pris il y a 2 jours. Ouvertewaiting-on: maintainer
TanStack/ai#1951 · 1 personne assignée ·
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de TanStack/ai
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 65/100
ehmpathy/rhachet-roles-ehmpathy#1231 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 66/100
Equal-Vote/bettervoting#1754 ·
Les mainteneurs répondent en général sous 2 jours
-
Difficulté 1/5 Moins d'une heure Accessibilité débutants 82/100
Doist/todoist-sdk-typescript#740 ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 1/5 Moins d'une heure Accessibilité débutants 62/100
privatenumber/cleye#47 ·
-
spec-feedback
Difficulté 2/5 1-3 heures Accessibilité débutants 62/100