-
Notifications
You must be signed in to change notification settings - Fork 28
Add inbound voice agent support with language detection and configuration #206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
- added alembic migration for removing phone_numbers from telephony_config, having inbound outbound numbers, supported default languages in voice_agents - respective changes in service, controller files - added phone and language validation
📝 WalkthroughWalkthroughThis PR adds multi-language voice agent support and restructures configuration management. Language support is introduced at the agent level with supported_languages and default_language fields, language detection capabilities, and inbound call handling via phone number-based agent lookup. TTS/STT configuration is simplified by moving language and parameters from config tables to voice agents. Phone number management is relocated from telephony configs to agent records. A database migration consolidates configuration data into the voice_agent table. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller
participant Twilio
participant Webhook as Webhook/Inbound
participant CacheService as Voice Agent Cache
participant FlowareAPI as Floware API
participant PipecatService as Pipecat Service
participant LLM
Caller->>Twilio: Call to phone number
Twilio->>Webhook: POST /inbound with From/To/CallSid
Webhook->>CacheService: get_agent_by_inbound_number(To)
CacheService->>CacheService: Check cache for inbound number
alt Cache hit with agent ID
CacheService->>FlowareAPI: GET full agent data
else Cache miss
CacheService->>FlowareAPI: GET agent by inbound number
FlowareAPI-->>CacheService: Return agent data
end
CacheService-->>Webhook: Voice agent config
Webhook->>CacheService: get_welcome_message_audio_url(agent_id)
CacheService->>FlowareAPI: GET presigned welcome audio URL
FlowareAPI-->>CacheService: URL (or empty if not exists)
CacheService-->>Webhook: Welcome audio URL
Webhook-->>Twilio: TwiML response with WebSocket URL
Twilio-->>Caller: Play welcome message + open WebSocket
Caller->>PipecatService: Audio stream via WebSocket
PipecatService->>PipecatService: Detect language from first user message
alt Multi-language enabled
PipecatService->>PipecatService: Switch STT/TTS to detected language
end
PipecatService->>LLM: Send message with language-specific system prompt
LLM-->>PipecatService: Response
PipecatService-->>Caller: TTS audio in detected language
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| from sqlalchemy import text | ||
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision: str = '6010e49da528' |
|
|
||
| # revision identifiers, used by Alembic. | ||
| revision: str = '6010e49da528' | ||
| down_revision: Union[str, None] = 'f7572bcd9510' |
| # revision identifiers, used by Alembic. | ||
| revision: str = '6010e49da528' | ||
| down_revision: Union[str, None] = 'f7572bcd9510' | ||
| branch_labels: Union[str, Sequence[str], None] = None |
| revision: str = '6010e49da528' | ||
| down_revision: Union[str, None] = 'f7572bcd9510' | ||
| branch_labels: Union[str, Sequence[str], None] = None | ||
| depends_on: Union[str, Sequence[str], None] = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
wavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/EditSttConfigDialog.tsx (1)
33-38: Schema and provider list mismatch may cause validation failures for existing configs.The schema hardcodes
provider: z.enum(['deepgram'])while the UI renders all providers fromVOICE_PROVIDERS_CONFIG.stt.providers. If an existing config uses a different provider (e.g., from legacy data or the SttProvider enum includes assemblyai, whisper, google, azure), the form will fail validation when opened.Consider either:
- Matching the schema to the server's
SttProviderenum values, or- Using a dynamic enum based on the config's actual provider value
Option 1: Match server enum values
const updateSttConfigSchema = z.object({ display_name: z.string().min(1, 'Display name is required').max(100, 'Display name must be 100 characters or less'), description: z.string().max(500, 'Description must be 500 characters or less').optional(), - provider: z.enum(['deepgram'] as [string, ...string[]]), + provider: z.enum(['deepgram', 'assemblyai', 'whisper', 'google', 'azure'] as [string, ...string[]]), api_key: z.string().optional(), });wavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/CreateSttConfigDialog.tsx (1)
32-37: Provider selection and validation mismatch will cause UX issues.The schema on line 35 only allows
'deepgram', but the Select component (lines 136-143) renders all providers fromVOICE_PROVIDERS_CONFIG.stt.providers. If this config includes other providers, users can select them but the form will fail zod validation.Either restrict the Select options to match the schema, or expand the schema to match available providers.
Option 1: Restrict Select to only show 'deepgram'
<SelectContent> - {VOICE_PROVIDERS_CONFIG.stt.providers.map((p) => { - const config = getProviderConfig('stt', p); - return ( - <SelectItem key={p} value={p}> - {config?.name || p} - </SelectItem> - ); - })} + <SelectItem value="deepgram"> + {getProviderConfig('stt', 'deepgram')?.name || 'Deepgram'} + </SelectItem> </SelectContent>Option 2: Expand schema to match VOICE_PROVIDERS_CONFIG
If multiple providers should be supported, update the schema to accept all provider values dynamically or list them explicitly.
Also applies to: 129-149
🤖 Fix all issues with AI agents
In @wavefront/client/src/pages/apps/[appId]/voice-agents/index.tsx:
- Around line 156-163: The count fallback currently uses
agent.supported_languages?.length || 1 which incorrectly shows "1" when
supported_languages is undefined or empty; update the span to show a truthful
empty state by using agent.supported_languages?.length ?? 0 (or render a dash
'-') and keep the title safe by guarding the join (e.g.,
title={agent.supported_languages?.map(getLanguageName).join(', ') || '-'}) so
the TableCell/span displays 0 or '-' when no languages exist and still shows the
default_language (agent.default_language || 'en') as before.
In
@wavefront/server/apps/call_processing/call_processing/constants/language_config.py:
- Around line 33-34: The language alias mapping contains overlapping entries for
'fil' and 'tl' (both include 'tagalog'/'filipino'), causing non-deterministic
detection; update the language aliases dict (the mapping that defines 'fil' and
'tl') to remove the ambiguity by either consolidating into a single canonical
code (e.g., keep only 'fil' and drop the 'tl' key) or by making their keyword
lists mutually exclusive (give each key unique trigger words), and update any
related comments/tests that reference 'fil' or 'tl' to reflect the new canonical
choice.
In
@wavefront/server/modules/db_repo_module/db_repo_module/alembic/versions/2026_01_08_1547-6010e49da528_add_inbound_voice_agent_support.py:
- Around line 152-178: The downgrade currently restores columns but fails to
repopulate them from the new voice_agents table, causing data loss; update
downgrade() to migrate data back before dropping voice_agents columns by: after
adding back columns (tts_configs.voice_id, tts_configs.parameters,
stt_configs.language, stt_configs.parameters, telephony_configs.phone_numbers)
run SQL updates (via op.execute) that join voice_agents to
tts_configs/stt_configs/telephony_configs and set the restored columns from
voice_agents.tts_voice_id, voice_agents.tts_parameters,
voice_agents.stt_parameters, voice_agents.default_language (and
supported_languages/outbound_numbers/inbound_numbers as appropriate), then only
drop the columns from voice_agents; alternatively document this as irreversible
if you decide not to implement the reverse migration.
In
@wavefront/server/modules/voice_agents_module/voice_agents_module/utils/language_validation.py:
- Around line 52-171: ELEVENLABS_LANGUAGES contains entries that don't match the
eleven_multilingual_v2 model; update ELEVENLABS_LANGUAGES to exactly reflect
ElevenLabs' supported languages (remove 'hi', 'no', 'vi' or any other
unsupported codes) or add a verification step that checks the configured
language against the live ElevenLabs model before use; locate and edit the
ELEVENLABS_LANGUAGES constant in language_validation.py and ensure any caller
(e.g., functions that reference ELEVENLABS_LANGUAGES) fails fast with a clear
error if an unsupported language is requested.
🧹 Nitpick comments (23)
wavefront/client/src/pages/apps/[appId]/voice-agents/telephony-configs/index.tsx (1)
78-86: LGTM - Filter updated correctly for the schema change.The addition of
connection_typeto the search filter is appropriate given the removal of phone numbers from telephony configs.One minor note:
descriptionhas a null guard (line 82), whileconnection_typedoes not. Ifconnection_typeis guaranteed to be non-null by theTelephonyConfigtype, this is fine. Otherwise, consider adding a similar guard for defensive consistency:- config.connection_type.toLowerCase().includes(query) + (config.connection_type && config.connection_type.toLowerCase().includes(query))wavefront/server/modules/voice_agents_module/voice_agents_module/services/tts_config_service.py (1)
37-43: Type hints use incorrect default None pattern.Parameters
providerandapi_keyhavestr = Nonewhich is technically incorrect—Noneis not a validstr. UseOptional[str]for clarity and type checker compatibility.Suggested fix
async def create_config( self, display_name: str, description: Optional[str] = None, - provider: str = None, - api_key: str = None, + provider: Optional[str] = None, + api_key: Optional[str] = None, ) -> dict:wavefront/server/modules/db_repo_module/db_repo_module/models/voice_agent.py (1)
71-100: Consider consolidating field name lists for maintainability.The array field names appear in two places (lines 71-78 and 93-98). If new JSONB fields are added later, both lists must be updated. Consider extracting to class-level constants.
Suggested approach
# At class level _JSONB_FIELDS = frozenset({ 'conversation_config', 'inbound_numbers', 'outbound_numbers', 'supported_languages', 'tts_parameters', 'stt_parameters', }) _JSONB_ARRAY_FIELDS = frozenset({ 'inbound_numbers', 'outbound_numbers', 'supported_languages', }) # Then in to_dict: elif column.name in self._JSONB_FIELDS: # ... result[column.name] = [] if column.name in self._JSONB_ARRAY_FIELDS else Nonewavefront/server/modules/db_repo_module/db_repo_module/alembic/versions/2026_01_08_1547-6010e49da528_add_inbound_voice_agent_support.py (1)
70-94: Row-by-row migration may be slow for large tables.The Python loop processes each agent individually with separate UPDATE statements. For large datasets, this could be slow. Consider a single SQL UPDATE with subquery if performance is a concern.
Alternative batch approach
UPDATE voice_agents va SET tts_voice_id = COALESCE(tc.voice_id, 'default'), tts_parameters = COALESCE(tc.parameters::jsonb, '{}'::jsonb), stt_parameters = COALESCE(sc.parameters::jsonb, '{}'::jsonb) FROM tts_configs tc, stt_configs sc WHERE va.tts_config_id = tc.id AND va.stt_config_id = sc.id AND va.is_deleted = false;This performs the migration in a single statement. The safety fallback at lines 98-102 would still catch any remaining NULLs.
wavefront/server/modules/voice_agents_module/voice_agents_module/controllers/tts_config_controller.py (1)
22-48: Unusedrequestparameter.The
request: Requestparameter on line 23 is not used within the function body. If it's no longer needed after the refactoring, consider removing it to keep the function signature clean.Suggested fix
@tts_config_router.post('/v1/tts-configs') @inject async def create_tts_config( - request: Request, payload: CreateTtsConfigPayload, response_formatter: ResponseFormatter = Depends( Provide[CommonContainer.response_formatter] ),Also remove
Requestfrom the imports on line 4 if no longer used elsewhere.wavefront/server/apps/call_processing/call_processing/controllers/cache_controller.py (2)
107-114: Consider unpacking for list construction.Minor style improvement suggested by static analysis.
Proposed fix
- valid_types = list(VALID_CONFIG_TYPES) + ['inbound_number'] + valid_types = [*VALID_CONFIG_TYPES, 'inbound_number']
140-147: Chain the exception for better debugging.The
raisestatement should includefromto preserve the original exception context per Python best practices.Proposed fix
try: config_id = UUID(config_id_str) except ValueError: logger.error(f'Invalid UUID format for config_id: {config_id_str}') raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f'config_id must be a valid UUID for config_type={config_type}', - ) + ) from NoneUsing
from Noneexplicitly suppresses the chained exception if you don't want users to see the internalValueError, or usefrom e(whereeis the caught exception) if you want full chaining.wavefront/server/apps/call_processing/call_processing/services/floware_http_client.py (1)
127-171: Consider URL-encoding the phone number for RFC 3986 compliance.While the
+character in URL paths is not converted to space (that only happens in query strings), RFC 3986 specifies that special characters in URLs should be percent-encoded. The backend's FastAPI endpoint handles raw path parameters correctly, but encoding ensures robustness across different proxies, load balancers, and URL parsing implementations.Suggested approach
+from urllib.parse import quote + async def get_agent_by_inbound_number( self, phone_number: str ) -> Optional[Dict[str, Any]]: ... url = ( - f'{self.base_url}/floware/v1/voice-agents/by-inbound-number/{phone_number}' + f'{self.base_url}/floware/v1/voice-agents/by-inbound-number/{quote(phone_number, safe="")}' )If the server explicitly expects raw phone numbers without encoding, document that assumption and confirm it handles E.164 format correctly.
wavefront/server/apps/call_processing/call_processing/services/voice_agent_cache_service.py (1)
385-386: Consider extracting the TTL magic number to a named constant.The value
7100seconds (~2 hours) is hardcoded. For maintainability, consider defining it as a class constant alongsideself.cache_ttl:WELCOME_URL_TTL = 7100 # ~2 hours, matches floware presigned URL expirywavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/EditTtsConfigDialog.tsx (1)
63-73: Potential unnecessary re-renders fromformin dependency array.Including
formin theuseEffectdependency array (line 73) can cause the effect to run more often than intended if the form object reference changes. Sinceformcomes fromuseForm()hook, its reference should be stable, but for clarity and to avoid potential issues:♻️ Suggested fix
useEffect(() => { if (isOpen && config) { form.reset({ display_name: config.display_name, description: config.description || '', provider: config.provider, api_key: '', }); } - }, [isOpen, config, form]); + }, [isOpen, config, form.reset]);wavefront/server/apps/call_processing/call_processing/controllers/webhook_controller.py (2)
86-97: Consider extracting duplicate URL conversion logic.The WebSocket URL construction logic (lines 86-97) is duplicated in
twiml_endpoint(lines 148-159). Consider extracting this into a helper function.♻️ Suggested refactor
def _build_websocket_url() -> str: """Build WebSocket URL from base URL environment variable.""" base_url = os.getenv('CALL_PROCESSING_BASE_URL', 'http://localhost:8003') if base_url.startswith('https://'): websocket_url = base_url.replace('https://', 'wss://') elif base_url.startswith('http://'): websocket_url = base_url.replace('http://', 'ws://') else: websocket_url = f'wss://{base_url}' return f'{websocket_url}/webhooks/ws'
116-116: Agent ID should be converted to string explicitly.
agent_idis extracted fromagent['id']which could be a UUID object. Twilio stream parameters expect string values. Ensure consistent string conversion.♻️ Proposed fix
- stream.parameter(name='voice_agent_id', value=agent_id) + stream.parameter(name='voice_agent_id', value=str(agent_id))wavefront/client/src/pages/apps/[appId]/voice-agents/EditVoiceAgentDialog.tsx (2)
339-536: Consider consolidating TTS and STT parameter rendering functions.
renderTtsParameterField(lines 339-436) andrenderSttParameterField(lines 439-536) are nearly identical, differing only in state variables and IDs. Consider extracting a shared function.♻️ Suggested approach
const renderParameterField = ( type: 'tts' | 'stt', key: string, provider: string | undefined, parameters: Record<string, unknown>, setParameter: (key: string, value: unknown) => void ) => { if (!provider) return null; const config = getProviderConfig(type, provider); if (!config) return null; const paramConfig = config.parameters[key]; if (!paramConfig) return null; // ... shared rendering logic with dynamic id prefix }; // Usage: {Object.keys(ttsProviderConfig.parameters) .filter((key) => key !== 'language') .map((key) => renderParameterField('tts', key, selectedTtsProvider, ttsParameters, setTtsParameter))}
853-858: Default language checkbox may become invalid when supported languages change.When a user unchecks a language that is currently set as the default language, the dropdown will no longer show that option, but the form value remains set. The validation at line 227-230 catches this on submit, but this could confuse users.
Consider resetting
default_languageto the first remaining supported language when the current default is removed fromsupported_languages.wavefront/server/apps/call_processing/call_processing/services/pipecat_service.py (2)
321-322: Unusedprocessorargument in event handler.The
processorparameter is declared but never used in the handler function. If not needed by the event handler contract, consider removing it or using_to indicate intentionally unused.♻️ Proposed fix
@transcript.event_handler('on_transcript_update') - async def handle_language_detection(processor, frame): + async def handle_language_detection(_processor, frame): """Detect language from first user message and switch services"""
428-428: Use list unpacking instead of concatenation for better performance.As flagged by static analysis, prefer list unpacking over concatenation.
♻️ Proposed fix
- new_messages = [updated_system_message] + current_messages[1:] + new_messages = [updated_system_message, *current_messages[1:]]wavefront/client/src/pages/apps/[appId]/voice-agents/CreateVoiceAgentDialog.tsx (2)
277-487: Significant code duplication with EditVoiceAgentDialog.The
renderTtsParameterFieldandrenderSttParameterFieldfunctions (lines 277-487) are nearly identical to those inEditVoiceAgentDialog.tsx. The same applies to phone number parsing/validation and language configuration UI.Consider extracting shared logic into:
- A shared parameter rendering utility or hook
- A shared phone number validation utility
- Common form field components for the language configuration section
808-813: Same UX issue with default language selection as in EditVoiceAgentDialog.When a user unchecks a language that is currently set as the default, the dropdown becomes empty but the form value remains. Consider adding an effect to auto-reset
default_languagewhen it's no longer insupported_languages.♻️ Suggested addition
// Add this useEffect after the form definition const watchedSupportedLanguages = form.watch('supported_languages'); const watchedDefaultLanguage = form.watch('default_language'); useEffect(() => { if (watchedSupportedLanguages && !watchedSupportedLanguages.includes(watchedDefaultLanguage)) { // Reset to first supported language or 'en' as fallback const newDefault = watchedSupportedLanguages[0] || 'en'; form.setValue('default_language', newDefault); } }, [watchedSupportedLanguages, watchedDefaultLanguage, form]);wavefront/server/modules/voice_agents_module/voice_agents_module/controllers/voice_agent_controller.py (2)
501-516: Missing agent existence validation before generating welcome audio URL.The
get_welcome_audio_urlendpoint callsvoice_agent_service.get_welcome_message_audio_url(agent_id)directly without first verifying the agent exists. If the agent doesn't exist or has no welcome message, the error will be generic. Consider adding an agent existence check for a better error response.♻️ Proposed fix
async def get_welcome_audio_url( agent_id: UUID = Path(..., description='The ID of the voice agent'), ... ): ... try: + # Verify agent exists first + agent = await voice_agent_service.get_agent(agent_id) + if not agent: + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content=response_formatter.buildErrorResponse( + f'Voice agent not found with id: {agent_id}' + ), + ) + + if not agent.get('welcome_message'): + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content=response_formatter.buildErrorResponse( + 'Voice agent has no welcome message configured' + ), + ) + url = await voice_agent_service.get_welcome_message_audio_url(agent_id)
376-382: HTTP 500 may not be the correct status code for missing telephony config.If the telephony config is not found, this indicates a data integrity issue (the agent references a non-existent or deleted config). Returning HTTP 500 suggests a server error, but this could also be considered a bad request if the agent's configuration is incomplete. Consider using HTTP 400 or 422 with a clearer message.
wavefront/server/modules/voice_agents_module/voice_agents_module/services/voice_agent_service.py (3)
189-202: Inbound number uniqueness check has O(n*m) complexity.For each inbound number being validated, the code queries all non-deleted agents (line 191). If validating multiple numbers, this results in
n * mdatabase/memory operations where n = number of inbound numbers and m = number of agents.Consider restructuring to query all agents once and check all numbers against the result, or add a database index/constraint for inbound number uniqueness.
♻️ Proposed optimization
+ # Query all agents once for efficiency + all_agents = await self.voice_agent_repository.find(is_deleted=False) + # Validate inbound number uniqueness for number in inbound_numbers: - # Query all agents and check if number is already assigned - all_agents = await self.voice_agent_repository.find(is_deleted=False) for agent in all_agents: # Skip self when updating if agent_id and agent.id == agent_id:
841-852: get_agent_by_inbound_number queries all agents on cache miss.On cache miss, this method fetches all non-deleted agents and iterates through them (lines 843-852). This approach doesn't scale well with a large number of agents.
Consider adding a dedicated database query that filters by inbound number using a JSONB operator (e.g.,
@>in PostgreSQL) or maintaining a separate lookup table for inbound numbers.
607-634: Multiple conditions for audio regeneration could be consolidated.The series of
ifstatements checking for audio regeneration (lines 607-634) are repetitive. Consider using a helper or consolidating the checks.♻️ Suggested consolidation
# Define fields that trigger audio regeneration audio_regen_fields = { 'welcome_message': existing_agent.welcome_message, 'supported_languages': existing_dict.get('supported_languages'), 'default_language': existing_dict.get('default_language'), 'tts_voice_id': existing_agent.tts_voice_id, 'tts_parameters': existing_dict.get('tts_parameters'), 'tts_config_id': existing_dict.get('tts_config_id'), } audio_regeneration_needed = any( key in update_data and update_data[key] != old_value for key, old_value in audio_regen_fields.items() )
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (45)
wavefront/client/src/constants/languages.tswavefront/client/src/pages/apps/[appId]/voice-agents/CreateVoiceAgentDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/EditVoiceAgentDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/OutboundCallDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/index.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/CreateSttConfigDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/EditSttConfigDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/index.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/telephony-configs/CreateTelephonyConfigDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/telephony-configs/EditTelephonyConfigDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/telephony-configs/index.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/CreateTtsConfigDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/EditTtsConfigDialog.tsxwavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/index.tsxwavefront/client/src/types/stt-config.tswavefront/client/src/types/telephony-config.tswavefront/client/src/types/tts-config.tswavefront/client/src/types/voice-agent.tswavefront/server/apps/call_processing/call_processing/constants/language_config.pywavefront/server/apps/call_processing/call_processing/controllers/cache_controller.pywavefront/server/apps/call_processing/call_processing/controllers/webhook_controller.pywavefront/server/apps/call_processing/call_processing/services/floware_http_client.pywavefront/server/apps/call_processing/call_processing/services/pipecat_service.pywavefront/server/apps/call_processing/call_processing/services/voice_agent_cache_service.pywavefront/server/modules/db_repo_module/db_repo_module/alembic/versions/2026_01_08_1547-6010e49da528_add_inbound_voice_agent_support.pywavefront/server/modules/db_repo_module/db_repo_module/models/stt_config.pywavefront/server/modules/db_repo_module/db_repo_module/models/telephony_config.pywavefront/server/modules/db_repo_module/db_repo_module/models/tts_config.pywavefront/server/modules/db_repo_module/db_repo_module/models/voice_agent.pywavefront/server/modules/voice_agents_module/voice_agents_module/controllers/stt_config_controller.pywavefront/server/modules/voice_agents_module/voice_agents_module/controllers/telephony_config_controller.pywavefront/server/modules/voice_agents_module/voice_agents_module/controllers/tts_config_controller.pywavefront/server/modules/voice_agents_module/voice_agents_module/controllers/voice_agent_controller.pywavefront/server/modules/voice_agents_module/voice_agents_module/models/stt_schemas.pywavefront/server/modules/voice_agents_module/voice_agents_module/models/telephony_schemas.pywavefront/server/modules/voice_agents_module/voice_agents_module/models/tts_schemas.pywavefront/server/modules/voice_agents_module/voice_agents_module/models/voice_agent_schemas.pywavefront/server/modules/voice_agents_module/voice_agents_module/services/stt_config_service.pywavefront/server/modules/voice_agents_module/voice_agents_module/services/telephony_config_service.pywavefront/server/modules/voice_agents_module/voice_agents_module/services/tts_config_service.pywavefront/server/modules/voice_agents_module/voice_agents_module/services/voice_agent_service.pywavefront/server/modules/voice_agents_module/voice_agents_module/utils/cache_invalidation.pywavefront/server/modules/voice_agents_module/voice_agents_module/utils/cache_utils.pywavefront/server/modules/voice_agents_module/voice_agents_module/utils/language_validation.pywavefront/server/modules/voice_agents_module/voice_agents_module/utils/phone_validation.py
💤 Files with no reviewable changes (7)
- wavefront/client/src/pages/apps/[appId]/voice-agents/telephony-configs/CreateTelephonyConfigDialog.tsx
- wavefront/server/modules/voice_agents_module/voice_agents_module/services/stt_config_service.py
- wavefront/client/src/types/telephony-config.ts
- wavefront/client/src/types/stt-config.ts
- wavefront/client/src/types/tts-config.ts
- wavefront/client/src/pages/apps/[appId]/voice-agents/telephony-configs/EditTelephonyConfigDialog.tsx
- wavefront/server/modules/voice_agents_module/voice_agents_module/controllers/telephony_config_controller.py
🧰 Additional context used
🧬 Code graph analysis (11)
wavefront/client/src/pages/apps/[appId]/voice-agents/CreateVoiceAgentDialog.tsx (3)
wavefront/client/src/config/voice-providers.ts (2)
initializeParameters(260-270)getProviderConfig(246-248)wavefront/client/src/utils/parameter-helpers.ts (4)
getBooleanParameterWithDefault(49-57)getNumberParameterWithDefault(34-44)getNumberOrStringParameter(25-29)getStringParameter(17-20)wavefront/client/src/constants/languages.ts (2)
SUPPORTED_LANGUAGES(12-54)getLanguageDisplayName(61-65)
wavefront/client/src/pages/apps/[appId]/voice-agents/index.tsx (1)
wavefront/client/src/constants/languages.ts (1)
getLanguageName(56-59)
wavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/EditSttConfigDialog.tsx (2)
wavefront/client/src/components/ui/input.tsx (1)
Input(22-22)wavefront/client/src/components/ui/select.tsx (1)
Select(133-133)
wavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/EditTtsConfigDialog.tsx (2)
wavefront/client/src/components/ui/input.tsx (1)
Input(22-22)wavefront/client/src/components/ui/select.tsx (1)
Select(133-133)
wavefront/server/apps/call_processing/call_processing/services/floware_http_client.py (2)
wavefront/server/apps/call_processing/call_processing/services/voice_agent_cache_service.py (2)
get_agent_by_inbound_number(266-349)get_welcome_message_audio_url(351-398)wavefront/server/modules/api_services_module/api_services_module/models/service.py (1)
error(131-143)
wavefront/client/src/pages/apps/[appId]/voice-agents/OutboundCallDialog.tsx (1)
wavefront/client/src/store/notification-store.ts (1)
useNotifyStore(32-32)
wavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/CreateTtsConfigDialog.tsx (1)
wavefront/client/src/components/ui/input.tsx (1)
Input(22-22)
wavefront/server/apps/call_processing/call_processing/services/voice_agent_cache_service.py (2)
wavefront/server/apps/call_processing/call_processing/services/floware_http_client.py (3)
get_agent_by_inbound_number(127-171)fetch_voice_agent(43-80)get_welcome_message_audio_url(173-216)wavefront/server/modules/voice_agents_module/voice_agents_module/utils/cache_utils.py (1)
get_voice_agent_cache_key(34-36)
wavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/CreateSttConfigDialog.tsx (3)
wavefront/client/src/components/ui/input.tsx (1)
Input(22-22)wavefront/client/src/components/ui/alert.tsx (2)
Alert(44-44)AlertDescription(44-44)wavefront/client/src/components/ui/dialog.tsx (1)
DialogFooter(115-115)
wavefront/server/modules/voice_agents_module/voice_agents_module/services/voice_agent_service.py (6)
wavefront/server/modules/voice_agents_module/voice_agents_module/utils/language_validation.py (3)
validate_languages_for_configs(228-282)validate_default_language(285-303)format_language_prompt(319-337)wavefront/server/modules/voice_agents_module/voice_agents_module/utils/phone_validation.py (1)
validate_phone_numbers(32-67)wavefront/server/modules/voice_agents_module/voice_agents_module/services/tts_config_service.py (1)
get_config(86-118)wavefront/server/modules/voice_agents_module/voice_agents_module/services/telephony_config_service.py (1)
get_config(98-132)wavefront/server/modules/voice_agents_module/voice_agents_module/services/stt_config_service.py (1)
get_config(86-118)wavefront/server/modules/voice_agents_module/voice_agents_module/utils/cache_invalidation.py (1)
invalidate_call_processing_cache(9-80)
wavefront/server/modules/voice_agents_module/voice_agents_module/controllers/voice_agent_controller.py (3)
wavefront/server/modules/voice_agents_module/voice_agents_module/services/telephony_config_service.py (1)
get_config(98-132)wavefront/server/modules/voice_agents_module/voice_agents_module/services/voice_agent_service.py (2)
get_agent_by_inbound_number(823-854)get_welcome_message_audio_url(734-777)wavefront/server/modules/api_services_module/api_services_module/models/service.py (1)
error(131-143)
🪛 Ruff (0.14.10)
wavefront/server/apps/call_processing/call_processing/services/floware_http_client.py
156-156: Consider moving this statement to an else block
(TRY300)
wavefront/server/modules/voice_agents_module/voice_agents_module/utils/language_validation.py
201-201: Avoid specifying long messages outside the exception class
(TRY003)
225-225: Avoid specifying long messages outside the exception class
(TRY003)
wavefront/server/apps/call_processing/call_processing/services/voice_agent_cache_service.py
312-312: Do not catch blind exception: Exception
(BLE001)
344-344: Do not catch blind exception: Exception
(BLE001)
393-393: Do not catch blind exception: Exception
(BLE001)
wavefront/server/apps/call_processing/call_processing/controllers/cache_controller.py
108-108: Consider [*list(VALID_CONFIG_TYPES), 'inbound_number'] instead of concatenation
Replace with [*list(VALID_CONFIG_TYPES), 'inbound_number']
(RUF005)
144-147: Within an except clause, raise exceptions with raise ... from err or raise ... from None to distinguish them from errors in exception handling
(B904)
wavefront/server/modules/db_repo_module/db_repo_module/alembic/versions/2026_01_08_1547-6010e49da528_add_inbound_voice_agent_support.py
116-119: Possible SQL injection vector through string-based query construction
(S608)
wavefront/server/apps/call_processing/call_processing/services/pipecat_service.py
322-322: Unused function argument: processor
(ARG001)
394-394: Do not catch blind exception: Exception
(BLE001)
428-428: Consider [updated_system_message, *current_messages[1:]] instead of concatenation
Replace with [updated_system_message, *current_messages[1:]]
(RUF005)
wavefront/server/apps/call_processing/call_processing/controllers/webhook_controller.py
42-44: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
82-82: Do not catch blind exception: Exception
(BLE001)
wavefront/server/modules/voice_agents_module/voice_agents_module/services/voice_agent_service.py
258-258: Avoid specifying long messages outside the exception class
(TRY003)
wavefront/server/modules/voice_agents_module/voice_agents_module/controllers/voice_agent_controller.py
443-445: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
446-448: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
481-481: Do not perform function call Path in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
482-484: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
485-487: Do not perform function call Depends in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
509-509: Do not catch blind exception: Exception
(BLE001)
510-510: Use explicit conversion flag
Replace with conversion flag
(RUF010)
514-514: Use explicit conversion flag
Replace with conversion flag
(RUF010)
🔇 Additional comments (55)
wavefront/server/modules/db_repo_module/db_repo_module/models/stt_config.py (1)
5-5: Import cleanup is correct with no broken references to removed fields.The removal of
Text(andjson) aligns with the removal of thelanguageandparametersfields. The remaining imports (String,func) are properly used. No code in the repository references the removedSttConfig.languageorSttConfig.parametersfields, confirming the refactoring was completed consistently across the codebase.wavefront/server/modules/db_repo_module/db_repo_module/models/tts_config.py (1)
1-47: LGTM!The model simplification is clean and aligns with the broader refactor moving language and voice configuration to the voice agent level. The
to_dictmethod correctly handles serialization and properly excludes the api_key by default for security.wavefront/server/modules/voice_agents_module/voice_agents_module/models/tts_schemas.py (1)
1-49: LGTM!The schema changes are consistent with the model refactor. The
UNSETsentinel pattern for partial updates is appropriate, and the response schema correctly excludes sensitiveapi_keydata.wavefront/server/modules/voice_agents_module/voice_agents_module/utils/cache_invalidation.py (1)
9-43: LGTM!The type expansion to accept
UUID | stris well implemented. The conversion logic correctly handles both types, and the change is backward compatible with existing UUID-based callers while enabling phone number identifiers for the newinbound_numberconfig type.wavefront/server/apps/call_processing/call_processing/constants/language_config.py (1)
1-100: Well-structured language configuration.Good inclusion of native script keywords (e.g.,
'हिंदी'for Hindi,'中文'for Chinese) alongside romanized forms—this improves detection accuracy when users specify their preferred language. The dual-language instructions (English + native) are a nice touch for clarity.wavefront/server/modules/voice_agents_module/voice_agents_module/services/telephony_config_service.py (1)
38-96: LGTM!The removal of
phone_numbersfromcreate_configaligns with the architectural change moving phone number management to voice agent records. The method signature and repository call are clean, and all callers have been updated accordingly.wavefront/server/modules/db_repo_module/db_repo_module/models/voice_agent.py (1)
37-51: New voice agent fields look well-structured.The new TTS/STT configuration fields and multi-language support are properly defined with appropriate JSONB types and sensible defaults. Using
default=lambda: ['en']correctly avoids the mutable default argument pitfall.wavefront/server/modules/voice_agents_module/voice_agents_module/utils/language_validation.py (2)
228-282: Validation logic handles edge cases appropriately.The approach of returning empty sets for unimplemented providers and treating all languages as valid in that case is a pragmatic choice that won't block functionality while still validating for known providers.
319-337: Well-implemented Oxford comma formatting.The
format_language_promptfunction correctly handles single, double, and multiple language cases with proper Oxford comma style.wavefront/server/modules/db_repo_module/db_repo_module/alembic/versions/2026_01_08_1547-6010e49da528_add_inbound_voice_agent_support.py (1)
23-49: Schema changes and default initialization look correct.The idempotent
ADD COLUMN IF NOT EXISTSapproach and the COALESCE-based default initialization handle re-runs gracefully.wavefront/server/modules/voice_agents_module/voice_agents_module/utils/cache_utils.py (1)
49-51: The concern about varying phone number formats is not applicable here. Phone numbers are validated to E.164 format (+[country code][number]) before reaching this function, as confirmed by thevalidate_phone_numbersfunction that enforces strict E.164 compliance. This standardized format eliminates the risk of cache misses due to format variations.However, note that this newly added function is not currently being imported or used anywhere in the codebase. The cache key pattern
f'inbound_number:{phone_number}'is hardcoded in multiple locations (voice_agent_service.py, voice_agent_cache_service.py, cache_controller.py) instead of using this function. Consider using this utility function in those places to maintain consistency and reduce duplication.Likely an incorrect or invalid review comment.
wavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/index.tsx (1)
78-85: LGTM!The filter logic correctly aligns with the simplified table columns. Search now covers the three relevant displayed fields: display name, description, and provider.
wavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/index.tsx (1)
78-85: LGTM!The filter logic is consistent with the TTS configs page and correctly removes language from search criteria to match the simplified table schema.
wavefront/client/src/pages/apps/[appId]/voice-agents/index.tsx (3)
12-12: LGTM!Import correctly added to support language name display in the tooltip.
137-139: LGTM!New table headers appropriately reflect the expanded voice agent data model with phone numbers and language support.
150-155: LGTM!Inbound and outbound number counts correctly use optional chaining with a
0fallback, providing accurate display when numbers aren't configured.wavefront/server/modules/db_repo_module/db_repo_module/models/telephony_config.py (1)
49-56: LGTM!The
to_dictmethod correctly updates JSON field parsing to exclude the removedphone_numbersfield, maintaining consistency with the schema migration.wavefront/client/src/pages/apps/[appId]/voice-agents/OutboundCallDialog.tsx (2)
24-28: LGTM!Props interface correctly simplified by removing the now-unnecessary
appIdprop, aligning with the refactored data flow.
30-37: LGTM!Clean refactoring that sources phone numbers directly from the voice agent rather than fetching from telephony config. This simplifies the component and reduces unnecessary data dependencies. The empty array fallback properly handles undefined/null cases.
wavefront/server/modules/voice_agents_module/voice_agents_module/controllers/tts_config_controller.py (1)
146-161: LGTM!The update logic correctly uses the
UNSETsentinel pattern for partial updates, only including fields that were explicitly provided in the payload. The early return with a 400 error when no fields are provided is appropriate.wavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/EditSttConfigDialog.tsx (3)
63-73: LGTM!The useEffect correctly resets the form when the dialog opens or when the config prop changes, ensuring the form always reflects the current config state.
135-152: Good UX improvement.Disabling the provider field and adding an explanatory description is a clear way to communicate that the provider cannot be changed after creation.
183-197: LGTM!The security note alert provides helpful guidance about API key handling. The placement within the FormItem for api_key is appropriate.
wavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/CreateTtsConfigDialog.tsx (4)
32-37: LGTM!The schema correctly defines the simplified TTS config fields with appropriate validation. The provider enum matches the expected TTS providers.
61-71: LGTM!The form reset on dialog close ensures a clean state for the next creation attempt.
73-91: LGTM!The submit handler correctly sends the simplified payload with proper trimming of string values. Error handling is appropriate.
172-186: LGTM!The API key field is correctly marked as required for creation (unlike edit where it's optional). The password input type appropriately masks the key.
wavefront/server/modules/voice_agents_module/voice_agents_module/models/stt_schemas.py (3)
35-38: LGTM!The
UpdateSttConfigPayloadcorrectly uses theUNSETsentinel pattern for partial updates. TheUnion[str, Any]typing allows for distinguishing between "field not provided" (UNSET) and "field explicitly set to None".
41-48: LGTM!The
SttConfigResponsemodel is appropriately simplified, removing thelanguageandparametersfields that are now managed at the voice agent level.
11-16: Hardcoded Zod schema creates maintenance risk: Update to derive provider validation fromVOICE_PROVIDERS_CONFIG.The client limits STT providers to
['deepgram']via hardcoded Zod schema, while the dialog renders providers dynamically fromVOICE_PROVIDERS_CONFIG.stt.providers. This disconnect means ifVOICE_PROVIDERS_CONFIGis updated with new providers, the Zod validation inCreateSttConfigDialog.tsx(line 32) andEditSttConfigDialog.tsx(line 26) will silently reject them.Additionally, the server's
SttProviderenum defines 5 providers (deepgram, assemblyai, whisper, google, azure) but the client only exposes deepgram. This mismatch suggests these other providers aren't yet implemented in pipecat. Either the server enum should document this limitation, or the Zod schema should derive its allowed values fromVOICE_PROVIDERS_CONFIG.stt.providersto avoid future sync issues.wavefront/client/src/pages/apps/[appId]/voice-agents/stt-configs/CreateSttConfigDialog.tsx (3)
61-71: LGTM!Form reset on dialog close is correctly implemented.
188-192: LGTM!The security alert provides appropriate guidance about API key handling.
198-200: LGTM!The submit button correctly shows loading state and disables during submission to prevent double-clicks.
wavefront/server/modules/voice_agents_module/voice_agents_module/utils/phone_validation.py (3)
6-9: LGTM! E.164 regex pattern is correct.The regex
^\+[1-9]\d{1,14}$correctly validates E.164 format: starts with+, followed by a digit 1-9 (country codes cannot start with 0), then 1-14 additional digits for a total of up to 15 digits.
12-29: LGTM!Clean implementation with thorough docstring and examples.
32-67: LGTM!Well-structured validation with clear error messages that include the offending values and expected format examples. The tuple return pattern
(is_valid, error_message)is clean.wavefront/server/modules/voice_agents_module/voice_agents_module/models/telephony_schemas.py (1)
1-5: LGTM! Schema cleanup aligns with PR objectives.Removing
phone_numbersfrom telephony schemas is consistent with the architectural change to manage phone numbers at the voice agent level instead of telephony configs.wavefront/server/apps/call_processing/call_processing/controllers/cache_controller.py (2)
30-30: LGTM! Good documentation of the type change.The comment clearly explains the dual-purpose nature of
config_id(UUID string for standard configs, phone number forinbound_numbertype).
118-136: LGTM! Clean separation for inbound_number handling.The early return for
inbound_numbertype correctly bypasses UUID validation and Floware refresh since inbound number mappings don't need to be re-fetched from the API.wavefront/server/apps/call_processing/call_processing/services/floware_http_client.py (1)
173-216: LGTM! Robust response handling.The method correctly handles multiple response formats (wrapped/unwrapped, dict/string) and gracefully returns empty string on 404. This defensive approach is appropriate for optional resources like welcome audio.
wavefront/client/src/constants/languages.ts (3)
56-65: LGTM! Clean utility functions with graceful fallbacks.The functions handle unknown codes gracefully by returning the input code, preventing UI errors when encountering unexpected language codes.
12-54: LGTM! Comprehensive language support.Good coverage of major world languages with native names for accessibility. The list includes both widely-spoken languages and regional Indian languages, aligning well with multi-language voice agent requirements.
1-4: Add missing languages to match backend support or clarify the language scope.The frontend
SUPPORTED_LANGUAGESlist is missing two languages that the backend supports:ka(Georgian) andtl(Tagalog). The current comment claims the list "Matches backend language validation," but the backend'sLANGUAGE_NAMESdictionary inlanguage_validation.pycontains 43 languages while the frontend only exposes 41. Either add these languages to the frontend list for full parity with backend capabilities, or update the comment to accurately reflect that the frontend list is a subset of backend-supported languages.wavefront/server/modules/voice_agents_module/voice_agents_module/models/voice_agent_schemas.py (2)
93-100: LGTM!The response schema correctly defines non-optional types for list fields (
inbound_numbers,outbound_numbers,supported_languages) anddefault_language, ensuring consistent API responses even when the underlying data is empty.
52-59: Validation already exists in the service layer.The constraint that
default_languagemust be insupported_languagesis already validated viavalidate_default_language()in the service layer (voice_agent_service.py, line 205-209). However, adding a Pydanticmodel_validatorat the schema level would still be beneficial for earlier validation and stricter API contract enforcement, as it would catch violations before they reach the service layer.wavefront/server/apps/call_processing/call_processing/services/voice_agent_cache_service.py (3)
312-315: Broad exception handling is acceptable for cache resilience.The
except Exceptioncatch here is intentional for fault tolerance - a failure to fetch the agent from API shouldn't crash the inbound lookup flow. The error is logged with context, and the code falls through to the cache-miss path. This pattern aligns with the service's fault-tolerant design.
266-283: LGTM on the caching strategy.The two-level caching approach (inbound number → agent ID mapping, plus full agent data cache) is efficient and well-documented. The docstring clearly explains the cache-first strategy with API fallback.
331-336: No action needed. Thecache_manager.add()method usesnx=Falseby default, which means it will overwrite existing values in Redis (not skip them if the key exists). If an inbound number's agent assignment changes, the mapping will be updated immediately on the next call, not persist as stale until TTL expires. The method name is somewhat misleading, but the current implementation correctly handles cache updates.Likely an incorrect or invalid review comment.
wavefront/server/modules/voice_agents_module/voice_agents_module/controllers/stt_config_controller.py (2)
43-48: LGTM on the simplified create flow.The controller now correctly delegates only credential-related fields (
display_name,description,provider,api_key) to the service, aligning with the PR's objective to move language/parameters to the voice agent level.
146-161: Clean partial update implementation.The update logic correctly excludes
providerfrom updatable fields (making it immutable after creation) and only includes fields that are explicitly set. This aligns with the frontend behavior where the provider field is disabled in the edit dialog.wavefront/client/src/pages/apps/[appId]/voice-agents/tts-configs/EditTtsConfigDialog.tsx (2)
75-97: LGTM on the submit handler.The update payload correctly excludes
provider(since it's immutable) and conditionally includesapi_keyonly when provided, preserving the existing key if left empty. This is clean and user-friendly.
127-156: Good UX for immutable provider field.The disabled provider select with clear explanation ("Provider cannot be changed after creation") provides good user feedback. This is consistent with the backend behavior where provider updates are not accepted.
wavefront/client/src/types/voice-agent.ts (2)
16-24: LGTM on the VoiceAgent interface extensions.The new fields are well-typed and consistent with the server-side
VoiceAgentResponseschema:
- Non-optional arrays for
inbound_numbers,outbound_numbers,supported_languages- Non-optional
default_languageandtts_voice_id- Nullable parameter objects for
tts_parametersandstt_parameters
42-50: Request payloads align with server schemas.The
tts_voice_idis correctly required inCreateVoiceAgentRequestwhile language and phone number fields are optional, matching the server-sideCreateVoiceAgentPayloadwheretts_voice_idis a required field and others have defaults or are optional.wavefront/server/apps/call_processing/call_processing/services/pipecat_service.py (1)
344-357: Keyword-based language detection may produce false positives.The language detection relies on matching keywords from
LANGUAGE_KEYWORDSagainst user messages. Common words could trigger incorrect detections (e.g., "yes" appearing in multiple languages, or loan words). Consider adding a confidence threshold or using the LLM for language detection in ambiguous cases.
| <TableCell> | ||
| <span | ||
| className="text-sm text-gray-600" | ||
| title={agent.supported_languages?.map(getLanguageName).join(', ')} | ||
| > | ||
| {agent.supported_languages?.length || 1} ({agent.default_language || 'en'}) | ||
| </span> | ||
| </TableCell> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Misleading fallback value for language count.
The fallback || 1 on line 161 displays "1" when supported_languages is undefined or empty, which may mislead users into thinking a language is configured when none actually is. Consider using || 0 or displaying a dash for consistency with other empty states.
Suggested fix
<TableCell>
<span
className="text-sm text-gray-600"
title={agent.supported_languages?.map(getLanguageName).join(', ')}
>
- {agent.supported_languages?.length || 1} ({agent.default_language || 'en'})
+ {agent.supported_languages?.length || 0} ({agent.default_language || 'en'})
</span>
</TableCell>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <TableCell> | |
| <span | |
| className="text-sm text-gray-600" | |
| title={agent.supported_languages?.map(getLanguageName).join(', ')} | |
| > | |
| {agent.supported_languages?.length || 1} ({agent.default_language || 'en'}) | |
| </span> | |
| </TableCell> | |
| <TableCell> | |
| <span | |
| className="text-sm text-gray-600" | |
| title={agent.supported_languages?.map(getLanguageName).join(', ')} | |
| > | |
| {agent.supported_languages?.length || 0} ({agent.default_language || 'en'}) | |
| </span> | |
| </TableCell> |
🤖 Prompt for AI Agents
In @wavefront/client/src/pages/apps/[appId]/voice-agents/index.tsx around lines
156 - 163, The count fallback currently uses agent.supported_languages?.length
|| 1 which incorrectly shows "1" when supported_languages is undefined or empty;
update the span to show a truthful empty state by using
agent.supported_languages?.length ?? 0 (or render a dash '-') and keep the title
safe by guarding the join (e.g.,
title={agent.supported_languages?.map(getLanguageName).join(', ') || '-'}) so
the TableCell/span displays 0 or '-' when no languages exist and still shows the
default_language (agent.default_language || 'en') as before.
| 'fil': ['filipino', 'pilipino', 'tagalog'], | ||
| 'tl': ['tagalog', 'filipino'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential ambiguity between fil and tl language codes.
Both fil (Filipino) and tl (Tagalog) share overlapping keywords ('tagalog', 'filipino'), which could cause non-deterministic behavior in language detection. When a user mentions "tagalog" or "filipino", the detected language depends on dictionary iteration order rather than explicit logic.
Consider either:
- Consolidating to a single code (e.g., keep only
filas the modern ISO 639-1 code for Filipino/Tagalog) - Differentiating keywords so each code has unique detection triggers
🤖 Prompt for AI Agents
In
@wavefront/server/apps/call_processing/call_processing/constants/language_config.py
around lines 33 - 34, The language alias mapping contains overlapping entries
for 'fil' and 'tl' (both include 'tagalog'/'filipino'), causing
non-deterministic detection; update the language aliases dict (the mapping that
defines 'fil' and 'tl') to remove the ambiguity by either consolidating into a
single canonical code (e.g., keep only 'fil' and drop the 'tl' key) or by making
their keyword lists mutually exclusive (give each key unique trigger words), and
update any related comments/tests that reference 'fil' or 'tl' to reflect the
new canonical choice.
| def downgrade() -> None: | ||
| # Restore phone_numbers column to telephony_configs table | ||
| op.add_column( | ||
| 'telephony_configs', sa.Column('phone_numbers', sa.Text(), nullable=True) | ||
| ) | ||
|
|
||
| # Restore TTS/STT config columns | ||
| op.add_column( | ||
| 'tts_configs', sa.Column('voice_id', sa.String(length=255), nullable=True) | ||
| ) | ||
| op.add_column( | ||
| 'tts_configs', sa.Column('language', sa.String(length=64), nullable=True) | ||
| ) | ||
| op.add_column('tts_configs', sa.Column('parameters', sa.Text(), nullable=True)) | ||
| op.add_column( | ||
| 'stt_configs', sa.Column('language', sa.String(length=64), nullable=True) | ||
| ) | ||
| op.add_column('stt_configs', sa.Column('parameters', sa.Text(), nullable=True)) | ||
|
|
||
| # Drop columns from voice_agents | ||
| op.drop_column('voice_agents', 'default_language') | ||
| op.drop_column('voice_agents', 'supported_languages') | ||
| op.drop_column('voice_agents', 'outbound_numbers') | ||
| op.drop_column('voice_agents', 'inbound_numbers') | ||
| op.drop_column('voice_agents', 'stt_parameters') | ||
| op.drop_column('voice_agents', 'tts_parameters') | ||
| op.drop_column('voice_agents', 'tts_voice_id') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Downgrade does not restore migrated data.
The upgrade migrates voice_id, parameters from tts_configs and stt_configs into voice_agents, then drops those columns. The downgrade restores the column structure but not the data. If a rollback is needed, this data will be lost.
Consider adding a reverse data migration or documenting this as a one-way migration.
🤖 Prompt for AI Agents
In
@wavefront/server/modules/db_repo_module/db_repo_module/alembic/versions/2026_01_08_1547-6010e49da528_add_inbound_voice_agent_support.py
around lines 152 - 178, The downgrade currently restores columns but fails to
repopulate them from the new voice_agents table, causing data loss; update
downgrade() to migrate data back before dropping voice_agents columns by: after
adding back columns (tts_configs.voice_id, tts_configs.parameters,
stt_configs.language, stt_configs.parameters, telephony_configs.phone_numbers)
run SQL updates (via op.execute) that join voice_agents to
tts_configs/stt_configs/telephony_configs and set the restored columns from
voice_agents.tts_voice_id, voice_agents.tts_parameters,
voice_agents.stt_parameters, voice_agents.default_language (and
supported_languages/outbound_numbers/inbound_numbers as appropriate), then only
drop the columns from voice_agents; alternatively document this as irreversible
if you decide not to implement the reverse migration.
| # Provider language support (extracted from pipecat language mappings) | ||
| ELEVENLABS_LANGUAGES: Set[str] = { | ||
| 'ar', | ||
| 'bg', | ||
| 'cs', | ||
| 'da', | ||
| 'de', | ||
| 'el', | ||
| 'en', | ||
| 'es', | ||
| 'fi', | ||
| 'fil', | ||
| 'fr', | ||
| 'hi', | ||
| 'hr', | ||
| 'hu', | ||
| 'id', | ||
| 'it', | ||
| 'ja', | ||
| 'ko', | ||
| 'ms', | ||
| 'nl', | ||
| 'no', | ||
| 'pl', | ||
| 'pt', | ||
| 'ro', | ||
| 'ru', | ||
| 'sk', | ||
| 'sv', | ||
| 'ta', | ||
| 'tr', | ||
| 'uk', | ||
| 'vi', | ||
| 'zh', | ||
| } | ||
|
|
||
| CARTESIA_LANGUAGES: Set[str] = { | ||
| 'ar', | ||
| 'bg', | ||
| 'bn', | ||
| 'cs', | ||
| 'da', | ||
| 'de', | ||
| 'en', | ||
| 'el', | ||
| 'es', | ||
| 'fi', | ||
| 'fr', | ||
| 'gu', | ||
| 'he', | ||
| 'hi', | ||
| 'hr', | ||
| 'hu', | ||
| 'id', | ||
| 'it', | ||
| 'ja', | ||
| 'ka', | ||
| 'kn', | ||
| 'ko', | ||
| 'ml', | ||
| 'mr', | ||
| 'ms', | ||
| 'nl', | ||
| 'no', | ||
| 'pa', | ||
| 'pl', | ||
| 'pt', | ||
| 'ro', | ||
| 'ru', | ||
| 'sk', | ||
| 'sv', | ||
| 'ta', | ||
| 'te', | ||
| 'th', | ||
| 'tl', | ||
| 'tr', | ||
| 'uk', | ||
| 'vi', | ||
| 'zh', | ||
| } | ||
|
|
||
| # Deepgram STT supports 40+ languages | ||
| DEEPGRAM_STT_LANGUAGES: Set[str] = { | ||
| 'ar', | ||
| 'bg', | ||
| 'ca', | ||
| 'cs', | ||
| 'da', | ||
| 'de', | ||
| 'el', | ||
| 'en', | ||
| 'es', | ||
| 'et', | ||
| 'fi', | ||
| 'fr', | ||
| 'hi', | ||
| 'hu', | ||
| 'id', | ||
| 'it', | ||
| 'ja', | ||
| 'ko', | ||
| 'lt', | ||
| 'lv', | ||
| 'ms', | ||
| 'nl', | ||
| 'no', | ||
| 'pl', | ||
| 'pt', | ||
| 'ro', | ||
| 'ru', | ||
| 'sk', | ||
| 'sv', | ||
| 'ta', | ||
| 'te', | ||
| 'th', | ||
| 'tr', | ||
| 'uk', | ||
| 'vi', | ||
| 'zh', | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
ElevenLabs TTS supported languages 2025 official documentation
💡 Result:
As of the official ElevenLabs documentation (checked Jan 12, 2026):
- Eleven v3: supports 74 languages. [1]
- Multilingual v2: supports 29 languages. [1][2]
- Flash v2.5 / Turbo v2.5: supports 32 languages (Flash v2.5 specifically listed as 32). [1][2]
For the full per-language lists, see ElevenLabs' support & docs pages. [1][2]
Sources:
[1] ElevenLabs Help — "What languages do you support?".
[2] ElevenLabs Documentation — "Text to Speech / Supported languages".
🌐 Web query:
Cartesia text-to-speech supported languages 2025
💡 Result:
As of 2025 Cartesia’s Sonic TTS lineup (Sonic-3 / Sonic series) supports the following languages:
English (en), French (fr), German (de), Spanish (es), Portuguese (pt), Chinese (zh), Japanese (ja), Hindi (hi), Italian (it), Korean (ko), Dutch (nl), Polish (pl), Russian (ru), Swedish (sv), Turkish (tr), Tagalog (tl), Bulgarian (bg), Romanian (ro), Arabic (ar), Czech (cs), Greek (el), Finnish (fi), Croatian (hr), Malay (ms), Slovak (sk), Danish (da), Tamil (ta), Ukrainian (uk), Hungarian (hu), Norwegian (no), Vietnamese (vi), Bengali (bn), Thai (th), Hebrew (he), Georgian (ka), Indonesian (id), Telugu (te), Gujarati (gu), Kannada (kn), Malayalam (ml), Marathi (mr), Punjabi (pa).
Sources: Cartesia TTS / Sonic docs (language support lists). [1][2]
References
[1] Cartesia Sonic-3 language support documentation. (docs.cartesia.ai)
[2] Cartesia TTS / Sonic model pages (Sonic / Sonic-2 / sonic-turbo). (docs.cartesia.ai)
Citations:
- 1: https://docs.cartesia.ai/build-with-cartesia/tts-models?utm_source=openai
- 2: https://docs.cartesia.ai/build-with-cartesia/models/tts?utm_source=openai
🌐 Web query:
Deepgram STT supported languages 2025 official documentation
💡 Result:
The official 2025 Deepgram Speech-to-Text supported languages are listed in Deepgram's documentation:
- Models & Languages Overview (full per-model language list). [1]
- Language Detection supported languages (detection-specific list). [2]
If you want, I can extract and paste the full language list from the Models & Languages page here.
Sources:
[1] Deepgram — Models & Languages Overview. [developers.deepgram.com/docs/models-languages-overview]
[2] Deepgram — Language Detection (supported languages). [developers.deepgram.com/docs/language-detection]
🏁 Script executed:
# Count the languages in each provider set from the code
cat > /tmp/count_languages.py << 'EOF'
elevenlabs = {
'ar', 'bg', 'cs', 'da', 'de', 'el', 'en', 'es', 'fi', 'fil',
'fr', 'hi', 'hr', 'hu', 'id', 'it', 'ja', 'ko', 'ms', 'nl',
'no', 'pl', 'pt', 'ro', 'ru', 'sk', 'sv', 'ta', 'tr', 'uk',
'vi', 'zh'
}
cartesia = {
'ar', 'bg', 'bn', 'cs', 'da', 'de', 'en', 'el', 'es', 'fi',
'fr', 'gu', 'he', 'hi', 'hr', 'hu', 'id', 'it', 'ja', 'ka',
'kn', 'ko', 'ml', 'mr', 'ms', 'nl', 'no', 'pa', 'pl', 'pt',
'ro', 'ru', 'sk', 'sv', 'ta', 'te', 'th', 'tl', 'tr', 'uk',
'vi', 'zh'
}
deepgram = {
'ar', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'et',
'fi', 'fr', 'hi', 'hu', 'id', 'it', 'ja', 'ko', 'lt', 'lv',
'ms', 'nl', 'no', 'pl', 'pt', 'ro', 'ru', 'sk', 'sv', 'ta',
'te', 'th', 'tr', 'uk', 'vi', 'zh'
}
print(f"ElevenLabs: {len(elevenlabs)} languages")
print(f"Cartesia: {len(cartesia)} languages")
print(f"Deepgram: {len(deepgram)} languages")
# Also show which languages are in code
print("\nElevenLabs languages:", sorted(elevenlabs))
print("\nCartesia languages:", sorted(cartesia))
print("\nDeepgram languages:", sorted(deepgram))
EOF
python /tmp/count_languages.pyRepository: rootflo/wavefront
Length of output: 147
🌐 Web query:
ElevenLabs supported languages complete list 2025 multilingual v2 model
💡 Result:
ElevenLabs Multilingual v2 — supported languages (29 total, per ElevenLabs docs, January 2026):
- Arabic (ARA)
- Bulgarian (BUL)
- Chinese / Mandarin (CMN)
- Croatian (HRV)
- Czech (CES)
- Danish (DAN)
- Dutch (NLD)
- English (ENG: USA, UK, Australia, Canada)
- Filipino (FIL)
- Finnish (FIN)
- French (FRA: France, Canada)
- German (DEU)
- Greek (ELL)
- Hindi (HIN)
- Indonesian (IND)
- Italian (ITA)
- Japanese (JPN)
- Korean (KOR)
- Malay (MSA)
- Polish (POL)
- Portuguese (POR: Brazil, Portugal)
- Romanian (RON)
- Russian (RUS)
- Slovak (SLK)
- Spanish (SPA: Spain, Mexico)
- Swedish (SWE)
- Tamil (TAM)
- Turkish (TUR)
- Ukrainian (UKR)
Sources: ElevenLabs model docs and Help Center. [1][2]
References
[1] ElevenLabs Models documentation — Multilingual v2 (supported languages).
[2] ElevenLabs Help Center — “What languages do you support?”
🌐 Web query:
Deepgram speech-to-text supported languages complete list 2025 nova model
💡 Result:
Do you mean Nova-2 or Nova-3? Quick answer below; I can expand with full language codes or a single combined list once you confirm which Nova you want.
-
Nova-2 (as announced / documented May 14, 2024) — 36 languages: Bulgarian; Catalan; Chinese (Mandarin, Simplified); Chinese (Mandarin, Traditional); Czech; Danish; Dutch; English; Estonian; Finnish; Flemish; French; German; German (Switzerland); Greek; Hindi; Hungarian; Indonesian; Italian; Japanese; Korean; Latvian; Lithuanian; Malay; Norwegian; Polish; Portuguese; Romanian; Russian; Slovak; Spanish; Swedish; Thai; Turkish; Ukrainian; Vietnamese. [1]
-
Nova-3 (highest‑performing Nova model; supports a multilingual "multi" option plus many per‑language models — documented with staged language rollouts through 2025). See the Models & Languages overview for Nova-3 supported languages and the changelog for recent additions. [2]
Sources:
[1] Deepgram announcement / changelog — "Nova-2 Now Supports 36 Languages" (May 14, 2024).
[2] Deepgram Developers — Models & Languages overview / Nova-3 docs and changelog (2025 updates).
🏁 Script executed:
# Search for ElevenLabs model references in the codebase
rg -i "eleven|multilingual|flash|v2|v3" --type py -A 2 -B 2 | head -100Repository: rootflo/wavefront
Length of output: 10974
🏁 Script executed:
# Look for voice agent implementations that might specify the model
find . -type f -name "*.py" | xargs grep -l "elevenlabs\|ElevenLabs" | head -10Repository: rootflo/wavefront
Length of output: 426
🏁 Script executed:
# Check the imports and usage in the language_validation file
head -60 wavefront/server/modules/voice_agents_module/voice_agents_module/utils/language_validation.pyRepository: rootflo/wavefront
Length of output: 1333
Remove unsupported languages from ELEVENLABS_LANGUAGES or verify against current ElevenLabs model.
The code uses eleven_multilingual_v2 model by default, which officially supports 29 languages per ElevenLabs documentation (Jan 2026). However, ELEVENLABS_LANGUAGES includes 32 languages with three unsupported additions: 'hi' (Hindi), 'no' (Norwegian), 'vi' (Vietnamese). This mismatch could allow users to configure languages that will fail at the API level.
CARTESIA_LANGUAGES and DEEPGRAM_STT_LANGUAGES appear accurate against current provider documentation.
🤖 Prompt for AI Agents
In
@wavefront/server/modules/voice_agents_module/voice_agents_module/utils/language_validation.py
around lines 52 - 171, ELEVENLABS_LANGUAGES contains entries that don't match
the eleven_multilingual_v2 model; update ELEVENLABS_LANGUAGES to exactly reflect
ElevenLabs' supported languages (remove 'hi', 'no', 'vi' or any other
unsupported codes) or add a verification step that checks the configured
language against the live ElevenLabs model before use; locate and edit the
ELEVENLABS_LANGUAGES constant in language_validation.py and ensure any caller
(e.g., functions that reference ELEVENLABS_LANGUAGES) fails fast with a clear
error if an unsupported language is requested.
Summary by CodeRabbit
New Features
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.