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.
@@ -40,60 +40,6 @@ class FeedbackManager:
40
40
  self.subgraph_client = subgraph_client
41
41
  self.indexer = indexer
42
42
 
43
- def signFeedbackAuth(
44
- self,
45
- agentId: AgentId,
46
- clientAddress: Address,
47
- indexLimit: Optional[int] = None,
48
- expiryHours: int = 24,
49
- ) -> bytes:
50
- """Sign feedback authorization for a client."""
51
- # Parse agent ID to get token ID
52
- if ":" in agentId:
53
- tokenId = int(agentId.split(":")[-1])
54
- else:
55
- tokenId = int(agentId)
56
-
57
- # Get current feedback index if not provided
58
- if indexLimit is None:
59
- try:
60
- lastIndex = self.web3_client.call_contract(
61
- self.reputation_registry,
62
- "getLastIndex",
63
- tokenId,
64
- clientAddress
65
- )
66
- indexLimit = lastIndex + 1
67
- except Exception as e:
68
- # If we can't get the index, default to 1 (for first feedback)
69
- indexLimit = 1
70
-
71
- # Calculate expiry timestamp
72
- expiry = int(time.time()) + (expiryHours * 3600)
73
-
74
- # Encode feedback auth data
75
- authData = self.web3_client.encodeFeedbackAuth(
76
- agentId=tokenId,
77
- clientAddress=clientAddress,
78
- indexLimit=indexLimit,
79
- expiry=expiry,
80
- chainId=self.web3_client.chain_id,
81
- identityRegistry=self.identity_registry.address if self.identity_registry else "0x0",
82
- signerAddress=self.web3_client.account.address
83
- )
84
-
85
- # Hash the encoded data first (matching contract's keccak256(abi.encode(...)))
86
- messageHash = self.web3_client.w3.keccak(authData)
87
-
88
- # Sign the hash with Ethereum signed message prefix (matching contract's .toEthSignedMessageHash())
89
- from eth_account.messages import encode_defunct
90
- signableMessage = encode_defunct(primitive=messageHash)
91
- signedMessage = self.web3_client.account.sign_message(signableMessage)
92
- signature = signedMessage.signature
93
-
94
- # Combine auth data and signature
95
- return authData + signature
96
-
97
43
  def prepareFeedback(
98
44
  self,
99
45
  agentId: AgentId,
@@ -104,6 +50,8 @@ class FeedbackManager:
104
50
  name: Optional[str] = None,
105
51
  skill: Optional[str] = None,
106
52
  task: Optional[str] = None,
53
+ endpoint: Optional[str] = None,
54
+ domain: Optional[str] = None,
107
55
  context: Optional[Dict[str, Any]] = None,
108
56
  proofOfPayment: Optional[Dict[str, Any]] = None,
109
57
  extra: Optional[Dict[str, Any]] = None,
@@ -128,12 +76,13 @@ class FeedbackManager:
128
76
  "agentId": tokenId,
129
77
  "clientAddress": f"eip155:{self.web3_client.chain_id}:{self.web3_client.account.address}",
130
78
  "createdAt": createdAt,
131
- "feedbackAuth": "", # Will be filled when giving feedback
132
79
  "score": int(score) if score else 0, # Score as integer (0-100)
133
80
 
134
81
  # MAY FIELDS
135
82
  "tag1": tags[0] if tags else None,
136
83
  "tag2": tags[1] if len(tags) > 1 else None,
84
+ "endpoint": endpoint,
85
+ "domain": domain,
137
86
  "skill": skill,
138
87
  "context": context,
139
88
  "task": task,
@@ -155,7 +104,6 @@ class FeedbackManager:
155
104
  agentId: AgentId,
156
105
  feedbackFile: Dict[str, Any],
157
106
  idem: Optional[IdemKey] = None,
158
- feedbackAuth: Optional[bytes] = None,
159
107
  ) -> Feedback:
160
108
  """Give feedback (maps 8004 endpoint)."""
161
109
  # Parse agent ID
@@ -180,22 +128,11 @@ class FeedbackManager:
180
128
  except Exception as e:
