Skip to content

fix(openai): continue a GPT Live backend only once every call in the conversation is answered - #7234

Open
dmt219 wants to merge 3 commits into
livekit:mainfrom
dmt219:fix/gpt-live-conversation-wide-continuation-barrier
Open

fix(openai): continue a GPT Live backend only once every call in the conversation is answered#7234
dmt219 wants to merge 3 commits into
livekit:mainfrom
dmt219:fix/gpt-live-conversation-wide-continuation-barrier

Conversation

@dmt219

@dmt219 dmt219 commented Sep 11, 2026

Copy link
Copy Markdown

GPT Live refuses response.create with function_call_outputs_required while any function call in the backend conversation is still unanswered, across delegations (the event carries no delegation id). _maybe_continue_response gated the continuation per response, so with two responses in flight the one whose tool finished first sent its continuation while the other's tool was still running; the service refused it, and since that response's bookkeeping had already been dropped, nothing ever continued it. The backend never answered and the voice model waited for good. Seen in production today with a slow lookup_preferences overlapping a fast lookup_plan:

code: function_call_outputs_required
message: Submit the pending function call outputs before response.create.
client_event_id: response_create_d003aedf7086

Make the barrier conversation-wide: a continuation goes out only when no tracked response has an open call, and every response that has finished asking then continues, oldest first. Remember each continuation until its response.created arrives; when the service refuses one anyway, put the response back behind the barrier so the next output re-sends it, bounded so a call this session never tracked cannot loop.

Regression coverage: two delegations with one slow call, a text-only response that neither blocks nor continues, a refused continuation re-armed and re-sent, and a repeatedly refused one given up on. The GPT Live and duplex adapter suites pass (101 tests); ruff check and ruff format --check are clean. I could not run make type-check locally (no uv).

…conversation is answered

GPT Live refuses `response.create` with `function_call_outputs_required` while any
function call in the backend conversation is still unanswered, across delegations.
`_maybe_continue_response` gated the continuation per response, so with two
responses in flight the one whose tool finished first sent its continuation while
the other's tool was still running; the service refused it, and since the
response's bookkeeping had already been dropped, nothing ever continued it. The
backend never answered and the voice model waited for good.

Make the barrier conversation-wide: a continuation goes out only when no tracked
response has an open call, and every response that has finished asking then
continues, oldest first. Remember each continuation until its response.created
arrives, and when the service refuses one anyway, put the response back behind the
barrier so the next output re-sends it, bounded so a call this session never
tracked cannot loop.

Regression coverage: two delegations with one slow call, a text-only response
that neither blocks nor continues, a refused continuation re-armed and re-sent,
and a repeatedly refused one given up on. The GPT Live and duplex adapter suites
pass (101 tests); ruff check and format are clean.
@dmt219
dmt219 requested a review from a team as a code owner September 11, 2026 14:47
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Duc Tran seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

devin-ai-integration[bot]

This comment was marked as resolved.

…rrives late

A response that fails with its call unanswered was removed without re-running the
barrier, stranding the continuations waiting behind it; and a refusal that arrives
after the output that opened the barrier re-armed its continuation without sending
it. Both now re-check the barrier at once.

response.created replaced a delegation's entry outright, so calls the replaced
response still waited on dropped out of the barrier's view; the new response now
inherits them, and the re-arm budget, which also follows a refused response
through a merge. Call ids in the two log lines move under an lk.pii key.
@dmt219

dmt219 commented Sep 11, 2026

Copy link
Copy Markdown
Author

Follow-up commit addresses the three review points: a failed or incomplete blocker re-runs the barrier so the responses waiting behind it continue; a refusal that lands after the output that opened the barrier is retried at once from the re-arm; call ids in both log lines moved under lk.pii. keys. Also closes a gap found while testing: response.created replaced a delegation's entry outright, so calls the replaced response still waited on dropped out of the barrier's view. The new response now inherits them and the re-arm budget. Five regression tests added; GPT Live and duplex suites at 105 passed, ruff check and format clean.

devin-ai-integration[bot]

This comment was marked as resolved.

…red calls

When response.created replaces a delegation's entry, the replaced response's answered
calls kept their call-id-to-delegation mapping, so the map grew per replacement and a
repeated output for one of them would have been forwarded again.
@dmt219

dmt219 commented Sep 11, 2026

Copy link
Copy Markdown
Author

Applied: a replaced response's answered calls now drop their routing entry (the suggested pop), with the assertion added to the inheritance test. 105 passed, ruff clean.

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.

2 participants