Minimal, streaming-first, protocol-driven foundation for LLM-powered agents
Project description
axio
Minimal, streaming-first, protocol-driven foundation for LLM-powered agents.
One dependency (pydantic). 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, ToolHandler
# 1. Define a tool
class Greet(ToolHandler):
"""Return a greeting for the given name."""
name: str
async def __call__(self) -> str:
return f"Hello, {self.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.", tools=[greet_tool], transport=transport)
# 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 abc import ABC, abstractmethod
from axio.context import ContextStore
from axio.messages import Message
class MyContextStore(ContextStore, ABC):
@abstractmethod
async def append(self, message: Message) -> None: ...
@abstractmethod
async def get_history(self) -> list[Message]: ...
# Everything else — session_id, close(), fork(), clear(),
# get/set_context_tokens() — has a default implementation.
PermissionGuard
from typing import Protocol, runtime_checkable
from axio.tool import ToolHandler
@runtime_checkable
class PermissionGuard(Protocol):
async def check(self, handler: ToolHandler) -> ToolHandler: ...
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, ToolHandler
class Summarise(ToolHandler):
"""Summarise the given text in one sentence."""
text: str
max_words: int = 20
async def __call__(self) -> str:
# 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-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-rag | RAG / semantic search plugin |
| axio-tui-guards | Permission guard plugins |
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file axio-0.5.0.tar.gz.
File metadata
- Download URL: axio-0.5.0.tar.gz
- Upload date:
- Size: 60.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46165143e46067fafac062e2c70204df904cf368bb4125eaaa09dc7c7d4222a1
|
|
| MD5 |
96b82c938c2e0877d4d4a1f57aa1d357
|
|
| BLAKE2b-256 |
697cf71f056b327b5c4dcd35440a675c247dfbf15a0806384b0d522e554eb789
|
Provenance
The following attestation bundles were made for axio-0.5.0.tar.gz:
Publisher:
publish.yml on axio-agent/monorepo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
axio-0.5.0.tar.gz -
Subject digest:
46165143e46067fafac062e2c70204df904cf368bb4125eaaa09dc7c7d4222a1 - Sigstore transparency entry: 1298200251
- Sigstore integration time:
-
Permalink:
axio-agent/monorepo@983fbfb4361b84a642990b1c00de88cceae0034a -
Branch / Tag:
refs/tags/0.5.0 - Owner: https://github.com/axio-agent
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@983fbfb4361b84a642990b1c00de88cceae0034a -
Trigger Event:
release
-
Statement type:
File details
Details for the file axio-0.5.0-py3-none-any.whl.
File metadata
- Download URL: axio-0.5.0-py3-none-any.whl
- Upload date:
- Size: 21.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7fe910a0dd9ac22abb3424c828f81cb1303857b4ff81f5df1714324b33c37cd
|
|
| MD5 |
54e8f4a45572e4c57c5cf8675d92f21b
|
|
| BLAKE2b-256 |
a99cc7ae7480c1d8c62015c9b26db004a9413c21eb7035a4349e56e00e7b8d4a
|
Provenance
The following attestation bundles were made for axio-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on axio-agent/monorepo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
axio-0.5.0-py3-none-any.whl -
Subject digest:
b7fe910a0dd9ac22abb3424c828f81cb1303857b4ff81f5df1714324b33c37cd - Sigstore transparency entry: 1298200360
- Sigstore integration time:
-
Permalink:
axio-agent/monorepo@983fbfb4361b84a642990b1c00de88cceae0034a -
Branch / Tag:
refs/tags/0.5.0 - Owner: https://github.com/axio-agent
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@983fbfb4361b84a642990b1c00de88cceae0034a -
Trigger Event:
release
-
Statement type: