From 1babab2fad6f14a3f8bb148df4270bb3d7bf54ea Mon Sep 17 00:00:00 2001 From: Udit Takkar Date: Tue, 16 Sep 2025 14:49:43 +0530 Subject: [PATCH 01/38] feat: lang support --- .../retellAI/RetellAIPhoneServiceProvider.ts | 3 +- .../providers/retellAI/RetellAIService.ts | 1 + .../retellAI/services/AgentService.ts | 14 ++-- .../components/AgentConfigurationSheet.tsx | 65 ++++++++++++++++++- .../viewer/aiVoiceAgent/update.handler.ts | 1 + .../viewer/aiVoiceAgent/update.schema.ts | 42 ++++++++++++ 6 files changed, 119 insertions(+), 7 deletions(-) diff --git a/packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts b/packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts index 5e280cff82c090..d4dfc4e892eb82 100644 --- a/packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts +++ b/packages/features/calAIPhone/providers/retellAI/RetellAIPhoneServiceProvider.ts @@ -22,7 +22,7 @@ import type { AgentRepositoryInterface } from "../interfaces/AgentRepositoryInte import type { PhoneNumberRepositoryInterface } from "../interfaces/PhoneNumberRepositoryInterface"; import type { TransactionInterface } from "../interfaces/TransactionInterface"; import { RetellAIService } from "./RetellAIService"; -import type { RetellAIRepository } from "./types"; +import type { RetellAIRepository, Language } from "./types"; export class RetellAIPhoneServiceProvider implements AIPhoneServiceProvider @@ -243,6 +243,7 @@ export class RetellAIPhoneServiceProvider beginMessage?: string | null; generalTools?: AIPhoneServiceTools; voiceId?: string; + language?: Language; }): Promise<{ message: string }> { return await this.service.updateAgentConfiguration(params); } diff --git a/packages/features/calAIPhone/providers/retellAI/RetellAIService.ts b/packages/features/calAIPhone/providers/retellAI/RetellAIService.ts index 850e59c8f6eb19..3d004e7b69df82 100644 --- a/packages/features/calAIPhone/providers/retellAI/RetellAIService.ts +++ b/packages/features/calAIPhone/providers/retellAI/RetellAIService.ts @@ -171,6 +171,7 @@ export class RetellAIService { beginMessage?: string | null; generalTools?: RetellLLMGeneralTools; voiceId?: string; + language?: Language; }) { return this.agentService.updateAgentConfiguration({ ...params, diff --git a/packages/features/calAIPhone/providers/retellAI/services/AgentService.ts b/packages/features/calAIPhone/providers/retellAI/services/AgentService.ts index 666c326f64d468..6c867c9c56a061 100644 --- a/packages/features/calAIPhone/providers/retellAI/services/AgentService.ts +++ b/packages/features/calAIPhone/providers/retellAI/services/AgentService.ts @@ -507,6 +507,7 @@ export class AgentService { beginMessage, generalTools, voiceId, + language, updateLLMConfiguration, }: { id: string; @@ -517,6 +518,7 @@ export class AgentService { beginMessage?: string | null; generalTools?: AIPhoneServiceTools; voiceId?: string; + language?: Language; updateLLMConfiguration: ( llmId: string, data: AIPhoneServiceUpdateModelParams @@ -539,7 +541,8 @@ export class AgentService { generalPrompt !== undefined || beginMessage !== undefined || generalTools !== undefined || - voiceId !== undefined; + voiceId !== undefined || + language !== undefined; if (hasRetellUpdates) { try { @@ -558,10 +561,11 @@ export class AgentService { await updateLLMConfiguration(llmId, llmUpdateData); } - if (voiceId) { - await this.updateAgent(agent.providerAgentId, { - voice_id: voiceId, - }); + if (voiceId || language) { + const agentUpdateData: Parameters[1] = {}; + if (voiceId) agentUpdateData.voice_id = voiceId; + if (language) agentUpdateData.language = language; + await this.updateAgent(agent.providerAgentId, agentUpdateData); } } catch (error) { this.logger.error("Failed to update agent configuration in external AI service", { diff --git a/packages/features/ee/workflows/components/AgentConfigurationSheet.tsx b/packages/features/ee/workflows/components/AgentConfigurationSheet.tsx index 5d1101bbb1c24b..7d07c1f990eb45 100644 --- a/packages/features/ee/workflows/components/AgentConfigurationSheet.tsx +++ b/packages/features/ee/workflows/components/AgentConfigurationSheet.tsx @@ -6,6 +6,7 @@ import type { UseFormReturn } from "react-hook-form"; import { z } from "zod"; import { Dialog } from "@calcom/features/components/controlled-dialog"; +import type { Language } from "@calcom/features/calAIPhone/providers/retellAI/types"; import { CAL_AI_PHONE_NUMBER_MONTHLY_PRICE } from "@calcom/lib/constants"; import { formatPhoneNumber } from "@calcom/lib/formatPhoneNumber"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -27,7 +28,7 @@ import { } from "@calcom/ui/components/dropdown"; import { AddVariablesDropdown } from "@calcom/ui/components/editor"; import { ToggleGroup, Switch } from "@calcom/ui/components/form"; -import { Label, TextArea, Input, TextField, Form } from "@calcom/ui/components/form"; +import { Label, TextArea, Input, TextField, Form, Select } from "@calcom/ui/components/form"; import { Icon } from "@calcom/ui/components/icon"; import { Sheet, @@ -68,10 +69,51 @@ const restorePromptComplexity = (prompt: string): string => { return restoredPrompt; }; +// Language options for the dropdown +const LANGUAGE_OPTIONS = [ + { value: "en-US", label: "English (US)" }, + { value: "en-IN", label: "English (India)" }, + { value: "en-GB", label: "English (UK)" }, + { value: "en-AU", label: "English (Australia)" }, + { value: "en-NZ", label: "English (New Zealand)" }, + { value: "de-DE", label: "German" }, + { value: "es-ES", label: "Spanish (Spain)" }, + { value: "es-419", label: "Spanish (Latin America)" }, + { value: "hi-IN", label: "Hindi" }, + { value: "fr-FR", label: "French (France)" }, + { value: "fr-CA", label: "French (Canada)" }, + { value: "ja-JP", label: "Japanese" }, + { value: "pt-PT", label: "Portuguese (Portugal)" }, + { value: "pt-BR", label: "Portuguese (Brazil)" }, + { value: "zh-CN", label: "Chinese (Simplified)" }, + { value: "ru-RU", label: "Russian" }, + { value: "it-IT", label: "Italian" }, + { value: "ko-KR", label: "Korean" }, + { value: "nl-NL", label: "Dutch (Netherlands)" }, + { value: "nl-BE", label: "Dutch (Belgium)" }, + { value: "pl-PL", label: "Polish" }, + { value: "tr-TR", label: "Turkish" }, + { value: "th-TH", label: "Thai" }, + { value: "vi-VN", label: "Vietnamese" }, + { value: "ro-RO", label: "Romanian" }, + { value: "bg-BG", label: "Bulgarian" }, + { value: "ca-ES", label: "Catalan" }, + { value: "da-DK", label: "Danish" }, + { value: "fi-FI", label: "Finnish" }, + { value: "el-GR", label: "Greek" }, + { value: "hu-HU", label: "Hungarian" }, + { value: "id-ID", label: "Indonesian" }, + { value: "no-NO", label: "Norwegian" }, + { value: "sk-SK", label: "Slovak" }, + { value: "sv-SE", label: "Swedish" }, + { value: "multi", label: "Multilingual" }, +]; + const agentSchema = z.object({ generalPrompt: z.string().min(1, "General prompt is required"), beginMessage: z.string().min(1, "Begin message is required"), numberToCall: z.string().optional(), + language: z.string().optional(), generalTools: z .array( z.object({ @@ -159,6 +201,7 @@ export function AgentConfigurationSheet({ generalPrompt: "", beginMessage: "", numberToCall: "", + language: "en-US", generalTools: [], }, }); @@ -170,6 +213,7 @@ export function AgentConfigurationSheet({ generalPrompt: cleanPromptForDisplay(retellData.generalPrompt || ""), beginMessage: retellData.beginMessage || "", numberToCall: "", + language: retellData.language || "en-US", generalTools: retellData.generalTools || [], }); } @@ -289,6 +333,7 @@ export function AgentConfigurationSheet({ const updatePayload = { generalPrompt: restorePromptComplexity(data.generalPrompt), beginMessage: data.beginMessage, + language: data.language as Language, // Don't include generalTools - they are managed by the backend }; @@ -434,6 +479,24 @@ export function AgentConfigurationSheet({ {activeTab === "prompt" && (
+
+ + ( + { + if (val) { + // Update selected event type + field.onChange([val]); + // Trigger workflow update if agent exists + if (inboundAgentId && val.value) { + updateInboundAgentPromptMutation.mutate({ + agentId: inboundAgentId, + eventTypeId: parseInt(val.value), + }); + } + } + }} + defaultValue={selectedEventType} + value={selectedEventType} + options={eventTypeOptions} + placeholder={t("select_event_type")} + /> + ); + }} + /> +
+ + {/* Language Selection */} +
+ + ( + +
+ + {/* General Prompt */} +
+
+ +

{t("general_prompt_description")}

+
+
+ {!readOnly && ( + + )} +
+