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.
@@ -0,0 +1,290 @@
1
+ """
2
+ Royalty Service - Revenue tracking and earnings management
3
+ """
4
+
5
+ from typing import List, Dict, Optional
6
+ from dataclasses import dataclass
7
+ from ..types import DID
8
+
9
+
10
+ @dataclass
11
+ class HeuristicRevenue:
12
+ """Revenue for a single heuristic"""
13
+ heuristic_id: str
14
+ pattern: str
15
+ domain: str
16
+ earned_usd: str
17
+ usage_count: int
18
+
19
+
20
+ @dataclass
21
+ class MonthlyEarning:
22
+ """Monthly earnings"""
23
+ month: str
24
+ earned_usd: str
25
+
26
+
27
+ @dataclass
28
+ class RevenueStats:
29
+ """Revenue statistics for an agent"""
30
+ agent_did: DID
31
+ total_earned_usd: str
32
+ heuristic_count: int
33
+ top_heuristics: List[HeuristicRevenue]
34
+ earnings_by_domain: Dict[str, str]
35
+ monthly_earnings: List[MonthlyEarning]
36
+
37
+
38
+ @dataclass
39
+ class PendingPayout:
40
+ """Pending payout information"""
41
+ heuristic_id: str
42
+ heuristic_key: str
43
+ pending_amount: str
44
+ last_query_at: str
45
+ query_count: int
46
+
47
+
48
+ @dataclass
49
+ class PlatformRevenue:
50
+ """Platform-wide revenue statistics"""
51
+ total_revenue: str
52
+ total_agents: int
53
+ total_heuristics: int
54
+ total_queries: int
55
+ average_revenue_per_agent: str
56
+
57
+
58
+ @dataclass
59
+ class TopEarner:
60
+ """Top earner information"""
61
+ rank: int
62
+ agent_did: DID
63
+ total_earned_usd: str
64
+ heuristic_count: int
65
+ reputation_score: float
66
+
67
+
68
+ class RoyaltyService:
69
+ """
70
+ Royalty service for revenue tracking and earnings management
71
+
72
+ Track royalties earned from heuristic contributions and pending payouts.
73
+ """
74
+
75
+ def __init__(self, client):
76
+ self.client = client
77
+
78
+ async def get_revenue_stats(self, agent_did: DID) -> RevenueStats:
79
+ """
80
+ Get revenue statistics for an agent
81
+
82
+ Args:
83
+ agent_did: Agent DID to get stats for
84
+
85
+ Returns:
86
+ Revenue statistics
87
+
88
+ Example:
89
+ ```python
90
+ stats = await client.royalty.get_revenue_stats("did:agent:evm:0x1234...")
91
+
92
+ print(f"Total earned: ${stats.total_earned_usd}")
93
+ print(f"Heuristics: {stats.heuristic_count}")
94
+
95
+ for h in stats.top_heuristics:
96
+ print(f" {h.domain}: ${h.earned_usd} ({h.usage_count} uses)")
97
+ ```
98
+ """
99
+ from urllib.parse import quote
100
+
101
+ response = await self.client.request(
102
+ "GET", f"/v1/royalty/revenue/{quote(agent_did, safe='')}"
103
+ )
104
+
105
+ if not response.success or not response.data:
106
+ raise Exception(
107
+ response.error.get("message", "Failed to get revenue stats")
108
+ if response.error
109
+ else "Failed to get revenue stats"
110
+ )
111
+
112
+ data = response.data
113
+ return RevenueStats(
114
+ agent_did=data["agentDID"],
115
+ total_earned_usd=data["totalEarnedUSD"],
116
+ heuristic_count=data["heuristicCount"],
117
+ top_heuristics=[
118
+ HeuristicRevenue(
119
+ heuristic_id=h["heuristicId"],
120
+ pattern=h["pattern"],
121
+ domain=h["domain"],
122
+ earned_usd=h["earnedUSD"],
123
+ usage_count=h["usageCount"],
124
+ )
125
+ for h in data.get("topHeuristics", [])
126
+ ],
127
+ earnings_by_domain=data.get("earningsByDomain", {}),
128
+ monthly_earnings=[
129
+ MonthlyEarning(month=m["month"], earned_usd=m["earnedUSD"])
130
+ for m in data.get("monthlyEarnings", [])
131
+ ],
132
+ )
133
+
134
+ async def get_pending_payouts(self, agent_did: DID) -> Dict:
135
+ """
136
+ Get pending payouts for an agent
137
+
138
+ Args:
139
+ agent_did: Agent DID
140
+
141
+ Returns:
142
+ Pending payouts with total amount
143
+
144
+ Example:
145
+ ```python
146
+ result = await client.royalty.get_pending_payouts("did:agent:evm:0x1234...")
147
+
148
+ print(f"Total pending: ${result['total_pending_amount']}")
149
+ for p in result['pending_payouts']:
150
+ print(f" {p.heuristic_key}: ${p.pending_amount}")
151
+ ```
152
+ """
153
+ from urllib.parse import quote
154
+
155
+ response = await self.client.request(
156
+ "GET", f"/v1/royalty/payouts/{quote(agent_did, safe='')}"
157
+ )
158
+
159
+ if not response.success or not response.data:
160
+ raise Exception(
161
+ response.error.get("message", "Failed to get pending payouts")
162
+ if response.error
163
+ else "Failed to get pending payouts"
164
+ )
165
+
166
+ data = response.data
167
+ return {
168
+ "agent_did": data["agentDID"],
169
+ "pending_payouts": [
170
+ PendingPayout(
171
+ heuristic_id=p["heuristicId"],
172
+ heuristic_key=p["heuristicKey"],
173
+ pending_amount=p["pendingAmount"],
174
+ last_query_at=p["lastQueryAt"],
175
+ query_count=p["queryCount"],
176
+ )
177
+ for p in data.get("pendingPayouts", [])
178
+ ],
179
+ "total_pending_count": data.get("totalPendingCount", 0),
180
+ "total_pending_amount": data.get("totalPendingAmount", "0"),
181
+ }
182
+
183
+ async def get_platform_revenue(self) -> PlatformRevenue:
184
+ """
185
+ Get platform-wide revenue statistics
186
+
187
+ Returns:
188
+ Platform revenue statistics
189
+
190
+ Example:
191
+ ```python
192
+ platform = await client.royalty.get_platform_revenue()
193
+
194
+ print(f"Platform revenue: ${platform.total_revenue}")
195
+ print(f"Active agents: {platform.total_agents}")
196
+ print(f"Total heuristics: {platform.total_heuristics}")
197
+ ```
198
+ """
199
+ response = await self.client.request("GET", "/v1/royalty/platform")
200
+
201
+ if not response.success or not response.data:
202
+ raise Exception(
203
+ response.error.get("message", "Failed to get platform revenue")
204
+ if response.error
205
+ else "Failed to get platform revenue"
206
+ )
207
+
208
+ data = response.data
209
+ return PlatformRevenue(
210
+ total_revenue=data["totalRevenue"],
211
+ total_agents=data["totalAgents"],
212
+ total_heuristics=data["totalHeuristics"],
213
+ total_queries=data["totalQueries"],
214
+ average_revenue_per_agent=data["averageRevenuePerAgent"],
215
+ )
216
+
217
+ async def get_top_earners(self, limit: int = 20) -> Dict:
218
+ """
219
+ Get top earning agents
220
+
221
+ Args:
222
+ limit: Number of top earners to return (default: 20)
223
+
224
+ Returns:
225
+ List of top earners
226
+
227
+ Example:
228
+ ```python
229
+ result = await client.royalty.get_top_earners(10)
230
+
231
+ for earner in result['top_earners']:
232
+ print(f"#{earner.rank} {earner.agent_did}")
233
+ print(f" Earned: ${earner.total_earned_usd}")
234
+ ```
235
+ """
236
+ response = await self.client.request(
237
+ "GET", f"/v1/royalty/top-earners?limit={limit}"
238
+ )
239
+
240
+ if not response.success or not response.data:
241
+ raise Exception(
242
+ response.error.get("message", "Failed to get top earners")
243
+ if response.error
244
+ else "Failed to get top earners"
245
+ )
246
+
247
+ data = response.data
248
+ return {
249
+ "top_earners": [
250
+ TopEarner(
251
+ rank=e["rank"],
252
+ agent_did=e["agentDID"],
253
+ total_earned_usd=e["totalEarnedUSD"],
254
+ heuristic_count=e["heuristicCount"],
255
+ reputation_score=e["reputationScore"],
256
+ )
257
+ for e in data.get("topEarners", [])
258
+ ],
259
+ "total": data.get("total", 0),
260
+ }
261
+
262
+ async def get_my_revenue(self) -> RevenueStats:
263
+ """
264
+ Get revenue stats for the current authenticated agent
265
+
266
+ Returns:
267
+ Revenue statistics for authenticated agent
268
+
269
+ Example:
270
+ ```python
271
+ my_stats = await client.royalty.get_my_revenue()
272
+ print(f"My earnings: ${my_stats.total_earned_usd}")
273
+ ```
274
+ """
275
+ return await self.get_revenue_stats(self.client.did)
276
+
277
+ async def get_my_pending_payouts(self) -> Dict:
278
+ """
279
+ Get pending payouts for the current authenticated agent
280
+
281
+ Returns:
282
+ Pending payouts for authenticated agent
283
+
284
+ Example:
285
+ ```python
286
+ result = await client.royalty.get_my_pending_payouts()
287
+ print(f"Pending: ${result['total_pending_amount']}")
288
+ ```
289
+ """
290
+ return await self.get_pending_payouts(self.client.did)
@@ -0,0 +1,268 @@
1
+ """
2
+ Session Service - x402 v2 wallet session management
3
+ """
4
+
5
+ from typing import Optional, List
6
+ from dataclasses import dataclass
7
+
8
+
9
+ @dataclass
10
+ class WalletSession:
11
+ """Wallet session for pre-authorized payments"""
12
+ session_id: str
13
+ wallet_address: str
14
+ chain: str
15
+ network: str
16
+ created_at: str
17
+ expires_at: str
18
+ max_amount: str
19
+ spent_amount: str
20
+ remaining_amount: str
21
+ scope: List[str]
22
+ active: bool
23
+
24
+
25
+ @dataclass
26
+ class SessionValidation:
27
+ """Session validation result"""
28
+ valid: bool
29
+ session_id: str
30
+ has_budget: bool
31
+ remaining_amount: str
32
+ reason: Optional[str] = None
33
+
34
+
35
+ @dataclass
36
+ class CreateSessionOptions:
37
+ """Options for creating a session"""
38
+ wallet_address: str
39
+ chain: str
40
+ network: str
41
+ duration_seconds: int = 3600 # 1 hour default
42
+ max_amount: Optional[str] = None
43
+ scope: Optional[List[str]] = None
44
+
45
+
46
+ class SessionService:
47
+ """
48
+ Session service for x402 v2 wallet session management
49
+
50
+ Sessions allow pre-authorized payments within a budget and time limit,
51
+ reducing the need for per-request wallet signatures.
52
+ """
53
+
54
+ def __init__(self, client):
55
+ self.client = client
56
+
57
+ async def create(self, options: CreateSessionOptions) -> WalletSession:
58
+ """
59
+ Create a new wallet session for pre-authorized payments
60
+
61
+ Args:
62
+ options: Session creation options
63
+
64
+ Returns:
65
+ Created wallet session
66
+
67
+ Example:
68
+ ```python
69
+ from xache.services.sessions import CreateSessionOptions
70
+
71
+ session = await client.sessions.create(CreateSessionOptions(
72
+ wallet_address="0x1234...",
73
+ chain="evm",
74
+ network="base-sepolia",
75
+ duration_seconds=3600, # 1 hour
76
+ max_amount="10000000", # 10 USDC
77
+ scope=["memory:store", "memory:retrieve"]
78
+ ))
79
+
80
+ print(f"Session ID: {session.session_id}")
81
+ print(f"Expires: {session.expires_at}")
82
+ ```
83
+ """
84
+ body = {
85
+ "walletAddress": options.wallet_address,
86
+ "chain": options.chain,
87
+ "network": options.network,
88
+ "durationSeconds": options.duration_seconds,
89
+ }
90
+
91
+ if options.max_amount:
92
+ body["maxAmount"] = options.max_amount
93
+ if options.scope:
94
+ body["scope"] = options.scope
95
+
96
+ response = await self.client.request("POST", "/v1/sessions", body)
97
+
98
+ if not response.success or not response.data:
99
+ raise Exception(
100
+ response.error.get("message", "Failed to create session")
101
+ if response.error
102
+ else "Failed to create session"
103
+ )
104
+
105
+ return self._parse_session(response.data.get("session", response.data))
106
+
107
+ async def get(self, session_id: str) -> Optional[WalletSession]:
108
+ """
109
+ Get session by ID
110
+
111
+ Args:
112
+ session_id: Session identifier
113
+
114
+ Returns:
115
+ Wallet session or None if not found
116
+
117
+ Example:
118
+ ```python
119
+ session = await client.sessions.get("sess_abc123")
120
+ if session:
121
+ print(f"Remaining: {session.remaining_amount}")
122
+ ```
123
+ """
124
+ response = await self.client.request("GET", f"/v1/sessions/{session_id}")
125
+
126
+ if not response.success:
127
+ if response.error and response.error.get("code") == "NOT_FOUND":
128
+ return None
129
+ raise Exception(
130
+ response.error.get("message", "Failed to get session")
131
+ if response.error
132
+ else "Failed to get session"
133
+ )
134
+
135
+ if not response.data:
136
+ return None
137
+
138
+ return self._parse_session(response.data.get("session", response.data))
139
+
140
+ async def validate(
141
+ self, session_id: str, amount: str, scope: Optional[str] = None
142
+ ) -> SessionValidation:
143
+ """
144
+ Validate session for a specific operation
145
+
146
+ Args:
147
+ session_id: Session identifier
148
+ amount: Amount to validate (in smallest unit, e.g., microcents)
149
+ scope: Operation scope to validate (e.g., "memory:store")
150
+
151
+ Returns:
152
+ Validation result
153
+
154
+ Example:
155
+ ```python
156
+ validation = await client.sessions.validate(
157
+ "sess_abc123",
158
+ amount="1000",
159
+ scope="memory:store"
160
+ )
161
+
162
+ if validation.valid and validation.has_budget:
163
+ print("Session is valid for this operation")
164
+ else:
165
+ print(f"Invalid: {validation.reason}")
166
+ ```
167
+ """
168
+ body = {"amount": amount}
169
+ if scope:
170
+ body["scope"] = scope
171
+
172
+ response = await self.client.request(
173
+ "POST", f"/v1/sessions/{session_id}/validate", body
174
+ )
175
+
176
+ if not response.success or not response.data:
177
+ raise Exception(
178
+ response.error.get("message", "Failed to validate session")
179
+ if response.error
180
+ else "Failed to validate session"
181
+ )
182
+
183
+ data = response.data
184
+ return SessionValidation(
185
+ valid=data.get("valid", False),
186
+ session_id=data.get("sessionId", session_id),
187
+ has_budget=data.get("hasBudget", False),
188
+ remaining_amount=data.get("remainingAmount", "0"),
189
+ reason=data.get("reason"),
190
+ )
191
+
192
+ async def revoke(self, session_id: str) -> bool:
193
+ """
194
+ Revoke a session
195
+
196
+ Args:
197
+ session_id: Session identifier
198
+
199
+ Returns:
200
+ True if successfully revoked
201
+
202
+ Example:
203
+ ```python
204
+ revoked = await client.sessions.revoke("sess_abc123")
205
+ if revoked:
206
+ print("Session revoked successfully")
207
+ ```
208
+ """
209
+ response = await self.client.request("DELETE", f"/v1/sessions/{session_id}")
210
+
211
+ if not response.success:
212
+ raise Exception(
213
+ response.error.get("message", "Failed to revoke session")
214
+ if response.error
215
+ else "Failed to revoke session"
216
+ )
217
+
218
+ return True
219
+
220
+ async def list_active(self, wallet_address: Optional[str] = None) -> List[WalletSession]:
221
+ """
222
+ List active sessions
223
+
224
+ Args:
225
+ wallet_address: Optional wallet address to filter by
226
+
227
+ Returns:
228
+ List of active sessions
229
+
230
+ Example:
231
+ ```python
232
+ sessions = await client.sessions.list_active()
233
+ print(f"Active sessions: {len(sessions)}")
234
+ for s in sessions:
235
+ print(f" {s.session_id}: expires {s.expires_at}")
236
+ ```
237
+ """
238
+ endpoint = "/v1/sessions"
239
+ if wallet_address:
240
+ endpoint += f"?walletAddress={wallet_address}"
241
+
242
+ response = await self.client.request("GET", endpoint)
243
+
244
+ if not response.success or not response.data:
245
+ raise Exception(
246
+ response.error.get("message", "Failed to list sessions")
247
+ if response.error
248
+ else "Failed to list sessions"
249
+ )
250
+
251
+ sessions_data = response.data.get("sessions", [])
252
+ return [self._parse_session(s) for s in sessions_data]
253
+
254
+ def _parse_session(self, data: dict) -> WalletSession:
255
+ """Parse session data into WalletSession object"""
256
+ return WalletSession(
257
+ session_id=data["sessionId"],
258
+ wallet_address=data["walletAddress"],
259
+ chain=data["chain"],
260
+ network=data["network"],
261
+ created_at=data["createdAt"],
262
+ expires_at=data["expiresAt"],
263
+ max_amount=data.get("maxAmount", "0"),
264
+ spent_amount=data.get("spentAmount", "0"),
265
+ remaining_amount=data.get("remainingAmount", "0"),
266
+ scope=data.get("scope", []),
267
+ active=data.get("active", True),
268
+ )