Skip to content

fix(openai): pass response_id in ResponseCancelEvent on interrupt - #7255

Open
rrfunde wants to merge 5 commits into
livekit:mainfrom
rrfunde:fix/openai-realtime-interrupt-response-id
Open

fix(openai): pass response_id in ResponseCancelEvent on interrupt#7255
rrfunde wants to merge 5 commits into
livekit:mainfrom
rrfunde:fix/openai-realtime-interrupt-response-id

Conversation

@rrfunde

@rrfunde rrfunde commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Summary

RealtimeSession.interrupt() in the OpenAI Realtime plugin was emitting:

self.send_event(ResponseCancelEvent(type="response.cancel"))

without specifying response_id. Under OpenAI Realtime API semantics, a cancel event without response_id is a no-op when multiple responses are in flight.

This PR:

  1. Records response_id on _ResponseGeneration from ResponseCreatedEvent.response.id.
  2. Updates RealtimeSession.interrupt() to pass response_id=self._current_generation.response_id when available, cleanly cancelling the targeted in-flight response, while falling back to a general cancel if absent.

Fixes #5564

Testing

  • Added test_interrupt_includes_response_id_from_current_generation in tests/test_realtime/test_openai_realtime_model.py.
  • uv run pytest tests/test_realtime/test_openai_realtime_model.py passed (26 tests).
  • uv run pytest tests/test_realtime_interrupt_cancels_generation.py passed (6 tests).
  • uv run ruff check passed.

RealtimeSession.interrupt() sent ResponseCancelEvent(type="response.cancel")
without specifying response_id. Under OpenAI Realtime API semantics, a cancel
without response_id is a no-op when multiple responses are in flight.
Track response_id on _ResponseGeneration and forward it in interrupt() when
available, falling back to cancel-all if absent.

Fixes livekit#5564
@rrfunde
rrfunde requested a review from a team as a code owner September 12, 2026 11:57
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-12T16:15:31.779828Z c91b7b1 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

This report is out of date. Scroll down for Devin Review's latest report on this PR.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6723825f38

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1759 to +1762
ResponseCancelEvent(
type="response.cancel",
response_id=self._current_generation.response_id,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the legacy Azure cancellation schema

When the session uses legacy Azure mode (is_azure with an api_version, such as the documented 2024-10-01-preview configuration), _run_ws serializes this event unchanged, but that beta response.cancel schema does not support response_id. Azure therefore rejects normal interrupts after response.created instead of stopping the response. Keep emitting the bare cancel event for legacy Azure sessions and include the ID only for the GA protocol.

Useful? React with 👍 / 👎.

@rrfunde

rrfunde commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Fixed: Preserved bare response.cancel for legacy Azure sessions (is_azure with api_version) by omitting response_id both in interrupt() / discarded response cancellation and during client event normalization in _normalize_azure_client_event.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9e2e3e411f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1766 to +1770
self.send_event(
ResponseCancelEvent(
type="response.cancel",
response_id=self._current_generation.response_id,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep targeted cancellation off the xAI subclass

When this session is used through livekit.plugins.xai.realtime.RealtimeSession, _opts.is_azure is false and interrupt() delegates here via super().interrupt(), so every active xAI response now receives response.cancel with response_id. The xAI v1 Realtime protocol uses the legacy bare cancellation event—the xAI-specific _discard_say() path accordingly still emits one without an ID—so the endpoint rejects this targeted form and normal barge-ins no longer stop speaking responses. Gate the new field to providers that support it, rather than excluding only legacy Azure.

Useful? React with 👍 / 👎.

@rrfunde

rrfunde commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Fixed: Added _supports_targeted_cancellation to gate response.cancel with response_id strictly to the OpenAI Realtime API (including Azure GA), keeping the bare cancel schema for subclasses like xAI Realtime API as well as legacy Azure.

devin-ai-integration[bot]

This comment was marked as resolved.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c1494749f9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1768 to +1769
if model is not None and getattr(model, "_provider_label", None) != "OpenAI Realtime API":
return False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enable targeted cancellation for OpenAI inference sessions

When using InferenceRealtimeModel("openai/gpt-realtime", ...), the model sets _provider_label to "LiveKit Inference Realtime" in inference_realtime_model.py:146, so this label check always disables targeted cancellation even though that class routes the OpenAI-compatible protocol and explicitly clears the Azure flags. Consequently, an interrupt with multiple responses in flight still sends the bare response.cancel that this change is intended to replace, allowing the OpenAI-backed generation to continue; distinguish the routed provider/model capability from the diagnostic label.

Useful? React with 👍 / 👎.

@rrfunde

rrfunde commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Addressed review feedback:

  • Represented targeted cancellation capability explicitly via _supports_targeted_cancellation on RealtimeModel.
  • Direct OpenAI models and LiveKit Inference openai/... models keep targeted cancellation enabled.
  • Legacy Azure and xAI (livekit-plugins-xai and xai/... inference routes) have targeted cancellation disabled and use the bare response.cancel event without response_id.
  • Added unit tests covering LiveKit Inference OpenAI and xAI routes and xAI model cancellation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RealtimeSession.interrupt() is a no-op without response_id under concurrent in-flight responses (OpenAI plugin)

1 participant