181
129
  raise ValueError(f"Failed to get feedback index: {e}")
182
130
 
183
- # Prepare feedback auth (use provided auth or create new one)
184
- if feedbackAuth is None:
185
- feedbackAuth = self.sign_feedbackAuth(
186
- agentId=agentId,
187
- clientAddress=clientAddress,
188
- indexLimit=feedbackIndex,
189
- expiryHours=24
190
- )
191
-
192
- # Update feedback file with auth
193
- feedbackFile["feedbackAuth"] = feedbackAuth.hex()
194
-
195
- # Prepare on-chain data (only basic fields, no capability/endpoint)
131
+ # Prepare on-chain data
196
132
  score = feedbackFile.get("score", 0) # Already in 0-100 range
197
- tag1 = self._stringToBytes32(feedbackFile.get("tag1", ""))
198
- tag2 = self._stringToBytes32(feedbackFile.get("tag2", ""))
133
+ tag1 = feedbackFile.get("tag1", "") or ""
134
+ tag2 = feedbackFile.get("tag2", "") or ""
135
+ endpoint = feedbackFile.get("endpoint", "") or ""
199
136
 
200
137
  # Handle off-chain file storage
201
138
  feedbackUri = ""
@@ -216,7 +153,7 @@ class FeedbackManager:
216
153
  # If we have rich data but no IPFS, we need to store it somewhere
217
154
  raise ValueError("Rich feedback data requires IPFS client for storage")
218
155
 
219
- # Submit to blockchain
156
+ # Submit to blockchain with new signature: giveFeedback(agentId, score, tag1, tag2, endpoint, feedbackURI, feedbackHash)
220
157
  try:
221
158
  txHash = self.web3_client.transact_contract(
222
159
  self.reputation_registry,
@@ -225,9 +162,9 @@ class FeedbackManager:
225
162
  score,
226
163
  tag1,
227
164
  tag2,
165
+ endpoint,
228
166
  feedbackUri,
229
- feedbackHash,
230
- feedbackAuth
167
+ feedbackHash
231
168
  )
232
169
 
233
170
  # Wait for transaction confirmation
@@ -244,11 +181,12 @@ class FeedbackManager:
244
181
  agentId=agentId,
245
182
  reviewer=clientAddress, # create_id normalizes the ID; reviewer field can remain as-is
246
183
  score=int(score) if score and score > 0 else None,
247
- tags=[feedbackFile.get("tag1"), feedbackFile.get("tag2")] if feedbackFile.get("tag1") else [],
184
+ tags=[tag1, tag2] if tag1 or tag2 else [],
248
185
  text=feedbackFile.get("text"),
249
186
  context=feedbackFile.get("context"),
250
187
  proofOfPayment=feedbackFile.get("proofOfPayment"),
251
188
  fileURI=feedbackUri if feedbackUri else None,
189
+ endpoint=endpoint if endpoint else None,
252
190
  createdAt=int(time.time()),
253
191
  isRevoked=False,
254
192
  # Off-chain only fields
@@ -265,17 +203,21 @@ class FeedbackManager:
265
203
  feedbackIndex: int,
266
204
  ) -> Feedback:
267
205
  """Get single feedback with responses from subgraph or blockchain."""
268
- # Use indexer for subgraph queries (unified search interface)
206
+ # Prefer subgraph/indexer for richer data, but fall back to chain when subgraph is behind
269
207
  if self.indexer and self.subgraph_client:
270
- # Indexer handles subgraph queries for unified search architecture
271
- # This enables future semantic search capabilities
272
- return self.indexer.get_feedback(agentId, clientAddress, feedbackIndex)
273
-
274
- # Fallback: direct subgraph access (if indexer not available)
208
+ try:
209
+ return self.indexer.get_feedback(agentId, clientAddress, feedbackIndex)
210
+ except Exception as e:
211
+ logger.debug(f"Indexer/subgraph get_feedback failed, falling back to blockchain: {e}")
212
+ return self._get_feedback_from_blockchain(agentId, clientAddress, feedbackIndex)
213
+
275
214
  if self.subgraph_client:
