axio 0.8.0__tar.gz → 0.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {axio-0.8.0 → axio-0.9.1}/PKG-INFO +26 -33
  2. {axio-0.8.0 → axio-0.9.1}/README.md +24 -30
  3. {axio-0.8.0 → axio-0.9.1}/pyproject.toml +5 -3
  4. axio-0.9.1/src/axio/__init__.py +74 -0
  5. axio-0.9.1/src/axio/agent.py +523 -0
  6. {axio-0.8.0 → axio-0.9.1}/src/axio/agent_loader.py +21 -28
  7. {axio-0.8.0 → axio-0.9.1}/src/axio/blocks.py +56 -4
  8. {axio-0.8.0 → axio-0.9.1}/src/axio/compaction.py +2 -2
  9. axio-0.9.1/src/axio/events.py +183 -0
  10. axio-0.9.1/src/axio/field.py +179 -0
  11. {axio-0.8.0 → axio-0.9.1}/src/axio/models.py +9 -0
  12. axio-0.9.1/src/axio/permission.py +66 -0
  13. axio-0.9.1/src/axio/realtime.py +225 -0
  14. axio-0.9.1/src/axio/schema.py +145 -0
  15. {axio-0.8.0 → axio-0.9.1}/src/axio/testing.py +4 -7
  16. axio-0.9.1/src/axio/tool.py +256 -0
  17. {axio-0.8.0 → axio-0.9.1}/src/axio/tool_args.py +9 -0
  18. axio-0.9.1/src/axio/transport.py +211 -0
  19. {axio-0.8.0 → axio-0.9.1}/tests/test_agent_branch.py +10 -13
  20. axio-0.9.1/tests/test_agent_capabilities.py +106 -0
  21. {axio-0.8.0 → axio-0.9.1}/tests/test_agent_permission.py +37 -49
  22. {axio-0.8.0 → axio-0.9.1}/tests/test_agent_run.py +10 -13
  23. {axio-0.8.0 → axio-0.9.1}/tests/test_agent_tools.py +128 -48
  24. {axio-0.8.0 → axio-0.9.1}/tests/test_blocks.py +38 -2
  25. {axio-0.8.0 → axio-0.9.1}/tests/test_compaction.py +1 -1
  26. {axio-0.8.0 → axio-0.9.1}/tests/test_context.py +1 -1
  27. {axio-0.8.0 → axio-0.9.1}/tests/test_events.py +1 -1
  28. {axio-0.8.0 → axio-0.9.1}/tests/test_exceptions.py +8 -10
  29. axio-0.9.1/tests/test_field.py +328 -0
  30. axio-0.9.1/tests/test_init.py +13 -0
  31. axio-0.9.1/tests/test_permission.py +92 -0
  32. axio-0.9.1/tests/test_realtime.py +463 -0
  33. axio-0.9.1/tests/test_schema.py +394 -0
  34. axio-0.9.1/tests/test_selector.py +67 -0
  35. {axio-0.8.0 → axio-0.9.1}/tests/test_stream.py +1 -1
  36. axio-0.9.1/tests/test_testing.py +150 -0
  37. axio-0.9.1/tests/test_tool.py +650 -0
  38. {axio-0.8.0 → axio-0.9.1}/tests/test_tool_args.py +5 -5
  39. {axio-0.8.0 → axio-0.9.1}/tests/test_transport.py +31 -3
  40. {axio-0.8.0 → axio-0.9.1}/tests/test_types.py +4 -5
  41. axio-0.8.0/src/axio/__init__.py +0 -1
  42. axio-0.8.0/src/axio/agent.py +0 -258
  43. axio-0.8.0/src/axio/events.py +0 -98
  44. axio-0.8.0/src/axio/permission.py +0 -61
  45. axio-0.8.0/src/axio/tool.py +0 -76
  46. axio-0.8.0/src/axio/transport.py +0 -107
  47. axio-0.8.0/tests/test_permission.py +0 -29
  48. axio-0.8.0/tests/test_tool.py +0 -161
  49. {axio-0.8.0 → axio-0.9.1}/.github/workflows/publish.yml +0 -0
  50. {axio-0.8.0 → axio-0.9.1}/.github/workflows/tests.yml +0 -0
  51. {axio-0.8.0 → axio-0.9.1}/.gitignore +0 -0
  52. {axio-0.8.0 → axio-0.9.1}/LICENSE +0 -0
  53. {axio-0.8.0 → axio-0.9.1}/Makefile +0 -0
  54. {axio-0.8.0 → axio-0.9.1}/src/axio/context.py +0 -0
  55. {axio-0.8.0 → axio-0.9.1}/src/axio/exceptions.py +0 -0
  56. {axio-0.8.0 → axio-0.9.1}/src/axio/messages.py +0 -0
  57. {axio-0.8.0 → axio-0.9.1}/src/axio/py.typed +0 -0
  58. {axio-0.8.0 → axio-0.9.1}/src/axio/selector.py +0 -0
  59. {axio-0.8.0 → axio-0.9.1}/src/axio/stream.py +0 -0
  60. {axio-0.8.0 → axio-0.9.1}/src/axio/types.py +0 -0
  61. {axio-0.8.0 → axio-0.9.1}/tests/conftest.py +0 -0
  62. {axio-0.8.0 → axio-0.9.1}/tests/test_agent_loader.py +0 -0
  63. {axio-0.8.0 → axio-0.9.1}/tests/test_agent_stream.py +0 -0
  64. {axio-0.8.0 → axio-0.9.1}/tests/test_models.py +0 -0
  65. {axio-0.8.0 → axio-0.9.1}/uv.lock +0 -0
