chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,344 @@
|
||||
# Realtime agents guide
|
||||
|
||||
This guide explains how the OpenAI Agents SDK's realtime layer maps onto the OpenAI Realtime API, and what extra behavior the Python SDK adds on top.
|
||||
|
||||
!!! note "Start here"
|
||||
|
||||
If you want the default Python path, read the [quickstart](quickstart.md) first. If you are deciding whether your app should use server-side WebSocket or SIP, read [Realtime transport](transport.md). Browser WebRTC transport is not part of the Python SDK.
|
||||
|
||||
## Overview
|
||||
|
||||
Realtime agents keep a long-lived connection open to the Realtime API so the model can process text and audio incrementally, stream audio output, call tools, and handle interruptions without restarting a fresh request on every turn.
|
||||
|
||||
The main SDK components are:
|
||||
|
||||
- **RealtimeAgent**: Instructions, tools, output guardrails, and handoffs for one realtime specialist
|
||||
- **RealtimeRunner**: Session factory that wires a starting agent to a realtime transport
|
||||
- **RealtimeSession**: A live session that sends input, receives events, tracks history, and executes tools
|
||||
- **RealtimeModel**: The transport abstraction. The default is OpenAI's server-side WebSocket implementation.
|
||||
|
||||
## Session lifecycle
|
||||
|
||||
A typical realtime session looks like this:
|
||||
|
||||
1. Create one or more `RealtimeAgent`s.
|
||||
2. Create a `RealtimeRunner` with the starting agent.
|
||||
3. Call `await runner.run()` to get a `RealtimeSession`.
|
||||
4. Enter the session with `async with session:` or `await session.enter()`.
|
||||
5. Send user input with `send_message()` or `send_audio()`.
|
||||
6. Iterate over session events until the conversation ends.
|
||||
|
||||
Unlike text-only runs, `runner.run()` does not produce a final result immediately. It returns a live session object that keeps local history, background tool execution, guardrail state, and the active agent configuration in sync with the transport layer.
|
||||
|
||||
By default, `RealtimeRunner` uses `OpenAIRealtimeWebSocketModel`, so the default Python path is a server-side WebSocket connection to the Realtime API. If you pass a different `RealtimeModel`, the same session lifecycle and agent features still apply, while the connection mechanics can change.
|
||||
|
||||
## Agent and session configuration
|
||||
|
||||
`RealtimeAgent` is intentionally narrower than the regular `Agent` type:
|
||||
|
||||
- Model choice is configured at the session level, not per agent.
|
||||
- Structured outputs are not supported.
|
||||
- Voice can be configured, but it cannot change after the session has already produced spoken audio.
|
||||
- Instructions, function tools, handoffs, hooks, and output guardrails all still work.
|
||||
|
||||
`RealtimeSessionModelSettings` supports both a newer nested `audio` config and older flat aliases. Prefer the nested shape for new code, and start with `gpt-realtime-2.1` for new realtime agents:
|
||||
|
||||
```python
|
||||
runner = RealtimeRunner(
|
||||
starting_agent=agent,
|
||||
config={
|
||||
"model_settings": {
|
||||
"model_name": "gpt-realtime-2.1",
|
||||
"audio": {
|
||||
"input": {
|
||||
"format": "pcm16",
|
||||
"transcription": {"model": "gpt-4o-mini-transcribe"},
|
||||
"turn_detection": {"type": "semantic_vad", "interrupt_response": True},
|
||||
},
|
||||
"output": {"format": "pcm16", "voice": "ash"},
|
||||
},
|
||||
"tool_choice": "auto",
|
||||
}
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
Useful session-level settings include:
|
||||
|
||||
- `audio.input.format`, `audio.output.format`
|
||||
- `audio.input.transcription`
|
||||
- `audio.input.noise_reduction`
|
||||
- `audio.input.turn_detection`
|
||||
- `audio.output.voice`, `audio.output.speed`
|
||||
- `output_modalities`
|
||||
- `tool_choice`
|
||||
- `prompt`
|
||||
- `tracing`
|
||||
|
||||
Useful run-level settings on `RealtimeRunner(config=...)` include:
|
||||
|
||||
- `async_tool_calls`
|
||||
- `output_guardrails`
|
||||
- `guardrails_settings.debounce_text_length`
|
||||
- `tool_error_formatter`
|
||||
- `tracing_disabled`
|
||||
|
||||
See [`RealtimeRunConfig`][agents.realtime.config.RealtimeRunConfig] and [`RealtimeSessionModelSettings`][agents.realtime.config.RealtimeSessionModelSettings] for the full typed surface.
|
||||
|
||||
## Inputs and outputs
|
||||
|
||||
### Text and structured user messages
|
||||
|
||||
Use [`session.send_message()`][agents.realtime.session.RealtimeSession.send_message] for plain text or structured realtime messages.
|
||||
|
||||
```python
|
||||
from agents.realtime import RealtimeUserInputMessage
|
||||
|
||||
await session.send_message("Summarize what we discussed so far.")
|
||||
|
||||
message: RealtimeUserInputMessage = {
|
||||
"type": "message",
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "input_text", "text": "Describe this image."},
|
||||
{"type": "input_image", "image_url": image_data_url, "detail": "high"},
|
||||
],
|
||||
}
|
||||
await session.send_message(message)
|
||||
```
|
||||
|
||||
Structured messages are the main way to include image input in a realtime conversation. The example web demo in [`examples/realtime/app/server.py`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/app/server.py) forwards `input_image` messages this way.
|
||||
|
||||
### Audio input
|
||||
|
||||
Use [`session.send_audio()`][agents.realtime.session.RealtimeSession.send_audio] to stream raw audio bytes:
|
||||
|
||||
```python
|
||||
await session.send_audio(audio_bytes)
|
||||
```
|
||||
|
||||
If server-side turn detection is disabled, you are responsible for marking turn boundaries. The high-level convenience is:
|
||||
|
||||
```python
|
||||
await session.send_audio(audio_bytes, commit=True)
|
||||
```
|
||||
|
||||
If you need lower-level control, you can also send raw client events such as `input_audio_buffer.commit` through the underlying model transport.
|
||||
|
||||
### Manual response control
|
||||
|
||||
`session.send_message()` sends user input using the high-level path and starts a response for you. Raw audio buffering does **not** automatically do the same in every configuration.
|
||||
|
||||
At the Realtime API level, manual turn control means clearing `turn_detection` with a raw `session.update`, then sending `input_audio_buffer.commit` and `response.create` yourself.
|
||||
|
||||
If you are managing turns manually, you can send raw client events through the model transport:
|
||||
|
||||
```python
|
||||
from agents.realtime.model_inputs import RealtimeModelSendRawMessage
|
||||
|
||||
await session.model.send_event(
|
||||
RealtimeModelSendRawMessage(
|
||||
message={
|
||||
"type": "response.create",
|
||||
}
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
This pattern is useful when:
|
||||
|
||||
- `turn_detection` is disabled and you want to decide when the model should respond
|
||||
- you want to inspect or gate user input before triggering a response
|
||||
- you need a custom prompt for an out-of-band response
|
||||
|
||||
The SIP example in [`examples/realtime/twilio_sip/server.py`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/twilio_sip/server.py) uses a raw `response.create` to force an opening greeting.
|
||||
|
||||
## Events, history, and interruptions
|
||||
|
||||
`RealtimeSession` emits higher-level SDK events while still forwarding raw model events when you need them.
|
||||
|
||||
High-value session events include:
|
||||
|
||||
- `audio`, `audio_end`, `audio_interrupted`
|
||||
- `agent_start`, `agent_end`
|
||||
- `tool_start`, `tool_end`, `tool_approval_required`
|
||||
- `handoff`
|
||||
- `history_added`, `history_updated`
|
||||
- `guardrail_tripped`
|
||||
- `input_audio_timeout_triggered`
|
||||
- `error`
|
||||
- `raw_model_event`
|
||||
|
||||
The most useful events for UI state are usually `history_added` and `history_updated`. They expose the session's local history as `RealtimeItem` objects, including user messages, assistant messages, and tool calls.
|
||||
|
||||
### Interruptions and playback tracking
|
||||
|
||||
When the user interrupts the assistant, the session emits `audio_interrupted` and updates history so the server-side conversation stays aligned with what the user actually heard.
|
||||
|
||||
In low-latency local playback, the default playback tracker is often enough. In remote or delayed playback scenarios, especially telephony, use [`RealtimePlaybackTracker`][agents.realtime.model.RealtimePlaybackTracker] so interruption truncation is based on actual playback progress rather than assuming all generated audio has already been heard.
|
||||
|
||||
The Twilio example in [`examples/realtime/twilio/twilio_handler.py`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/twilio/twilio_handler.py) shows this pattern.
|
||||
|
||||
## Tools, approvals, handoffs, and guardrails
|
||||
|
||||
### Function tools
|
||||
|
||||
Realtime agents support function tools during live conversations:
|
||||
|
||||
```python
|
||||
from agents import function_tool
|
||||
|
||||
|
||||
@function_tool
|
||||
def get_weather(city: str) -> str:
|
||||
"""Get current weather for a city."""
|
||||
return f"The weather in {city} is sunny, 72F."
|
||||
|
||||
|
||||
agent = RealtimeAgent(
|
||||
name="Assistant",
|
||||
instructions="You can answer weather questions.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
```
|
||||
|
||||
### Tool approvals
|
||||
|
||||
Function tools can require human approval before execution. When that happens, the session emits `tool_approval_required` and pauses the tool run until you call `approve_tool_call()` or `reject_tool_call()`.
|
||||
|
||||
If the tool also has input guardrails, those guardrails run immediately before execution after approval. To run them before the approval event is emitted, create the runner with `RealtimeRunner(..., config={"tool_execution": {"pre_approval_tool_input_guardrails": True}})`. Calls that pass this pre-approval check are still checked again after approval before execution.
|
||||
|
||||
```python
|
||||
async for event in session:
|
||||
if event.type == "tool_approval_required":
|
||||
await session.approve_tool_call(event.call_id)
|
||||
```
|
||||
|
||||
For a concrete server-side approval loop, see [`examples/realtime/app/server.py`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/app/server.py). The human-in-the-loop docs also point back to this flow in [Human in the loop](../human_in_the_loop.md).
|
||||
|
||||
### Handoffs
|
||||
|
||||
Realtime handoffs let one agent transfer the live conversation to another specialist:
|
||||
|
||||
```python
|
||||
from agents.realtime import RealtimeAgent, realtime_handoff
|
||||
|
||||
billing_agent = RealtimeAgent(
|
||||
name="Billing Support",
|
||||
instructions="You specialize in billing issues.",
|
||||
)
|
||||
|
||||
main_agent = RealtimeAgent(
|
||||
name="Customer Service",
|
||||
instructions="Triage the request and hand off when needed.",
|
||||
handoffs=[
|
||||
realtime_handoff(
|
||||
billing_agent,
|
||||
tool_description_override="Transfer to billing support",
|
||||
)
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
Bare `RealtimeAgent` handoffs are auto-wrapped, and `realtime_handoff(...)` lets you customize names, descriptions, validation, callbacks, and availability. Realtime handoffs do **not** support the regular handoff `input_filter`.
|
||||
|
||||
### Guardrails
|
||||
|
||||
Realtime agents support output guardrails on agent responses and input guardrails on function-tool calls. Output guardrails run on debounced transcript accumulation rather than on every partial token, and they emit `guardrail_tripped` instead of raising an exception.
|
||||
|
||||
```python
|
||||
from agents.guardrail import GuardrailFunctionOutput, OutputGuardrail
|
||||
|
||||
|
||||
def sensitive_data_check(context, agent, output):
|
||||
return GuardrailFunctionOutput(
|
||||
tripwire_triggered="password" in output,
|
||||
output_info=None,
|
||||
)
|
||||
|
||||
|
||||
agent = RealtimeAgent(
|
||||
name="Assistant",
|
||||
instructions="...",
|
||||
output_guardrails=[OutputGuardrail(guardrail_function=sensitive_data_check)],
|
||||
)
|
||||
```
|
||||
|
||||
When a realtime output guardrail trips, the session interrupts the active response, forces `response.cancel`, emits `guardrail_tripped`, and sends a follow-up user message that names the triggered guardrail so the model can produce a replacement response. Your audio player should still listen for `audio_interrupted` and stop local playback immediately, because guardrails run on debounced transcript text and some audio may already be buffered when the tripwire fires.
|
||||
|
||||
## SIP and telephony
|
||||
|
||||
The Python SDK includes a first-class SIP attach flow via [`OpenAIRealtimeSIPModel`][agents.realtime.openai_realtime.OpenAIRealtimeSIPModel].
|
||||
|
||||
Use it when a call arrives through the Realtime Calls API and you want to attach an agent session to the resulting `call_id`:
|
||||
|
||||
```python
|
||||
from agents.realtime import RealtimeRunner
|
||||
from agents.realtime.openai_realtime import OpenAIRealtimeSIPModel
|
||||
|
||||
runner = RealtimeRunner(starting_agent=agent, model=OpenAIRealtimeSIPModel())
|
||||
|
||||
async with await runner.run(
|
||||
model_config={
|
||||
"call_id": call_id_from_webhook,
|
||||
}
|
||||
) as session:
|
||||
async for event in session:
|
||||
...
|
||||
```
|
||||
|
||||
If you need to accept the call first and want the accept payload to match the agent-derived session configuration, use `OpenAIRealtimeSIPModel.build_initial_session_payload(...)`. The complete flow is shown in [`examples/realtime/twilio_sip/server.py`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/twilio_sip/server.py).
|
||||
|
||||
## Low-level access and custom endpoints
|
||||
|
||||
You can access the underlying transport object through `session.model`.
|
||||
|
||||
Use this when you need:
|
||||
|
||||
- custom listeners via `session.model.add_listener(...)`
|
||||
- raw client events such as `response.create` or `session.update`
|
||||
- custom `url`, `headers`, or `api_key` handling through `model_config`
|
||||
- `call_id` attach to an existing realtime call
|
||||
|
||||
`RealtimeModelConfig` supports:
|
||||
|
||||
- `api_key`
|
||||
- `url`
|
||||
- `headers`
|
||||
- `initial_model_settings`
|
||||
- `playback_tracker`
|
||||
- `call_id`
|
||||
|
||||
This repository's shipped `call_id` example is SIP. The broader Realtime API also uses `call_id` for some server-side control flows, but those are not packaged as Python examples here.
|
||||
|
||||
When connecting to Azure OpenAI, pass a GA Realtime endpoint URL and explicit headers. For example:
|
||||
|
||||
```python
|
||||
session = await runner.run(
|
||||
model_config={
|
||||
"url": "wss://<your-resource>.openai.azure.com/openai/v1/realtime?model=<deployment-name>",
|
||||
"headers": {"api-key": "<your-azure-api-key>"},
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
For token-based authentication, use a bearer token in `headers`:
|
||||
|
||||
```python
|
||||
session = await runner.run(
|
||||
model_config={
|
||||
"url": "wss://<your-resource>.openai.azure.com/openai/v1/realtime?model=<deployment-name>",
|
||||
"headers": {"authorization": f"Bearer {token}"},
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
If you pass `headers`, the SDK does not add `Authorization` automatically. Avoid the legacy beta path (`/openai/realtime?api-version=...`) with realtime agents.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Realtime transport](transport.md)
|
||||
- [Quickstart](quickstart.md)
|
||||
- [OpenAI Realtime conversations](https://developers.openai.com/api/docs/guides/realtime-conversations/)
|
||||
- [OpenAI Realtime server-side controls](https://developers.openai.com/api/docs/guides/realtime-server-controls/)
|
||||
- [`examples/realtime`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime)
|
||||
@@ -0,0 +1,154 @@
|
||||
# Quickstart
|
||||
|
||||
Realtime agents in the Python SDK are server-side, low-latency agents built on the OpenAI Realtime API over WebSocket transport.
|
||||
|
||||
!!! note "Python SDK boundary"
|
||||
|
||||
The Python SDK does **not** provide a browser WebRTC transport. This page only covers Python-managed realtime sessions over server-side WebSockets. Use this SDK for server-side orchestration, tools, approvals, and telephony integrations. See also [Realtime transport](transport.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.10 or higher
|
||||
- OpenAI API key
|
||||
- Basic familiarity with the OpenAI Agents SDK
|
||||
|
||||
## Installation
|
||||
|
||||
If you haven't already, install the OpenAI Agents SDK:
|
||||
|
||||
```bash
|
||||
pip install openai-agents
|
||||
```
|
||||
|
||||
## Create a server-side realtime session
|
||||
|
||||
### 1. Import the realtime components
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
|
||||
from agents.realtime import RealtimeAgent, RealtimeRunner
|
||||
```
|
||||
|
||||
### 2. Define the starting agent
|
||||
|
||||
```python
|
||||
agent = RealtimeAgent(
|
||||
name="Assistant",
|
||||
instructions="You are a helpful voice assistant. Keep responses short and conversational.",
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Configure the runner
|
||||
|
||||
Prefer the nested `audio.input` / `audio.output` session settings shape for new code. For new realtime agents, start with `gpt-realtime-2.1`.
|
||||
|
||||
```python
|
||||
runner = RealtimeRunner(
|
||||
starting_agent=agent,
|
||||
config={
|
||||
"model_settings": {
|
||||
"model_name": "gpt-realtime-2.1",
|
||||
"audio": {
|
||||
"input": {
|
||||
"format": "pcm16",
|
||||
"transcription": {"model": "gpt-4o-mini-transcribe"},
|
||||
"turn_detection": {
|
||||
"type": "semantic_vad",
|
||||
"interrupt_response": True,
|
||||
},
|
||||
},
|
||||
"output": {
|
||||
"format": "pcm16",
|
||||
"voice": "ash",
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Start the session and send input
|
||||
|
||||
`runner.run()` returns a `RealtimeSession`. The connection is opened when you enter the session context.
|
||||
|
||||
```python
|
||||
async def main() -> None:
|
||||
session = await runner.run()
|
||||
|
||||
async with session:
|
||||
await session.send_message("Say hello in one short sentence.")
|
||||
|
||||
async for event in session:
|
||||
if event.type == "audio":
|
||||
# Forward or play event.audio.data.
|
||||
pass
|
||||
elif event.type == "history_added":
|
||||
print(event.item)
|
||||
elif event.type == "agent_end":
|
||||
# One assistant turn finished.
|
||||
break
|
||||
elif event.type == "error":
|
||||
print(f"Error: {event.error}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
`session.send_message()` accepts either a plain string or a structured realtime message. For raw audio chunks, use [`session.send_audio()`][agents.realtime.session.RealtimeSession.send_audio].
|
||||
|
||||
## What this quickstart does not include
|
||||
|
||||
- Microphone capture and speaker playback code. See the realtime examples in [`examples/realtime`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime).
|
||||
- SIP / telephony attach flows. See [Realtime transport](transport.md) and the [SIP section](guide.md#sip-and-telephony).
|
||||
|
||||
## Key settings
|
||||
|
||||
Once the basic session works, the settings most people reach for next are:
|
||||
|
||||
- `model_name`
|
||||
- `audio.input.format`, `audio.output.format`
|
||||
- `audio.input.transcription`
|
||||
- `audio.input.noise_reduction`
|
||||
- `audio.input.turn_detection` for automatic turn detection
|
||||
- `audio.output.voice`
|
||||
- `tool_choice`, `prompt`, `tracing`
|
||||
- `async_tool_calls`, `tool_execution.pre_approval_tool_input_guardrails`, `guardrails_settings.debounce_text_length`, `tool_error_formatter`
|
||||
|
||||
The older flat aliases such as `input_audio_format`, `output_audio_format`, `input_audio_transcription`, and `turn_detection` still work, but nested `audio` settings are preferred for new code.
|
||||
|
||||
For manual turn control, use a raw `session.update` / `input_audio_buffer.commit` / `response.create` flow as described in the [Realtime agents guide](guide.md#manual-response-control).
|
||||
|
||||
For the full schema, see [`RealtimeRunConfig`][agents.realtime.config.RealtimeRunConfig] and [`RealtimeSessionModelSettings`][agents.realtime.config.RealtimeSessionModelSettings].
|
||||
|
||||
## Connection options
|
||||
|
||||
Set your API key in the environment:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY="your-api-key-here"
|
||||
```
|
||||
|
||||
Or pass it directly when starting the session:
|
||||
|
||||
```python
|
||||
session = await runner.run(model_config={"api_key": "your-api-key"})
|
||||
```
|
||||
|
||||
`model_config` also supports:
|
||||
|
||||
- `url`: Custom WebSocket endpoint
|
||||
- `headers`: Custom request headers
|
||||
- `call_id`: Attach to an existing realtime call. In this repo, the documented attach flow is SIP.
|
||||
- `playback_tracker`: Report how much audio the user has actually heard
|
||||
|
||||
If you pass `headers` explicitly, the SDK will **not** inject an `Authorization` header for you.
|
||||
|
||||
When connecting to Azure OpenAI, pass a GA Realtime endpoint URL in `model_config["url"]` and explicit headers. Avoid the legacy beta path (`/openai/realtime?api-version=...`) with realtime agents. See the [Realtime agents guide](guide.md#low-level-access-and-custom-endpoints) for details.
|
||||
|
||||
## Next steps
|
||||
|
||||
- Read [Realtime transport](transport.md) to choose between server-side WebSocket and SIP.
|
||||
- Read the [Realtime agents guide](guide.md) for lifecycle, structured input, approvals, handoffs, guardrails, and low-level control.
|
||||
- Browse the examples in [`examples/realtime`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime).
|
||||
@@ -0,0 +1,72 @@
|
||||
# Realtime transport
|
||||
|
||||
Use this page to decide how realtime agents fit into your Python application.
|
||||
|
||||
!!! note "Python SDK boundary"
|
||||
|
||||
The Python SDK does **not** include a browser WebRTC transport. This page is only about Python SDK transport choices: server-side WebSockets and SIP attach flows. Browser WebRTC is a separate platform topic, documented in the official [Realtime API with WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc/) guide.
|
||||
|
||||
## Decision guide
|
||||
|
||||
| Goal | Start with | Why |
|
||||
| --- | --- | --- |
|
||||
| Build a server-managed realtime app | [Quickstart](quickstart.md) | The default Python path is a server-side WebSocket session managed by `RealtimeRunner`. |
|
||||
| Understand which transport and deployment shape to choose | This page | Use this before you commit to a transport or deployment shape. |
|
||||
| Attach agents to phone or SIP calls | [Realtime guide](guide.md) and [`examples/realtime/twilio_sip`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/twilio_sip) | The repo ships a SIP attach flow driven by `call_id`. |
|
||||
|
||||
## Server-side WebSocket is the default Python path
|
||||
|
||||
`RealtimeRunner` uses `OpenAIRealtimeWebSocketModel` unless you pass a custom `RealtimeModel`.
|
||||
|
||||
That means the standard Python topology looks like this:
|
||||
|
||||
1. Your Python service creates a `RealtimeRunner`.
|
||||
2. `await runner.run()` returns a `RealtimeSession`.
|
||||
3. Enter the session and send text, structured messages, or audio.
|
||||
4. Consume `RealtimeSessionEvent` items and forward audio or transcripts to your application.
|
||||
|
||||
This is the topology used by the core demo app, the CLI example, and the Twilio Media Streams example:
|
||||
|
||||
- [`examples/realtime/app`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/app)
|
||||
- [`examples/realtime/cli`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/cli)
|
||||
- [`examples/realtime/twilio`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/twilio)
|
||||
|
||||
Use this path when your server owns the audio pipeline, tool execution, approval flow, and history handling.
|
||||
|
||||
## SIP attach is the telephony path
|
||||
|
||||
For the telephony flow documented in this repository, the Python SDK attaches to an existing realtime call via `call_id`.
|
||||
|
||||
This topology looks like:
|
||||
|
||||
1. OpenAI sends your service a webhook such as `realtime.call.incoming`.
|
||||
2. Your service accepts the call through the Realtime Calls API.
|
||||
3. Your Python service starts a `RealtimeRunner(..., model=OpenAIRealtimeSIPModel())`.
|
||||
4. The session connects with `model_config={"call_id": ...}` and then processes events like any other realtime session.
|
||||
|
||||
This is the topology shown in [`examples/realtime/twilio_sip`](https://github.com/openai/openai-agents-python/tree/main/examples/realtime/twilio_sip).
|
||||
|
||||
The broader Realtime API also uses `call_id` for some server-side control patterns, but this repository's shipped attach example is SIP.
|
||||
|
||||
## Browser WebRTC is outside this SDK
|
||||
|
||||
If your app's primary client is a browser using Realtime WebRTC:
|
||||
|
||||
- Treat it as outside the scope of the Python SDK docs in this repository.
|
||||
- Use the official [Realtime API with WebRTC](https://developers.openai.com/api/docs/guides/realtime-webrtc/) and [Realtime conversations](https://developers.openai.com/api/docs/guides/realtime-conversations/) docs for the client-side flow and event model.
|
||||
- Use the official [Realtime server-side controls](https://developers.openai.com/api/docs/guides/realtime-server-controls/) guide if you need a sideband server connection on top of a browser WebRTC client.
|
||||
- Do not expect this repository to provide a browser-side `RTCPeerConnection` abstraction or a ready-made browser WebRTC sample.
|
||||
|
||||
This repository also does not currently ship a browser WebRTC plus Python sideband example.
|
||||
|
||||
## Custom endpoints and attach points
|
||||
|
||||
The transport configuration surface in [`RealtimeModelConfig`][agents.realtime.model.RealtimeModelConfig] lets you adapt the default paths:
|
||||
|
||||
- `url`: Override the WebSocket endpoint
|
||||
- `headers`: Provide explicit headers such as Azure auth headers
|
||||
- `api_key`: Pass an API key directly or via callback
|
||||
- `call_id`: Attach to an existing realtime call. In this repository, the documented example is SIP.
|
||||
- `playback_tracker`: Report actual playback progress for interruption handling
|
||||
|
||||
See the [Realtime agents guide](guide.md) for the detailed lifecycle and capability surface once you've chosen a topology.
|
||||
Reference in New Issue
Block a user