Skip to main content

axio-transport-openai

PyPI Python License: MIT

OpenAI-compatible streaming transport for axio.

Works with any API that speaks the OpenAI chat completions format - OpenAI itself, local models via Ollama/vLLM/LM Studio, Nebius AI Studio, OpenRouter, and any other compatible provider.

Features

  • Full SSE streaming - parses data: chunks incrementally; no waiting for full responses
  • Automatic retry - configurable backoff on 429 and 5xx responses; honours Retry-After header
  • Tool calling - streams tool-use JSON fragments as ToolInputDelta events; parallel tool calls supported
  • Reasoning support - <think>...</think> blocks emitted as ReasoningDelta events
  • Embeddings - embed() method for models that support /v1/embeddings
  • Sub-transports - NebiusTransport, OpenRouterTransport, and OpenAICompatibleTransport for common providers
  • aiohttp-based - zero blocking I/O
  • Optional TUI settings screen - install with [tui] extra for a Textual configuration UI

Installation

pip install axio-transport-openai

With TUI settings screens:

pip install "axio-transport-openai[tui]"

Usage

import asyncio
import aiohttp
from axio.agent import Agent
from axio.context import MemoryContextStore
from axio.events import TextDelta
from axio_transport_openai import OpenAITransport, OPENAI_MODELS


