fix(openai): continue a GPT Live backend only once every call in the conversation is answered - #7234
Conversation
…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.
|
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. |
…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.
|
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 |
…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.
|
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. |
GPT Live refuses
response.createwithfunction_call_outputs_requiredwhile any function call in the backend conversation is still unanswered, across delegations (the event carries no delegation id)._maybe_continue_responsegated 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 slowlookup_preferencesoverlapping a fastlookup_plan: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.createdarrives; 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 checkandruff format --checkare clean. I could not runmake type-checklocally (no uv).