Skip to content
Closed
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
12 changes: 11 additions & 1 deletion src/agents/realtime/model_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,17 @@ class RealtimeModelSendToolOutput:

@dataclass
class RealtimeModelSendInterrupt:
"""Send an interrupt to the model."""
"""Send an interrupt to the model.
Args:
force_cancel: If True, always send response.cancel regardless of
interrupt_response setting. This should be True for SDK-side interrupts
(e.g., guardrails) where the API doesn't know about the interrupt.
For user voice interrupts, this should be False (default) to allow
the API's automatic response cancellation when interrupt_response=True.
"""

force_cancel: bool = False


@dataclass
Expand Down
5 changes: 4 additions & 1 deletion src/agents/realtime/openai_realtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,10 @@ async def _send_interrupt(self, event: RealtimeModelSendInterrupt) -> None:
and session.audio.input.turn_detection is not None
and session.audio.input.turn_detection.interrupt_response is True,
)
if not automatic_response_cancellation_enabled:
# Always cancel for force_cancel=True (e.g., guardrail interrupts)
# For user voice interrupts (force_cancel=False), only cancel if automatic
# cancellation is disabled
if event.force_cancel or not automatic_response_cancellation_enabled:
await self._cancel_response()

self._audio_state_tracker.on_interrupted()
Expand Down
4 changes: 3 additions & 1 deletion src/agents/realtime/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,9 @@ async def _run_output_guardrails(self, text: str, response_id: str) -> bool:
)

# Interrupt the model
await self._model.send_event(RealtimeModelSendInterrupt())
# Use force_cancel=True for guardrail interrupts because they are SDK-side logic
# and the API doesn't know about them (unlike user voice interrupts)
await self._model.send_event(RealtimeModelSendInterrupt(force_cancel=True))

# Send guardrail triggered message
guardrail_names = [result.guardrail.get_name() for result in triggered_results]
Expand Down