axon-protocol 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sarthak Dhatrak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,158 @@
1
+ Metadata-Version: 2.4
2
+ Name: axon-protocol
3
+ Version: 0.1.0
4
+ Summary: Python SDK for Axon Protocol — persistent memory, coordination, and reasoning receipts for AI agents
5
+ Author-email: Sarthak Dhatrak <sarthakmdhtr@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/axon-protocol/axon-sdk-python
8
+ Project-URL: Documentation, https://docs.axon.so
9
+ Project-URL: Repository, https://github.com/axon-protocol/axon-sdk-python
10
+ Project-URL: Issues, https://github.com/axon-protocol/axon-sdk-python/issues
11
+ Keywords: ai,agents,memory,coordination,llm,multiagent,axon
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: httpx>=0.27.0
25
+ Requires-Dist: websockets>=12.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
29
+ Requires-Dist: respx>=0.21.0; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # Axon Protocol — Python SDK (`axon-protocol`)
33
+
34
+ Official Python client library for integration with the Axon core server. Handles semantic memory, coordination locks, and tamper-proof reasoning receipts for multi-agent AI environments.
35
+
36
+ ---
37
+
38
+ ## Installation
39
+
40
+ Install the library locally:
41
+ ```bash
42
+ pip install axon-protocol
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Quickstart
48
+
49
+ ### Async Usage
50
+
51
+ ```python
52
+ import asyncio
53
+ from axon import AxonClient
54
+
55
+ async def main():
56
+ async with AxonClient(api_key="axon-...", project_id="my-project") as axon:
57
+ # Store memory
58
+ await axon.memory.store("User prefers dark mode")
59
+
60
+ # Search memory
61
+ results = await axon.memory.search("ui preferences")
62
+ print(results.results[0].content)
63
+
64
+ # Acquire lock
65
+ async with axon.lock("resource_1"):
66
+ print("Lock held!")
67
+
68
+ asyncio.run(main())
69
+ ```
70
+
71
+ ### Sync Usage
72
+
73
+ ```python
74
+ from axon import AxonSyncClient
75
+
76
+ with AxonSyncClient(api_key="axon-...", project_id="my-project") as axon:
77
+ # Store memory
78
+ axon.memory.store("User prefers dark mode")
79
+
80
+ # Search memory
81
+ results = axon.memory.search("ui preferences")
82
+ print(results.results[0].content)
83
+
84
+ # Acquire lock
85
+ with axon.lock("resource_1"):
86
+ print("Lock held!")
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Features
92
+
93
+ ### A. Persistent Memory (Semantic Vector Search)
94
+
95
+ Store memories and query them with semantic similarity matching:
96
+
97
+ ```python
98
+ # Async
99
+ memory_id = await axon.memory.store(
100
+ content="The API service key is stored in the vault.",
101
+ tags={"category": "vault"},
102
+ scope="project",
103
+ ttl=3600 # auto-expire in 1 hour
104
+ )
105
+
106
+ results = await axon.memory.search(
107
+ query="Where is the API key?",
108
+ limit=5,
109
+ min_similarity=0.6
110
+ )
111
+ ```
112
+
113
+ ### B. Resource Locking (Mutex)
114
+
115
+ Prevent race conditions in multi-agent environments. Use the lock context manager to automatically release locks:
116
+
117
+ ```python
118
+ # Sync Lock Context Manager
119
+ with axon.lock("resource_lock", timeout=60) as lock:
120
+ # Exclusive access section
121
+ print(f"Lock acquired, expires at: {lock.expires_at}")
122
+ ```
123
+
124
+ ### C. Reasoning Steps Recording
125
+
126
+ Generate cryptographically chained, signed receipts validating agent reasoning processes:
127
+
128
+ ```python
129
+ # Async receipt creation
130
+ receipt = await axon.receipts.create(
131
+ input="Fix the login bug in auth.py",
132
+ steps=[
133
+ ReasoningStep(thought="Read the login function"),
134
+ ReasoningStep(
135
+ thought="Found missing token validation",
136
+ tool_called="read_file",
137
+ result="Null check missing at line 47"
138
+ )
139
+ ],
140
+ output="Bug fixed"
141
+ )
142
+
143
+ # Automated decoration
144
+ @axon.receipts.track(input_param="task")
145
+ async def agent_function(task: str, steps_logger=None) -> str:
146
+ steps_logger.add(thought="Analyzing the task")
147
+ return "Done"
148
+ ```
149
+
150
+ ### D. Real-Time Events Stream
151
+
152
+ Listen to event streams (like memory additions or lock releases) happening across your project:
153
+
154
+ ```python
155
+ # Async stream
156
+ async for event in axon.events.listen():
157
+ print(f"Event Captured: {event['event_type']}")
158
+ ```
@@ -0,0 +1,127 @@
1
+ # Axon Protocol — Python SDK (`axon-protocol`)
2
+
3
+ Official Python client library for integration with the Axon core server. Handles semantic memory, coordination locks, and tamper-proof reasoning receipts for multi-agent AI environments.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ Install the library locally:
10
+ ```bash
11
+ pip install axon-protocol
12
+ ```
13
+
14
+ ---
15
+
16
+ ## Quickstart
17
+
18
+ ### Async Usage
19
+
20
+ ```python
21
+ import asyncio
22
+ from axon import AxonClient
23
+
24
+ async def main():
25
+ async with AxonClient(api_key="axon-...", project_id="my-project") as axon:
26
+ # Store memory
27
+ await axon.memory.store("User prefers dark mode")
28
+
29
+ # Search memory
30
+ results = await axon.memory.search("ui preferences")
31
+ print(results.results[0].content)
32
+
33
+ # Acquire lock
34
+ async with axon.lock("resource_1"):
35
+ print("Lock held!")
36
+
37
+ asyncio.run(main())
38
+ ```
39
+
40
+ ### Sync Usage
41
+
42
+ ```python
43
+ from axon import AxonSyncClient
44
+
45
+ with AxonSyncClient(api_key="axon-...", project_id="my-project") as axon:
46
+ # Store memory
47
+ axon.memory.store("User prefers dark mode")
48
+
49
+ # Search memory
50
+ results = axon.memory.search("ui preferences")
51
+ print(results.results[0].content)
52
+
53
+ # Acquire lock
54
+ with axon.lock("resource_1"):
55
+ print("Lock held!")
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Features
61
+
62
+ ### A. Persistent Memory (Semantic Vector Search)
63
+
64
+ Store memories and query them with semantic similarity matching:
65
+
66
+ ```python
67
+ # Async
68
+ memory_id = await axon.memory.store(
69
+ content="The API service key is stored in the vault.",
70
+ tags={"category": "vault"},
71
+ scope="project",
72
+ ttl=3600 # auto-expire in 1 hour
73
+ )
74
+
75
+ results = await axon.memory.search(
76
+ query="Where is the API key?",
77
+ limit=5,
78
+ min_similarity=0.6
79
+ )
80
+ ```
81
+
82
+ ### B. Resource Locking (Mutex)
83
+
84
+ Prevent race conditions in multi-agent environments. Use the lock context manager to automatically release locks:
85
+
86
+ ```python
87
+ # Sync Lock Context Manager
88
+ with axon.lock("resource_lock", timeout=60) as lock:
89
+ # Exclusive access section
90
+ print(f"Lock acquired, expires at: {lock.expires_at}")
91
+ ```
92
+
93
+ ### C. Reasoning Steps Recording
94
+
95
+ Generate cryptographically chained, signed receipts validating agent reasoning processes:
96
+
97
+ ```python
98
+ # Async receipt creation
99
+ receipt = await axon.receipts.create(
100
+ input="Fix the login bug in auth.py",
101
+ steps=[
102
+ ReasoningStep(thought="Read the login function"),
103
+ ReasoningStep(
104
+ thought="Found missing token validation",
105
+ tool_called="read_file",
106
+ result="Null check missing at line 47"
107
+ )
108
+ ],
109
+ output="Bug fixed"
110
+ )
111
+
112
+ # Automated decoration
113
+ @axon.receipts.track(input_param="task")
114
+ async def agent_function(task: str, steps_logger=None) -> str:
115
+ steps_logger.add(thought="Analyzing the task")
116
+ return "Done"
117
+ ```
118
+
119
+ ### D. Real-Time Events Stream
120
+
121
+ Listen to event streams (like memory additions or lock releases) happening across your project:
122
+
123
+ ```python
124
+ # Async stream
125
+ async for event in axon.events.listen():
126
+ print(f"Event Captured: {event['event_type']}")
127
+ ```
@@ -0,0 +1,51 @@
1
+ from axon.client import AxonClient, AxonSyncClient
2
+ from axon.messages import MessagesClient, SyncMessagesClient
3
+ from axon.types import (
4
+ MemoryResult,
5
+ MemorySearchResponse,
6
+ StoredMemory,
7
+ LockInfo,
8
+ LockStatus,
9
+ ReasoningStep,
10
+ StepsLogger,
11
+ ReceiptInfo,
12
+ ReceiptVerifyResult,
13
+ )
14
+ from axon.exceptions import (
15
+ AxonError,
16
+ AuthError,
17
+ LockConflictError,
18
+ NotFoundError,
19
+ AxonPermissionError,
20
+ RateLimitError,
21
+ ServerError,
22
+ AxonConnectionError,
23
+ )
24
+
25
+ __version__ = "0.1.0"
26
+
27
+ __all__ = [
28
+ "AxonClient",
29
+ "AxonSyncClient",
30
+ "MessagesClient",
31
+ "SyncMessagesClient",
32
+ # Types
33
+ "MemoryResult",
34
+ "MemorySearchResponse",
35
+ "StoredMemory",
36
+ "LockInfo",
37
+ "LockStatus",
38
+ "ReasoningStep",
39
+ "StepsLogger",
40
+ "ReceiptInfo",
41
+ "ReceiptVerifyResult",
42
+ # Exceptions
43
+ "AxonError",
44
+ "AuthError",
45
+ "LockConflictError",
46
+ "NotFoundError",
47
+ "AxonPermissionError",
48
+ "RateLimitError",
49
+ "ServerError",
50
+ "AxonConnectionError",
51
+ ]
@@ -0,0 +1,142 @@
1
+ import httpx
2
+ import time
3
+ import asyncio
4
+ from axon.exceptions import (
5
+ AuthError,
6
+ NotFoundError,
7
+ AxonPermissionError,
8
+ LockConflictError,
9
+ RateLimitError,
10
+ ServerError,
11
+ AxonConnectionError,
12
+ )
13
+
14
+
15
+ class _BaseClient:
16
+ def __init__(self, http: httpx.AsyncClient, base_url: str):
17
+ self._http = http
18
+ self._base_url = base_url.rstrip("/")
19
+
20
+ async def _request(self, method: str, path: str, **kwargs) -> dict:
21
+ url = f"{self._base_url}{path}"
22
+ retries = 3
23
+ backoff = 0.5
24
+
25
+ for attempt in range(retries):
26
+ try:
27
+ response = await self._http.request(method, url, **kwargs)
28
+ if response.status_code >= 500 and attempt < retries - 1:
29
+ await asyncio.sleep(backoff * (2 ** attempt))
30
+ continue
31
+ break
32
+ except httpx.ConnectError:
33
+ if attempt == retries - 1:
34
+ raise AxonConnectionError(
35
+ f"Cannot connect to Axon server at {self._base_url}. "
36
+ f"Make sure the server is running."
37
+ )
38
+ await asyncio.sleep(backoff * (2 ** attempt))
39
+ except httpx.TimeoutException:
40
+ if attempt == retries - 1:
41
+ raise AxonConnectionError(
42
+ f"Request to {url} timed out. Server may be overloaded."
43
+ )
44
+ await asyncio.sleep(backoff * (2 ** attempt))
45
+
46
+ # Success
47
+ if response.status_code == 200:
48
+ return response.json()
49
+
50
+ # Parse error detail from response body
51
+ try:
52
+ detail = response.json().get("detail", response.text)
53
+ except Exception:
54
+ detail = response.text
55
+
56
+ # Map status codes to specific exceptions
57
+ if response.status_code == 401:
58
+ raise AuthError(f"Authentication failed: {detail}", 401)
59
+ elif response.status_code == 403:
60
+ raise AxonPermissionError(f"Permission denied: {detail}", 403)
61
+ elif response.status_code == 404:
62
+ raise NotFoundError(f"Not found: {detail}", 404)
63
+ elif response.status_code == 409:
64
+ raise LockConflictError("", detail=detail)
65
+ elif response.status_code == 429:
66
+ retry_after = int(response.headers.get("Retry-After", 60))
67
+ raise RateLimitError(retry_after)
68
+ elif response.status_code >= 500:
69
+ raise ServerError(
70
+ f"Server error ({response.status_code}): {detail}",
71
+ response.status_code,
72
+ )
73
+ else:
74
+ raise ServerError(
75
+ f"Unexpected status {response.status_code}: {detail}",
76
+ response.status_code,
77
+ )
78
+
79
+
80
+ class _BaseSyncClient:
81
+ def __init__(self, http: httpx.Client, base_url: str):
82
+ self._http = http
83
+ self._base_url = base_url.rstrip("/")
84
+
85
+ def _request(self, method: str, path: str, **kwargs) -> dict:
86
+ url = f"{self._base_url}{path}"
87
+ retries = 3
88
+ backoff = 0.5
89
+
90
+ for attempt in range(retries):
91
+ try:
92
+ response = self._http.request(method, url, **kwargs)
93
+ if response.status_code >= 500 and attempt < retries - 1:
94
+ time.sleep(backoff * (2 ** attempt))
95
+ continue
96
+ break
97
+ except httpx.ConnectError:
98
+ if attempt == retries - 1:
99
+ raise AxonConnectionError(
100
+ f"Cannot connect to Axon server at {self._base_url}. "
101
+ f"Make sure the server is running."
102
+ )
103
+ time.sleep(backoff * (2 ** attempt))
104
+ except httpx.TimeoutException:
105
+ if attempt == retries - 1:
106
+ raise AxonConnectionError(
107
+ f"Request to {url} timed out. Server may be overloaded."
108
+ )
109
+ time.sleep(backoff * (2 ** attempt))
110
+
111
+ # Success
112
+ if response.status_code == 200:
113
+ return response.json()
114
+
115
+ # Parse error detail from response body
116
+ try:
117
+ detail = response.json().get("detail", response.text)
118
+ except Exception:
119
+ detail = response.text
120
+
121
+ # Map status codes to specific exceptions
122
+ if response.status_code == 401:
123
+ raise AuthError(f"Authentication failed: {detail}", 401)
124
+ elif response.status_code == 403:
125
+ raise AxonPermissionError(f"Permission denied: {detail}", 403)
126
+ elif response.status_code == 404:
127
+ raise NotFoundError(f"Not found: {detail}", 404)
128
+ elif response.status_code == 409:
129
+ raise LockConflictError("", detail=detail)
130
+ elif response.status_code == 429:
131
+ retry_after = int(response.headers.get("Retry-After", 60))
132
+ raise RateLimitError(retry_after)
133
+ elif response.status_code >= 500:
134
+ raise ServerError(
135
+ f"Server error ({response.status_code}): {detail}",
136
+ response.status_code,
137
+ )
138
+ else:
139
+ raise ServerError(
140
+ f"Unexpected status {response.status_code}: {detail}",
141
+ response.status_code,
142
+ )
@@ -0,0 +1,132 @@
1
+ import httpx
2
+ from axon.memory import MemoryClient, SyncMemoryClient
3
+ from axon.coordination import CoordinationClient, SyncCoordinationClient
4
+ from axon.receipts import ReceiptsClient, SyncReceiptsClient
5
+ from axon.events import EventsClient, SyncEventsClient
6
+ from axon.messages import MessagesClient, SyncMessagesClient
7
+
8
+
9
+ class AxonClient:
10
+ """
11
+ Async entry point for the Axon Protocol Python SDK.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ api_key: str,
17
+ project_id: str,
18
+ agent_token: str = None,
19
+ agent_id: str = None,
20
+ base_url: str = "http://localhost:8000",
21
+ timeout: float = 30.0,
22
+ ):
23
+ self.api_key = api_key
24
+ self.project_id = project_id
25
+ self.agent_token = agent_token
26
+ self.agent_id = agent_id
27
+ self.base_url = base_url.rstrip("/")
28
+
29
+ headers = {}
30
+ if api_key:
31
+ headers["X-API-Key"] = api_key
32
+ if agent_token:
33
+ headers["Authorization"] = f"Bearer {agent_token}"
34
+
35
+ self._http = httpx.AsyncClient(
36
+ headers=headers,
37
+ timeout=httpx.Timeout(timeout),
38
+ limits=httpx.Limits(
39
+ max_connections=20,
40
+ max_keepalive_connections=10,
41
+ keepalive_expiry=30,
42
+ ),
43
+ )
44
+
45
+ self.memory = MemoryClient(self._http, self.base_url)
46
+ self.lock = CoordinationClient(self._http, self.base_url)
47
+ self.receipts = ReceiptsClient(self._http, self.base_url)
48
+ self.events = EventsClient(self.base_url, api_key, project_id)
49
+ self.messages = MessagesClient(self._http, self.base_url)
50
+
51
+ async def ping(self) -> bool:
52
+ """
53
+ Check if the Axon server is reachable (async).
54
+ """
55
+ try:
56
+ response = await self._http.get(f"{self.base_url}/v1/health")
57
+ return response.status_code == 200
58
+ except Exception:
59
+ return False
60
+
61
+ async def close(self):
62
+ """Close the underlying HTTP connection pool."""
63
+ await self._http.aclose()
64
+
65
+ async def __aenter__(self):
66
+ return self
67
+
68
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
69
+ await self.close()
70
+
71
+
72
+ class AxonSyncClient:
73
+ """
74
+ Sync entry point for the Axon Protocol Python SDK.
75
+ """
76
+
77
+ def __init__(
78
+ self,
79
+ api_key: str,
80
+ project_id: str,
81
+ agent_token: str = None,
82
+ agent_id: str = None,
83
+ base_url: str = "http://localhost:8000",
84
+ timeout: float = 30.0,
85
+ ):
86
+ self.api_key = api_key
87
+ self.project_id = project_id
88
+ self.agent_token = agent_token
89
+ self.agent_id = agent_id
90
+ self.base_url = base_url.rstrip("/")
91
+
92
+ headers = {}
93
+ if api_key:
94
+ headers["X-API-Key"] = api_key
95
+ if agent_token:
96
+ headers["Authorization"] = f"Bearer {agent_token}"
97
+
98
+ self._http = httpx.Client(
99
+ headers=headers,
100
+ timeout=httpx.Timeout(timeout),
101
+ limits=httpx.Limits(
102
+ max_connections=20,
103
+ max_keepalive_connections=10,
104
+ keepalive_expiry=30,
105
+ ),
106
+ )
107
+
108
+ self.memory = SyncMemoryClient(self._http, self.base_url)
109
+ self.lock = SyncCoordinationClient(self._http, self.base_url)
110
+ self.receipts = SyncReceiptsClient(self._http, self.base_url)
111
+ self.events = SyncEventsClient(self.base_url, api_key, project_id)
112
+ self.messages = SyncMessagesClient(self._http, self.base_url)
113
+
114
+ def ping(self) -> bool:
115
+ """
116
+ Check if the Axon server is reachable (sync).
117
+ """
118
+ try:
119
+ response = self._http.get(f"{self.base_url}/v1/health")
120
+ return response.status_code == 200
121
+ except Exception:
122
+ return False
123
+
124
+ def close(self):
125
+ """Close the underlying HTTP connection pool."""
126
+ self._http.close()
127
+
128
+ def __enter__(self):
129
+ return self
130
+
131
+ def __exit__(self, exc_type, exc_val, exc_tb):
132
+ self.close()