mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
sdk/python: add first-class login support (#23093)
## Why The Python SDK can already create threads and run turns, but authentication still has to be arranged outside the SDK. App-server already exposes account login, account inspection, logout, and `account/login/completed` notifications, so SDK users currently have to work around a missing public client layer for a core setup step. This change makes authentication a normal SDK workflow while preserving the backend flow shape: API-key login completes immediately, and interactive ChatGPT flows return live handles that complete later through app-server notifications. ## What changed - Added public sync and async auth methods on `Codex` / `AsyncCodex`: - `login_api_key(...)` - `login_chatgpt()` - `login_chatgpt_device_code()` - `account(...)` - `logout()` - Added public browser-login and device-code handle types with attempt-local `wait()` and `cancel()` helpers. Cancellation stays on the handle instead of a root-level SDK method. - Extended the Python app-server client and notification router so login completion events are routed by `login_id` without consuming unrelated global notifications. - Kept login request/handle logic in a focused internal `_login.py` module so `api.py` remains the public facade instead of absorbing more auth plumbing. - Exported the new handle types plus curated account/login response types from the SDK surfaces. - Updated SDK docs, added sync/async login walkthrough examples, and added a notebook login walkthrough cell. ## Verification Added SDK coverage for: - API-key login, account readback, and logout through the app-server harness in both sync and async clients. - Browser login cancellation plus `handle.wait()` completion through the real app-server boundary used by the Python SDK harness. - Waiter routing that stays scoped across replaced interactive login attempts, plus async handle cancellation coverage. - Login notification demuxing, replay of early completion events, and async client delegation. - Public export/signature assertions. - Real integration-suite smoke coverage for the new examples and notebook login cell.
This commit is contained in:
committed by
GitHub
Unverified
parent
0445b290fe
commit
4c89772314
@@ -22,6 +22,16 @@ from ._inputs import (
|
||||
_normalize_run_input,
|
||||
_to_wire_input,
|
||||
)
|
||||
from ._login import (
|
||||
AsyncChatgptLoginHandle,
|
||||
AsyncDeviceCodeLoginHandle,
|
||||
ChatgptLoginHandle,
|
||||
DeviceCodeLoginHandle,
|
||||
async_start_chatgpt_login,
|
||||
async_start_device_code_login,
|
||||
start_chatgpt_login,
|
||||
start_device_code_login,
|
||||
)
|
||||
from ._run import (
|
||||
RunResult,
|
||||
_collect_async_run_result,
|
||||
@@ -30,6 +40,10 @@ from ._run import (
|
||||
from .async_client import AsyncAppServerClient
|
||||
from .client import AppServerClient, AppServerConfig
|
||||
from .generated.v2_all import (
|
||||
ApiKeyLoginAccountParams,
|
||||
GetAccountParams,
|
||||
GetAccountResponse,
|
||||
LoginAccountParams,
|
||||
ModelListResponse,
|
||||
Personality,
|
||||
ReasoningEffort,
|
||||
@@ -85,6 +99,33 @@ class Codex:
|
||||
def close(self) -> None:
|
||||
self._client.close()
|
||||
|
||||
def login_api_key(self, api_key: str) -> None:
|
||||
"""Authenticate app-server with an API key."""
|
||||
self._client.account_login_start(
|
||||
LoginAccountParams(
|
||||
root=ApiKeyLoginAccountParams(
|
||||
api_key=api_key,
|
||||
type="apiKey",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def login_chatgpt(self) -> ChatgptLoginHandle:
|
||||
"""Start browser-based ChatGPT login and return its live handle."""
|
||||
return start_chatgpt_login(self._client)
|
||||
|
||||
def login_chatgpt_device_code(self) -> DeviceCodeLoginHandle:
|
||||
"""Start device-code ChatGPT login and return its live handle."""
|
||||
return start_device_code_login(self._client)
|
||||
|
||||
def account(self, *, refresh_token: bool = False) -> GetAccountResponse:
|
||||
"""Read the current app-server account state."""
|
||||
return self._client.account_read(GetAccountParams(refresh_token=refresh_token))
|
||||
|
||||
def logout(self) -> None:
|
||||
"""Clear the current app-server account session."""
|
||||
self._client.account_logout()
|
||||
|
||||
# BEGIN GENERATED: Codex.flat_methods
|
||||
def thread_start(
|
||||
self,
|
||||
@@ -286,6 +327,38 @@ class AsyncCodex:
|
||||
self._init = None
|
||||
self._initialized = False
|
||||
|
||||
async def login_api_key(self, api_key: str) -> None:
|
||||
"""Authenticate app-server with an API key."""
|
||||
await self._ensure_initialized()
|
||||
await self._client.account_login_start(
|
||||
LoginAccountParams(
|
||||
root=ApiKeyLoginAccountParams(
|
||||
api_key=api_key,
|
||||
type="apiKey",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
async def login_chatgpt(self) -> AsyncChatgptLoginHandle:
|
||||
"""Start browser-based ChatGPT login and return its live handle."""
|
||||
await self._ensure_initialized()
|
||||
return await async_start_chatgpt_login(self)
|
||||
|
||||
async def login_chatgpt_device_code(self) -> AsyncDeviceCodeLoginHandle:
|
||||
"""Start device-code ChatGPT login and return its live handle."""
|
||||
await self._ensure_initialized()
|
||||
return await async_start_device_code_login(self)
|
||||
|
||||
async def account(self, *, refresh_token: bool = False) -> GetAccountResponse:
|
||||
"""Read the current app-server account state."""
|
||||
await self._ensure_initialized()
|
||||
return await self._client.account_read(GetAccountParams(refresh_token=refresh_token))
|
||||
|
||||
async def logout(self) -> None:
|
||||
"""Clear the current app-server account session."""
|
||||
await self._ensure_initialized()
|
||||
await self._client.account_logout()
|
||||
|
||||
# BEGIN GENERATED: AsyncCodex.flat_methods
|
||||
async def thread_start(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user