async def main() -> None:
    async with aiohttp.ClientSession() as session:
        transport = OpenAITransport(
            api_key="sk-...",
            model=OPENAI_MODELS["gpt-4.1-mini"],
            session=session,
        )
        agent = Agent(system="You are a helpful assistant.", tools=[], transport=transport)
        ctx = MemoryContextStore()
        async for event in agent.run_stream("What is 2 + 2?", 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.

Sign in with ChatGPT

axio_transport_openai.oauth provides browser registration and returning sign-in. It uses aiohttp for HTTP and PyJWT for ID-token verification. It does not require Textual. ChatGPTTransport uses this account session for subscription inference.

import aiohttp
from axio_transport_openai.oauth import ChatGPTAccount, ChatGPTOAuth


async def sign_in(host_id: str, account: ChatGPTAccount | None = None) -> ChatGPTAccount:
    async with aiohttp.ClientSession() as session:
        oauth = ChatGPTOAuth(session=session, host_id=host_id)
        return await oauth.sign_in(account)

Choose and persist a unique host ID before calling this function, for example a urn:uuid: value. Reuse it for every sign-in on that host. Pass a saved account to reauthorize its issued client ID; omit it to register another account. The caller owns credential storage. Keep tokens in protected storage and never log them.

sign_in() returns an immutable ChatGPTAccount after verifying the ID token's signature, issuer, audience, expiry, nonce, and subject. It does not modify the selected account. The returned tokens.scopes contains the granted permissions; tokens.plan_enabled reports permission to use the ChatGPT plan. Identity-only sign-in succeeds with this property set to False.

The callback binds to 127.0.0.1 on an available port and uses /auth/callback. Pass port= when a fixed port is required. The default timeout is 300 seconds and covers the complete flow. Cancellation or timeout closes the listener. A declined consent raises OAuthDenied; invalid identity or callback data raises OAuthValidationError. Other OAuth failures raise OAuthError with safe status and code fields. An expired authorization code triggers one fresh browser attempt with the issued client ID.

After validation, the browser shows a local status page with the Axio owl. It supports light and dark themes and attempts to close after five seconds. The close button also attempts immediate closure. If the browser blocks closure, the page asks the user to close the tab manually. The page loads no remote assets and includes no account details or credentials.

See the OpenAI registration and sign-in specification.

ChatGPT subscription transport

ChatGPTTransport implements completion using the selected ChatGPTSession and the caller's aiohttp.ClientSession. Ordinary sign-in uses public /v1/responses; native sign-in (axio-chatgpt login --codex) uses the Codex backend for completion and image generation. The registration determines the route. It does not read OPENAI_API_KEY or import credentials from Codex CLI.

import aiohttp
from axio import Agent, MemoryContextStore
from axio_transport_openai import ChatGPTTransport
from axio_transport_openai.session import ChatGPTSession


async def ask(account: ChatGPTSession, http: aiohttp.ClientSession) -> str:
    transport = ChatGPTTransport(account=account, session=http)
    await transport.fetch_models()
    choices = [model for model in transport.catalog if model.visibility == "list"]
    if not choices:
        raise ValueError("This account has no visible models.")
    selected = transport.with_model(choices[0].id)
    agent = Agent(system="Be concise.", transport=selected)
    return await agent.run("Hello", MemoryContextStore())

The catalog retains slug as id, display_name, visibility, and server order. models contains visible entries; catalog also retains hidden entries. Refresh with fetch_models(). A failed refresh clears the catalog and raises its error. There is no static fallback. A removed selected model requires a new selection.

with_model() shares the account session, connection pool, and catalog. Each new request reads current credentials, including rotated tokens. with_account() creates an independent catalog for the new account; existing transports remain bound to their original account. Fetch the new catalog before showing its choices. stream() fetches lazily if needed. Keep both sessions open until consumers finish.

Capabilities and token budgets come from the selected account's API catalog. All server completion catalog models are treated as supporting text and function tools, including new model IDs. input_modalities enables vision when it contains image; supported_reasoning_levels enables reasoning and restricts explicit effort values in payloads. Unsupported efforts raise ValueError before inference, including after with_model() changes the selected model. When the field is absent, an explicitly supplied reasoning-capable ModelSpec uses the generic effort levels. context_window is the configured context budget; max_context_window does not replace it. Positive max_output_tokens values are used when the server provides them.

Missing metadata uses explicitly supplied model_specs=[...], or generic token budgets when no override exists. The ChatGPT transport does not consult the static OPENAI_MODELS registry. ChatGPTModel.input_cost and output_cost are NaN: subscription pricing is unknown here, not zero. API-key prices do not describe subscription billing.

Requests include the complete history, encrypted reasoning, and tool results. History system messages become developer messages. Current function declarations occupy one developer additional_tools item at the start of input, without namespaces. Its position stays fixed across turns. A selector or schema change replaces this declaration; old calls remain in history. This can invalidate cached prefixes, but it does not retain removed tools or share tool state across contexts. Native Codex sessions instead send current function declarations in top-level tools.

reasoning_effort and reasoning_summary configure known reasoning models. The builder always sets store=false and stream=true; it exposes no arbitrary extra parameters or unsupported sampling/output limits. Audio, video, unsupported hosted tool history, and images without verified model support fail before inference. Embedding and realtime interfaces are not exposed.

The subscription API also accepts inline input_file attachments, including PDF and text files. The Files upload API is unavailable on this route. Axio's current content-block API does not expose native file attachments: the REPL reads local text files through tools and sends images as ImageBlock. Callers that construct raw Responses inputs can use ChatGPTResponses for inline files.

See the subscription route restrictions and account model discovery.

ChatGPT image generation

Run axio-chatgpt login --codex once to authorize and select a native ChatGPT registration. This uses the public OAuth client and backend protocol from Codex CLI rust-v0.163.0, with a loopback callback on port 1455 by default. --manual also works over SSH. The ordinary Sign in with ChatGPT token cannot call this image route; Axio reports that an explicit native sign-in is needed. Existing registrations remain saved. Reauthorize a native registration with login --account CLIENT_ID.

ChatGPTImageTransport implements ImageGenTransport and returns list[bytes]. ChatGPTImageModel describes an image-only model with Capability.image_generation and Capability.image_editing. CHATGPT_IMAGE_MODELS currently contains gpt-image-2. After a successful catalog fetch, native ChatGPTTransport.models appends these image presets to the server's completion models. Presets describe the implemented endpoint; the endpoint still checks account access and quota. Select completion models by Capability.text and image models by Capability.image_generation. Image models cannot be used with stream().

import aiohttp
from axio.models import Capability
from axio_transport_openai.accounts import open_chatgpt
from axio_transport_openai.chatgpt_images import image_media_type
from axio_transport_openai.credential_stores import FileCredentialStore


async def draw(prompt: str) -> tuple[bytes, str]:
    async with aiohttp.ClientSession() as http:
        async with open_chatgpt(FileCredentialStore(), http) as chatgpt:
            model = chatgpt.models.by_capability(Capability.image_generation).first()
            generator = chatgpt.image_transport(model)
            images = await generator.generate_images(prompt, quality="low")
            return images[0], image_media_type(images[0])

For an existing account session, construct ChatGPTImageTransport(account=account, session=http) directly; it needs no completion catalog request. chatgpt.with_model("gpt-image-2").generate_images(prompt) is also supported. The generator shares credential renewal, account selection, and quota state with completion. It never changes the account or billing method.

The current interface generates one image per call (n=1). The standalone class accepts size="auto", "1024x1024", "1536x1024", or "1024x1536"; quality="auto", "low", "medium", or "high"; and transparent_background=True. Native generation returns PNG; image_media_type recognizes PNG, JPEG and WebP signatures. Output format selection, partial previews, seeds and reproducibility are not exposed.

Requests use POST https://chatgpt.com/backend-api/codex/images/generations. This backend protocol can change independently of the public OpenAI API. ChatGPTImageError (a ResponsesAPIError) retains provider errors and request IDs. Missing or invalid image results raise an error. Quota exhaustion pauses the shared session until resume_plan_usage(). Generation POSTs are never automatically retried: cancellation or a lost response may leave a completed generation on the server. Closing the caller's sessions does not sign out.

Editing images and agent tools

ChatGPTImageTransport also implements axio.transport.ImageEditTransport. edit_images(prompt, images=[...]) requires a nonempty list of ImageBlock sources, preserves their order, and posts inline data to /images/edits using the same account session. PNG, JPEG and WebP are accepted; a mismatched MIME type is rejected before HTTP. The model's image_editing capability is independent of vision (understanding images) and image_generation (drawing from text).

from axio.blocks import ImageBlock
from axio_transport_openai import ChatGPTImageTransport


async def recolor(transport: ChatGPTImageTransport, source: ImageBlock) -> bytes:
    images = await transport.edit_images("Change the circle to red", images=[source])
    return images[0]

ChatGPTTransport.edit_images() exposes the same core interface. The standalone image transport additionally accepts the same size, quality, and background options as generation. One result per request is supported. The native Codex protocol has no mask field, so a non-None mask raises ValueError instead of being ignored. Editing has the same quota gate and no automatic request retries.

ChatGPTImageTools provides separate generate_image(prompt) and edit_image(prompt, images) tools. Model-facing images contains registered IDs; the host supplies the actual ImageBlock data. Results include both an image ID and the image, allowing subsequent edits. Neither tool reads paths or downloads URLs.

from axio import Agent
from axio.blocks import ImageBlock
from axio_transport_openai import ChatGPTImageTools, ChatGPTTransport


def editing_agent(chatgpt: ChatGPTTransport, source: ImageBlock) -> Agent:
    images = ChatGPTImageTools(chatgpt.image_transport())
    source_id = images.add_image(source)
    return Agent(
        system=f"Edit the supplied attachment when requested. Its image ID is {source_id}.",
        transport=chatgpt.with_model("gpt-6-luna"),
        tools=images.tools,
    )

Keep one ChatGPTImageTools instance per conversation. Its images mapping retains sources and results; the application controls their lifetime. To resume, persist the mapping using image block serialization and pass it as images= to a new instance. Session and conversation-history persistence remain the host's job. Both tools declare external access for normal Axio permission guards.

Token renewal and application storage

ChatGPTSession renews tokens in the background inside an async context manager. access_token() also checks expiry and renews on demand. A session stays pinned to its issued client ID. Selecting another account in the application does not change an existing session.

from collections.abc import Awaitable, Callable

import aiohttp
from axio_transport_openai.credentials import CredentialStore
from axio_transport_openai.oauth import ChatGPTOAuth
from axio_transport_openai.session import ChatGPTSession


async def with_chatgpt(
    store: CredentialStore,
    host_id: str,
    client_id: str,
    run: Callable[[ChatGPTSession], Awaitable[None]],
) -> None:
    async with aiohttp.ClientSession() as http:
        oauth = ChatGPTOAuth(http, host_id)
        async with ChatGPTSession(store, oauth, client_id) as session:
            await run(session)

Inside run, call await session.access_token() before each new API request. Keep the HTTP session open until the ChatGPT session closes. start() and close() provide the same lifecycle without a context manager. Closing stops background renewal and waits for active credential commits; it does not sign out. Cancellation of an exchange waits for its result to be stored before propagating.

Use FileCredentialStore or SQLiteCredentialStore from axio_transport_openai.credential_stores, or implement CredentialStore for PostgreSQL, Redis, or another backend. The interface has three methods:

These classes bind the shared axio.credentials backends to CredentialRecord. The transport supplies the dataclass and filename. Core owns serialization, file protection, process locks, and cancellation handling.

Method Required behavior
lock(client_id) Return an async context manager that excludes writers for this registration across all participating processes. Keep the lock through the HTTP exchange and each save.
load(client_id) Return the latest CredentialRecord, or None. Read under the lock; do not return a stale cached record.
save(record) Atomically commit the entire record before returning. Raise CredentialError with a safe message on failure. Call under the lock.

Each save must be durable independently. The lock is not a database transaction that rolls back earlier saves when an exception occurs. Distributed lock leases must stay exclusive through the complete operation. Protect stored secrets with file permissions, database access controls, or equivalent. Persist a stable host ID separately and retain it after sign-out. Key registrations by issued client ID and verified identity; use email only as a display label.

Store every CredentialRecord field: account, revision, and operation. The account contains the complete token set, scopes, expiry, and verified identity. A recovery marker is saved before refreshing. If saving rotated tokens fails, the same live session can retry the save without another token exchange. After a process crash with that marker present, reauthorize the same registration; the old refresh token may already have been spent. record.public_account() omits tokens when such an operation was interrupted.

Use save_account(store, account) from axio_transport_openai.credentials to save an initial validated sign-in. For returning sign-in, load the existing record under its lock, pass record.public_account() to oauth.sign_in(), then call save_account(store, result, expected=record). CredentialConflict means another operation changed the record during sign-in; it prevents overwriting newer tokens.

Renewal normally starts 60 seconds before expiry, or halfway through a shorter token lifetime. Failures before the refresh request and explicit temporary HTTP errors retain credentials and retry with bounded backoff. A still-valid access token remains usable on those paths. A lost response or timeout after sending retains the recovery marker: the old refresh token must not be replayed. Reauthorize the same registration to recover from that uncertain outcome. last_error exposes background failures without logging secrets. Terminal refresh errors clear tokens and raise SignInRequired; missing plan consent raises PlanConsentRequired. Neither case switches accounts or billing modes.

await session.sign_out() stops renewal, revokes the latest refresh token, and clears local tokens while retaining the registration. Its SignOutResult contains revocation_confirmed. If false, local tokens are cleared but remote revocation could not be confirmed. Storage failures raise CredentialError instead of reporting successful local sign-out.

See the OpenAI account lifecycle specification.

Subscription request execution

ChatGPTResponses in axio_transport_openai.subscription sends a prepared subscription request through a ChatGPTSession and an aiohttp.ClientSession. It provides HTTP/SSE execution for transport integrations; it does not build agent requests or discover models. The caller must supply a subscription-compatible body and keep both sessions open.

stream(payload) checks plan consent before sending and reads through response completion. HTTP and SSE failures raise axio_responses.ResponsesAPIError, which retains the provider code, parameter, HTTP status, request ID, and diagnostic body. ResponsesIncompleteError and ResponsesInterruptedError distinguish incomplete responses from disconnected streams. SignInRequired and PlanConsentRequired also remain catchable as StreamError.

Temporary failures receive at most max_attempts attempts, including the initial request. Backoff uses Retry-After or retry_base_delay and is capped at 300 seconds. No retry occurs after an event reaches the caller. Authentication and eligibility failures retain credentials; they do not trigger OAuth loops or a switch to API-key billing.

A quota error sets session.plan_usage_error and pauses new requests through that session, including requests from another executor sharing it. Direct the user to ChatGPT settings → Usage. After resolving the limit, the caller can explicitly call session.resume_plan_usage(). The session does not infer a reset time. This pause is local to the session, not persisted across processes. Token renewal remains independent of the inference pause.

See the OpenAI recovery rules.

File and SQLite credentials

from pathlib import Path
from axio_transport_openai.credential_stores import FileCredentialStore, SQLiteCredentialStore

file_store = FileCredentialStore()
sqlite_store = SQLiteCredentialStore(Path.home() / ".config" / "axio" / "chatgpt.db")

FileCredentialStore() defaults to $XDG_CONFIG_HOME/axio/chatgpt.auth, or ~/.config/axio/chatgpt.auth when the XDG variable is unset, empty, or relative. Pass a path to override it. The file holds all registrations and the host ID. Writes replace the complete file atomically. A short file-update lock prevents concurrent writes to different registrations from overwriting each other.

SQLiteCredentialStore(path) uses a caller-selected database. Each load opens a fresh mode=ro connection, reads, and closes within one worker-thread call. Each write opens its own connection, commits, and closes in one worker thread. Reads retain SQLite locking and change detection. immutable=1 is unsuitable for a database that other connections modify; SQLite documents possible incorrect results and SQLITE_CORRUPT errors. See SQLite URI parameters.

Both stores provide await store.host_id() to create or retrieve the stable host identity. Each save commits separately, including recovery markers. An adjacent <filename>.locks directory holds per-registration process locks across OAuth requests. Keep that directory intact while applications use the store. Neither backend needs a close() call.

The built-in stores require a local POSIX filesystem with flock, atomic rename, and durable writes. They create directories with mode 0700 and files with mode 0600. Existing directories must belong to the current user and must not be writable by other users. Insecure files, symlinks, and hard-linked files are rejected. Credentials use filesystem access controls; they are not encrypted. Custom backends can support other platforms or shared database services.

LocalCredentialStore.read_value() and write_value() are public extension methods inherited from axio.credentials.Credentials. They exchange dictionaries and run in worker threads under the corresponding registration lock. JSON is encoded only at the file or SQLite boundary. SQLiteCredentialStore.connect() can also be overridden; its caller closes the connection. Shared file helpers are public in axio.credentials.files, and CredentialCodec provides typed serialization in axio.credentials. The previous axio_transport_openai.credential_files imports remain available.

Use accounts.list_accounts(store) for metadata without tokens. The generic store.list_records() returns complete records and must not be used directly as CLI output. Existing credential files and SQLite databases retain their records, selection, host identity, and recovery markers.

Terminal and SSH sign-in

Install this package to get the axio-chatgpt command. It does not require Textual.

axio-chatgpt login
axio-chatgpt accounts
axio-chatgpt select CLIENT_ID
axio-chatgpt status
axio-chatgpt models

login saves and selects a registration only after successful authorization. Use login --account CLIENT_ID to reauthorize it; plain login registers another account. Account references are issued client IDs, so accounts with the same email remain distinct. Identity-only authorization succeeds, but status reports consent_required until the user grants ChatGPT plan access.

accounts, status, and models support --json. Status is local and offline; ready describes saved credentials, not remaining plan quota. Expired credentials with a refresh token report refresh_required; opening a session renews them. models lists the selected account's visible catalog and fails without a fallback if discovery fails. Commands do not print token values or provider error bodies.

Pass --credentials PATH to any command to override the default XDG file. status, models, and logout accept --account CLIENT_ID without changing the saved selection. Otherwise, commands use the saved selection, or the sole stored registration. Multiple registrations without a selection require an explicit choice. Selection affects new sessions; running sessions keep their original account.

axio-chatgpt logout clears local tokens and attempts server revocation. It keeps the registration and selection for returning sign-in. If revocation fails, it prints a warning after clearing local credentials. Command failures exit with status 1, invalid arguments with 2, and cancellation with 130.

For the terminal agent, install axio-repl and run:

axio-repl --transport chatgpt --model gpt-6-luna

On first use with an empty credential store, the REPL offers sign-in and continues after browser authorization. This requires terminal input; piped input does not prompt. The separate axio-chatgpt login command is optional for this workflow.

The standalone example runs an agent with a local multiplication tool and the selected saved account:

python axio-transport-openai/examples/chatgpt_agent.py --model gpt-6-luna

Run this example from the repository root. It uses accounts.open_chatgpt() to resolve the account, fetch its models, and close background renewal on exit. The caller owns the HTTP session. Credentials remain saved for later runs.

Use axio-chatgpt login --manual over SSH. The standalone sign-in module remains available for callers that manage selection themselves:

python -m axio_transport_openai.auth

This command signs in and saves credentials in the default XDG file. It prints the issued client ID after saving. Use --client-id ID for returning sign-in, --credentials PATH to override the file, and --timeout SECONDS to change the default five-minute timeout.

When no browser is available, or when Axio runs over SSH, the command prints the authorization URL to stdout and accepts the final redirect URL on stdin. Use --manual to select this behavior explicitly:

ssh user@remote
python -m axio_transport_openai.auth --manual

Open the printed URL in your local browser and approve sign-in. The browser may then report that it cannot connect to 127.0.0.1. Copy the complete URL from its address bar and paste it into the remote terminal. No SSH port forwarding is required. The pasted callback uses the same state and identity checks as a browser callback. The command does not print access, refresh, or ID tokens.

A browser callback can still complete the flow while stdin is waiting. For SSH port forwarding instead of pasting, start SSH with -L 1455:127.0.0.1:1455 and run the command with --port 1455.

Library callers can supply an asynchronous read_callback function to ChatGPTOAuth.sign_in(). It must return the complete redirect URL. The callback listener remains active while the function waits; completion, cancellation, or timeout cancels the pending input reader.

Environment variables

Variable Description
OPENAI_API_KEY Default API key if not passed to the constructor
OPENAI_BASE_URL Default base URL (falls back to https://api.openai.com/v1)

Local models (Ollama, vLLM, LM Studio)

from axio.models import ModelSpec, Capability
from axio_transport_openai import OpenAITransport

transport = OpenAITransport(
    api_key="ollama",  # any non-empty string
    model=ModelSpec(id="llama3.2", capabilities=frozenset({Capability.text})),
    base_url="http://localhost:11434/v1",
)

Streaming events

import asyncio
from axio.agent import Agent
from axio.context import MemoryContextStore
from axio.testing import StubTransport, make_text_response
from axio.events import TextDelta, SessionEndEvent

agent = Agent(
    system="",
    tools=[],
    transport=StubTransport([make_text_response("Why did the chicken cross the road?")]),
)


async def main() -> None:
    ctx = MemoryContextStore()
    async for event in agent.run_stream("Tell me a joke", ctx):
        match event:
            case TextDelta(delta=text):
                print(text, end="", flush=True)
            case SessionEndEvent(total_usage=usage):
                print(f"\n[{usage.input_tokens}in / {usage.output_tokens}out tokens]")


asyncio.run(main())

Configuration reference

OpenAITransport is a dataclass with the following fields:

Field Type Default Description
name str "OpenAI" Display name (used by TUI)
api_key str $OPENAI_API_KEY API key
base_url str $OPENAI_BASE_URL or https://api.openai.com/v1 API base URL
model ModelSpec OPENAI_MODELS["gpt-4.1-mini"] Active model
models ModelRegistry all OPENAI_MODELS Available models
session aiohttp.ClientSession | None None HTTP session (required for streaming)
max_retries int 10 Maximum retry attempts on 429 / 5xx
retry_base_delay float 5.0 Base delay in seconds for exponential backoff

Models

Model ID Context Max output Capabilities Price (in/out per M tokens)
gpt-5.4 1,050,000 128,000 text, vision, tool use $10 / $40
gpt-5.4-mini 400,000 128,000 text, vision, tool use $1.50 / $6
gpt-5.4-nano 400,000 128,000 text, tool use $0.30 / $1.20
gpt-5.1 400,000 128,000 text, vision, tool use $5 / $20
gpt-5 400,000 128,000 text, vision, tool use $5 / $20
gpt-5-mini 400,000 128,000 text, vision, tool use $1.25 / $5
gpt-5-nano 400,000 128,000 text, tool use $0.25 / $1
o4-mini 200,000 100,000 text, reasoning, tool use $1.10 / $4.40
o3 200,000 100,000 text, reasoning, tool use $10 / $40
o3-mini 200,000 100,000 text, reasoning, tool use $1.10 / $4.40
gpt-4.1 1,047,576 32,768 text, vision, tool use $2 / $8
gpt-4.1-mini 1,047,576 32,768 text, vision, tool use $0.40 / $1.60
gpt-4.1-nano 1,047,576 32,768 text, tool use $0.10 / $0.40
gpt-4o 128,000 16,384 text, vision, tool use $2.50 / $10
gpt-4o-mini 128,000 16,384 text, vision, tool use $0.15 / $0.60

The default model is gpt-4.1-mini.

fetch_models()

await transport.fetch_models() resets transport.models to the built-in OPENAI_MODELS registry. It does not make a network request. Override model directly to switch the active model.

Serialisation

OpenAITransport supports JSON round-trip for storing and restoring configuration:

# Serialise
data = transport.to_dict()  # -> {"name": ..., "base_url": ..., "api_key": ..., "models": [...]}

# Restore
transport = OpenAITransport.from_dict(data, session=session)

from_dict falls back to OPENAI_API_KEY and OPENAI_BASE_URL environment variables if the stored values are empty.

Sub-transports

NebiusTransport

NebiusTransport connects to Nebius AI Studio (https://api.tokenfactory.nebius.com/v1). It inherits all retry and streaming behaviour from OpenAITransport.

from axio_transport_openai.nebius import NebiusTransport

transport = NebiusTransport(
    api_key="...",  # or set NEBIUS_API_KEY
    session=session,
)
Field Default
name "Nebius AI Studio"
api_key $NEBIUS_API_KEY
base_url https://api.tokenfactory.nebius.com/v1
model deepseek-ai/DeepSeek-V3-0324

fetch_models() queries /v1/models?verbose=true and populates transport.models with all models returned by the API, including their context windows, output limits, capabilities (text, vision, tool use, embedding), and pricing.

OpenRouterTransport

OpenRouterTransport connects to OpenRouter (https://openrouter.ai/api/v1), which provides a unified API over hundreds of models from many providers.

from axio_transport_openai.openrouter import OpenRouterTransport

transport = OpenRouterTransport(
    api_key="...",  # or set OPENROUTER_API_KEY
    session=session,
)
Field Default
name "OpenRouter"
api_key $OPENROUTER_API_KEY
base_url https://openrouter.ai/api/v1
model google/gemini-2.5-pro-preview

fetch_models() queries /v1/models and populates transport.models with all models returned by the API, including their context windows, output limits, capabilities (text, vision, tool use, embedding), and pricing.

OpenAICompatibleTransport

OpenAICompatibleTransport is a thin subclass of OpenAITransport for user-defined custom providers. Instances are created by the TUI hub screen and persisted to ~/.local/share/axio/openai-custom.json. You can also instantiate them directly:

from axio.models import ModelSpec, ModelRegistry, Capability
from axio_transport_openai.custom import OpenAICompatibleTransport

transport = OpenAICompatibleTransport(
    name="localai",
    base_url="http://localhost:8080/v1",
    api_key="",
    models=ModelRegistry(
        [
            ModelSpec(
                id="llama3.2",
                context_window=131_072,
                max_output_tokens=4_096,
                capabilities=frozenset({Capability.text, Capability.tool_use}),
            )
        ]
    ),
    session=session,
)

fetch_models() is a no-op for this transport - the model list is provided at construction time.

The JSON configuration format used by the TUI is:

[
  {
    "name": "localai",
    "base_url": "http://localhost:8080/v1",
    "api_key": "",
    "models": [
      {
        "id": "llama3.2",
        "context_window": 131072,
        "max_output_tokens": 4096,
        "capabilities": ["text", "tool_use"],
        "input_cost": 0.0,
        "output_cost": 0.0
      }
    ]
  }
]

Plugin registration

When installed, this package registers these transports via entry points. ChatGPT requires an authenticated account session supplied by its caller. TUI opens this session through open_for_app() and uses with_model() to retain shared credentials. Its Configure ChatGPT screen provides login, account selection, permission status, sign-out, and a ChatGPT Usage link, as described in the official recovery guidance. The screen stores only the credential-file path and registration reference in TUI settings.

[project.entry-points."axio.transport"]
chatgpt        = "axio_transport_openai:ChatGPTTransport"
openai         = "axio_transport_openai:OpenAITransport"
nebius         = "axio_transport_openai.nebius:NebiusTransport"
openrouter     = "axio_transport_openai.openrouter:OpenRouterTransport"
openai-custom  = "axio_transport_openai.custom:OpenAICompatibleTransport"

Part of the axio ecosystem

axio · axio-transport-codex · axio-transport-anthropic · axio-tui

License

MIT

Metadata

Release files for axio-transport-openai 0.12.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for axio-transport-openai 0.12.1
File Size Uploaded Provenance Actions
axio_transport_openai-0.12.1.tar.gz 217.5 kB With provenance Details for axio_transport_openai-0.12.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for axio-transport-openai 0.12.1
File Interpreter ABI Platform Actions
axio_transport_openai-0.12.1-py3-none-any.whl Python 3 none any Details for axio_transport_openai-0.12.1-py3-none-any.whl

Total release size: 302.8 kB

Release files / axio_transport_openai-0.12.1.tar.gz

Download URL axio_transport_openai-0.12.1.tar.gz
Size 217.5 kB
Tags Source
SHA-256 checksum
How to use checksums
df5d57ec086a37c6c6aac16d15cb868da57a71c042a4c026c528e78c3889318e
BLAKE2b-256 checksum
How to use checksums
c108de984592d44950f6e19ca6f326b51f7fab008553c317399871bc87438a19
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Release files / axio_transport_openai-0.12.1-py3-none-any.whl

Download URL axio_transport_openai-0.12.1-py3-none-any.whl
Size 85.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4dd9a8d9098c1b17898e28e14b2f65828421d64a93aa755a616dfaf466eaa331
BLAKE2b-256 checksum
How to use checksums
4490b239a5b96ada59d89492a22488aaf9126b709ac2e1d332f1cecb10ccc906
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

Security for axio-transport-openai 0.12.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for axio_transport_openai-0.12.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 17, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.12.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page