Skip to content

Commit 8cc0762

Browse files
authored
Merge pull request #110 from pipecat-ai/mb/twilio-outbound-updates
Twilio Outbound: Improve the FastAPI server
2 parents cf646ff + 942472a commit 8cc0762

File tree

6 files changed

+295
-248
lines changed

6 files changed

+295
-248
lines changed

twilio-chatbot/outbound/README.md

Lines changed: 13 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This project is a Pipecat-based chatbot that integrates with Twilio to make outb
66

77
When you want to make an outbound call:
88

9-
1. **Send POST request**: `POST /start` with a phone number to call
9+
1. **Send POST request**: `POST /dialout` with a phone number to call
1010
2. **Server initiates call**: Uses Twilio's REST API to make the outbound call
1111
3. **Call answered**: When answered, Twilio fetches TwiML from your server's `/twiml` endpoint
1212
4. **Server returns TwiML**: Tells Twilio to start a WebSocket stream to your bot
@@ -16,7 +16,7 @@ When you want to make an outbound call:
1616
## Architecture
1717

1818
```
19-
curl request → /start endpoint → Twilio REST API → Call initiated →
19+
curl request → /dialout endpoint → Twilio REST API → Call initiated →
2020
TwiML fetched → WebSocket connection → Bot conversation
2121
```
2222

@@ -30,7 +30,7 @@ TwiML fetched → WebSocket connection → Bot conversation
3030

3131
### AI Services
3232

33-
- OpenAI API key for the LLM inference
33+
- Google API key for the LLM inference
3434
- Deepgram API key for speech-to-text
3535
- Cartesia API key for text-to-speech
3636

@@ -97,7 +97,7 @@ The server will start on port 7860.
9797

9898
> Tip: Use the `--subdomain` flag for a reusable ngrok URL.
9999
100-
Copy the ngrok URL (e.g., `https://abc123.ngrok.io`)
100+
Copy the ngrok URL (e.g., `https://abc123.ngrok.io`) and update `LOCAL_SERVER_URL` in your `.env` file.
101101

102102
3. No additional Twilio configuration needed
103103

@@ -107,41 +107,22 @@ The server will start on port 7860.
107107

108108
With the server running and exposed via ngrok, you can initiate outbound calls:
109109

110-
### Basic Call
111-
112-
```bash
113-
curl -X POST https://your-ngrok-url.ngrok.io/start \
114-
-H "Content-Type: application/json" \
115-
-d '{
116-
"phone_number": "+1234567890"
117-
}'
118-
```
119-
120-
### Call with Body Data
121-
122-
You can include arbitrary body data that will be available to your bot:
123-
124110
```bash
125-
curl -X POST https://your-ngrok-url.ngrok.io/start \
111+
curl -X POST https://your-ngrok-url.ngrok.io/dialout \
126112
-H "Content-Type: application/json" \
127113
-d '{
128-
"phone_number": "+1234567890",
129-
"body": {
130-
"user": {
131-
"id": "user123",
132-
"name": "John Doe",
133-
"account_type": "premium"
134-
}
135-
}
114+
"to_number": "+15551234567",
115+
"from_number": "+15559876543"
136116
}'
137117
```
138118

139-
The body data can be any JSON structure - nested objects, arrays, etc. Your bot will receive this data automatically.
140-
141119
Replace:
142120

143121
- `your-ngrok-url.ngrok.io` with your actual ngrok URL
144-
- `+1234567890` with the phone number you want to call
122+
- `+15551234567` with the phone number to call (E.164 format)
123+
- `+15559876543` with your Twilio phone number (E.164 format)
124+
125+
> Note: the `from_number` must be a phone number owned by your Twilio account
145126
146127
## Production Deployment
147128

@@ -181,31 +162,6 @@ As you did before, initiate a call via `curl` command to trigger your bot to dia
181162

182163
## Accessing Call Information in Your Bot
183164

184-
Your bot automatically receives call information and body data through Twilio's Parameters:
185-
186-
- **Body Data**: Any data you include in the `body` field is passed as a JSON string in the `body` parameter
187-
188-
The Pipecat development runner extracts this data using the `parse_telephony_websocket` function:
189-
190-
```python
191-
async def bot(runner_args: RunnerArguments):
192-
transport_type, call_data = await parse_telephony_websocket(runner_args.websocket)
193-
194-
if transport_type == "twilio":
195-
# Body data (JSON string parameter)
196-
import json
197-
body_param = call_data["custom_parameters"].get("body")
198-
if body_param:
199-
body_data = json.loads(body_param)
200-
201-
# Access nested data
202-
user_data = body_data.get("user", {})
203-
user_id = user_data.get("id")
204-
user_name = user_data.get("name")
205-
user_account_type = user_data.get("account_type")
206-
207-
# Use this data to personalize the conversation
208-
print(f"User: {user_name} (ID: {user_id}, Type: {user_account_type})")
209-
```
165+
Your bot automatically receives call information through Twilio Stream Parameters. In this example, the phone numbers (`to_number` and `from_number`) are passed as parameters and extracted by the `parse_telephony_websocket` function.
210166