276
- return self._get_feedback_from_subgraph(agentId, clientAddress, feedbackIndex)
277
-
278
- # Fallback to blockchain (direct contract query)
215
+ try:
216
+ return self._get_feedback_from_subgraph(agentId, clientAddress, feedbackIndex)
217
+ except Exception as e:
218
+ logger.debug(f"Subgraph get feedback failed, falling back to blockchain: {e}")
219
+ return self._get_feedback_from_blockchain(agentId, clientAddress, feedbackIndex)
220
+
279
221
  return self._get_feedback_from_blockchain(agentId, clientAddress, feedbackIndex)
280
222
 
281
223
  def _get_feedback_from_subgraph(
@@ -320,24 +262,14 @@ class FeedbackManager:
320
262
  'createdAt': resp.get('createdAt')
321
263
  })
322
264
 
323
- # Map tags - check if they're hex bytes32 or plain strings
324
- tags = []
265
+ # Map tags: rely on whatever the subgraph returns (may be legacy bytes/hash-like values)
266
+ tags: List[str] = []
325
267
  tag1 = feedback_data.get('tag1') or feedback_file.get('tag1')
326
268
  tag2 = feedback_data.get('tag2') or feedback_file.get('tag2')
327
-
328
- # Convert hex bytes32 to readable tags
329
- if tag1 or tag2:
330
- tags = self._hexBytes32ToTags(
331
- tag1 if isinstance(tag1, str) else "",
332
- tag2 if isinstance(tag2, str) else ""
333
- )
334
-
335
- # If conversion failed, try as plain strings
336
- if not tags:
337
- if tag1 and not tag1.startswith("0x"):
338
- tags.append(tag1)
339
- if tag2 and not tag2.startswith("0x"):
340
- tags.append(tag2)
269
+ if isinstance(tag1, str) and tag1:
270
+ tags.append(tag1)
271
+ if isinstance(tag2, str) and tag2:
272
+ tags.append(tag2)
341
273
 
342
274
  return Feedback(
343
275
  id=Feedback.create_id(agentId, clientAddress, feedbackIndex), # create_id now normalizes
@@ -354,7 +286,8 @@ class FeedbackManager:
354
286
  'chainId': feedback_file.get('proofOfPaymentChainId'),
355
287
  'txHash': feedback_file.get('proofOfPaymentTxHash'),
356
288
  } if feedback_file.get('proofOfPaymentFromAddress') else None,
357
- fileURI=feedback_data.get('feedbackUri'),
289
+ fileURI=feedback_data.get('feedbackURI') or feedback_data.get('feedbackUri'), # Handle both old and new field names
290
+ endpoint=feedback_data.get('endpoint'),
358
291
  createdAt=feedback_data.get('createdAt', int(time.time())),
359
292
  answers=answers,
360
293
  isRevoked=feedback_data.get('isRevoked', False),
@@ -380,7 +313,7 @@ class FeedbackManager:
380
313
  tokenId = int(agentId)
381
314
 
382
315
  try:
