axio
Minimal, streaming-first, protocol-driven foundation for LLM-powered agents.
No dependencies. Three protocols. An agent loop that just works.
Features
- Streaming agent loop -
run_stream()yields typed events as they arrive; no buffering, no polling - Three clean protocols -
CompletionTransport,ContextStore,PermissionGuard; swap any piece without touching the rest - Concurrent tool dispatch - all tool calls in a turn run via
asyncio.gatherautomatically - Context compaction -
compact_context()summarises old history to stay within token limits - Testing helpers -
StubTransport,make_tool_use_response(),make_echo_tool()ship inaxio.testing - Plugin-ready - entry-point groups (
axio.tools,axio.transport,axio.guards) for drop-in extensions
Installation
pip install axio
Quick start
import asyncio
from axio.agent import Agent
from axio.context import MemoryContextStore
from axio.tool import Tool
# 1. Define a tool
async def greet(name: str) -> str:
"""Return a greeting for the given name."""
return f"Hello, {name}!"
greet_tool = Tool(name="greet", description="Greet someone by name", handler=greet)
# 2. Wire up the agent (transport comes from an axio-transport-* package)
from axio_transport_openai import OpenAITransport
transport = OpenAITransport(api_key="sk-...", model="gpt-4o-mini")
agent = Agent(system="You are helpful.", transport=transport, tools=[greet_tool])
# 3. Run
async def main() -> None:
ctx = MemoryContextStore()
async for event in agent.run_stream("Please greet Alice", ctx):
print(event)
asyncio.run(main())
Architecture
User message
│
▼
┌─────────┐ stream() ┌─────────────────────┐
│ Agent │ ────────────▶ │ CompletionTransport │
│ loop │ ◀──────────── │ (Anthropic, OpenAI, …) │
└─────────┘ StreamEvent └─────────────────────┘
│
│ tool_use?
▼
┌──────────┐ check() ┌─────────────────┐
│ Tool │ ──────────▶ │ PermissionGuard │
│ handler │ │ (path, LLM, …) │
└──────────┘ └─────────────────┘
│
▼
┌──────────────┐
│ ContextStore │ append() / get_history() / fork() / compact()
└──────────────┘
Protocols
CompletionTransport
from typing import Protocol, runtime_checkable
from collections.abc import AsyncIterator
from axio.events import StreamEvent
from axio.messages import Message
from axio.tool import Tool
@runtime_checkable
class CompletionTransport(Protocol):
def stream(self, messages: list[Message], tools: list[Tool], system: str) -> AsyncIterator[StreamEvent]: ...
ContextStore
from axio.context import ContextStore
from axio.messages import Message
class MyContextStore(ContextStore):
def __init__(self) -> None:
self._messages: list[Message] = []
async def append(self, message: Message) -> None:
self._messages.append(message)
async def get_history(self) -> list[Message]:
return list(self._messages)
# Everything else - session_id, close(), fork(), clear(),
# get/set_context_tokens(), add_context_tokens(), list_sessions()
# - has a default implementation.
PermissionGuard
PermissionGuard is an abstract base class (ABC). Subclass it and implement
check():
from typing import Any
from axio.permission import PermissionGuard
from axio.tool import Tool
class MyGuard(PermissionGuard):
async def check(self, tool: Tool[Any], **kwargs: Any) -> dict[str, Any]:
# return kwargs to allow, raise GuardError to deny
return kwargs
Stream events
| Event | Description |
|---|---|
TextDelta |
Incremental assistant text chunk |
ToolUseStart |
Tool call begins (name + id) |
ToolInputDelta |
Streaming JSON fragment for tool arguments |
ToolResult |
Tool execution result |
IterationEnd |
One LLM round complete - carries Usage + StopReason |
Error |
Transport or tool exception |
SessionEndEvent |
Agent loop finished - carries total Usage |
Tools
from axio.tool import Tool
async def summarise(text: str, max_words: int = 20) -> str:
"""Summarise the given text in one sentence."""
# your implementation
return "..."
tool = Tool(
name="summarise",
description="Summarise text", # overrides docstring if set
handler=summarise,
concurrency=4, # max parallel executions
)
Testing
from axio.agent import Agent
from axio.testing import (
StubTransport,
make_tool_use_response,
make_text_response,
make_ephemeral_context,
make_echo_tool,
)
async def test_agent_calls_tool():
transport = StubTransport(
[
make_tool_use_response("echo", tool_input={"msg": "hi"}),
make_text_response("Done"),
]
)
agent = Agent(system="", tools=[make_echo_tool()], transport=transport)
result = await agent.run("say hi", make_ephemeral_context())
assert result == "Done"
Plugin entry points
[project.entry-points."axio.tools"]
my_tool = "my_package:MyHandler"
[project.entry-points."axio.transport"]
my_backend = "my_package:MyTransport"
[project.entry-points."axio.guards"]
my_guard = "my_package:MyGuard"
Ecosystem
| Package | Purpose |
|---|---|
| axio-transport-anthropic | Anthropic Claude transport |
| axio-transport-openai | OpenAI-compatible transport (OpenAI, Nebius, OpenRouter, custom) |
| axio-transport-codex | ChatGPT OAuth transport |
| axio-context-sqlite | SQLite-backed persistent context store |
| axio-tools-local | Shell, file, Python tools |
| axio-tools-mcp | MCP server bridge |
| axio-tools-docker | Docker sandbox tools |
| axio-tui | Textual TUI application |
| axio-tui-guards | Permission guard plugins |
License
MIT
Metadata
Release files for axio 0.11.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| axio-0.11.3.tar.gz | 127.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| axio-0.11.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 185.5 kB
Release files / axio-0.11.3.tar.gz
| Download URL | axio-0.11.3.tar.gz |
|---|---|
| Size | 127.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
52b1833fce881e23550e210c5cb69c7ead4617aa3378a27e47206af21a70c69c
|
|
BLAKE2b-256 checksum How to use checksums |
4342d5c9c5e978b3eb667d4806a3f16a08775a2fde65d654c29adcdb126ec8d9
|
| 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 Sep 17, 2026.
Transparency logRelease files / axio-0.11.3-py3-none-any.whl
| Download URL | axio-0.11.3-py3-none-any.whl |
|---|---|
| Size | 58.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d6d6d1f809ddbc4e1a9f6d0f5bf66678439403180d384427182ab50c87ce44c3
|
|
BLAKE2b-256 checksum How to use checksums |
722dc753af4c5f97257901d9e0893f8f552370cb805bcd24fbe6a72c1d33b588
|
| 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 Sep 17, 2026.
Transparency log