211-
This allows your bot to provide personalized responses based on the body data and context.
167+
You can extend the `DialoutRequest` model in `server_utils.py` to include additional custom data (customer info, campaign data, etc.) and pass it through as stream parameters for personalized conversations. See `bot.py` for implementation details.

twilio-chatbot/outbound/bot.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
from pipecat.pipeline.pipeline import Pipeline
1414
from pipecat.pipeline.runner import PipelineRunner
1515
from pipecat.pipeline.task import PipelineParams, PipelineTask
16-
from pipecat.processors.aggregators.openai_llm_context import OpenAILLMContext
16+
from pipecat.processors.aggregators.llm_context import LLMContext
17+
from pipecat.processors.aggregators.llm_response_universal import LLMContextAggregatorPair
1718
from pipecat.runner.types import RunnerArguments
1819
from pipecat.runner.utils import parse_telephony_websocket
1920
from pipecat.serializers.twilio import TwilioFrameSerializer
2021
from pipecat.services.cartesia.tts import CartesiaTTSService
2122
from pipecat.services.deepgram.stt import DeepgramSTTService
22-
from pipecat.services.openai.llm import OpenAILLMService
23+
from pipecat.services.google.llm import GoogleLLMService
2324
from pipecat.transports.base_transport import BaseTransport
2425
from pipecat.transports.websocket.fastapi import (
2526
FastAPIWebsocketParams,
@@ -33,7 +34,7 @@
3334

3435

3536
async def run_bot(transport: BaseTransport, handle_sigint: bool):
36-
llm = OpenAILLMService(api_key=os.getenv("OPENAI_API_KEY"))
37+
llm = GoogleLLMService(api_key=os.getenv("GOOGLE_API_KEY"))
3738

3839
stt = DeepgramSTTService(api_key=os.getenv("DEEPGRAM_API_KEY"))
3940

@@ -53,8 +54,8 @@ async def run_bot(transport: BaseTransport, handle_sigint: bool):
5354
},
5455
]
5556

56-
context = OpenAILLMContext(messages)
57-
context_aggregator = llm.create_context_aggregator(context)
57+
context = LLMContext(messages)
58+
context_aggregator = LLMContextAggregatorPair(context)
5859

5960
pipeline = Pipeline(
6061
[
@@ -95,10 +96,18 @@ async def on_client_disconnected(transport, client):
9596

9697
async def bot(runner_args: RunnerArguments):
9798
"""Main bot entry point compatible with Pipecat Cloud."""
98-
9999
transport_type, call_data = await parse_telephony_websocket(runner_args.websocket)
100100
logger.info(f"Auto-detected transport: {transport_type}")
101101

102+
# Access custom stream parameters passed from TwiML
103+
# Use the body data to personalize the conversation
104+
# by loading customer data based on the to_number or from_number
105+
body_data = call_data.get("body", {})
106+
to_number = body_data.get("to_number")
107+
from_number = body_data.get("from_number")
108+
109+
logger.info(f"Call metadata - To: {to_number}, From: {from_number}")
110+
102111
serializer = TwilioFrameSerializer(
103112
stream_sid=call_data["stream_id"],
104113
call_sid=call_data["call_id"],
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
OPENAI_API_KEY=
1+
# Service keys
2+
GOOGLE_API_KEY=
23
DEEPGRAM_API_KEY=
34
CARTESIA_API_KEY=
5+
6+
# Twilio credentials
47
TWILIO_ACCOUNT_SID=
58
TWILIO_AUTH_TOKEN=
6-
TWILIO_PHONE_NUMBER=
79

810
# Environment configuration
911
ENV=local
1012
AGENT_NAME=twilio-chatbot-dial-out
11-
ORGANIZATION_NAME=
13+
ORGANIZATION_NAME=
14+
LOCAL_SERVER_URL=https://your-url.ngrok.io

twilio-chatbot/outbound/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ description = "Twilio dial-out example for Pipecat"
55
readme = "README.md"
66
requires-python = ">=3.10"
77
dependencies = [
8-
"pipecat-ai[websocket,cartesia,openai,silero,deepgram,runner]>=0.0.85",
9-
"pipecatcloud>=0.2.4",
8+
"pipecat-ai[websocket,cartesia,google,silero,deepgram,runner]>=0.0.91",
9+
"pipecatcloud>=0.2.7",
1010
"twilio"
1111
]

0 commit comments

Comments
 (0)