axio-transport-codex¶
ChatGPT OAuth transport for axio using the OpenAI Responses API.
Authenticates via the same OAuth2 PKCE flow used by the ChatGPT desktop client - no API key purchase required; your ChatGPT subscription covers usage. Implements the OpenAI Responses API (not the legacy chat completions endpoint).
Features¶
No API key purchase - authenticates with your ChatGPT account via OAuth2 PKCE
Responses API - uses OpenAI’s newer Responses endpoint (
/backend-api/codex/responses)Automatic token refresh - access tokens are silently refreshed before expiry; callers notified via
on_auth_refreshFull streaming - incremental text, reasoning, and tool-call events via SSE
Tool calling - works with all axio tool handlers; parallel tool calls enabled
Retry logic - automatic backoff on 429 and 5xx responses; honours
Retry-Afterheaderaiohttp-based - non-blocking I/O throughout
Installation¶
pip install axio-transport-codex
With the TUI settings screen (ChatGPT sign-in dialog):
pip install "axio-transport-codex[tui]"
Authentication¶
CodexTransport authenticates with ChatGPT using OAuth2 PKCE. The transport stores an access token (api_key), a refresh token, and an expiry timestamp. It refreshes the access token automatically when it is within 30 seconds of expiry.
Running the OAuth flow¶
Use run_oauth_flow() from axio_transport_codex.oauth to obtain tokens for the first time. It opens a browser window for ChatGPT sign-in and waits for the callback on http://localhost:1455/auth/callback.
import asyncio
from axio_transport_codex.oauth import run_oauth_flow
from axio_transport_codex import CodexTransport
async def main() -> None:
tokens = await run_oauth_flow()
# tokens = {
# "access_token": "...",
# "refresh_token": "...",
# "expires_at": "1234567890", # Unix timestamp as string
# "account_id": "...",
# }
transport = CodexTransport(
api_key=tokens["access_token"],
refresh_token=tokens["refresh_token"],
expires_at=tokens["expires_at"],
account_id=tokens["account_id"],
)
Persisting tokens across sessions¶
Pass an on_auth_refresh callback to receive updated credentials whenever the access token is refreshed. Save the returned dict to disk or a secrets store and restore it on the next run.
import json, pathlib, aiohttp
from axio_transport_codex import CodexTransport
CRED_FILE = pathlib.Path("~/.config/axio/codex.json").expanduser()
async def save_tokens(tokens: dict[str, str]) -> None:
CRED_FILE.parent.mkdir(parents=True, exist_ok=True)
CRED_FILE.write_text(json.dumps(tokens))
async def main() -> None:
creds = json.loads(CRED_FILE.read_text()) if CRED_FILE.exists() else {}
async with aiohttp.ClientSession() as session:
transport = CodexTransport(
api_key=creds.get("api_key", ""),
refresh_token=creds.get("refresh_token", ""),
expires_at=creds.get("expires_at", ""),
account_id=creds.get("account_id", ""),
session=session,
on_auth_refresh=save_tokens,
)
# use transport with an Agent ...
Refreshing tokens manually¶
from axio_transport_codex.oauth import refresh_access_token
tokens = await refresh_access_token(refresh_token="...")
# tokens = {"access_token": ..., "refresh_token": ..., "expires_at": ..., "account_id": ...}
Usage¶
import asyncio
import aiohttp
from axio.agent import Agent
from axio.context import MemoryContextStore
from axio.events import TextDelta
from axio_transport_codex import CodexTransport, CODEX_MODELS
async def main() -> None:
async with aiohttp.ClientSession() as session:
transport = CodexTransport(
api_key="<your-chatgpt-access-token>",
refresh_token="<your-refresh-token>",
expires_at="<unix-timestamp-string>",
account_id="<your-account-id>",
model=CODEX_MODELS["gpt-4.1"],
session=session,
)
agent = Agent(system="You are a helpful assistant.", tools=[], transport=transport)
ctx = MemoryContextStore()
async for event in agent.run_stream("Explain the Rust borrow checker.", ctx):
if isinstance(event, TextDelta):
print(event.delta, end="", flush=True)
print()
asyncio.run(main())
The session parameter is required for streaming. Create an aiohttp.ClientSession in an async context and pass it to the transport.
Configuration reference¶
CodexTransport is a dataclass with the following fields:
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
Display name (used by TUI) |
|
|
|
ChatGPT OAuth access token |
|
|
|
OAuth refresh token for silent renewal |
|
|
|
Access token expiry as a Unix timestamp string |
|
|
|
ChatGPT account/organisation ID (sent as |
|
|
|
API base URL |
|
|
|
Active model |
|
|
all |
Available models |
|
|
|
HTTP session (required for streaming) |
|
|
|
Callback invoked with fresh credentials after token refresh |
|
|
|
Maximum retry attempts on 429 / 5xx |
|
|
|
Base delay in seconds for exponential backoff |
Models¶
Model ID |
Context |
Max output |
Capabilities |
|---|---|---|---|
|
1,047,576 |
32,768 |
text, vision, tool use |
|
1,047,576 |
32,768 |
text, vision, tool use |
|
1,047,576 |
32,768 |
text, tool use |
|
128,000 |
16,384 |
text, vision, tool use |
|
128,000 |
16,384 |
text, vision, tool use |
|
200,000 |
100,000 |
text, reasoning, tool use |
|
200,000 |
100,000 |
text, reasoning, tool use |
|
200,000 |
100,000 |
text, reasoning, tool use |
The default model is gpt-4.1.
fetch_models()¶
await transport.fetch_models() queries the Codex /models endpoint for the list of models your account has access to. If the request fails or the account is not configured, it falls back to the built-in CODEX_MODELS registry. Models not found in the built-in registry are added with basic text and tool_use capabilities.
session and api_key must be set before calling fetch_models().
Plugin registration¶
When installed, this package registers itself via entry points so axio-tui discovers it automatically:
[project.entry-points."axio.transport"]
codex = "axio_transport_codex:CodexTransport"
[project.entry-points."axio.transport.settings"]
codex = "axio_transport_codex.tui:CodexSettingsScreen"
Part of the axio ecosystem¶
axio · axio-transport-openai · axio-transport-anthropic · axio-tui
License¶
MIT