@@ -1,14 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: axio
3
- Version: 0.8.0
3
+ Version: 0.9.1
4
4
  Summary: Minimal, streaming-first, protocol-driven foundation for LLM-powered agents
5
5
  Project-URL: Homepage, https://github.com/mosquito/axio-agent
6
6
  Project-URL: Repository, https://github.com/mosquito/axio-agent
7
7
  License: MIT
8
8
  License-File: LICENSE
9
- Keywords: agent,ai,async,llm,protocol,pydantic,streaming
9
+ Keywords: agent,ai,async,llm,protocol,streaming
10
10
  Requires-Python: >=3.12
11
- Requires-Dist: pydantic>=2
12
11
  Description-Content-Type: text/markdown
13
12
 
14
13
  # axio
@@ -19,16 +18,16 @@ Description-Content-Type: text/markdown
19
18
 
20
19
  Minimal, streaming-first, protocol-driven foundation for LLM-powered agents.
21
20
 
22
- One dependency (`pydantic`). Three protocols. An agent loop that just works.
21
+ No dependencies. Three protocols. An agent loop that just works.
23
22
 
24
23
  ## Features
25
24
 
26
- - **Streaming agent loop** `run_stream()` yields typed events as they arrive; no buffering, no polling
27
- - **Three clean protocols** `CompletionTransport`, `ContextStore`, `PermissionGuard`; swap any piece without touching the rest
28
- - **Concurrent tool dispatch** all tool calls in a turn run via `asyncio.gather` automatically
29
- - **Context compaction** `compact_context()` summarises old history to stay within token limits
30
- - **Testing helpers** `StubTransport`, `make_tool_use_response()`, `make_echo_tool()` ship in `axio.testing`
31
- - **Plugin-ready** entry-point groups (`axio.tools`, `axio.transport`, `axio.guards`) for drop-in extensions
25
+ - **Streaming agent loop** - `run_stream()` yields typed events as they arrive; no buffering, no polling
26
+ - **Three clean protocols** - `CompletionTransport`, `ContextStore`, `PermissionGuard`; swap any piece without touching the rest
27
+ - **Concurrent tool dispatch** - all tool calls in a turn run via `asyncio.gather` automatically
28
+ - **Context compaction** - `compact_context()` summarises old history to stay within token limits
29
+ - **Testing helpers** - `StubTransport`, `make_tool_use_response()`, `make_echo_tool()` ship in `axio.testing`
30
+ - **Plugin-ready** - entry-point groups (`axio.tools`, `axio.transport`, `axio.guards`) for drop-in extensions
32
31
 
33
32
  ## Installation
34
33
 
@@ -57,17 +56,14 @@ sys.modules["axio_transport_openai"] = _m
57
56
  import asyncio
58
57
  from axio.agent import Agent
59
58
  from axio.context import MemoryContextStore
60
- from axio.tool import Tool, ToolHandler
59
+ from axio.tool import Tool
61
60
 
