Skip to content

Commit ab45f9d

Browse files
committed
fix review comments
1 parent 54cf596 commit ab45f9d

2 files changed

Lines changed: 51 additions & 1 deletion

File tree

src/agents/realtime/openai_realtime.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,9 @@ async def _mark_response_done(self) -> None:
492492
await self._set_response_control("free")
493493

494494
async def _release_response_waiters(self) -> None:
495+
# Connection teardown means no response.done will arrive, so local
496+
# response sequencing must be released explicitly.
497+
self._ongoing_response = False
495498
await self._set_response_control("free")
496499

497500
async def _send_response_create_when_idle(self) -> None:
@@ -701,7 +704,6 @@ async def _handle_conversation_item(
701704

702705
async def close(self) -> None:
703706
"""Close the session."""
704-
await self._release_response_waiters()
705707
if self._websocket:
706708
await self._websocket.close()
707709
self._websocket = None
@@ -712,6 +714,7 @@ async def close(self) -> None:
712714
except asyncio.CancelledError:
713715
pass
714716
self._websocket_task = None
717+
await self._release_response_waiters()
715718

716719
async def _cancel_response(self) -> None:
717720
async with self._response_control_condition:

tests/realtime/test_openai_realtime.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,53 @@ async def fake_send_raw(event):
812812

813813
assert payload_types == ["conversation.item.create", "response.create"]
814814

815+
@pytest.mark.asyncio
816+
async def test_release_response_waiters_clears_active_response_state(self, model):
817+
"""Releasing waiters should also clear local active-response bookkeeping."""
818+
await model._mark_response_created()
819+
820+
await model._release_response_waiters()
821+
822+
assert model._ongoing_response is False
823+
assert model._response_control == "free"
824+
825+
@pytest.mark.asyncio
826+
async def test_close_unblocks_waiting_response_create_after_active_response(self, model):
827+
"""Closing should unblock waiters without sending a new response.create."""
828+
sent_types: list[str] = []
829+
websocket_closed = False
830+
831+
async def send(payload: str) -> None:
832+
nonlocal websocket_closed
833+
if websocket_closed:
834+
raise AssertionError("send should not run after close")
835+
sent_types.append(json.loads(payload)["type"])
836+
837+
async def close() -> None:
838+
nonlocal websocket_closed
839+
websocket_closed = True
840+
841+
model._websocket = SimpleNamespace(send=send, close=close)
842+
await model._mark_response_created()
843+
844+
task = asyncio.create_task(
845+
model._send_user_input(RealtimeModelSendUserInput(user_input="hi"))
846+
)
847+
await asyncio.sleep(0)
848+
849+
assert sent_types == ["conversation.item.create"]
850+
assert task.done() is False
851+
852+
await model.close()
853+
854+
with pytest.raises(AssertionError, match="Not connected"):
855+
await asyncio.wait_for(task, timeout=1)
856+
857+
assert sent_types == ["conversation.item.create"]
858+
assert model._ongoing_response is False
859+
assert model._response_control == "free"
860+
assert model._websocket is None
861+
815862
@pytest.mark.asyncio
816863
async def test_tool_output_start_response_waits_for_response_done_before_response_create(
817864
self, model, monkeypatch

0 commit comments

Comments
 (0)