mirror of
https://github.com/openai/codex.git
synced 2026-11-13 21:30:47 +00:00
Start Python SDK turn subscriptions at their attachment point (#44400)
## What changed Make turn event delivery depend on when each consumer attaches: - Handles returned by `thread.turn(...)` receive events from when the request is sent, including events received before the response arrives. - Other handles and explicit low-level registrations receive only events from when they join. Remove replay of completed items, token usage, and terminal events; use `thread.read(include_turns=True)` for earlier history. - Retain unread events for active subscribers and pending starts, release unused turn state, and close the returned subscription when an async start is cancelled. Preserve the existing low-level subscription when repeated `turn_start(...)` calls return the same turn. ## Testing Update coverage for late joins, request-time buffering, completion or transport failure before a start returns, async cancellation cleanup, and repeated low-level starts. Adjust external-message integration tests to allow handles to collect different items while receiving the same final response. GitOrigin-RevId: 3b7f1268df1193dee88385006ae9510101086c24
This commit is contained in:
@@ -296,19 +296,31 @@ class AsyncCodexClient:
|
||||
params: V2TurnStartParams | JsonObject | None = None,
|
||||
) -> TurnStartResponse:
|
||||
"""Start a turn, releasing an unclaimed result if the caller is cancelled."""
|
||||
return (await self._start_turn(thread_id, input_items, params, for_handle=False))[0]
|
||||
|
||||
async def _start_turn(
|
||||
self,
|
||||
thread_id: str,
|
||||
input_items: list[JsonObject] | JsonObject | str,
|
||||
params: V2TurnStartParams | JsonObject | None,
|
||||
for_handle: bool,
|
||||
) -> tuple[TurnStartResponse, _TurnSubscription | None]:
|
||||
operation = _TURN_START_EXECUTOR.submit(
|
||||
copy_context().run, self._sync.turn_start, thread_id, input_items, params
|
||||
copy_context().run, self._sync._start_turn, thread_id, input_items, params, for_handle
|
||||
)
|
||||
try:
|
||||
return await asyncio.wrap_future(operation)
|
||||
except asyncio.CancelledError:
|
||||
|
||||
def discard_cancelled_result(completed: Future[TurnStartResponse]) -> None:
|
||||
def discard_cancelled_result(
|
||||
completed: Future[tuple[TurnStartResponse, _TurnSubscription | None]],
|
||||
) -> None:
|
||||
try:
|
||||
started = completed.result()
|
||||
_, subscription = completed.result()
|
||||
except BaseException:
|
||||
return
|
||||
self._sync._subscribe_turn_notifications(started.turn.id).close()
|
||||
if subscription is not None:
|
||||
subscription.close()
|
||||
|
||||
operation.add_done_callback(discard_cancelled_result)
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user