From e7adbc7bad9c409265efe5b60692fdfcf2fa9875 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Thu, 2 Apr 2026 14:48:13 +0530 Subject: [PATCH] feat: add default telephony variables --- api/routes/telephony.py | 14 ++++++++++++-- .../campaign/campaign_call_dispatcher.py | 2 ++ api/services/telephony/base.py | 1 + .../telephony/providers/ari_provider.py | 1 + .../telephony/providers/cloudonix_provider.py | 1 + .../telephony/providers/telnyx_provider.py | 1 + .../telephony/providers/twilio_provider.py | 1 + .../telephony/providers/vobiz_provider.py | 1 + .../telephony/providers/vonage_provider.py | 1 + docs/core-concepts/context-and-variables.mdx | 19 +++++++++++++++++-- 10 files changed, 38 insertions(+), 4 deletions(-) diff --git a/api/routes/telephony.py b/api/routes/telephony.py index 723a6cd..475f40a 100644 --- a/api/routes/telephony.py +++ b/api/routes/telephony.py @@ -187,6 +187,7 @@ async def initiate_call( call_type=CallType.OUTBOUND, initial_context={ "phone_number": phone_number, + "called_number": phone_number, "provider": provider.PROVIDER_NAME, }, ) @@ -220,13 +221,22 @@ async def initiate_call( **keywords, ) - # Store provider type and any provider-specific metadata in workflow run context + # Store provider metadata and caller_number in workflow run context gathered_context = { "provider": provider.PROVIDER_NAME, **(result.provider_metadata or {}), } + # Merge caller_number into initial_context now that we know which number was used + updated_initial_context = { + **(workflow_run.initial_context or {}), + "called_number": phone_number, + } + if result.caller_number: + updated_initial_context["caller_number"] = result.caller_number await db_client.update_workflow_run( - run_id=workflow_run_id, gathered_context=gathered_context + run_id=workflow_run_id, + gathered_context=gathered_context, + initial_context=updated_initial_context, ) return {"message": f"Call initiated successfully with run name {workflow_run_name}"} diff --git a/api/services/campaign/campaign_call_dispatcher.py b/api/services/campaign/campaign_call_dispatcher.py index ce393a7..505c7f4 100644 --- a/api/services/campaign/campaign_call_dispatcher.py +++ b/api/services/campaign/campaign_call_dispatcher.py @@ -205,6 +205,8 @@ class CampaignCallDispatcher: "campaign_id": campaign.id, "provider": provider.PROVIDER_NAME, "source_uuid": queued_run.source_uuid, + "caller_number": from_number, + "called_number": phone_number, } logger.info(f"Final initial_context: {initial_context}") diff --git a/api/services/telephony/base.py b/api/services/telephony/base.py index a154946..3c75865 100644 --- a/api/services/telephony/base.py +++ b/api/services/telephony/base.py @@ -18,6 +18,7 @@ class CallInitiationResult: call_id: str # Provider's call identifier (SID for Twilio, UUID for Vonage) status: str # Initial status (e.g., "queued", "initiated", "started") + caller_number: Optional[str] = None # Caller ID used for the outbound call provider_metadata: Dict[str, Any] = field( default_factory=dict ) # Data that needs to be persisted diff --git a/api/services/telephony/providers/ari_provider.py b/api/services/telephony/providers/ari_provider.py index 4e2071c..125fddb 100644 --- a/api/services/telephony/providers/ari_provider.py +++ b/api/services/telephony/providers/ari_provider.py @@ -143,6 +143,7 @@ class ARIProvider(TelephonyProvider): return CallInitiationResult( call_id=channel_id, status=response_data.get("state", "created"), + caller_number=from_number, provider_metadata={ "call_id": channel_id, "channel_name": response_data.get("name", ""), diff --git a/api/services/telephony/providers/cloudonix_provider.py b/api/services/telephony/providers/cloudonix_provider.py index 9269d7e..367a8c7 100644 --- a/api/services/telephony/providers/cloudonix_provider.py +++ b/api/services/telephony/providers/cloudonix_provider.py @@ -189,6 +189,7 @@ class CloudonixProvider(TelephonyProvider): return CallInitiationResult( call_id=session_token, status="initiated", + caller_number=from_number, provider_metadata={ "call_id": session_token, "domain_id": domain_id, diff --git a/api/services/telephony/providers/telnyx_provider.py b/api/services/telephony/providers/telnyx_provider.py index 9b33cac..a57ba93 100644 --- a/api/services/telephony/providers/telnyx_provider.py +++ b/api/services/telephony/providers/telnyx_provider.py @@ -124,6 +124,7 @@ class TelnyxProvider(TelephonyProvider): return CallInitiationResult( call_id=call_control_id, status="initiated", + caller_number=from_number, provider_metadata={ "call_control_id": call_control_id, "call_leg_id": call_leg_id, diff --git a/api/services/telephony/providers/twilio_provider.py b/api/services/telephony/providers/twilio_provider.py index 764227e..e92281a 100644 --- a/api/services/telephony/providers/twilio_provider.py +++ b/api/services/telephony/providers/twilio_provider.py @@ -111,6 +111,7 @@ class TwilioProvider(TelephonyProvider): return CallInitiationResult( call_id=response_data["sid"], status=response_data.get("status", "queued"), + caller_number=from_number, provider_metadata={"call_id": response_data["sid"]}, raw_response=response_data, ) diff --git a/api/services/telephony/providers/vobiz_provider.py b/api/services/telephony/providers/vobiz_provider.py index 7e91bed..18a3f2f 100644 --- a/api/services/telephony/providers/vobiz_provider.py +++ b/api/services/telephony/providers/vobiz_provider.py @@ -150,6 +150,7 @@ class VobizProvider(TelephonyProvider): return CallInitiationResult( call_id=call_id, status="queued", # Vobiz returns "message": "call fired" + caller_number=from_number, provider_metadata={"call_id": call_id}, raw_response=response_data, ) diff --git a/api/services/telephony/providers/vonage_provider.py b/api/services/telephony/providers/vonage_provider.py index 357d5b4..1d7975d 100644 --- a/api/services/telephony/providers/vonage_provider.py +++ b/api/services/telephony/providers/vonage_provider.py @@ -137,6 +137,7 @@ class VonageProvider(TelephonyProvider): return CallInitiationResult( call_id=response_data["uuid"], status=response_data.get("status", "started"), + caller_number=from_number, provider_metadata={ "call_uuid": response_data["uuid"] }, # Vonage needs UUID persisted for WebSocket diff --git a/docs/core-concepts/context-and-variables.mdx b/docs/core-concepts/context-and-variables.mdx index a3f3174..663a0dd 100644 --- a/docs/core-concepts/context-and-variables.mdx +++ b/docs/core-concepts/context-and-variables.mdx @@ -45,9 +45,9 @@ whether they'd like to continue. When the call starts, Dograh substitutes the values before sending the prompt to the LLM — so the agent speaks naturally as if it already knows the contact. -### Built-in template variables +### Default variables -Dograh provides built-in variables for current time and weekday that you can use in any prompt without setting up `initial_context`. +Built-in variables for current time and weekday, available in any prompt without setting up `initial_context`. | Variable | Description | Example output | |---|---|---| @@ -66,6 +66,21 @@ Today is {{current_weekday}} and the current time is {{current_time_America/New_ When you use a timezone suffix on **either** `current_time` or `current_weekday`, the other variable without a suffix will automatically use the same timezone instead of UTC. For example, if your prompt contains both `{{current_time_Asia/Kolkata}}` and `{{current_weekday}}`, the weekday will also be resolved in `Asia/Kolkata`. +### Telephony variables + +For telephony calls (inbound and outbound), Dograh automatically adds these variables to `initial_context`: + +| Variable | Description | Example | +|---|---|---| +| `{{caller_number}}` | The phone number that initiated the call | `+14155550100` | +| `{{called_number}}` | The phone number that received the call | `+18005550199` | + +For **inbound** calls, `caller_number` is the customer's number and `called_number` is your Dograh number. For **outbound** calls, it's the reverse — `caller_number` is your Dograh number and `called_number` is the customer's number. + +``` +You are speaking with the caller at {{caller_number}}. +``` + ### gathered_context Data the agent collects *during* the call. You configure what to extract in the agent node's extraction settings — each variable has a name, type, and a prompt that tells the LLM what to look for.