383
- # Read from blockchain
316
+ # Read from blockchain - new signature: readFeedback(agentId, clientAddress, feedbackIndex)
384
317
  result = self.web3_client.call_contract(
385
318
  self.reputation_registry,
386
319
  "readFeedback",
@@ -395,17 +328,25 @@ class FeedbackManager:
395
328
  normalized_address = self.web3_client.normalize_address(clientAddress)
396
329
  feedbackId = Feedback.create_id(agentId, normalized_address, feedbackIndex)
397
330
 
331
+ # Tags are now strings, not bytes32
332
+ tags = []
333
+ if tag1:
334
+ tags.append(tag1)
335
+ if tag2:
336
+ tags.append(tag2)
337
+
398
338
  return Feedback(
399
339
  id=feedbackId,
400
340
  agentId=agentId,
401
341
  reviewer=normalized_address,
402
342
  score=int(score) if score and score > 0 else None,
403
- tags=self._bytes32ToTags(tag1, tag2),
343
+ tags=tags,
404
344
  text=None, # Not stored on-chain
405
345
  capability=None, # Not stored on-chain
406
346
  context=None, # Not stored on-chain
407
347
  proofOfPayment=None, # Not stored on-chain
408
348
  fileURI=None, # Would need to be retrieved separately
349
+ endpoint=None, # Not stored on-chain in readFeedback
409
350
  createdAt=int(time.time()), # Not stored on-chain
410
351
  isRevoked=is_revoked
411
352
  )
@@ -453,12 +394,12 @@ class FeedbackManager:
453
394
  tokenId = int(agentId)
454
395
 
455
396
  try:
456
- # Prepare filter parameters
397
+ # Prepare filter parameters - tags are now strings
457
398
  client_list = clientAddresses if clientAddresses else []
458
- tag1_filter = self._stringToBytes32(tags[0] if tags else "")
459
- tag2_filter = self._stringToBytes32(tags[1] if tags and len(tags) > 1 else "")
399
+ tag1_filter = tags[0] if tags else ""
400
+ tag2_filter = tags[1] if tags and len(tags) > 1 else ""
460
401
 
461
- # Read from blockchain
402
+ # Read from blockchain - new signature returns: (clientAddresses, feedbackIndexes, scores, tag1s, tag2s, revokedStatuses)
462
403
  result = self.web3_client.call_contract(
463
404
  self.reputation_registry,
464
405
  "readAllFeedback",
@@ -469,19 +410,27 @@ class FeedbackManager:
469
410
  include_revoked
470
411
  )
471
412
 
472
- clients, scores, tag1s, tag2s, revoked_statuses = result
413
+ clients, feedback_indexes, scores, tag1s, tag2s, revoked_statuses = result
473
414
 
474
415
  # Convert to Feedback objects
475
416
  feedbacks = []
476
417
  for i in range(len(clients)):
477
- feedbackId = Feedback.create_id(agentId, clients[i], i + 1) # Assuming 1-indexed
418
+ feedback_index = int(feedback_indexes[i]) if i < len(feedback_indexes) else (i + 1)
419
+ feedbackId = Feedback.create_id(agentId, clients[i], feedback_index)
420
+
421
+ # Tags are now strings
422
+ tags_list = []
423
+ if i < len(tag1s) and tag1s[i]:
424
+ tags_list.append(tag1s[i])
425
+ if i < len(tag2s) and tag2s[i]:
426
+ tags_list.append(tag2s[i])
478
427
 
479
428
  feedback = Feedback(
480
429
  id=feedbackId,
481
430
  agentId=agentId,
482
431
  reviewer=clients[i],
483
432
  score=int(scores[i]) if scores[i] and scores[i] > 0 else None,
484
- tags=self._bytes32ToTags(tag1s[i], tag2s[i]),
433
+ tags=tags_list,
485
434
  text=None,
486
435
  capability=None,
487
436
  endpoint=None,
@@ -489,7 +438,7 @@ class FeedbackManager:
489
438
  proofOfPayment=None,
490
439
  fileURI=None,
491
440
  createdAt=int(time.time()),
492
- isRevoked=revoked_statuses[i]
441
+ isRevoked=revoked_statuses[i] if i < len(revoked_statuses) else False
493
442
  )
494
443
  feedbacks.append(feedback)
495
444
 
@@ -555,24 +504,14 @@ class FeedbackManager:
555
504
  'createdAt': resp.get('createdAt')
556
505
  })
557
506
 
558
- # Map tags - check if they're hex bytes32 or plain strings
559
- tags_list = []
507
+ # Map tags: rely on whatever the subgraph returns (may be legacy bytes/hash-like values)
508
+ tags_list: List[str] = []
560
509
  tag1 = fb_data.get('tag1') or feedback_file.get('tag1')
561
510
  tag2 = fb_data.get('tag2') or feedback_file.get('tag2')
