Packages¶
The Axio packages documented here each have a focused responsibility. They are top-level directories in the monorepo. A uv workspace manages them.
Overview¶
Package |
Purpose |
Entry Point Groups |
|---|---|---|
|
Core framework |
- |
|
SQLite-backed persistent context store |
- |
|
|
- |
|
OpenAI Responses API: request items and stream events |
- |
|
Anthropic Claude transport |
|
|
OpenAI Responses transport, plus the compatible chat endpoints (Nebius, OpenRouter, custom) |
|
|
ChatGPT (Codex) OAuth transport |
|
|
Google Gemini transport + Gemini Live realtime |
|
|
Microphone and speaker helpers for realtime agents |
- |
|
Filesystem & shell tools |
|
|
MCP tool loader |
|
|
Docker sandbox tools |
- |
|
Interactive terminal coding assistant |
- |
|
Textual chat application |
|
|
Path and LLM guards for the TUI |
|
Core¶
axio¶
The foundation. Defines the agent loop, all protocols (CompletionTransport,
ContextStore, PermissionGuard), the tool system, stream events, and testing
helpers. Has no entry points. Other packages depend on it.
Dependencies: none (stdlib only)
Wire formats¶
axio-sse¶
Reads text/event-stream. Decoder is the format as a synchronous state machine.
Feed it chunks cut anywhere, and take the events they completed. events() and
payloads() are the async skin over it.
A stream whose events are all one shape needs nothing above payloads(). A stream
that names each event subclasses Reader and writes one @on(...) method per
event. Wire says what one payload looks like. Fields are read by declared name
and type, so a misspelled key is a type error at the place that uses it, rather
than a default quietly standing in for the value.
A Reader names only the events it interprets. Everything else reaches
unmatched(), which returns nothing by default. A reader overrides it to forward
instead of drop. The reason is that an endpoint which runs tools publishes one
event family per tool. That set therefore depends on which tools exist and which
the caller declared, not on the protocol. Named one by one, the list is stale the
day a tool is added. A new tool then reads as news about the protocol when it is
news about the tools. strict=True still raises UnknownEvent for any name no
method claims. A test can therefore hold names() against the schema the provider
publishes, without the reader carrying a list it cannot keep true.
The package takes chunks and never lines. aiohttp’s readuntil raises
LineTooLong past 131072 bytes. LineTooLong is not a ClientError. One large
reasoning event kills a turn with no answer.
Dependencies: none (stdlib only)
axio-responses¶
The OpenAI Responses API as axio speaks it. convert_messages and convert_tools
build the request. Responses is an axio_sse.Reader that turns the stream into
StreamEvents.
The claim names() supports runs one way only: every name the reader claims is
one the schema publishes. That is what catches a typo in a claimed name. The
reverse does not hold, and deliberately. The reader does not name the hosted-tool
event families. It forwards them through unmatched() as
ProviderEvent(provider="openai", ...).
Both halves live here rather than in a transport because two transports speak this
API: axio-transport-openai against /v1/responses, and axio-transport-codex
against the ChatGPT backend.
Dependencies: axio, axio-sse
Context Stores¶
axio-context-sqlite¶
SQLite-backed persistent context store. Implements the axio.context.ContextStore
protocol so conversations survive process restarts. Multiple sessions can coexist
in the same database file, isolated by session_id and project.
Features:
Automatic gzip compression for large payloads (> 512 bytes)
WAL journal mode with a 5-second busy timeout for concurrent access
list_sessions()- list all sessions for a project, ordered newest firstfork()- copy a session’s messages into a new session IDadd_context_tokens()- atomic token-count increment via SQL UPSERT
Dependencies: axio, aiosqlite>=0.20
Transports¶
axio-transport-anthropic¶
Anthropic Claude transport. It posts with aiohttp. It reads the stream through
axio-sse: an axio_sse.Reader keyed on the format’s own event: field, with
one @on(...) method per event. It supports all Claude models with prompt caching
(cache_control). It retries automatically on rate-limit (429) and overload (529)
responses.
Its input_tokens are converted into the axio rule before they leave. The API
counts only the tokens after the last cache breakpoint. The cache read and cache
write counts are therefore added back, and reported as slices of an inclusive
total.
Entry points:
axio.transport→AnthropicTransportaxio.transport.settings→AnthropicSettingsScreen
Dependencies: axio, axio-sse, aiohttp>=3.11
axio-transport-openai¶
OpenAI HTTP streaming transports. They post with aiohttp and read the stream
through axio-sse. The Responses vocabulary itself lives in axio-responses.
Four transports are registered as entry points:
Entry point name |
Class |
Provider |
|---|---|---|
|
|
OpenAI API |
|
|
Nebius AI Studio |
|
|
OpenRouter |
|
|
Any OpenAI-compatible endpoint |
Settings screens are registered under axio.transport.settings for each.
OpenAIRealtimeTransport is registered under axio.transport.realtime as
openai.
api: Literal["responses", "chat"] selects the endpoint. OpenAITransport
defaults to "responses" and posts to /v1/responses, because
/v1/chat/completions refuses function tools beside any reasoning effort other
than "none" for a model that reasons. A request carrying tools fails there with
a 400 naming a parameter the caller never sent. The three compatible subclasses
say "chat", since compatible servers rarely implement /v1/responses. See
the troubleshooting entry.
extra_params is folded into the request. Its tools are merged rather than
substituted. A caller adding a hosted tool would otherwise take away the function
declarations the agent needs dispatched. The turn would then read as the model
simply choosing to call nothing. A declaration whose name matches one already
there wins.
Dependencies: axio, axio-responses, axio-sse, aiohttp>=3.11
axio-transport-codex¶
ChatGPT (Codex) transport, speaking the Responses API against the ChatGPT backend
with OAuth authentication. It shares axio-responses with
axio-transport-openai, so it depends on axio-sse only through it.
Entry points:
axio.transport→CodexTransportaxio.transport.settings→CodexSettingsScreen
Dependencies: axio, axio-responses, aiohttp>=3.11
axio-transport-google¶
Google GenAI (Gemini) transport for the Developer API and Vertex AI. Supports standard completion and Gemini Live realtime sessions. Also registers image and video generation tools when installed.
Entry points:
axio.transport→GoogleTransport,VertexAITransportaxio.transport.realtime→GeminiLiveTransport(gemini),VertexLiveTransport(vertex)axio.transport.settings→GoogleSettingsScreen,VertexSettingsScreenaxio.tools→generate_image,generate_video
Gemini’s stream carries no per-event discriminator, so this transport reads it
with axio_sse.payloads() and Wire shapes rather than with a Reader. Its
token counts are converted into the axio rule on the way out. Tool-use prompt
tokens are not inside promptTokenCount, and thinking is not inside
candidatesTokenCount, so both are added.
See the Google Transport guide.
Dependencies: axio, axio-sse, axio-transport-anthropic[vertexai], google-auth[urllib3]>=2.0
aiohttp is not declared here. The HTTP client arrives through
axio-transport-anthropic, which this package builds on for Vertex AI
authentication.
Audio¶
axio-audio¶
Microphone capture and speaker playback for realtime voice agents.
Provides Microphone, Speaker, and DuplexAudio (single-clock duplex
stream for production-grade echo cancellation).
See the Realtime Audio guide.
Dependencies: axio, sounddevice>=0.5, numpy>=2
Tools¶
axio-tools-local¶
Filesystem and shell tool handlers for local development:
Entry Point |
Handler |
Description |
|---|---|---|
|
|
Run shell commands |
|
|
Execute Python code |
|
|
Write content to a file |
|
|
Apply patches to files |
|
|
Read file contents |
|
|
List directory contents |
Dependencies: axio
axio-tools-mcp¶
Dynamic tool loader that connects to MCP (Model Context Protocol) servers and
returns regular Tool objects for explicit use in an agent.
Entry points:
axio.tools.settings→MCPPlugin
Dependencies: axio, aiohttp-tiny-mcp>=0.3.0, aiohttp>=3.11
axio-tools-docker¶
Docker sandbox environment as an async context manager. Spins up an isolated
container via aiodocker. Exposes six tools that mirror axio-tools-local:
shell, write_file, read_file, list_files, run_python, patch_file. No
entry points. Use DockerSandbox directly in code.
async with DockerSandbox(image="python:3.12-slim") as sandbox:
agent = Agent(..., tools=sandbox.tools)
Dependencies: axio, aiodocker>=0.26
Applications¶
axio-tui¶
Textual chat application. Stores conversations with axio-context-sqlite. Keeps
per-project configuration in a database of its own. Discovers transports, tools,
guards and settings screens through the entry-point groups above. Optional extras
name the transports and tool packages it can drive: anthropic, openai,
codex, local, mcp, guards, and all for the lot.
Entry points:
axio.tools→status_line,confirm,subagent,vision
Console script: axio = "axio_tui.__main__:main"
Dependencies: axio, axio-context-sqlite, textual>=2.1.0, textual-serve>=1.1, argclass>=1.6
axio-tui-guards¶
The TUI offers two guards. PathGuard asks before a tool touches a path, and
remembers the answer for that directory. LLMGuard puts the decision to an agent,
and feeds the user’s overrides back into its context. Both are registered as entry
points, so the TUI can list them without importing them.
Entry points:
axio.guards→path(PathGuard),llm(LLMGuard)
Dependencies: axio, axio-tui
REPL¶
axio-repl¶
Terminal coding assistant. Runs an agent loop with file/shell tools. Streams every
token and tool call to the terminal. Auto-detects the transport from environment
variables. Supports model switching, streaming tool arguments and output, vision,
and workspace-level AGENTS.md instructions.
Console script: axio-repl = "axio_repl:main_sync"
See the axio-repl guide.
Dependencies: axio, axio-tools-local, axio-transport-openai, aiohttp>=3.11