Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1 @@
OPENAI_API_KEY=your_api_key
GEMINI_API_KEY=your_api_key
36 changes: 17 additions & 19 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
# Speech Assistant with Twilio Voice and the OpenAI Realtime API (Python)
# Speech Assistant with Twilio Voice and the Gemini Live API (Python)

This application demonstrates how to use Python, [Twilio Voice](https://www.twilio.com/docs/voice) and [Media Streams](https://www.twilio.com/docs/voice/media-streams), and [OpenAI's Realtime API](https://platform.openai.com/docs/) to make a phone call to speak with an AI Assistant.
This application demonstrates how to use Python, [Twilio Voice](https://www.twilio.com/docs/voice) and [Media Streams](https://www.twilio.com/docs/voice/media-streams), and [Google's Gemini Live API](https://ai.google.dev/gemini-api/docs/live) to make a phone call to speak with an AI Assistant.

The application opens websockets with the OpenAI Realtime API and Twilio, and sends voice audio from one to the other to enable a two-way conversation.
The application opens websockets with the Gemini Live API and Twilio, and sends voice audio from one to the other to enable a two-way conversation.

See [here](https://www.twilio.com/en-us/blog/voice-ai-assistant-openai-realtime-api-python) for a tutorial overview of the code.

This application uses the following Twilio products in conjunction with OpenAI's Realtime API:
This application uses the following Twilio products in conjunction with the Gemini Live API:
- Voice (and TwiML, Media Streams)
- Phone Numbers

> [!NOTE]
> Outbound calling is beyond the scope of this app. However, we demoed [one way to do it here](https://www.twilio.com/en-us/blog/outbound-calls-python-openai-realtime-api-voice).
> Outbound calling is beyond the scope of this app.

## Prerequisites

To use the app, you will need:

- **Python 3.9+** We used \`3.9.13\` for development; download from [here](https://www.python.org/downloads/).
- **Python 3.9+** (3.11 or earlier recommended; `audioop` is removed in 3.13+). Download from [here](https://www.python.org/downloads/).
- **A Twilio account.** You can sign up for a free trial [here](https://www.twilio.com/try-twilio).
- **A Twilio number with _Voice_ capabilities.** [Here are instructions](https://help.twilio.com/articles/223135247-How-to-Search-for-and-Buy-a-Twilio-Phone-Number-from-Console) to purchase a phone number.
- **An OpenAI account and an OpenAI API Key.** You can sign up [here](https://platform.openai.com/).
- **OpenAI Realtime API access.**
- **A Google AI Studio account and a Gemini API Key.** You can get one [here](https://aistudio.google.com/apikey).

## Local Setup

Expand All @@ -43,7 +40,7 @@ Once the tunnel has been opened, copy the `Forwarding` URL. It will look somethi
need this when configuring your Twilio number setup.

Note that the `ngrok` command above forwards to a development server running on port `5050`, which is the default port configured in this application. If
you override the `PORT` defined in `index.js`, you will need to update the `ngrok` command accordingly.
you override the `PORT` defined in `main.py`, you will need to update the `ngrok` command accordingly.

Keep in mind that each time you run the `ngrok http` command, a new URL will be created, and you'll need to update it everywhere it is referenced below.

Expand Down Expand Up @@ -72,13 +69,13 @@ In your Phone Number configuration settings, update the first **A call comes in*

### Update the .env file

Create a `/env` file, or copy the `.env.example` file to `.env`:
Create a `.env` file, or copy the `.env.example` file to `.env`:

```
cp .env.example .env
```

In the .env file, update the `OPENAI_API_KEY` to your OpenAI API key from the **Prerequisites**.
In the .env file, update the `GEMINI_API_KEY` to your Gemini API key from the **Prerequisites**.

## Run the app
Once ngrok is running, dependencies are installed, Twilio is configured properly, and the `.env` is set up, run the dev server with the following command:
Expand All @@ -88,12 +85,13 @@ python main.py
## Test the app
With the development server running, call the phone number you purchased in the **Prerequisites**. After the introduction, you should be able to talk to the AI Assistant. Have fun!

## Special features
## Audio Pipeline

Twilio sends PCMU (G.711 μ-law, 8kHz) audio. Gemini Live expects raw PCM (16-bit, little-endian, 16kHz). The server handles transcoding:

### Have the AI speak first
To have the AI voice assistant talk before the user, uncomment the line `# await send_initial_conversation_item(openai_ws)`. The initial greeting is controlled in `async def send_initial_conversation_item(openai_ws)`.
- **Twilio → Gemini**: PCMU 8kHz → `audioop.ulaw2lin` → PCM 8kHz → `audioop.ratecv` (upsample 8→16kHz) → Gemini
- **Gemini → Twilio**: PCM 24kHz → struct averaging (downsample 24→8kHz) → `audioop.lin2ulaw` → PCMU → Twilio

### Interrupt handling/AI preemption
When the user speaks and OpenAI sends `input_audio_buffer.speech_started`, the code will clear the Twilio Media Streams buffer and send OpenAI `conversation.item.truncate`.
## Interrupt handling / Barge-in

Depending on your application's needs, you may want to use the [`input_audio_buffer.speech_stopped`](https://platform.openai.com/docs/api-reference/realtime-server-events/input-audio-buffer-speech-stopped) event, instead, or a combination of the two.
Gemini Live handles barge-in natively with server-side VAD. When the user speaks during an AI response, Gemini sends `serverContent.interrupted: true`. The server then clears Twilio's audio buffer so the caller stops hearing the old response immediately. No manual truncation messages needed.
Loading