mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[2/4] Add private Python goal operations (#27111)
## Why The Python SDK must treat the runtime's initial goal turn and its continuations as one logical operation. That requires a private lifecycle engine before the public API can return the existing turn handle and result types. ## What - start goals by composing the existing clear/set goal RPCs - enforce persisted, idle threads and a bounded startup handshake - coalesce continuation notifications under a stable logical turn ID - aggregate items, usage, timing, and terminal status - support rollover-aware steering, interruption, cancellation, and cleanup - provide equivalent sync and async internals This is the second PR in the stack and intentionally adds no public API. ## Test plan - online CI, including the Python SDK suite - behavioral coverage is added in the following two stack PRs
This commit is contained in:
committed by
GitHub
Unverified
parent
cc8325f181
commit
9316acf9b2
@@ -1,7 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import threading
|
||||
from collections.abc import Iterator
|
||||
from concurrent.futures import Future
|
||||
from typing import AsyncIterator, Callable, ParamSpec, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
@@ -227,6 +229,58 @@ class AsyncCodexClient:
|
||||
"""Pause the active goal through the wrapped sync client."""
|
||||
return await self._call_sync(self._sync.pause_goal, thread_id)
|
||||
|
||||
async def cancel_goal_operation(self, state: _GoalOperationState) -> None:
|
||||
"""Stop continuation work after a logical goal operation is cancelled."""
|
||||
await self._call_sync(self._sync.cancel_goal_operation, state)
|
||||
|
||||
async def start_goal_operation(
|
||||
self,
|
||||
thread_id: str,
|
||||
objective: str,
|
||||
) -> tuple[_GoalOperationState, str]:
|
||||
"""Start a logical goal through the wrapped sync client."""
|
||||
operation: Future[tuple[_GoalOperationState, str]] = Future()
|
||||
|
||||
def start_operation() -> None:
|
||||
try:
|
||||
operation.set_result(self._sync.start_goal_operation(thread_id, objective))
|
||||
except BaseException as exc:
|
||||
operation.set_exception(exc)
|
||||
|
||||
worker = threading.Thread(
|
||||
target=start_operation,
|
||||
name="codex-goal-start",
|
||||
daemon=True,
|
||||
)
|
||||
worker.start()
|
||||
try:
|
||||
return await asyncio.shield(asyncio.wrap_future(operation))
|
||||
except asyncio.CancelledError:
|
||||
|
||||
def cleanup_cancelled_start(
|
||||
completed: Future[tuple[_GoalOperationState, str]],
|
||||
) -> None:
|
||||
try:
|
||||
state, _ = completed.result()
|
||||
except BaseException:
|
||||
return
|
||||
|
||||
def stop_cancelled_goal() -> None:
|
||||
try:
|
||||
self._sync.cancel_goal_operation(state)
|
||||
finally:
|
||||
state.finish()
|
||||
self._sync.unregister_goal_operation(state)
|
||||
|
||||
threading.Thread(
|
||||
target=stop_cancelled_goal,
|
||||
name="codex-goal-start-cleanup",
|
||||
daemon=True,
|
||||
).start()
|
||||
|
||||
operation.add_done_callback(cleanup_cancelled_start)
|
||||
raise
|
||||
|
||||
async def turn_start(
|
||||
self,
|
||||
thread_id: str,
|
||||
|
||||
Reference in New Issue
Block a user