Frontend and token flow¶
The generated frontend is a LiveKit room client. It does not construct VAANI, choose raw providers, or hold provider credentials.
How a browser call starts¶
browser -> GET /api/status -> backend-approved development route summary
browser -> POST /api/livekit/token -> short-lived LiveKit token
browser -> LiveKit room -> microphone audio and live transcripts
LiveKit -> named VAANI worker -> configured STT, LLM, and TTS
In development, /api/status returns the secret-free
DevelopmentEvaluationCatalog.public_summary(). The browser independently
selects one ready STT target, LLM target, TTS target, supported voice, and
language from that response. In production, the catalog is absent and
selection is rejected.
The token backend validates the choice and sends the selected target IDs as private LiveKit job metadata. One worker binds that immutable selection to the call before building provider adapters.
Provider/model labels are deliberately visible for development testing. Credential names and values, endpoints, fallback policy, VAD, noise, temperature, deadlines, and arbitrary settings remain backend-owned. A stale or modified frontend cannot create an unapproved stack because the token API applies the same allow-list and readiness checks.
Token endpoint¶
The generated FastAPI endpoint validates the room name and caller identity, checks that the Assistant is ready, and issues a short-lived LiveKit token. Keep the LiveKit API secret on this server. The response contains only the token and public LiveKit URL.
For a production frontend:
- authenticate and authorize the person requesting a token;
- restrict allowed origins rather than copying the local CORS list;
- derive caller identity from trusted server context;
- rate-limit token creation;
- never return
.env, provider credentials, or production-lock contents.
Browser test calls use typed test telephony attributes so Call.caller has a
useful value. They do not prove that a real trunk supplies the same identity.
Keep the UI server-configured¶
You may change layout, branding, call controls, device selection, and transcript
presentation in frontend/. Preserve these boundaries:
- use
/api/statusas the configuration source; - send only target IDs, language, and voice returned by the development catalog;
- omit selection fields in production;
- subscribe to LiveKit participant and assistant transcripts;
- end through VAANI's control stream before disconnecting from the room;
- render normalized readiness issues without leaking secret values.
The generated END button sends the strict
vaani.control.v1 text-stream message {"kind":"end_call"}. VAANI cancels
in-flight generation/playout, finalizes the call, and returns COMPLETED. The
browser then listens for the remote VAANI participant to leave and calls
room.disconnect() locally. LiveKit does not disconnect the browser merely
because the agent left. The generated 1.5-second bound keeps the UI from
remaining in a completed room when the participant event is late or missing.
Do not replace this with an immediate local disconnect: that can close the transport before VAANI receives the graceful control. If sending the control itself fails, the generated fallback still releases the browser; inspect the worker result because graceful completion is not proven in that case.
Run ./scripts/dev, open http://localhost:3000, allow microphone access, and
place a real browser voice call. Continue with
live testing when the room connects, or
troubleshooting when it does not.