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 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()
@@ -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
+ ]