agent0-sdk 0.31__py3-none-any.whl → 1.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.
- agent0_sdk/__init__.py +1 -1
- agent0_sdk/core/agent.py +172 -30
- agent0_sdk/core/contracts.py +93 -58
- agent0_sdk/core/feedback_manager.py +90 -161
- agent0_sdk/core/indexer.py +54 -26
- agent0_sdk/core/models.py +6 -19
- agent0_sdk/core/oasf_validator.py +1 -1
- agent0_sdk/core/sdk.py +31 -16
- agent0_sdk/core/subgraph_client.py +34 -15
- agent0_sdk/core/web3_client.py +184 -17
- {agent0_sdk-0.31.dist-info → agent0_sdk-1.0.0.dist-info}/METADATA +21 -7
- agent0_sdk-1.0.0.dist-info/RECORD +19 -0
- {agent0_sdk-0.31.dist-info → agent0_sdk-1.0.0.dist-info}/top_level.txt +0 -1
- agent0_sdk-0.31.dist-info/RECORD +0 -33
- tests/__init__.py +0 -1
- tests/config.py +0 -46
- tests/conftest.py +0 -22
- tests/discover_test_data.py +0 -445
- tests/test_feedback.py +0 -417
- tests/test_models.py +0 -224
- tests/test_multi_chain.py +0 -588
- tests/test_oasf_management.py +0 -404
- tests/test_real_public_servers.py +0 -103
- tests/test_registration.py +0 -267
- tests/test_registrationIpfs.py +0 -227
- tests/test_sdk.py +0 -240
- tests/test_search.py +0 -415
- tests/test_transfer.py +0 -255
- {agent0_sdk-0.31.dist-info → agent0_sdk-1.0.0.dist-info}/WHEEL +0 -0
- {agent0_sdk-0.31.dist-info → agent0_sdk-1.0.0.dist-info}/licenses/LICENSE +0 -0
agent0_sdk/core/sdk.py
CHANGED
|
@@ -278,7 +278,12 @@ class SDK:
|
|
|
278
278
|
return Agent(sdk=self, registration_file=registration_file)
|
|
279
279
|
|
|
280
280
|
def loadAgent(self, agentId: AgentId) -> Agent:
|
|
281
|
-
"""Load an existing agent (hydrates from registration file if registered).
|
|
281
|
+
"""Load an existing agent (hydrates from registration file if registered).
|
|
282
|
+
|
|
283
|
+
Note: Agents can be minted with an empty token URI (e.g. IPFS flow where publish fails).
|
|
284
|
+
In that case we return a partially-hydrated Agent with an empty registration file so the
|
|
285
|
+
caller can resume publishing and set the URI later.
|
|
286
|
+
"""
|
|
282
287
|
# Convert agentId to string if it's an integer
|
|
283
288
|
agentId = str(agentId)
|
|
284
289
|
|
|
@@ -292,16 +297,22 @@ class SDK:
|
|
|
292
297
|
|
|
293
298
|
# Get token URI from contract
|
|
294
299
|
try:
|
|
295
|
-
|
|
296
|
-
self.identity_registry, "tokenURI", int(token_id)
|
|
300
|
+
agent_uri = self.web3_client.call_contract(
|
|
301
|
+
self.identity_registry, "tokenURI", int(token_id) # tokenURI is ERC-721 standard, but represents agentURI
|
|
297
302
|
)
|
|
298
303
|
except Exception as e:
|
|
299
304
|
raise ValueError(f"Failed to load agent {agentId}: {e}")
|
|
300
305
|
|
|
301
|
-
# Load registration file
|
|
302
|
-
registration_file = self._load_registration_file(
|
|
306
|
+
# Load registration file (or fall back to a minimal file if agent URI is missing)
|
|
307
|
+
registration_file = self._load_registration_file(agent_uri)
|
|
303
308
|
registration_file.agentId = agentId
|
|
304
|
-
registration_file.agentURI =
|
|
309
|
+
registration_file.agentURI = agent_uri if agent_uri else None
|
|
310
|
+
|
|
311
|
+
if not agent_uri or not str(agent_uri).strip():
|
|
312
|
+
logger.warning(
|
|
313
|
+
f"Agent {agentId} has no agentURI set on-chain yet. "
|
|
314
|
+
"Returning a partial agent; update info and call registerIPFS() to publish and set URI."
|
|
315
|
+
)
|
|
305
316
|
|
|
306
317
|
# Store registry address for proper JSON generation
|
|
307
318
|
registry_address = self._registries.get("IDENTITY")
|
|
@@ -315,7 +326,13 @@ class SDK:
|
|
|
315
326
|
return Agent(sdk=self, registration_file=registration_file)
|
|
316
327
|
|
|
317
328
|
def _load_registration_file(self, uri: str) -> RegistrationFile:
|
|
318
|
-
"""Load registration file from URI.
|
|
329
|
+
"""Load registration file from URI.
|
|
330
|
+
|
|
331
|
+
If uri is empty/None/whitespace, returns an empty RegistrationFile to allow resume flows.
|
|
332
|
+
"""
|
|
333
|
+
if not uri or not str(uri).strip():
|
|
334
|
+
return RegistrationFile()
|
|
335
|
+
|
|
319
336
|
if uri.startswith("ipfs://"):
|
|
320
337
|
if not self.ipfs_client:
|
|
321
338
|
raise ValueError("IPFS client not configured")
|
|
@@ -346,21 +363,20 @@ class SDK:
|
|
|
346
363
|
# For now, we'll leave it empty
|
|
347
364
|
registration_file.operators = []
|
|
348
365
|
|
|
349
|
-
# Hydrate
|
|
366
|
+
# Hydrate agentWallet from on-chain (now uses getAgentWallet() instead of metadata)
|
|
350
367
|
agent_id = token_id
|
|
351
368
|
try:
|
|
352
|
-
#
|
|
353
|
-
|
|
354
|
-
self.identity_registry, "
|
|
369
|
+
# Get agentWallet using the new dedicated function
|
|
370
|
+
wallet_address = self.web3_client.call_contract(
|
|
371
|
+
self.identity_registry, "getAgentWallet", agent_id
|
|
355
372
|
)
|
|
356
|
-
if
|
|
357
|
-
wallet_address = "0x" + wallet_bytes.hex()
|
|
373
|
+
if wallet_address and wallet_address != "0x0000000000000000000000000000000000000000":
|
|
358
374
|
registration_file.walletAddress = wallet_address
|
|
359
375
|
# If wallet is read from on-chain, use current chain ID
|
|
360
376
|
# (the chain ID from the registration file might be outdated)
|
|
361
377
|
registration_file.walletChainId = self.chainId
|
|
362
378
|
except Exception as e:
|
|
363
|
-
# No on-chain wallet, will fall back to registration file
|
|
379
|
+
# No on-chain wallet set, will fall back to registration file
|
|
364
380
|
pass
|
|
365
381
|
|
|
366
382
|
try:
|
|
@@ -904,11 +920,10 @@ class SDK:
|
|
|
904
920
|
agentId: "AgentId",
|
|
905
921
|
feedbackFile: Dict[str, Any],
|
|
906
922
|
idem: Optional["IdemKey"] = None,
|
|
907
|
-
feedbackAuth: Optional[bytes] = None,
|
|
908
923
|
) -> "Feedback":
|
|
909
924
|
"""Give feedback (maps 8004 endpoint)."""
|
|
910
925
|
return self.feedback_manager.giveFeedback(
|
|
911
|
-
agentId, feedbackFile, idem
|
|
926
|
+
agentId, feedbackFile, idem
|
|
912
927
|
)
|
|
913
928
|
|
|
914
929
|
def getFeedback(
|
|
@@ -30,25 +30,38 @@ class SubgraphClient:
|
|
|
30
30
|
Returns:
|
|
31
31
|
JSON response from the subgraph
|
|
32
32
|
"""
|
|
33
|
-
|
|
33
|
+
def _do_query(q: str) -> Dict[str, Any]:
|
|
34
34
|
response = requests.post(
|
|
35
35
|
self.subgraph_url,
|
|
36
|
-
json={
|
|
37
|
-
'query': query,
|
|
38
|
-
'variables': variables or {}
|
|
39
|
-
},
|
|
36
|
+
json={'query': q, 'variables': variables or {}},
|
|
40
37
|
headers={'Content-Type': 'application/json'},
|
|
41
|
-
timeout=10
|
|
38
|
+
timeout=10,
|
|
42
39
|
)
|
|
43
40
|
response.raise_for_status()
|
|
44
41
|
result = response.json()
|
|
45
|
-
|
|
46
|
-
# Check for GraphQL errors
|
|
47
42
|
if 'errors' in result:
|
|
48
43
|
error_messages = [err.get('message', 'Unknown error') for err in result['errors']]
|
|
49
44
|
raise ValueError(f"GraphQL errors: {', '.join(error_messages)}")
|
|
50
|
-
|
|
51
45
|
return result.get('data', {})
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
return _do_query(query)
|
|
49
|
+
except ValueError as e:
|
|
50
|
+
# Backwards/forwards compatibility for hosted subgraphs:
|
|
51
|
+
# Some deployments still expose `responseUri` instead of `responseURI`.
|
|
52
|
+
msg = str(e)
|
|
53
|
+
if ("has no field" in msg and "responseURI" in msg) and ("responseURI" in query):
|
|
54
|
+
logger.debug("Subgraph schema missing responseURI; retrying query with responseUri")
|
|
55
|
+
return _do_query(query.replace("responseURI", "responseUri"))
|
|
56
|
+
# Some deployments don't expose agentWallet fields on AgentRegistrationFile.
|
|
57
|
+
if (
|
|
58
|
+
"Type `AgentRegistrationFile` has no field `agentWallet`" in msg
|
|
59
|
+
or "Type `AgentRegistrationFile` has no field `agentWalletChainId`" in msg
|
|
60
|
+
):
|
|
61
|
+
logger.debug("Subgraph schema missing agentWallet fields; retrying query without them")
|
|
62
|
+
q2 = query.replace("agentWalletChainId", "").replace("agentWallet", "")
|
|
63
|
+
return _do_query(q2)
|
|
64
|
+
raise
|
|
52
65
|
except requests.exceptions.RequestException as e:
|
|
53
66
|
raise ConnectionError(f"Failed to query subgraph: {e}")
|
|
54
67
|
|
|
@@ -248,10 +261,12 @@ class SubgraphClient:
|
|
|
248
261
|
) {{
|
|
249
262
|
id
|
|
250
263
|
score
|
|
264
|
+
feedbackIndex
|
|
251
265
|
tag1
|
|
252
266
|
tag2
|
|
267
|
+
endpoint
|
|
253
268
|
clientAddress
|
|
254
|
-
|
|
269
|
+
feedbackURI
|
|
255
270
|
feedbackURIType
|
|
256
271
|
feedbackHash
|
|
257
272
|
isRevoked
|
|
@@ -276,7 +291,7 @@ class SubgraphClient:
|
|
|
276
291
|
responses {{
|
|
277
292
|
id
|
|
278
293
|
responder
|
|
279
|
-
|
|
294
|
+
responseURI
|
|
280
295
|
responseHash
|
|
281
296
|
createdAt
|
|
282
297
|
}}
|
|
@@ -400,10 +415,12 @@ class SubgraphClient:
|
|
|
400
415
|
id
|
|
401
416
|
agent { id agentId chainId }
|
|
402
417
|
clientAddress
|
|
418
|
+
feedbackIndex
|
|
403
419
|
score
|
|
404
420
|
tag1
|
|
405
421
|
tag2
|
|
406
|
-
|
|
422
|
+
endpoint
|
|
423
|
+
feedbackURI
|
|
407
424
|
feedbackURIType
|
|
408
425
|
feedbackHash
|
|
409
426
|
isRevoked
|
|
@@ -429,7 +446,7 @@ class SubgraphClient:
|
|
|
429
446
|
responses {
|
|
430
447
|
id
|
|
431
448
|
responder
|
|
432
|
-
|
|
449
|
+
responseURI
|
|
433
450
|
responseHash
|
|
434
451
|
createdAt
|
|
435
452
|
}
|
|
@@ -548,10 +565,12 @@ class SubgraphClient:
|
|
|
548
565
|
id
|
|
549
566
|
agent {{ id agentId chainId }}
|
|
550
567
|
clientAddress
|
|
568
|
+
feedbackIndex
|
|
551
569
|
score
|
|
552
570
|
tag1
|
|
553
571
|
tag2
|
|
554
|
-
|
|
572
|
+
endpoint
|
|
573
|
+
feedbackURI
|
|
555
574
|
feedbackURIType
|
|
556
575
|
feedbackHash
|
|
557
576
|
isRevoked
|
|
@@ -577,7 +596,7 @@ class SubgraphClient:
|
|
|
577
596
|
responses {{
|
|
578
597
|
id
|
|
579
598
|
responder
|
|
580
|
-
|
|
599
|
+
responseURI
|
|
581
600
|
responseHash
|
|
582
601
|
createdAt
|
|
583
602
|
}}
|
agent0_sdk/core/web3_client.py
CHANGED
|
@@ -5,7 +5,7 @@ Web3 integration layer for smart contract interactions.
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple, Union, Callable
|
|
9
9
|
|
|
10
10
|
try:
|
|
11
11
|
from web3 import Web3
|
|
@@ -123,22 +123,6 @@ class Web3Client:
|
|
|
123
123
|
|
|
124
124
|
return event_filter.get_all_entries()
|
|
125
125
|
|
|
126
|
-
def encodeFeedbackAuth(
|
|
127
|
-
self,
|
|
128
|
-
agentId: int,
|
|
129
|
-
clientAddress: str,
|
|
130
|
-
indexLimit: int,
|
|
131
|
-
expiry: int,
|
|
132
|
-
chainId: int,
|
|
133
|
-
identityRegistry: str,
|
|
134
|
-
signerAddress: str
|
|
135
|
-
) -> bytes:
|
|
136
|
-
"""Encode feedback authorization data."""
|
|
137
|
-
return self.w3.codec.encode(
|
|
138
|
-
['uint256', 'address', 'uint64', 'uint256', 'uint256', 'address', 'address'],
|
|
139
|
-
[agentId, clientAddress, indexLimit, expiry, chainId, identityRegistry, signerAddress]
|
|
140
|
-
)
|
|
141
|
-
|
|
142
126
|
def signMessage(self, message: bytes) -> bytes:
|
|
143
127
|
"""Sign a message with the account's private key."""
|
|
144
128
|
# Create a SignableMessage from the raw bytes
|
|
@@ -190,3 +174,186 @@ class Web3Client:
|
|
|
190
174
|
def get_transaction_count(self, address: str) -> int:
|
|
191
175
|
"""Get transaction count (nonce) of an address."""
|
|
192
176
|
return self.w3.eth.get_transaction_count(address)
|
|
177
|
+
|
|
178
|
+
def encodeEIP712Domain(
|
|
179
|
+
self,
|
|
180
|
+
name: str,
|
|
181
|
+
version: str,
|
|
182
|
+
chain_id: int,
|
|
183
|
+
verifying_contract: str
|
|
184
|
+
) -> Dict[str, Any]:
|
|
185
|
+
"""Encode EIP-712 domain separator.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
name: Contract name
|
|
189
|
+
version: Contract version
|
|
190
|
+
chain_id: Chain ID
|
|
191
|
+
verifying_contract: Contract address
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Domain separator dictionary
|
|
195
|
+
"""
|
|
196
|
+
return {
|
|
197
|
+
"name": name,
|
|
198
|
+
"version": version,
|
|
199
|
+
"chainId": chain_id,
|
|
200
|
+
"verifyingContract": verifying_contract
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
def build_agent_wallet_set_typed_data(
|
|
204
|
+
self,
|
|
205
|
+
agent_id: int,
|
|
206
|
+
new_wallet: str,
|
|
207
|
+
owner: str,
|
|
208
|
+
deadline: int,
|
|
209
|
+
verifying_contract: str,
|
|
210
|
+
chain_id: int,
|
|
211
|
+
) -> Dict[str, Any]:
|
|
212
|
+
"""Build EIP-712 typed data for ERC-8004 IdentityRegistry setAgentWallet.
|
|
213
|
+
|
|
214
|
+
Contract expects:
|
|
215
|
+
- domain: name="ERC8004IdentityRegistry", version="1"
|
|
216
|
+
- primaryType: "AgentWalletSet"
|
|
217
|
+
- message: { agentId, newWallet, owner, deadline }
|
|
218
|
+
"""
|
|
219
|
+
domain = self.encodeEIP712Domain(
|
|
220
|
+
name="ERC8004IdentityRegistry",
|
|
221
|
+
version="1",
|
|
222
|
+
chain_id=chain_id,
|
|
223
|
+
verifying_contract=verifying_contract,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
message_types = {
|
|
227
|
+
"AgentWalletSet": [
|
|
228
|
+
{"name": "agentId", "type": "uint256"},
|
|
229
|
+
{"name": "newWallet", "type": "address"},
|
|
230
|
+
{"name": "owner", "type": "address"},
|
|
231
|
+
{"name": "deadline", "type": "uint256"},
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
message = {
|
|
236
|
+
"agentId": agent_id,
|
|
237
|
+
"newWallet": new_wallet,
|
|
238
|
+
"owner": owner,
|
|
239
|
+
"deadline": deadline,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
# eth_account.messages.encode_typed_data expects the "full_message" format
|
|
243
|
+
return {
|
|
244
|
+
"types": {
|
|
245
|
+
"EIP712Domain": [
|
|
246
|
+
{"name": "name", "type": "string"},
|
|
247
|
+
{"name": "version", "type": "string"},
|
|
248
|
+
{"name": "chainId", "type": "uint256"},
|
|
249
|
+
{"name": "verifyingContract", "type": "address"},
|
|
250
|
+
],
|
|
251
|
+
**message_types,
|
|
252
|
+
},
|
|
253
|
+
"domain": domain,
|
|
254
|
+
"primaryType": "AgentWalletSet",
|
|
255
|
+
"message": message,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
def sign_typed_data(
|
|
259
|
+
self,
|
|
260
|
+
full_message: Dict[str, Any],
|
|
261
|
+
signer: Union[str, BaseAccount],
|
|
262
|
+
) -> bytes:
|
|
263
|
+
"""Sign EIP-712 typed data with a provided signer (EOA).
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
full_message: Typed data dict compatible with encode_typed_data(full_message=...)
|
|
267
|
+
signer: Private key string or eth_account BaseAccount/LocalAccount
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
Signature bytes
|
|
271
|
+
"""
|
|
272
|
+
from eth_account.messages import encode_typed_data
|
|
273
|
+
|
|
274
|
+
if isinstance(signer, str):
|
|
275
|
+
acct: BaseAccount = Account.from_key(signer)
|
|
276
|
+
else:
|
|
277
|
+
acct = signer
|
|
278
|
+
|
|
279
|
+
encoded = encode_typed_data(full_message=full_message)
|
|
280
|
+
signed = acct.sign_message(encoded)
|
|
281
|
+
return signed.signature
|
|
282
|
+
|
|
283
|
+
def signEIP712Message(
|
|
284
|
+
self,
|
|
285
|
+
domain: Dict[str, Any],
|
|
286
|
+
message_types: Dict[str, List[Dict[str, str]]],
|
|
287
|
+
message: Dict[str, Any]
|
|
288
|
+
) -> bytes:
|
|
289
|
+
"""Sign an EIP-712 typed message.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
domain: EIP-712 domain separator
|
|
293
|
+
message_types: Type definitions for the message
|
|
294
|
+
message: Message data to sign
|
|
295
|
+
|
|
296
|
+
Returns:
|
|
297
|
+
Signature bytes
|
|
298
|
+
"""
|
|
299
|
+
if not self.account:
|
|
300
|
+
raise ValueError("Cannot sign message: SDK is in read-only mode. Provide a signer to enable signing.")
|
|
301
|
+
|
|
302
|
+
from eth_account.messages import encode_typed_data
|
|
303
|
+
|
|
304
|
+
structured_data = {
|
|
305
|
+
"types": {
|
|
306
|
+
"EIP712Domain": [
|
|
307
|
+
{"name": "name", "type": "string"},
|
|
308
|
+
{"name": "version", "type": "string"},
|
|
309
|
+
{"name": "chainId", "type": "uint256"},
|
|
310
|
+
{"name": "verifyingContract", "type": "address"}
|
|
311
|
+
],
|
|
312
|
+
**message_types
|
|
313
|
+
},
|
|
314
|
+
"domain": domain,
|
|
315
|
+
"primaryType": list(message_types.keys())[0] if message_types else "Message",
|
|
316
|
+
"message": message
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
encoded = encode_typed_data(full_message=structured_data)
|
|
320
|
+
signed = self.account.sign_message(encoded)
|
|
321
|
+
return signed.signature
|
|
322
|
+
|
|
323
|
+
def verifyEIP712Signature(
|
|
324
|
+
self,
|
|
325
|
+
domain: Dict[str, Any],
|
|
326
|
+
message_types: Dict[str, List[Dict[str, str]]],
|
|
327
|
+
message: Dict[str, Any],
|
|
328
|
+
signature: bytes
|
|
329
|
+
) -> str:
|
|
330
|
+
"""Verify an EIP-712 signature and recover the signer address.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
domain: EIP-712 domain separator
|
|
334
|
+
message_types: Type definitions for the message
|
|
335
|
+
message: Message data that was signed
|
|
336
|
+
signature: Signature bytes to verify
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
Recovered signer address
|
|
340
|
+
"""
|
|
341
|
+
from eth_account.messages import encode_typed_data
|
|
342
|
+
|
|
343
|
+
structured_data = {
|
|
344
|
+
"types": {
|
|
345
|
+
"EIP712Domain": [
|
|
346
|
+
{"name": "name", "type": "string"},
|
|
347
|
+
{"name": "version", "type": "string"},
|
|
348
|
+
{"name": "chainId", "type": "uint256"},
|
|
349
|
+
{"name": "verifyingContract", "type": "address"}
|
|
350
|
+
],
|
|
351
|
+
**message_types
|
|
352
|
+
},
|
|
353
|
+
"domain": domain,
|
|
354
|
+
"primaryType": list(message_types.keys())[0] if message_types else "Message",
|
|
355
|
+
"message": message
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
encoded = encode_typed_data(full_message=structured_data)
|
|
359
|
+
return self.w3.eth.account.recover_message(encoded, signature=signature)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent0-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Python SDK for agent portability, discovery and trust based on ERC-8004
|
|
5
|
-
Author-email: Marco De Rossi <marco
|
|
5
|
+
Author-email: Marco De Rossi <marco@ag0.xyz>
|
|
6
6
|
License: MIT License
|
|
7
7
|
|
|
8
8
|
Copyright (c) 2025 Marco De Rossi
|
|
@@ -152,12 +152,15 @@ agent.setENS("myagent.eth")
|
|
|
152
152
|
|
|
153
153
|
# Add OASF skills and domains (standardized taxonomies)
|
|
154
154
|
agent.addSkill("data_engineering/data_transformation_pipeline", validate_oasf=True)
|
|
155
|
-
agent.addSkill("natural_language_processing/summarization", validate_oasf=True)
|
|
155
|
+
agent.addSkill("natural_language_processing/natural_language_generation/summarization", validate_oasf=True)
|
|
156
156
|
agent.addDomain("finance_and_business/investment_services", validate_oasf=True)
|
|
157
|
-
agent.addDomain("technology/data_science", validate_oasf=True)
|
|
157
|
+
agent.addDomain("technology/data_science/data_science", validate_oasf=True)
|
|
158
158
|
|
|
159
159
|
# Configure wallet and trust
|
|
160
|
-
|
|
160
|
+
# Note: agentWallet is an on-chain verified attribute. setAgentWallet() is on-chain only.
|
|
161
|
+
# EOAs: the NEW wallet must sign an EIP-712 message. If you pass new_wallet_signer, the SDK will
|
|
162
|
+
# build + sign the typed data automatically.
|
|
163
|
+
# If the current SDK signer address matches the new wallet, it can auto-sign without new_wallet_signer.
|
|
161
164
|
agent.setTrust(reputation=True, cryptoEconomic=True)
|
|
162
165
|
|
|
163
166
|
# Add metadata and set status
|
|
@@ -168,6 +171,16 @@ agent.setActive(True)
|
|
|
168
171
|
agent.registerIPFS()
|
|
169
172
|
print(f"Agent registered: {agent.agentId}") # e.g., "11155111:123"
|
|
170
173
|
print(f"Agent URI: {agent.agentURI}") # e.g., "ipfs://Qm..."
|
|
174
|
+
|
|
175
|
+
# (Optional) Change the agent wallet after registration
|
|
176
|
+
# - On mint/registration, `agentWallet` defaults to the current owner address.
|
|
177
|
+
# - Call this only if you want a DIFFERENT wallet (or after a transfer, since the wallet resets to zero).
|
|
178
|
+
# - Transaction is sent by the SDK signer (agent owner), but the signature must be produced by the NEW wallet.
|
|
179
|
+
agent.setAgentWallet(
|
|
180
|
+
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
|
|
181
|
+
chainId=11155111,
|
|
182
|
+
new_wallet_signer=os.getenv("NEW_WALLET_PRIVATE_KEY"),
|
|
183
|
+
)
|
|
171
184
|
```
|
|
172
185
|
|
|
173
186
|
### 3. Load and Edit Agent
|
|
@@ -213,7 +226,8 @@ agent_summary = sdk.getAgent("11155111:123")
|
|
|
213
226
|
feedback_file = sdk.prepareFeedback(
|
|
214
227
|
agentId="11155111:123",
|
|
215
228
|
score=85, # 0-100 (mandatory)
|
|
216
|
-
tags=["data_analyst", "finance"], # Optional
|
|
229
|
+
tags=["data_analyst", "finance"], # Optional: tags are now strings (not bytes32)
|
|
230
|
+
endpoint="https://example.com/endpoint", # Optional: endpoint URI associated with feedback
|
|
217
231
|
capability="tools", # Optional: MCP capability
|
|
218
232
|
name="code_generation", # Optional: MCP tool name
|
|
219
233
|
skill="python" # Optional: A2A skill
|
|
@@ -307,7 +321,7 @@ OASF skills and domains appear in your agent's registration file:
|
|
|
307
321
|
],
|
|
308
322
|
"domains": [
|
|
309
323
|
"finance_and_business/investment_services",
|
|
310
|
-
"technology/data_science"
|
|
324
|
+
"technology/data_science/data_science"
|
|
311
325
|
]
|
|
312
326
|
}
|
|
313
327
|
]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
agent0_sdk/__init__.py,sha256=kzO10wMLu-eClp-rKDuUJg9y8DAkSxoLdbcaTSKocKk,919
|
|
2
|
+
agent0_sdk/core/agent.py,sha256=L8pDTXUWZ5ZmFgGjBwdheKTvMcGu9SlUP9MQD9TQ0MU,45061
|
|
3
|
+
agent0_sdk/core/contracts.py,sha256=euFfjnmpYHNfzzQFvYTvSI-xJGgzFTo2CNny6YaUKTg,21618
|
|
4
|
+
agent0_sdk/core/endpoint_crawler.py,sha256=QBkFc3tBSQqHj6PtSTZ5D3_HVB00KJZJdxE3uYpI9po,13611
|
|
5
|
+
agent0_sdk/core/feedback_manager.py,sha256=ndGROPISrQvfKjwrWiB_xJ9QIh0cLs4oHh666Ru-A_8,37650
|
|
6
|
+
agent0_sdk/core/indexer.py,sha256=BUL4QbbL9sN8eZ1osUdIn_Kgj-MF0SET8RtjbBERQm0,70326
|
|
7
|
+
agent0_sdk/core/ipfs_client.py,sha256=fml1ai1BdBkgb95xAkf-ft8QsahV1HL30hBYRz7rQwI,13929
|
|
8
|
+
agent0_sdk/core/models.py,sha256=1BSAX2LVbw0kL_qHK7DxBrIFx8PF3wQvzkzblcQTMUg,12042
|
|
9
|
+
agent0_sdk/core/oasf_validator.py,sha256=ZOtYYzQd7cJj3eJegi7Ch5ydoapJEjaJSxMvwzKSp5o,2980
|
|
10
|
+
agent0_sdk/core/sdk.py,sha256=c9vSKTer50aJr8VcJ6huj6N1pTRwCUtbCxl5G_5oXhk,40955
|
|
11
|
+
agent0_sdk/core/subgraph_client.py,sha256=VSK9gCB5uYc2OqAVQ659IgeF0N-tXwxPUbv7NK8Kz0U,29575
|
|
12
|
+
agent0_sdk/core/web3_client.py,sha256=h7s-Al3E1xfbb3QNcPvmQBotKJRg23Jm9xot4emr-hU,12283
|
|
13
|
+
agent0_sdk/taxonomies/all_domains.json,sha256=buM8_6mpY8_AMbBIPzM-gtu14Tl30QDmhuQxxrlJU4c,74625
|
|
14
|
+
agent0_sdk/taxonomies/all_skills.json,sha256=WVsutw3fqoj1jfDPru3CyWTr1kc1a5-EhBOWPfXnEi8,47483
|
|
15
|
+
agent0_sdk-1.0.0.dist-info/licenses/LICENSE,sha256=rhZZbZm_Ovz4Oa9LNQ-ms8a1tA36wWh90ZkC0OR7WMw,1072
|
|
16
|
+
agent0_sdk-1.0.0.dist-info/METADATA,sha256=N42e7yZDJiRjDhLhCbzgLPV2mNK45a_zmN-fz1wMD18,14555
|
|
17
|
+
agent0_sdk-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
agent0_sdk-1.0.0.dist-info/top_level.txt,sha256=p4520WUKNfhU0lVWJgkrB_jdeUfvHSY3K18k4oYLNfI,11
|
|
19
|
+
agent0_sdk-1.0.0.dist-info/RECORD,,
|
agent0_sdk-0.31.dist-info/RECORD
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
agent0_sdk/__init__.py,sha256=b1gWy_m93dJucBqmwz9zx2M3zs8JTO-S0CN1B0xBHcQ,918
|
|
2
|
-
agent0_sdk/core/agent.py,sha256=vqgQRQVrCBV0zAGkZeYHeYvWlRAG9-m0Wy61DxvEiD4,38001
|
|
3
|
-
agent0_sdk/core/contracts.py,sha256=LJY07PIyAb3ZSH_m2xD4OdXawsXKxaTUGhYHwGWUpmI,20435
|
|
4
|
-
agent0_sdk/core/endpoint_crawler.py,sha256=QBkFc3tBSQqHj6PtSTZ5D3_HVB00KJZJdxE3uYpI9po,13611
|
|
5
|
-
agent0_sdk/core/feedback_manager.py,sha256=MV3srP-rgFaDXXELeLTJBj2aDUPdENy0tzWUsJUqrL4,39860
|
|
6
|
-
agent0_sdk/core/indexer.py,sha256=erkDKrtyvAtfrblz0YHekPB2Hpw7tJm-1KJjafTMaHc,68470
|
|
7
|
-
agent0_sdk/core/ipfs_client.py,sha256=fml1ai1BdBkgb95xAkf-ft8QsahV1HL30hBYRz7rQwI,13929
|
|
8
|
-
agent0_sdk/core/models.py,sha256=SnHmy9nmKHnD5hMIv-r8Ztj1zfXF_1qxHwfnBFAjkAA,12505
|
|
9
|
-
agent0_sdk/core/oasf_validator.py,sha256=X_V6mZsrp9z2So2fAJ66fm5EypiVEl4Fxtg10Ot3cgQ,2952
|
|
10
|
-
agent0_sdk/core/sdk.py,sha256=CQUpKaP7g1Lu7aokRYs-QChv78XIdkfz6Yax6nhaWqQ,40166
|
|
11
|
-
agent0_sdk/core/subgraph_client.py,sha256=Iw-YEtT1-Rm9f-kY4EXZlCuwEkJQAq5-bHDpxrMVyGg,28419
|
|
12
|
-
agent0_sdk/core/web3_client.py,sha256=859ntu5dAmNlcJ3YM1w_VV2gI3mpCC9QEr-GN1236zU,6850
|
|
13
|
-
agent0_sdk/taxonomies/all_domains.json,sha256=buM8_6mpY8_AMbBIPzM-gtu14Tl30QDmhuQxxrlJU4c,74625
|
|
14
|
-
agent0_sdk/taxonomies/all_skills.json,sha256=WVsutw3fqoj1jfDPru3CyWTr1kc1a5-EhBOWPfXnEi8,47483
|
|
15
|
-
agent0_sdk-0.31.dist-info/licenses/LICENSE,sha256=rhZZbZm_Ovz4Oa9LNQ-ms8a1tA36wWh90ZkC0OR7WMw,1072
|
|
16
|
-
tests/__init__.py,sha256=60ffheccPhuMCtwiiKP1X-CJJXKpxJ_Ywa0aXGHR9bY,23
|
|
17
|
-
tests/config.py,sha256=1uePvkLBNubOQsvYkQSno0m007PMD1VACgm33fCYY6s,1429
|
|
18
|
-
tests/conftest.py,sha256=P-HCtVVYwSvscuaJqhrgZcv39XXNnr932ekEamzIqis,589
|
|
19
|
-
tests/discover_test_data.py,sha256=Fu0uQKnFk8m7qEqEp293BWbo_mT1CK5szVZcdcdlJQw,17678
|
|
20
|
-
tests/test_feedback.py,sha256=7lszWYSmseJE0I4BhKzZdBiIzf2bgpPqZTZvhRrCTjY,14638
|
|
21
|
-
tests/test_models.py,sha256=kCZdoPasIIcOjEw7ToPldqARdbGVK8v8byOhFwVo7OI,7115
|
|
22
|
-
tests/test_multi_chain.py,sha256=6C-HD037Lsjd7_H0pwp7NDU04jLTjhardT1LHBjahFM,26711
|
|
23
|
-
tests/test_oasf_management.py,sha256=SIC_YgnuapNXAstjm9y8gky3cLG1FCcujZ-y5dlHxYo,17738
|
|
24
|
-
tests/test_real_public_servers.py,sha256=pCo4aLSCG9qv4D6T7jbyVmP1gt3r1jWxdes6z5XSNhU,3433
|
|
25
|
-
tests/test_registration.py,sha256=pYanDPLAFETIfabBUvO34ZDmyD0Rbcv8vecSfgSrQ70,9542
|
|
26
|
-
tests/test_registrationIpfs.py,sha256=9O3IBiN2CVMKzB19bqb-jN-nhqsN22kQINMpe9THqiI,8400
|
|
27
|
-
tests/test_sdk.py,sha256=dALLFm_A6aXx0ec-CNOLGQoadaSPZ08EEeCS6Tgnm0M,9362
|
|
28
|
-
tests/test_search.py,sha256=SiUio8H-M6Za8OXD_h9tUZdln0ayzkPJ3doTrkHv-Fs,18382
|
|
29
|
-
tests/test_transfer.py,sha256=zRBllpoMs6NhagAmaZWmD4ckbYjSvsSUerBK4oS-HlA,9258
|
|
30
|
-
agent0_sdk-0.31.dist-info/METADATA,sha256=xsGC93w2qox04MG419dufzAAQO-l5W9TpNXMHomzbwA,13623
|
|
31
|
-
agent0_sdk-0.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
32
|
-
agent0_sdk-0.31.dist-info/top_level.txt,sha256=rgGBfOJlLi1zInQ85jBL2MpDu_ZJNbPjIGz-3Vn5rZs,17
|
|
33
|
-
agent0_sdk-0.31.dist-info/RECORD,,
|
tests/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# Tests for Agent0 SDK
|
tests/config.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Shared configuration loader for test examples.
|
|
3
|
-
Loads configuration from environment variables (.env file).
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
from dotenv import load_dotenv
|
|
9
|
-
|
|
10
|
-
# Load environment variables from .env file
|
|
11
|
-
# Look for .env in parent directory (project root)
|
|
12
|
-
env_path = Path(__file__).parent.parent.parent / ".env"
|
|
13
|
-
load_dotenv(dotenv_path=env_path)
|
|
14
|
-
|
|
15
|
-
# Chain Configuration
|
|
16
|
-
CHAIN_ID = int(os.getenv("CHAIN_ID", "11155111"))
|
|
17
|
-
RPC_URL = os.getenv(
|
|
18
|
-
"RPC_URL",
|
|
19
|
-
"https://eth-sepolia.g.alchemy.com/v2/7nkA4bJ0tKWcl2-5Wn15c5eRdpGZ8DDr"
|
|
20
|
-
)
|
|
21
|
-
AGENT_PRIVATE_KEY = os.getenv("AGENT_PRIVATE_KEY", "")
|
|
22
|
-
|
|
23
|
-
# IPFS Configuration (Pinata)
|
|
24
|
-
PINATA_JWT = os.getenv("PINATA_JWT", "")
|
|
25
|
-
|
|
26
|
-
# Subgraph Configuration
|
|
27
|
-
SUBGRAPH_URL = os.getenv(
|
|
28
|
-
"SUBGRAPH_URL",
|
|
29
|
-
"https://gateway.thegraph.com/api/00a452ad3cd1900273ea62c1bf283f93/subgraphs/id/6wQRC7geo9XYAhckfmfo8kbMRLeWU8KQd3XsJqFKmZLT"
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
# Agent ID for testing (can be overridden via env)
|
|
33
|
-
AGENT_ID = os.getenv("AGENT_ID", "11155111:374")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def print_config():
|
|
37
|
-
"""Print current configuration (hiding sensitive values)."""
|
|
38
|
-
print("Configuration:")
|
|
39
|
-
print(f" CHAIN_ID: {CHAIN_ID}")
|
|
40
|
-
print(f" RPC_URL: {RPC_URL[:50]}...")
|
|
41
|
-
print(f" AGENT_PRIVATE_KEY: {'***' if AGENT_PRIVATE_KEY else 'NOT SET'}")
|
|
42
|
-
print(f" PINATA_JWT: {'***' if PINATA_JWT else 'NOT SET'}")
|
|
43
|
-
print(f" SUBGRAPH_URL: {SUBGRAPH_URL[:50]}...")
|
|
44
|
-
print(f" AGENT_ID: {AGENT_ID}")
|
|
45
|
-
print()
|
|
46
|
-
|
tests/conftest.py
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Pytest configuration file for Agent0 SDK tests.
|
|
3
|
-
Sets up logging to debug level for agent0_sdk only.
|
|
4
|
-
"""
|
|
5
|
-
|
|
6
|
-
import logging
|
|
7
|
-
import sys
|
|
8
|
-
|
|
9
|
-
# Configure logging: root logger at WARNING to suppress noisy dependencies
|
|
10
|
-
logging.basicConfig(
|
|
11
|
-
level=logging.WARNING,
|
|
12
|
-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
13
|
-
datefmt='%Y-%m-%d %H:%M:%S',
|
|
14
|
-
handlers=[
|
|
15
|
-
logging.StreamHandler(sys.stdout)
|
|
16
|
-
]
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
# Set debug level ONLY for agent0_sdk loggers
|
|
20
|
-
logging.getLogger('agent0_sdk').setLevel(logging.DEBUG)
|
|
21
|
-
logging.getLogger('agent0_sdk.core').setLevel(logging.DEBUG)
|
|
22
|
-
|