Skip to content
Draft
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ The broker fetches HA's tools at startup and registers them on the Realtime sess
|---|---|---|
| `OPENAI_API_KEY` | — | required |
| `MODEL` | `gpt-realtime` | Realtime model |
| `REASONING_EFFORT` | unset | reasoning effort for reasoning-line models (`gpt-realtime-2.1`+): `minimal`/`low`/`medium`/`high`/`xhigh`; leave unset for non-reasoning models |
| `VOICE` | `marin` | Realtime voice |
| `INSTRUCTIONS` | generic | system prompt / persona |
| `WS_HOST` / `WS_PORT` | `0.0.0.0` / `8765` | where the device connects |
Expand Down
19 changes: 19 additions & 0 deletions broker/realtime_broker/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
AudioConfiguration,
AudioInput,
AudioOutput,
ClientEvent,
InputAudioTranscription,
SessionProperties,
SessionUpdateEvent,
TurnDetection,
)
from pipecat.services.openai.realtime.llm import OpenAIRealtimeLLMService
Expand All @@ -42,11 +44,27 @@ class VoicePERealtimeService(OpenAIRealtimeLLMService):
its own response.create).
"""

def __init__(self, *args, reasoning_effort: str | None = None, **kwargs):
super().__init__(*args, **kwargs)
self._reasoning_effort = reasoning_effort

async def _handle_context(self, context: LLMContext) -> None:
self._context = context
self._llm_needs_conversation_setup = False
await self._process_completed_function_calls(send_new_results=True)

async def send_client_event(self, event: ClientEvent) -> None:
# Pipecat 0.0.97's SessionProperties has no `reasoning` field and
# pydantic serializes by declared type, so a subclass field would be
# dropped — inject into the wire dict instead. Applies to every
# session.update (initial setup and mid-session).
if self._reasoning_effort and isinstance(event, SessionUpdateEvent):
dump = event.model_dump(exclude_none=True)
dump["session"]["reasoning"] = {"effort": self._reasoning_effort}
await self._ws_send(dump)
return
await super().send_client_event(event)

# Custom broker tools, registered with handlers by the server.
CUSTOM_TOOLS = [
{
Expand Down Expand Up @@ -206,6 +224,7 @@ async def build_agent(config: Config, mcp: MCPClient | None) -> OpenAIRealtimeLL
model=config.model,
session_properties=session,
start_audio_paused=False,
reasoning_effort=config.reasoning_effort,
)

if mcp is not None and tools_schema is not None:
Expand Down
6 changes: 6 additions & 0 deletions broker/realtime_broker/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ class Config:
model: str = "gpt-realtime"
voice: str = "marin"
instructions: str = "You are a helpful voice assistant."
# Reasoning effort for reasoning-line Realtime models (gpt-realtime-2.1+):
# minimal/low/medium/high/xhigh. The server default (low) makes the model
# deliberate and pad its spoken replies; "minimal" suits command-and-control.
# None = field not sent, required for non-reasoning models (gpt-realtime).
reasoning_effort: str | None = None

ws_host: str = "0.0.0.0"
ws_port: int = 8765
Expand Down Expand Up @@ -80,6 +85,7 @@ def from_env(cls) -> "Config":
openai_api_key=api_key,
model=os.environ.get("MODEL", "gpt-realtime"),
voice=os.environ.get("VOICE", "marin"),
reasoning_effort=os.environ.get("REASONING_EFFORT") or None,
instructions=os.environ.get("INSTRUCTIONS", cls.instructions),
ws_host=os.environ.get("WS_HOST", "0.0.0.0"),
ws_port=int(os.environ.get("WS_PORT", "8765")),
Expand Down
Loading