Add to and from phone information to the development runner
This commit is contained in:
22
CHANGELOG.md
22
CHANGELOG.md
@@ -13,6 +13,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
Using the universal `LLMContext` and associated `LLMContextAggregatorPair` is
|
Using the universal `LLMContext` and associated `LLMContextAggregatorPair` is
|
||||||
a pre-requisite for using `LLMSwitcher` to switch between LLMs at runtime.
|
a pre-requisite for using `LLMSwitcher` to switch between LLMs at runtime.
|
||||||
|
|
||||||
|
- Added `to` and `from` phone number information to the development runner for
|
||||||
|
Twilio, Telnyx, Plivo, and Exotel. Note: for Twilio and Plivo, `to` and `from`
|
||||||
|
phone number information must be manually provided.
|
||||||
|
|
||||||
|
- Twilio: TwiML returned must contain:
|
||||||
|
```
|
||||||
|
<Stream url="wss://server.com/ws">
|
||||||
|
<Parameter name="from" value="{{From}}"/>
|
||||||
|
<Parameter name="to" value="{{To}}"/>
|
||||||
|
</Stream>
|
||||||
|
```
|
||||||
|
- Plivo: Add `to` and `from` information as `extraHeaders` in the XML `Stream`
|
||||||
|
property.
|
||||||
|
|
||||||
|
This phone information acts as an identifer for inbound calls, allowing you
|
||||||
|
to programmatically inject information to your bot file based on corresponding
|
||||||
|
user information.
|
||||||
|
|
||||||
|
- Added the ability to retrieve custom data from the development runner for
|
||||||
|
Twilio, Telnyx, Plivo, and Exotel. This provides the ability to inject custom
|
||||||
|
data into the call for outbound calling cases.
|
||||||
|
|
||||||
- Added video streaming support to `LiveKitTransport`.
|
- Added video streaming support to `LiveKitTransport`.
|
||||||
|
|
||||||
- Added `OpenAIRealtimeLLMService` and `AzureRealtimeLLMService` which provide
|
- Added `OpenAIRealtimeLLMService` and `AzureRealtimeLLMService` which provide
|
||||||
|
|||||||
@@ -99,14 +99,20 @@ async def parse_telephony_websocket(websocket: WebSocket):
|
|||||||
tuple: (transport_type: str, call_data: dict)
|
tuple: (transport_type: str, call_data: dict)
|
||||||
|
|
||||||
call_data contains provider-specific fields:
|
call_data contains provider-specific fields:
|
||||||
- Twilio: {"stream_id": str, "call_id": str}
|
- Twilio: {"stream_id": str, "call_id": str, "from": str, "to": str}
|
||||||
- Telnyx: {"stream_id": str, "call_control_id": str, "outbound_encoding": str}
|
- Telnyx: {"stream_id": str, "call_control_id": str, "outbound_encoding": str, "from": str, "to": str}
|
||||||
- Plivo: {"stream_id": str, "call_id": str}
|
- Plivo: {"stream_id": str, "call_id": str, "from": str, "to": str}
|
||||||
- Exotel: {"stream_id": str, "call_id": str, "account_sid": str}
|
- Exotel: {"stream_id": str, "call_id": str, "account_sid": str, "from": str, "to": str}
|
||||||
|
|
||||||
Example usage::
|
Example usage::
|
||||||
|
|
||||||
transport_type, call_data = await parse_telephony_websocket(websocket)
|
transport_type, call_data = await parse_telephony_websocket(websocket)
|
||||||
|
|
||||||
|
# Access call information (works for all providers when available)
|
||||||
|
from_number = call_data.get("from", "")
|
||||||
|
to_number = call_data.get("to", "")
|
||||||
|
|
||||||
|
# Access provider-specific fields
|
||||||
if transport_type == "telnyx":
|
if transport_type == "telnyx":
|
||||||
outbound_encoding = call_data["outbound_encoding"]
|
outbound_encoding = call_data["outbound_encoding"]
|
||||||
"""
|
"""
|
||||||
@@ -151,9 +157,13 @@ async def parse_telephony_websocket(websocket: WebSocket):
|
|||||||
# Extract provider-specific data
|
# Extract provider-specific data
|
||||||
if transport_type == "twilio":
|
if transport_type == "twilio":
|
||||||
start_data = call_data_raw.get("start", {})
|
start_data = call_data_raw.get("start", {})
|
||||||
|
custom_params = start_data.get("customParameters", {})
|
||||||
|
|
||||||
call_data = {
|
call_data = {
|
||||||
"stream_id": start_data.get("streamSid"),
|
"stream_id": start_data.get("streamSid"),
|
||||||
"call_id": start_data.get("callSid"),
|
"call_id": start_data.get("callSid"),
|
||||||
|
"from": custom_params.get("from", ""),
|
||||||
|
"to": custom_params.get("to", ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
elif transport_type == "telnyx":
|
elif transport_type == "telnyx":
|
||||||
@@ -163,13 +173,47 @@ async def parse_telephony_websocket(websocket: WebSocket):
|
|||||||
"outbound_encoding": call_data_raw.get("start", {})
|
"outbound_encoding": call_data_raw.get("start", {})
|
||||||
.get("media_format", {})
|
.get("media_format", {})
|
||||||
.get("encoding"),
|
.get("encoding"),
|
||||||
|
"from": call_data_raw.get("start", {}).get("from", ""),
|
||||||
|
"to": call_data_raw.get("start", {}).get("to", ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
elif transport_type == "plivo":
|
elif transport_type == "plivo":
|
||||||
start_data = call_data_raw.get("start", {})
|
start_data = call_data_raw.get("start", {})
|
||||||
|
custom_params = start_data.get("customParameters", {})
|
||||||
|
|
||||||
|
# Extract from/to from extra_headers if available
|
||||||
|
from_number = ""
|
||||||
|
to_number = ""
|
||||||
|
|
||||||
|
# First try customParameters (for query parameter approach)
|
||||||
|
if custom_params.get("from"):
|
||||||
|
from_number = custom_params.get("from", "")
|
||||||
|
if custom_params.get("to"):
|
||||||
|
to_number = custom_params.get("to", "")
|
||||||
|
|
||||||
|
# If not found in customParameters, try extra_headers
|
||||||
|
if not from_number or not to_number:
|
||||||
|
# Check for extra_headers in both root level and start event
|
||||||
|
extra_headers = call_data_raw.get("extra_headers", "") or start_data.get(
|
||||||
|
"extra_headers", ""
|
||||||
|
)
|
||||||
|
if extra_headers:
|
||||||
|
# Parse format: "{X-PH-from: 14129162450, X-PH-to: 17242775935}"
|
||||||
|
import re
|
||||||
|
|
||||||
|
from_match = re.search(r"X-PH-from:\s*([^,\s}]+)", extra_headers)
|
||||||
|
to_match = re.search(r"X-PH-to:\s*([^,\s}]+)", extra_headers)
|
||||||
|
|
||||||
|
if from_match and not from_number:
|
||||||
|
from_number = from_match.group(1).strip()
|
||||||
|
if to_match and not to_number:
|
||||||
|
to_number = to_match.group(1).strip()
|
||||||
|
|
||||||
call_data = {
|
call_data = {
|
||||||
"stream_id": start_data.get("streamId"),
|
"stream_id": start_data.get("streamId"),
|
||||||
"call_id": start_data.get("callId"),
|
"call_id": start_data.get("callId"),
|
||||||
|
"from": from_number,
|
||||||
|
"to": to_number,
|
||||||
}
|
}
|
||||||
|
|
||||||
elif transport_type == "exotel":
|
elif transport_type == "exotel":
|
||||||
@@ -178,6 +222,8 @@ async def parse_telephony_websocket(websocket: WebSocket):
|
|||||||
"stream_id": start_data.get("stream_sid"),
|
"stream_id": start_data.get("stream_sid"),
|
||||||
"call_id": start_data.get("call_sid"),
|
"call_id": start_data.get("call_sid"),
|
||||||
"account_sid": start_data.get("account_sid"),
|
"account_sid": start_data.get("account_sid"),
|
||||||
|
"from": start_data.get("from", ""),
|
||||||
|
"to": start_data.get("to", ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user