Voice Configuration
Backend:
services/api-gateway/app/core/voice_constants.pyFrontend:apps/web-app/src/lib/voiceConstants.tsStatus: Production Ready Last Updated: 2025-12-05
Overview
The VoiceAssist platform uses a centralized voice configuration system to ensure consistent voice selection across all components. This prevents issues where different parts of the system use different default voices, which would result in inconsistent user experience (e.g., dual voices playing simultaneously).
Architecture
┌─────────────────────────────────────────────────────────────────────────┐
│ SINGLE SOURCE OF TRUTH │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Backend: voice_constants.py Frontend: voiceConstants.ts │
│ ┌───────────────────────────┐ ┌───────────────────────────┐ │
│ │ DEFAULT_VOICE_ID = BRIAN │ │ DEFAULT_VOICE_ID = BRIAN │ │
│ │ DEFAULT_TTS_MODEL │ │ VoiceInfo metadata │ │
│ │ ElevenLabsVoice enum │ │ ElevenLabsVoices enum │ │
│ └─────────────┬─────────────┘ └─────────────┬─────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ config.py │ │ voiceSettingsStore.ts │ │
│ │ elevenlabs_service.py │ │ ThinkerTalkerVoicePanel │ │
│ │ voice_pipeline_service │ │ useBargeInPromptAudio │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Backend Configuration
voice_constants.py
The central configuration file for all voice-related constants:
from app.core.voice_constants import ( DEFAULT_VOICE_ID, DEFAULT_TTS_MODEL, DEFAULT_TTS_OUTPUT_FORMAT, ElevenLabsVoice, get_openai_voice_for_elevenlabs, ) # Current default voice print(DEFAULT_VOICE_ID) # "nPczCjzI2devNBz1zQrb" (Brian) # Available voices for voice in ElevenLabsVoice: print(f"{voice.name}: {voice.value}")
Available Voices
| Voice ID | Name | Gender | Style |
|---|---|---|---|
nPczCjzI2devNBz1zQrb | Brian (default) | Male | Warm, natural |
TxGEqnHWrfWFTfGW9XjX | Josh | Male | Deep, authoritative |
21m00Tcm4TlvDq8ikWAM | Rachel | Female | Clear, professional |
pNInz6obpgDQGcFmaJgB | Adam | Male | Deep, narrator |
EXAVITQu4vr4xnSDxMaL | Bella | Female | Soft, storytelling |
MF3mGyEYCl7XYWbV9V6O | Elli | Female | Young, friendly |
yoZ06aMxZJJ28mfd3POQ | Sam | Male | Young, casual |
XB0fDUnXU5powFXDhCwa | Layla | Female | Arabic |
Services Using voice_constants
- config.py - Environment configuration defaults
- elevenlabs_service.py - ElevenLabs TTS service
- voice_pipeline_service.py - Voice pipeline configuration
Frontend Configuration
voiceConstants.ts
The frontend equivalent for voice configuration:
import { DEFAULT_VOICE_ID, ElevenLabsVoices, VoiceInfo, getVoiceName, isValidVoiceId, getAvailableVoices, } from "../lib/voiceConstants"; // Current default voice console.log(DEFAULT_VOICE_ID); // "nPczCjzI2devNBz1zQrb" (Brian) // Get voice name console.log(getVoiceName(DEFAULT_VOICE_ID)); // "Brian" // List all voices for a selector const voices = getAvailableVoices(); voices.forEach((v) => console.log(`${v.name}: ${v.id}`));
Components Using voiceConstants
- voiceSettingsStore.ts - User voice preferences
- ThinkerTalkerVoicePanel.tsx - Voice mode UI
- useBargeInPromptAudio.ts - Barge-in prompt audio
Changing the Default Voice
To change the default voice across the entire system, update only these two files:
Step 1: Update Backend
Edit services/api-gateway/app/core/voice_constants.py:
# Change from Brian to Josh DEFAULT_VOICE_ID: str = ElevenLabsVoice.JOSH.value DEFAULT_VOICE_NAME: str = "Josh"
Step 2: Update Frontend
Edit apps/web-app/src/lib/voiceConstants.ts:
// Change from Brian to Josh export const DEFAULT_VOICE_ID = ElevenLabsVoices.JOSH; export const DEFAULT_VOICE_NAME = "Josh";
Step 3: Rebuild and Deploy
# Rebuild Docker container docker compose build voiceassist-server docker compose up -d voiceassist-server # Rebuild frontend (if needed) cd apps/web-app && pnpm build
User Voice Selection
Users can override the default voice through the Voice Mode Settings:
- Click the Settings button in Voice Mode
- Select a voice from the Voice dropdown
- The selected voice is stored in
voiceSettingsStore(persisted in localStorage) - The frontend sends the selected
voice_idto the backend with each request
The backend always uses the voice_id provided by the client. The default is only used when no voice_id is specified.
Preventing Dual Voice Issues
The centralized configuration prevents several common issues:
Problem: Multiple Default Voices
Before: Voice IDs were hardcoded in multiple files:
config.py→ Rachelvoice_pipeline_service.py→ Joshelevenlabs_service.py→ BrianThinkerTalkerVoicePanel.tsx→ Josh
This caused dual voices when different components used different defaults.
After: All components import from the single source of truth:
- All backend services →
voice_constants.py - All frontend components →
voiceConstants.ts
Problem: Browser TTS Fallback
Before: When ElevenLabs failed, the system fell back to browser TTS (SpeechSynthesis), which used a completely different voice.
After: Browser TTS fallback has been removed. If ElevenLabs fails, the prompt is silently skipped rather than played in a different voice.
Adding New Voices
Backend
- Add the voice to
ElevenLabsVoiceenum invoice_constants.py:
class ElevenLabsVoice(str, Enum): # ... existing voices ... NEW_VOICE = "new-elevenlabs-voice-id"
- Add voice info to
get_voice_info():
cls.NEW_VOICE.value: {"name": "New Voice", "gender": "male", "style": "description"},
- Add OpenAI fallback mapping:
ELEVENLABS_TO_OPENAI_VOICE_MAP: Dict[str, str] = { # ... existing mappings ... ElevenLabsVoice.NEW_VOICE.value: "onyx", }
Frontend
- Add the voice to
ElevenLabsVoicesinvoiceConstants.ts:
export const ElevenLabsVoices = { // ... existing voices ... NEW_VOICE: "new-elevenlabs-voice-id", } as const;
- Add voice info to
VoiceInfo:
[ElevenLabsVoices.NEW_VOICE]: { name: "New Voice", gender: "male", style: "description" },
API Reference
Backend: voice_constants.py
| Export | Type | Description |
|---|---|---|
DEFAULT_VOICE_ID | str | Default ElevenLabs voice ID |
DEFAULT_VOICE_NAME | str | Default voice display name |
DEFAULT_TTS_MODEL | str | Default TTS model (eleven_flash_v2_5) |
DEFAULT_TTS_OUTPUT_FORMAT | str | Audio format (pcm_24000) |
DEFAULT_STABILITY | float | Voice stability (0.65) |
DEFAULT_SIMILARITY_BOOST | float | Voice similarity (0.80) |
DEFAULT_STYLE | float | Voice style/emotion (0.15) |
ElevenLabsVoice | Enum | Available voice IDs |
VoiceProvider | Enum | TTS providers (elevenlabs, openai) |
get_openai_voice_for_elevenlabs() | function | Get OpenAI fallback voice |
Frontend: voiceConstants.ts
| Export | Type | Description |
|---|---|---|
DEFAULT_VOICE_ID | string | Default ElevenLabs voice ID |
DEFAULT_VOICE_NAME | string | Default voice display name |
ElevenLabsVoices | object | Voice ID constants |
VoiceInfo | Record | Voice metadata (name, gender, style) |
getVoiceName() | function | Get display name from voice ID |
isValidVoiceId() | function | Validate voice ID |
getAvailableVoices() | function | Get all voices for selectors |
Troubleshooting
Dual Voice Issue
If you hear two different voices:
- Check that all backend services are using
voice_constants.py - Check that all frontend components are using
voiceConstants.ts - Verify the Docker container has been rebuilt with latest code
- Clear browser localStorage to reset user preferences
Voice Not Changing
If the voice doesn't change after updating settings:
- Ensure the voice_id is being sent with the WebSocket connection
- Check the
voiceSettingsStorehas the new voice_id - Verify the backend logs show the correct voice_id
ElevenLabs API Errors
If ElevenLabs returns errors:
- Check API key is configured:
ELEVENLABS_API_KEY - Verify the voice_id is valid (use ElevenLabs dashboard)
- Check rate limits and quota