562
-
563
- # Convert hex bytes32 to readable tags
564
- if tag1 or tag2:
565
- tags_list = self._hexBytes32ToTags(
566
- tag1 if isinstance(tag1, str) else "",
567
- tag2 if isinstance(tag2, str) else ""
568
- )
569
-
570
- # If conversion failed, try as plain strings
571
- if not tags_list:
572
- if tag1 and not tag1.startswith("0x"):
573
- tags_list.append(tag1)
574
- if tag2 and not tag2.startswith("0x"):
575
- tags_list.append(tag2)
511
+ if isinstance(tag1, str) and tag1:
512
+ tags_list.append(tag1)
513
+ if isinstance(tag2, str) and tag2:
514
+ tags_list.append(tag2)
576
515
 
577
516
  # Parse agentId from feedback ID
578
517
  feedback_id = fb_data['id']
@@ -601,7 +540,8 @@ class FeedbackManager:
601
540
  'chainId': feedback_file.get('proofOfPaymentChainId'),
602
541
  'txHash': feedback_file.get('proofOfPaymentTxHash'),
603
542
  } if feedback_file.get('proofOfPaymentFromAddress') else None,
604
- fileURI=fb_data.get('feedbackUri'),
543
+ fileURI=fb_data.get('feedbackURI') or fb_data.get('feedbackUri'), # Handle both old and new field names
544
+ endpoint=fb_data.get('endpoint'),
605
545
  createdAt=fb_data.get('createdAt', int(time.time())),
606
546
  answers=answers,
607
547
  isRevoked=fb_data.get('isRevoked', False),
@@ -682,7 +622,7 @@ class FeedbackManager:
682
622
  tokenId,
683
623
  clientAddress,
684
624
  feedbackIndex,
685
- responseUri,
625
+ responseUri, # Note: contract uses responseURI but variable name kept for compatibility
686
626
  responseHash
687
627
  )
688
628
 
@@ -750,16 +690,16 @@ class FeedbackManager:
750
690
 
751
691
  try:
752
692
  client_list = clientAddresses if clientAddresses else []
753
- tag1_bytes = self._stringToBytes32(tag1) if tag1 else b"\x00" * 32
754
- tag2_bytes = self._stringToBytes32(tag2) if tag2 else b"\x00" * 32
693
+ tag1_str = tag1 if tag1 else ""
694
+ tag2_str = tag2 if tag2 else ""
755
695
 
756
696
  result = self.web3_client.call_contract(
757
697
  self.reputation_registry,
758
698
  "getSummary",
759
699
  tokenId,
760
700
  client_list,
761
- tag1_bytes,
762
- tag2_bytes
701
+ tag1_str,
702
+ tag2_str
763
703
  )
764
704
 
765
705
  count, average_score = result
@@ -948,39 +888,28 @@ class FeedbackManager:
948
888
 
949
889
  return "|".join(key_parts)
950
890
 
951
- def _stringToBytes32(self, text: str) -> bytes:
952
- """Convert string to bytes32 for blockchain storage."""
953
- if not text:
954
- return b"\x00" * 32
955
-
956
- # Encode as UTF-8 and pad/truncate to 32 bytes
957
- encoded = text.encode('utf-8')
958
- if len(encoded) > 32:
959
- encoded = encoded[:32]
960
- else:
961
- encoded = encoded.ljust(32, b'\x00')
962
-
963
- return encoded
964
-
965
- def _bytes32ToTags(self, tag1: bytes, tag2: bytes) -> List[str]:
966
- """Convert bytes32 tags back to strings."""
967
- tags = []
968
-
969
- if tag1 and tag1 != b"\x00" * 32:
970
- tag1_str = tag1.rstrip(b'\x00').decode('utf-8', errors='ignore')
971
- if tag1_str:
972
- tags.append(tag1_str)
891
+ def _normalizeTag(self, tag: str) -> str:
892
+ """Normalize string tag (trim, validate length if needed).
973
893
 
974
- if tag2 and tag2 != b"\x00" * 32:
975
- tag2_str = tag2.rstrip(b'\x00').decode('utf-8', errors='ignore')
976
- if tag2_str:
977
- tags.append(tag2_str)
978
-
979
- return tags
894
+ Args:
895
+ tag: Tag string to normalize
896
+
897
+ Returns:
898
+ Normalized tag string
899
+ """
900
+ if not tag:
901
+ return ""
902
+ # Trim whitespace
903
+ normalized = tag.strip()
904
+ # Tags are now strings with no length limit, but we can validate if needed
905
+ return normalized
980
906
 