62
61
  # 1. Define a tool
63
- class Greet(ToolHandler):
62
+ async def greet(name: str) -> str:
64
63
  """Return a greeting for the given name."""
65
- name: str
66
-
67
- async def __call__(self) -> str:
68
- return f"Hello, {self.name}!"
64
+ return f"Hello, {name}!"
69
65
 
70
- greet_tool = Tool(name="greet", description="Greet someone by name", handler=Greet)
66
+ greet_tool = Tool(name="greet", description="Greet someone by name", handler=greet)
71
67
 
72
68
  # 2. Wire up the agent (transport comes from an axio-transport-* package)
73
69
  from axio_transport_openai import OpenAITransport
@@ -144,9 +140,9 @@ class MyContextStore(ContextStore):
144
140
  async def get_history(self) -> list[Message]:
145
141
  return list(self._messages)
146
142
 
147
- # Everything else session_id, close(), fork(), clear(),
143
+ # Everything else - session_id, close(), fork(), clear(),
148
144
  # get/set_context_tokens(), add_context_tokens(), list_sessions()
149
- # has a default implementation.
145
+ # - has a default implementation.
150
146
  ```
151
147
 
152
148
  ### PermissionGuard
@@ -158,11 +154,12 @@ class MyContextStore(ContextStore):
158
154
  ```python
159
155
  from typing import Any
160
156
  from axio.permission import PermissionGuard
157
+ from axio.tool import Tool
161
158
 
162
159
  class MyGuard(PermissionGuard):
163
- async def check(self, handler: Any) -> Any:
164
- # return handler to allow, raise GuardError to deny
165
- return handler
160
+ async def check(self, tool: Tool[Any], **kwargs: Any) -> dict[str, Any]:
161
+ # return kwargs to allow, raise GuardError to deny
162
+ return kwargs
166
163
  ```
167
164
 
168
165
  ## Stream events
@@ -173,29 +170,25 @@ class MyGuard(PermissionGuard):
173
170
  | `ToolUseStart` | Tool call begins (name + id) |
174
171
  | `ToolInputDelta` | Streaming JSON fragment for tool arguments |
175
172
  | `ToolResult` | Tool execution result |
176
- | `IterationEnd` | One LLM round complete carries `Usage` + `StopReason` |
173
+ | `IterationEnd` | One LLM round complete - carries `Usage` + `StopReason` |
177
174
  | `Error` | Transport or tool exception |
178
- | `SessionEndEvent` | Agent loop finished carries total `Usage` |
175
+ | `SessionEndEvent` | Agent loop finished - carries total `Usage` |
179
176
 
180
177
  ## Tools
181
178
 
182
179
  <!-- name: test_readme_tools -->
183
180
  ```python
184
- from axio.tool import Tool, ToolHandler
181
+ from axio.tool import Tool
185
182
 
186
- class Summarise(ToolHandler):
183
+ async def summarise(text: str, max_words: int = 20) -> str:
187
184
  """Summarise the given text in one sentence."""
188
- text: str
189
- max_words: int = 20
190
-
191
- async def __call__(self) -> str:
192
- # your implementation
193
- return "..."
185
+ # your implementation
186
+ return "..."
194
187
 
195
188
  tool = Tool(
196
189
  name="summarise",
197
190
  description="Summarise text", # overrides docstring if set
198
- handler=Summarise,
191
+ handler=summarise,
199
192
  concurrency=4, # max parallel executions
200
193
  )
