xache 5.0.0__py3-none-any.whl
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.
- xache/__init__.py +142 -0
- xache/client.py +331 -0
- xache/crypto/__init__.py +17 -0
- xache/crypto/signing.py +244 -0
- xache/crypto/wallet.py +240 -0
- xache/errors.py +184 -0
- xache/payment/__init__.py +5 -0
- xache/payment/handler.py +244 -0
- xache/services/__init__.py +29 -0
- xache/services/budget.py +285 -0
- xache/services/collective.py +174 -0
- xache/services/extraction.py +173 -0
- xache/services/facilitator.py +296 -0
- xache/services/identity.py +415 -0
- xache/services/memory.py +401 -0
- xache/services/owner.py +293 -0
- xache/services/receipts.py +202 -0
- xache/services/reputation.py +274 -0
- xache/services/royalty.py +290 -0
- xache/services/sessions.py +268 -0
- xache/services/workspaces.py +447 -0
- xache/types.py +399 -0
- xache/utils/__init__.py +5 -0
- xache/utils/cache.py +214 -0
- xache/utils/http.py +209 -0
- xache/utils/retry.py +101 -0
- xache-5.0.0.dist-info/METADATA +337 -0
- xache-5.0.0.dist-info/RECORD +30 -0
- xache-5.0.0.dist-info/WHEEL +5 -0
- xache-5.0.0.dist-info/top_level.txt +1 -0
xache/__init__.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Xache Protocol Python SDK
|
|
3
|
+
Official Python client library for Xache Protocol
|
|
4
|
+
|
|
5
|
+
Example:
|
|
6
|
+
```python
|
|
7
|
+
import asyncio
|
|
8
|
+
from xache import XacheClient
|
|
9
|
+
|
|
10
|
+
async def main():
|
|
11
|
+
async with XacheClient(
|
|
12
|
+
api_url="https://api.xache.xyz",
|
|
13
|
+
did="did:agent:evm:0xYourWalletAddress",
|
|
14
|
+
private_key="0x...",
|
|
15
|
+
) as client:
|
|
16
|
+
# Register identity
|
|
17
|
+
identity = await client.identity.register(
|
|
18
|
+
wallet_address="0xYourWalletAddress",
|
|
19
|
+
key_type="evm",
|
|
20
|
+
chain="base",
|
|
21
|
+
)
|
|
22
|
+
print(f"DID: {identity.did}")
|
|
23
|
+
|
|
24
|
+
asyncio.run(main())
|
|
25
|
+
```
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
__version__ = "5.0.0"
|
|
29
|
+
|
|
30
|
+
# Main client
|
|
31
|
+
from .client import XacheClient
|
|
32
|
+
|
|
33
|
+
# Types
|
|
34
|
+
from .types import (
|
|
35
|
+
DID,
|
|
36
|
+
KeyType,
|
|
37
|
+
Chain,
|
|
38
|
+
StorageTier,
|
|
39
|
+
ErrorCode,
|
|
40
|
+
RegisterIdentityRequest,
|
|
41
|
+
RegisterIdentityResponse,
|
|
42
|
+
StoreMemoryRequest,
|
|
43
|
+
StoreMemoryResponse,
|
|
44
|
+
RetrieveMemoryRequest,
|
|
45
|
+
RetrieveMemoryResponse,
|
|
46
|
+
ContributeHeuristicRequest,
|
|
47
|
+
ContributeHeuristicResponse,
|
|
48
|
+
QueryCollectiveRequest,
|
|
49
|
+
QueryCollectiveResponse,
|
|
50
|
+
HeuristicMatch,
|
|
51
|
+
BudgetStatus,
|
|
52
|
+
Receipt,
|
|
53
|
+
ReceiptWithProof,
|
|
54
|
+
UsageAnalytics,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Errors
|
|
58
|
+
from .errors import (
|
|
59
|
+
XacheError,
|
|
60
|
+
UnauthenticatedError,
|
|
61
|
+
PaymentRequiredError,
|
|
62
|
+
RateLimitedError,
|
|
63
|
+
BudgetExceededError,
|
|
64
|
+
InvalidInputError,
|
|
65
|
+
ConflictError,
|
|
66
|
+
RetryLaterError,
|
|
67
|
+
InternalError,
|
|
68
|
+
NetworkError,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Services (for type hints)
|
|
72
|
+
from .services import (
|
|
73
|
+
IdentityService,
|
|
74
|
+
MemoryService,
|
|
75
|
+
CollectiveService,
|
|
76
|
+
BudgetService,
|
|
77
|
+
ReceiptsService,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Utilities
|
|
81
|
+
from .utils.retry import RetryPolicy, with_retry
|
|
82
|
+
from .utils.cache import CacheConfig, LRUCache
|
|
83
|
+
|
|
84
|
+
# Wallet generation
|
|
85
|
+
from .crypto.wallet import (
|
|
86
|
+
WalletGenerator,
|
|
87
|
+
WalletGenerationResult,
|
|
88
|
+
WalletGenerationOptions,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
__all__ = [
|
|
92
|
+
"__version__",
|
|
93
|
+
# Main client
|
|
94
|
+
"XacheClient",
|
|
95
|
+
# Types
|
|
96
|
+
"DID",
|
|
97
|
+
"KeyType",
|
|
98
|
+
"Chain",
|
|
99
|
+
"StorageTier",
|
|
100
|
+
"ErrorCode",
|
|
101
|
+
"RegisterIdentityRequest",
|
|
102
|
+
"RegisterIdentityResponse",
|
|
103
|
+
"StoreMemoryRequest",
|
|
104
|
+
"StoreMemoryResponse",
|
|
105
|
+
"RetrieveMemoryRequest",
|
|
106
|
+
"RetrieveMemoryResponse",
|
|
107
|
+
"ContributeHeuristicRequest",
|
|
108
|
+
"ContributeHeuristicResponse",
|
|
109
|
+
"QueryCollectiveRequest",
|
|
110
|
+
"QueryCollectiveResponse",
|
|
111
|
+
"HeuristicMatch",
|
|
112
|
+
"BudgetStatus",
|
|
113
|
+
"Receipt",
|
|
114
|
+
"ReceiptWithProof",
|
|
115
|
+
"UsageAnalytics",
|
|
116
|
+
# Errors
|
|
117
|
+
"XacheError",
|
|
118
|
+
"UnauthenticatedError",
|
|
119
|
+
"PaymentRequiredError",
|
|
120
|
+
"RateLimitedError",
|
|
121
|
+
"BudgetExceededError",
|
|
122
|
+
"InvalidInputError",
|
|
123
|
+
"ConflictError",
|
|
124
|
+
"RetryLaterError",
|
|
125
|
+
"InternalError",
|
|
126
|
+
"NetworkError",
|
|
127
|
+
# Services
|
|
128
|
+
"IdentityService",
|
|
129
|
+
"MemoryService",
|
|
130
|
+
"CollectiveService",
|
|
131
|
+
"BudgetService",
|
|
132
|
+
"ReceiptsService",
|
|
133
|
+
# Utilities
|
|
134
|
+
"RetryPolicy",
|
|
135
|
+
"with_retry",
|
|
136
|
+
"CacheConfig",
|
|
137
|
+
"LRUCache",
|
|
138
|
+
# Wallet generation
|
|
139
|
+
"WalletGenerator",
|
|
140
|
+
"WalletGenerationResult",
|
|
141
|
+
"WalletGenerationOptions",
|
|
142
|
+
]
|
xache/client.py
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Xache Protocol Python SDK
|
|
3
|
+
Main client class
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
from typing import Dict, Optional, Any, Literal
|
|
8
|
+
|
|
9
|
+
from .types import XacheClientConfig, APIResponse
|
|
10
|
+
from .utils.http import HttpClient
|
|
11
|
+
from .crypto.signing import generate_auth_headers, validate_did
|
|
12
|
+
from .errors import PaymentRequiredError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class XacheClient:
|
|
16
|
+
"""
|
|
17
|
+
Main Xache client with async support
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
```python
|
|
21
|
+
async with XacheClient(
|
|
22
|
+
api_url="https://api.xache.xyz",
|
|
23
|
+
did="did:agent:evm:0xYourWalletAddress",
|
|
24
|
+
private_key="0x...",
|
|
25
|
+
) as client:
|
|
26
|
+
# Register identity
|
|
27
|
+
identity = await client.identity.register(
|
|
28
|
+
wallet_address="0xYourWalletAddress",
|
|
29
|
+
key_type="evm",
|
|
30
|
+
chain="base",
|
|
31
|
+
)
|
|
32
|
+
print(f"DID: {identity.did}")
|
|
33
|
+
```
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
api_url: str,
|
|
39
|
+
did: str,
|
|
40
|
+
private_key: str,
|
|
41
|
+
payment_provider: Optional[Dict[str, Any]] = None,
|
|
42
|
+
timeout: int = 30,
|
|
43
|
+
debug: bool = False,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Initialize Xache client
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
api_url: API Gateway URL
|
|
50
|
+
did: Agent DID
|
|
51
|
+
private_key: Private key for signing (hex string)
|
|
52
|
+
payment_provider: Payment provider configuration
|
|
53
|
+
timeout: Request timeout in seconds
|
|
54
|
+
debug: Enable debug logging
|
|
55
|
+
"""
|
|
56
|
+
# Validate configuration
|
|
57
|
+
self._validate_config(api_url, did, private_key)
|
|
58
|
+
|
|
59
|
+
# Store configuration
|
|
60
|
+
self.api_url = api_url
|
|
61
|
+
self.did = did
|
|
62
|
+
self.private_key = private_key
|
|
63
|
+
self.payment_provider = payment_provider
|
|
64
|
+
self.timeout = timeout
|
|
65
|
+
self.debug = debug
|
|
66
|
+
|
|
67
|
+
# Initialize HTTP client
|
|
68
|
+
self._http_client = HttpClient(timeout=timeout, debug=debug)
|
|
69
|
+
|
|
70
|
+
# Initialize payment handler (lazy loading)
|
|
71
|
+
self._payment_handler = None
|
|
72
|
+
|
|
73
|
+
# Initialize services (lazy loading)
|
|
74
|
+
self._identity_service = None
|
|
75
|
+
self._memory_service = None
|
|
76
|
+
self._collective_service = None
|
|
77
|
+
self._budget_service = None
|
|
78
|
+
self._receipts_service = None
|
|
79
|
+
self._reputation_service = None
|
|
80
|
+
self._extraction_service = None
|
|
81
|
+
self._facilitator_service = None
|
|
82
|
+
self._session_service = None
|
|
83
|
+
self._royalty_service = None
|
|
84
|
+
self._workspace_service = None
|
|
85
|
+
self._owner_service = None
|
|
86
|
+
|
|
87
|
+
if self.debug:
|
|
88
|
+
print(f"Xache client initialized: api_url={api_url}, did={did}")
|
|
89
|
+
|
|
90
|
+
async def __aenter__(self):
|
|
91
|
+
"""Async context manager entry"""
|
|
92
|
+
await self._http_client.__aenter__()
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
96
|
+
"""Async context manager exit"""
|
|
97
|
+
await self._http_client.__aexit__(exc_type, exc_val, exc_tb)
|
|
98
|
+
|
|
99
|
+
def _validate_config(self, api_url: str, did: str, private_key: str):
|
|
100
|
+
"""Validate client configuration"""
|
|
101
|
+
if not api_url:
|
|
102
|
+
raise ValueError("api_url is required")
|
|
103
|
+
|
|
104
|
+
if not did:
|
|
105
|
+
raise ValueError("did is required")
|
|
106
|
+
|
|
107
|
+
if not validate_did(did):
|
|
108
|
+
raise ValueError(
|
|
109
|
+
f"Invalid DID format: {did}. Expected: did:agent:<evm|sol>:<address>"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if not private_key:
|
|
113
|
+
raise ValueError("private_key is required")
|
|
114
|
+
|
|
115
|
+
# Validate private key format (hex string)
|
|
116
|
+
clean_key = private_key[2:] if private_key.startswith("0x") else private_key
|
|
117
|
+
if len(clean_key) != 64 or not all(c in "0123456789abcdefABCDEF" for c in clean_key):
|
|
118
|
+
raise ValueError("private_key must be a 64-character hex string")
|
|
119
|
+
|
|
120
|
+
async def request(
|
|
121
|
+
self,
|
|
122
|
+
method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
123
|
+
path: str,
|
|
124
|
+
body: Optional[Dict[str, Any]] = None,
|
|
125
|
+
idempotency_key: Optional[str] = None,
|
|
126
|
+
skip_auth: bool = False,
|
|
127
|
+
) -> APIResponse:
|
|
128
|
+
"""
|
|
129
|
+
Make authenticated API request
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
method: HTTP method
|
|
133
|
+
path: API path
|
|
134
|
+
body: Request body
|
|
135
|
+
idempotency_key: Idempotency key for 402 payment
|
|
136
|
+
skip_auth: Skip authentication headers
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
API response
|
|
140
|
+
"""
|
|
141
|
+
url = f"{self.api_url}{path}"
|
|
142
|
+
body_str = json.dumps(body) if body else ""
|
|
143
|
+
|
|
144
|
+
# Build headers
|
|
145
|
+
headers: Dict[str, str] = {}
|
|
146
|
+
|
|
147
|
+
# Add authentication headers (unless skipped)
|
|
148
|
+
if not skip_auth:
|
|
149
|
+
auth_headers = generate_auth_headers(
|
|
150
|
+
method, path, body_str, self.did, self.private_key
|
|
151
|
+
)
|
|
152
|
+
headers.update(auth_headers)
|
|
153
|
+
|
|
154
|
+
# Add idempotency key if provided
|
|
155
|
+
if idempotency_key:
|
|
156
|
+
headers["Idempotency-Key"] = idempotency_key
|
|
157
|
+
|
|
158
|
+
if self.debug:
|
|
159
|
+
print(f"{method} {path}", {"body": body, "headers": headers})
|
|
160
|
+
|
|
161
|
+
# Make request
|
|
162
|
+
response = await self._http_client.request(
|
|
163
|
+
method, url, headers, body_str or None
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
if self.debug:
|
|
167
|
+
print(f"{method} {path} response:", response)
|
|
168
|
+
|
|
169
|
+
return response
|
|
170
|
+
|
|
171
|
+
async def request_with_payment(
|
|
172
|
+
self,
|
|
173
|
+
method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
174
|
+
path: str,
|
|
175
|
+
body: Optional[Dict[str, Any]] = None,
|
|
176
|
+
) -> APIResponse:
|
|
177
|
+
"""
|
|
178
|
+
Make authenticated API request with automatic 402 payment handling
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
method: HTTP method
|
|
182
|
+
path: API path
|
|
183
|
+
body: Request body
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
API response
|
|
187
|
+
"""
|
|
188
|
+
try:
|
|
189
|
+
# First attempt without idempotency key
|
|
190
|
+
return await self.request(method, path, body)
|
|
191
|
+
except PaymentRequiredError as e:
|
|
192
|
+
if self.debug:
|
|
193
|
+
print("402 Payment Required:", e)
|
|
194
|
+
|
|
195
|
+
# Handle payment
|
|
196
|
+
payment_handler = await self._get_payment_handler()
|
|
197
|
+
payment_result = await payment_handler.handle_payment(
|
|
198
|
+
challenge_id=e.challenge_id,
|
|
199
|
+
amount=e.amount,
|
|
200
|
+
chain_hint=e.chain_hint,
|
|
201
|
+
pay_to=e.pay_to,
|
|
202
|
+
description=e.description,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if not payment_result["success"]:
|
|
206
|
+
raise Exception(f"Payment failed: {payment_result.get('error')}")
|
|
207
|
+
|
|
208
|
+
# Retry request with idempotency key
|
|
209
|
+
return await self.request(
|
|
210
|
+
method, path, body, idempotency_key=e.challenge_id
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
async def _get_payment_handler(self):
|
|
214
|
+
"""Get or create payment handler"""
|
|
215
|
+
if self._payment_handler is None:
|
|
216
|
+
from .payment.handler import PaymentHandler
|
|
217
|
+
|
|
218
|
+
self._payment_handler = PaymentHandler(self.payment_provider)
|
|
219
|
+
return self._payment_handler
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def identity(self):
|
|
223
|
+
"""Get identity service"""
|
|
224
|
+
if self._identity_service is None:
|
|
225
|
+
from .services.identity import IdentityService
|
|
226
|
+
|
|
227
|
+
self._identity_service = IdentityService(self)
|
|
228
|
+
return self._identity_service
|
|
229
|
+
|
|
230
|
+
@property
|
|
231
|
+
def memory(self):
|
|
232
|
+
"""Get memory service"""
|
|
233
|
+
if self._memory_service is None:
|
|
234
|
+
from .services.memory import MemoryService
|
|
235
|
+
|
|
236
|
+
self._memory_service = MemoryService(self)
|
|
237
|
+
return self._memory_service
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def collective(self):
|
|
241
|
+
"""Get collective service"""
|
|
242
|
+
if self._collective_service is None:
|
|
243
|
+
from .services.collective import CollectiveService
|
|
244
|
+
|
|
245
|
+
self._collective_service = CollectiveService(self)
|
|
246
|
+
return self._collective_service
|
|
247
|
+
|
|
248
|
+
@property
|
|
249
|
+
def budget(self):
|
|
250
|
+
"""Get budget service"""
|
|
251
|
+
if self._budget_service is None:
|
|
252
|
+
from .services.budget import BudgetService
|
|
253
|
+
|
|
254
|
+
self._budget_service = BudgetService(self)
|
|
255
|
+
return self._budget_service
|
|
256
|
+
|
|
257
|
+
@property
|
|
258
|
+
def receipts(self):
|
|
259
|
+
"""Get receipts service"""
|
|
260
|
+
if self._receipts_service is None:
|
|
261
|
+
from .services.receipts import ReceiptsService
|
|
262
|
+
|
|
263
|
+
self._receipts_service = ReceiptsService(self)
|
|
264
|
+
return self._receipts_service
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def reputation(self):
|
|
268
|
+
"""Get reputation service"""
|
|
269
|
+
if self._reputation_service is None:
|
|
270
|
+
from .services.reputation import ReputationService
|
|
271
|
+
|
|
272
|
+
self._reputation_service = ReputationService(self)
|
|
273
|
+
return self._reputation_service
|
|
274
|
+
|
|
275
|
+
@property
|
|
276
|
+
def extraction(self):
|
|
277
|
+
"""Get extraction service"""
|
|
278
|
+
if self._extraction_service is None:
|
|
279
|
+
from .services.extraction import ExtractionService
|
|
280
|
+
|
|
281
|
+
self._extraction_service = ExtractionService(self)
|
|
282
|
+
return self._extraction_service
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def facilitators(self):
|
|
286
|
+
"""Get facilitator service"""
|
|
287
|
+
if self._facilitator_service is None:
|
|
288
|
+
from .services.facilitator import FacilitatorService
|
|
289
|
+
|
|
290
|
+
self._facilitator_service = FacilitatorService(self)
|
|
291
|
+
return self._facilitator_service
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def sessions(self):
|
|
295
|
+
"""Get session service"""
|
|
296
|
+
if self._session_service is None:
|
|
297
|
+
from .services.sessions import SessionService
|
|
298
|
+
|
|
299
|
+
self._session_service = SessionService(self)
|
|
300
|
+
return self._session_service
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def royalty(self):
|
|
304
|
+
"""Get royalty service"""
|
|
305
|
+
if self._royalty_service is None:
|
|
306
|
+
from .services.royalty import RoyaltyService
|
|
307
|
+
|
|
308
|
+
self._royalty_service = RoyaltyService(self)
|
|
309
|
+
return self._royalty_service
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def workspaces(self):
|
|
313
|
+
"""Get workspace service"""
|
|
314
|
+
if self._workspace_service is None:
|
|
315
|
+
from .services.workspaces import WorkspaceService
|
|
316
|
+
|
|
317
|
+
self._workspace_service = WorkspaceService(self)
|
|
318
|
+
return self._workspace_service
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def owner(self):
|
|
322
|
+
"""Get owner service"""
|
|
323
|
+
if self._owner_service is None:
|
|
324
|
+
from .services.owner import OwnerService
|
|
325
|
+
|
|
326
|
+
self._owner_service = OwnerService(self)
|
|
327
|
+
return self._owner_service
|
|
328
|
+
|
|
329
|
+
async def close(self):
|
|
330
|
+
"""Close HTTP client"""
|
|
331
|
+
await self._http_client.close()
|
xache/crypto/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Crypto utilities"""
|
|
2
|
+
|
|
3
|
+
from .signing import (
|
|
4
|
+
sign_request,
|
|
5
|
+
create_signature_message,
|
|
6
|
+
validate_did,
|
|
7
|
+
validate_timestamp,
|
|
8
|
+
generate_auth_headers,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"sign_request",
|
|
13
|
+
"create_signature_message",
|
|
14
|
+
"validate_did",
|
|
15
|
+
"validate_timestamp",
|
|
16
|
+
"generate_auth_headers",
|
|
17
|
+
]
|