981
907
  def _hexBytes32ToTags(self, tag1: str, tag2: str) -> List[str]:
982
908
  """Convert hex bytes32 tags back to strings, or return plain strings as-is.
983
909
 
910
+ DEPRECATED: This method is kept for backward compatibility with old data
911
+ that may have bytes32 tags. New tags are strings and don't need conversion.
912
+
984
913
  The subgraph now stores tags as human-readable strings (not hex),
985
914
  so this method handles both formats for backwards compatibility.
986
915
  """
@@ -249,9 +249,9 @@ class AgentIndexer:
249
249
  # Get basic agent data from contract
250
250
  try:
251
251
  if self.identity_registry:
252
- token_uri = self.web3_client.call_contract(
252
+ agent_uri = self.web3_client.call_contract(
253
253
  self.identity_registry,
254
- "tokenURI",
254
+ "tokenURI", # ERC-721 standard function name, but represents agentURI
255
255
  int(token_id)
256
256
  )
257
257
  else:
@@ -1041,29 +1041,42 @@ class AgentIndexer:
1041
1041
  for resp in responses_data:
1042
1042
  answers.append({
1043
1043
  'responder': resp.get('responder'),
1044
- 'responseUri': resp.get('responseUri'),
1044
+ 'responseURI': resp.get('responseURI') or resp.get('responseUri'), # Handle both old and new field names
1045
1045
  'responseHash': resp.get('responseHash'),
1046
1046
  'createdAt': resp.get('createdAt')
1047
1047
  })
1048
1048
 
1049
- # Map tags - check if they're hex bytes32 or plain strings
1049
+ # Map tags - tags are now strings (not bytes32)
1050
1050
  tags = []
1051
1051
  tag1 = feedback_data.get('tag1') or feedback_file.get('tag1')
1052
1052
  tag2 = feedback_data.get('tag2') or feedback_file.get('tag2')
1053
1053
 
1054
- # Convert hex bytes32 to readable tags
1055
- if tag1 or tag2:
1056
- tags = self._hexBytes32ToTags(
1057
- tag1 if isinstance(tag1, str) else "",
1058
- tag2 if isinstance(tag2, str) else ""
1059
- )
1060
-
1061
- # If conversion failed, try as plain strings
1062
- if not tags:
1063
- if tag1 and not tag1.startswith("0x"):
1054
+ # Tags are now plain strings, but handle backward compatibility with hex bytes32
1055
+ if tag1:
1056
+ if isinstance(tag1, str) and not tag1.startswith("0x"):
1064
1057
  tags.append(tag1)
1065
- if tag2 and not tag2.startswith("0x"):
1058
+ elif isinstance(tag1, str) and tag1.startswith("0x"):
1059
+ # Try to convert from hex bytes32 (old format)
1060
+ try:
1061
+ hex_bytes = bytes.fromhex(tag1[2:])
1062
+ tag1_str = hex_bytes.rstrip(b'\x00').decode('utf-8', errors='ignore')
1063
+ if tag1_str:
1064
+ tags.append(tag1_str)
1065
+ except Exception:
1066
+ pass # Ignore invalid hex strings
1067
+
1068
+ if tag2:
1069
+ if isinstance(tag2, str) and not tag2.startswith("0x"):
1066
1070
  tags.append(tag2)
1071
+ elif isinstance(tag2, str) and tag2.startswith("0x"):
1072
+ # Try to convert from hex bytes32 (old format)
1073
+ try:
1074
+ hex_bytes = bytes.fromhex(tag2[2:])
1075
+ tag2_str = hex_bytes.rstrip(b'\x00').decode('utf-8', errors='ignore')
1076
+ if tag2_str:
1077
+ tags.append(tag2_str)
1078
+ except Exception:
1079
+ pass # Ignore invalid hex strings
1067
1080
 
1068
1081
  return Feedback(
1069
1082
  id=Feedback.create_id(agentId, clientAddress, feedbackIndex),
@@ -1080,7 +1093,8 @@ class AgentIndexer:
1080
1093
  'chainId': feedback_file.get('proofOfPaymentChainId'),
1081
1094
  'txHash': feedback_file.get('proofOfPaymentTxHash'),
1082
1095
  } if feedback_file.get('proofOfPaymentFromAddress') else None,
1083
- fileURI=feedback_data.get('feedbackUri'),
1096
+ fileURI=feedback_data.get('feedbackURI') or feedback_data.get('feedbackUri'), # Handle both old and new field names
1097
+ endpoint=feedback_data.get('endpoint'),
1084
1098
  createdAt=feedback_data.get('createdAt', int(time.time())),
1085
1099
  answers=answers,
1086
1100
  isRevoked=feedback_data.get('isRevoked', False),
@@ -1276,10 +1290,10 @@ class AgentIndexer:
1276
1290
  def _get_agent_from_blockchain(self, token_id: int, sdk) -> Optional[Dict[str, Any]]:
1277
1291
  """Get agent data from blockchain."""
1278
1292
  try:
1279
- # Get token URI from contract
1280
- token_uri = self.web3_client.call_contract(
1293
+ # Get agent URI from contract (using ERC-721 tokenURI function)
1294
+ agent_uri = self.web3_client.call_contract(
1281
1295
  sdk.identity_registry,
1282
- "tokenURI",
1296
+ "tokenURI", # ERC-721 standard function name, but represents agentURI
1283
1297
  token_id
1284
1298
  )
1285
1299
 
@@ -1290,27 +1304,41 @@ class AgentIndexer:
1290
1304
  token_id
1291
1305
  )
1292
1306
 
1307
+ # Get agentWallet using new dedicated function
1308
+ wallet_address = None
1309
+ try:
1310
+ wallet_address = self.web3_client.call_contract(
1311
+ sdk.identity_registry,
1312
+ "getAgentWallet",
1313
+ token_id
1314
+ )
1315
+ if wallet_address == "0x0000000000000000000000000000000000000000":
1316
+ wallet_address = None
1317
+ except Exception:
1318
+ # Fallback to registration file if getAgentWallet not available
1319
+ pass
1320
+
1293
1321
  # Create agent ID
1294
1322
  agent_id = f"{sdk.chain_id}:{token_id}"
1295
1323
 
1296
1324
  # Try to load registration data from IPFS
1297
- registration_data = self._load_registration_from_ipfs(token_uri, sdk)
1325
+ registration_data = self._load_registration_from_ipfs(agent_uri, sdk)
1298
1326
 
1299
1327
  if registration_data:
1300
- # Use data from IPFS
1328
+ # Use data from IPFS, but prefer on-chain wallet if available
1301
1329
  return {
1302
1330
  "agentId": agent_id,
1303
1331
  "name": registration_data.get("name", f"Agent {token_id}"),
1304
1332
  "description": registration_data.get("description", f"Agent registered with token ID {token_id}"),
1305
1333
  "owner": owner,
1306
1334
  "tokenId": token_id,
1307
- "tokenURI": token_uri,
1308
- "x402support": registration_data.get("x402support", False),
1335
+ "agentURI": agent_uri, # Updated field name
1336
+ "x402support": registration_data.get("x402Support", registration_data.get("x402support", False)),
1309
1337
  "trustModels": registration_data.get("trustModels", ["reputation"]),
1310
1338
  "active": registration_data.get("active", True),
1311
1339
  "endpoints": registration_data.get("endpoints", []),
1312
1340
  "image": registration_data.get("image"),
1313
- "walletAddress": registration_data.get("walletAddress"),
1341
+ "walletAddress": wallet_address or registration_data.get("walletAddress"), # Prefer on-chain wallet
1314
1342
  "metadata": registration_data.get("metadata", {})
1315
1343
  }
1316
1344
  else:
@@ -1321,13 +1349,13 @@ class AgentIndexer:
1321
1349
  "description": f"Agent registered with token ID {token_id}",
1322
1350
  "owner": owner,
1323
1351
  "tokenId": token_id,
1324
- "tokenURI": token_uri,
1352
+ "agentURI": agent_uri, # Updated field name
1325
1353
  "x402support": False,
1326
1354
  "trustModels": ["reputation"],
1327
1355
  "active": True,
1328
1356
  "endpoints": [],
1329
1357
  "image": None,
1330
- "walletAddress": None,
1358
+ "walletAddress": wallet_address,
1331
1359
  "metadata": {}
1332
1360
  }