201
194
  ```
@@ -6,16 +6,16 @@
6
6
 
7
7
  Minimal, streaming-first, protocol-driven foundation for LLM-powered agents.
8
8
 
9
- One dependency (`pydantic`). Three protocols. An agent loop that just works.
9
+ No dependencies. Three protocols. An agent loop that just works.
10
10
 
11
11
  ## Features
12
12
 
13
- - **Streaming agent loop** `run_stream()` yields typed events as they arrive; no buffering, no polling
14
- - **Three clean protocols** `CompletionTransport`, `ContextStore`, `PermissionGuard`; swap any piece without touching the rest
15
- - **Concurrent tool dispatch** all tool calls in a turn run via `asyncio.gather` automatically
16
- - **Context compaction** `compact_context()` summarises old history to stay within token limits
17
- - **Testing helpers** `StubTransport`, `make_tool_use_response()`, `make_echo_tool()` ship in `axio.testing`
18
- - **Plugin-ready** entry-point groups (`axio.tools`, `axio.transport`, `axio.guards`) for drop-in extensions
13
+ - **Streaming agent loop** - `run_stream()` yields typed events as they arrive; no buffering, no polling
14
+ - **Three clean protocols** - `CompletionTransport`, `ContextStore`, `PermissionGuard`; swap any piece without touching the rest
15
+ - **Concurrent tool dispatch** - all tool calls in a turn run via `asyncio.gather` automatically
16
+ - **Context compaction** - `compact_context()` summarises old history to stay within token limits
17
+ - **Testing helpers** - `StubTransport`, `make_tool_use_response()`, `make_echo_tool()` ship in `axio.testing`
18
+ - **Plugin-ready** - entry-point groups (`axio.tools`, `axio.transport`, `axio.guards`) for drop-in extensions
19
19
 
20
20
  ## Installation
21
21
 
@@ -44,17 +44,14 @@ sys.modules["axio_transport_openai"] = _m
44
44
  import asyncio
45
45
  from axio.agent import Agent
46
46
  from axio.context import MemoryContextStore
47
- from axio.tool import Tool, ToolHandler
47
+ from axio.tool import Tool
48
48
 
49
49
  # 1. Define a tool
50
- class Greet(ToolHandler):
50
+ async def greet(name: str) -> str:
51
51
  """Return a greeting for the given name."""
52
- name: str
53
-
54
- async def __call__(self) -> str:
55
- return f"Hello, {self.name}!"
52
+ return f"Hello, {name}!"
56
53
 
57
- greet_tool = Tool(name="greet", description="Greet someone by name", handler=Greet)
54
+ greet_tool = Tool(name="greet", description="Greet someone by name", handler=greet)
58
55
 
59
56
  # 2. Wire up the agent (transport comes from an axio-transport-* package)
60
57
  from axio_transport_openai import OpenAITransport
@@ -131,9 +128,9 @@ class MyContextStore(ContextStore):
131
128
  async def get_history(self) -> list[Message]:
132
129
  return list(self._messages)
133
130
 
134
- # Everything else session_id, close(), fork(), clear(),
131
+ # Everything else - session_id, close(), fork(), clear(),
135
132
  # get/set_context_tokens(), add_context_tokens(), list_sessions()
136
- # has a default implementation.
133
+ # - has a default implementation.
137
134
  ```
138
135
 
139
136
  ### PermissionGuard
@@ -145,11 +142,12 @@ class MyContextStore(ContextStore):
145
142
  ```python
146
143
  from typing import Any
147
144
  from axio.permission import PermissionGuard
145
+ from axio.tool import Tool
148
146
 
149
147
  class MyGuard(PermissionGuard):
150
- async def check(self, handler: Any) -> Any:
151
- # return handler to allow, raise GuardError to deny
152
- return handler
148
+ async def check(self, tool: Tool[Any], **kwargs: Any) -> dict[str, Any]:
149
+ # return kwargs to allow, raise GuardError to deny
150
+ return kwargs
153
151
  ```
154
152
 
155
153
  ## Stream events
@@ -160,29 +158,25 @@ class MyGuard(PermissionGuard):
160
158
  | `ToolUseStart` | Tool call begins (name + id) |
161
159
  | `ToolInputDelta` | Streaming JSON fragment for tool arguments |
162
160
  | `ToolResult` | Tool execution result |
163
- | `IterationEnd` | One LLM round complete carries `Usage` + `StopReason` |
161
+ | `IterationEnd` | One LLM round complete - carries `Usage` + `StopReason` |
164
162
  | `Error` | Transport or tool exception |
165
- | `SessionEndEvent` | Agent loop finished carries total `Usage` |
163
+ | `SessionEndEvent` | Agent loop finished - carries total `Usage` |
166
164
 
167
165
  ## Tools
168
166
 
169
167
  <!-- name: test_readme_tools -->