1333
1361
  except Exception as e:
agent0_sdk/core/models.py CHANGED
@@ -88,23 +88,8 @@ class RegistrationFile:
88
88
  }
89
89
  endpoints.append(endpoint_dict)
90
90
 
91
- # Add walletAddress as an endpoint if present
92
- if self.walletAddress:
93
- # Use stored walletChainId if available, otherwise extract from agentId
94
- chain_id_for_wallet = self.walletChainId
95
- if chain_id_for_wallet is None:
96
- # Extract chain ID from agentId if available, otherwise use 1 as default
97
- chain_id_for_wallet = 1 # Default to mainnet
98
- if self.agentId and ":" in self.agentId:
99
- try:
100
- chain_id_for_wallet = int(self.agentId.split(":")[1])
101
- except (ValueError, IndexError):
102
- chain_id_for_wallet = 1
103
-
104
- endpoints.append({
105
- "name": "agentWallet",
106
- "endpoint": f"eip155:{chain_id_for_wallet}:{self.walletAddress}"
107
- })
91
+ # Note: agentWallet is no longer included in endpoints array.
92
+ # It's now a reserved on-chain metadata key managed via setAgentWallet().
108
93
 
109
94
  # Build registrations array
110
95
  registrations = []
@@ -125,7 +110,7 @@ class RegistrationFile:
125
110
  "registrations": registrations,