170
168
  ```python
171
- from axio.tool import Tool, ToolHandler
169
+ from axio.tool import Tool
172
170
 
173
- class Summarise(ToolHandler):
171
+ async def summarise(text: str, max_words: int = 20) -> str:
174
172
  """Summarise the given text in one sentence."""
175
- text: str
176
- max_words: int = 20
177
-
178
- async def __call__(self) -> str:
179
- # your implementation
180
- return "..."
173
+ # your implementation
174
+ return "..."
181
175
 
182
176
  tool = Tool(
183
177
  name="summarise",
184
178
  description="Summarise text", # overrides docstring if set
185
- handler=Summarise,
179
+ handler=summarise,
186
180
  concurrency=4, # max parallel executions
187
181
  )
188
182
  ```
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "axio"
3
- version = "0.8.0"
3
+ version = "0.9.1" # bumped to the git tag by CI on publish
4
4
  description = "Minimal, streaming-first, protocol-driven foundation for LLM-powered agents"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
7
7
  license = {text = "MIT"}
8
- keywords = ["llm", "agent", "ai", "async", "streaming", "pydantic", "protocol"]
9
- dependencies = ["pydantic>=2"]
8
+ keywords = ["llm", "agent", "ai", "async", "streaming", "protocol"]
9
+ dependencies = []
10
10
 
11
11
 
12
12
  [project.urls]
@@ -25,6 +25,7 @@ testpaths = ["tests", "README.md"]
25
25
 
26
26
  [tool.ruff]
27
27
  line-length = 119
28
+ output-format = "concise"
28
29
  target-version = "py312"
29
30
 
30
31
  [tool.ruff.lint]
@@ -34,6 +35,7 @@ select = ["E", "F", "I", "UP"]
34
35
  strict = true
35
36
  python_version = "3.12"
36
37
 
38
+
37
39
  [dependency-groups]
38
40
  dev = [
39
41
  "pytest>=8",
@@ -0,0 +1,74 @@
1
+ """axio - public API."""
2
+
3
+ from .agent import Agent
4
+ from .blocks import TextBlock, ToolResultBlock, ToolUseBlock
5
+ from .context import ContextStore, MemoryContextStore
6
+ from .events import (
7
+ AudioOutputDelta,
8
+ IterationEnd,
9
+ SpeechStarted,
10
+ SpeechStopped,
11
+ StreamEvent,
12
+ TextDelta,
13
+ ToolInputDelta,
14
+ ToolResult,
15
+ ToolUseStart,
16
+ TranscriptDelta,
17
+ TurnComplete,
18
+ )
19
+ from .exceptions import GuardError, HandlerError
20
+ from .field import Field, FieldInfo, StrictStr
21
+ from .messages import Message
22
+ from .permission import ConcurrentGuard, PermissionGuard
23
+ from .realtime import RealtimeAgent
24
+ from .selector import ToolSelector
25
+ from .stream import AgentStream
26
+ from .tool import CONTEXT, Tool
27
+ from .transport import CompletionTransport, RealtimeSession, RealtimeTransport
28
+ from .types import StopReason, Usage
29
+
30
+ __all__ = [
31
+ # core
32
+ "Agent",
33
+ "Tool",
34
+ "CONTEXT",
35
+ "ContextStore",
36
+ "MemoryContextStore",
37
+ "CompletionTransport",
38
+ # events
39
+ "StreamEvent",
40
+ "TextDelta",
41
+ "IterationEnd",
42
+ "ToolUseStart",
43
+ "ToolInputDelta",
44
+ "ToolResult",
45
+ # realtime
46
+ "RealtimeAgent",
47
+ "RealtimeTransport",
48
+ "RealtimeSession",
49
+ "AudioOutputDelta",
50
+ "TranscriptDelta",
51
+ "SpeechStarted",
52
+ "SpeechStopped",
53
+ "TurnComplete",
54
+ # messages & blocks
55
+ "Message",
56
+ "TextBlock",
57
+ "ToolUseBlock",
58
+ "ToolResultBlock",
59
+ # types & errors
60
+ "StopReason",
61
+ "Usage",
62
+ "GuardError",
63
+ "HandlerError",
64
+ # permissions
65
+ "PermissionGuard",
66
+ "ConcurrentGuard",
67
+ # field annotations
68
+ "Field",
69
+ "FieldInfo",
70
+ "StrictStr",
71
+ # advanced
72
+ "ToolSelector",
73
+ "AgentStream",
74
+ ]