126
111
  "supportedTrust": [tm.value if isinstance(tm, TrustModel) else tm for tm in self.trustModels],
127
112
  "active": self.active,
128
- "x402support": self.x402support,
113
+ "x402Support": self.x402support, # Use camelCase in JSON output per spec
129
114
  "updatedAt": self.updatedAt,
130
115
  }
131
116
 
@@ -163,7 +148,7 @@ class RegistrationFile:
163
148
  endpoints=endpoints,
164
149
  trustModels=trust_models,
165
150
  active=data.get("active", False),
166
- x402support=data.get("x402support", False),
151
+ x402support=data.get("x402Support", data.get("x402support", False)), # Handle both camelCase and lowercase
167
152
  metadata=data.get("metadata", {}),
168
153
  updatedAt=data.get("updatedAt", int(datetime.now().timestamp())),
169
154
  )
@@ -206,6 +191,7 @@ class Feedback:
206
191
  context: Optional[Dict[str, Any]] = None
207
192
  proofOfPayment: Optional[Dict[str, Any]] = None
208
193
  fileURI: Optional[URI] = None
194
+ endpoint: Optional[str] = None # Endpoint URI associated with feedback
209
195
  createdAt: Timestamp = field(default_factory=lambda: int(datetime.now().timestamp()))
210
196
  answers: List[Dict[str, Any]] = field(default_factory=list)
211
197
  isRevoked: bool = False
@@ -304,6 +290,7 @@ class SearchFeedbackParams:
304
290
  skills: Optional[List[str]] = None
305
291
  tasks: Optional[List[str]] = None
306
292
  names: Optional[List[str]] = None # MCP tool/resource/prompt names
293
+ endpoint: Optional[str] = None # Filter by endpoint URI
307
294
  minScore: Optional[int] = None # 0-100
308
295
  maxScore: Optional[int] = None # 0-100
309
296
  includeRevoked: bool = False
@@ -64,7 +64,7 @@ def validate_skill(slug: str) -> bool:
64
64
  Validate if a skill slug exists in the OASF taxonomy.
65
65
 
66
66
  Args:
67
- slug: The skill slug to validate (e.g., "natural_language_processing/summarization")
67
+ slug: The skill slug to validate (e.g., "natural_language_processing/natural_language_generation/summarization")
68
68
 
69
69
  Returns:
70
70
  True if the skill exists in the taxonomy, False otherwise