agent0-sdk 1.5.0b1__tar.gz → 1.5.1b1__tar.gz

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.
Files changed (28) hide show
  1. {agent0_sdk-1.5.0b1/agent0_sdk.egg-info → agent0_sdk-1.5.1b1}/PKG-INFO +151 -7
  2. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/README.md +150 -6
  3. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/__init__.py +1 -1
  4. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/feedback_manager.py +75 -78
  5. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/indexer.py +81 -727
  6. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/models.py +6 -8
  7. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/sdk.py +7 -16
  8. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/semantic_search_client.py +10 -6
  9. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1/agent0_sdk.egg-info}/PKG-INFO +151 -7
  10. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/pyproject.toml +1 -1
  11. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/LICENSE +0 -0
  12. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/MANIFEST.in +0 -0
  13. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/agent.py +0 -0
  14. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/contracts.py +0 -0
  15. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/endpoint_crawler.py +0 -0
  16. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/ipfs_client.py +0 -0
  17. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/oasf_validator.py +0 -0
  18. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/subgraph_client.py +0 -0
  19. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/transaction_handle.py +0 -0
  20. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/value_encoding.py +0 -0
  21. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/core/web3_client.py +0 -0
  22. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/taxonomies/all_domains.json +0 -0
  23. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk/taxonomies/all_skills.json +0 -0
  24. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk.egg-info/SOURCES.txt +0 -0
  25. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk.egg-info/dependency_links.txt +0 -0
  26. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk.egg-info/requires.txt +0 -0
  27. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/agent0_sdk.egg-info/top_level.txt +0 -0
  28. {agent0_sdk-1.5.0b1 → agent0_sdk-1.5.1b1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent0-sdk
3
- Version: 1.5.0b1
3
+ Version: 1.5.1b1
4
4
  Summary: Python SDK for agent portability, discovery and trust based on ERC-8004
5
5
  Author-email: Agent0 Team <team@ag0.xyz>
6
6
  License: MIT License
@@ -104,10 +104,10 @@ Agent0 SDK enables you to:
104
104
  pip install agent0-sdk
105
105
  ```
106
106
 
107
- To install the **1.5.0 pre-release** explicitly:
107
+ To install the **1.5.1 pre-release** explicitly:
108
108
 
109
109
  ```bash
110
- pip install --pre agent0-sdk==1.5.0b1
110
+ pip install --pre agent0-sdk==1.5.1b1
111
111
  ```
112
112
 
113
113
  ### Install from Source
@@ -212,10 +212,10 @@ results = sdk.searchAgents(
212
212
  "x402support": True,
213
213
  "feedback": {"minValue": 80, "tag": "enterprise", "includeRevoked": False},
214
214
  },
215
- options={"pageSize": 20, "sort": ["updatedAt:desc"]},
215
+ options={"sort": ["updatedAt:desc"]},
216
216
  )
217
217
 
218
- for agent in results['items']:
218
+ for agent in results:
219
219
  print(f"{agent.name}: {agent.description}")
220
220
  print(f" Tools: {agent.mcpTools}")
221
221
  print(f" Skills: {agent.a2aSkills}")
@@ -365,6 +365,151 @@ The SDK includes complete OASF v0.8.0 taxonomy files:
365
365
 
366
366
  Browse these files to find appropriate skill and domain slugs. For more information, see the [OASF specification](https://github.com/agntcy/oasf) and [Release Notes v0.31](RELEASE_NOTES_0.31.md).
367
367
 
368
+ ## Unified Search Reference (Exhaustive)
369
+
370
+ The unified search API is:
371
+
372
+ ```python
373
+ results = sdk.searchAgents(filters: dict | SearchFilters | None = None, options: dict | SearchOptions | None = None)
374
+ # results: list[AgentSummary]
375
+ ```
376
+
377
+ ### `FeedbackFilters` (used as `filters["feedback"]`)
378
+
379
+ ```python
380
+ @dataclass
381
+ class FeedbackFilters:
382
+ hasFeedback: Optional[bool] = None
383
+ hasNoFeedback: Optional[bool] = None
384
+ includeRevoked: Optional[bool] = None
385
+ minValue: Optional[float] = None
386
+ maxValue: Optional[float] = None
387
+ minCount: Optional[int] = None
388
+ maxCount: Optional[int] = None
389
+ fromReviewers: Optional[List[Address]] = None
390
+ endpoint: Optional[str] = None # substring match
391
+ hasResponse: Optional[bool] = None
392
+ tag1: Optional[str] = None
393
+ tag2: Optional[str] = None
394
+ tag: Optional[str] = None # matches tag1 OR tag2
395
+ ```
396
+
397
+ | Field | Semantics |
398
+ | --- | --- |
399
+ | `hasFeedback` / `hasNoFeedback` | Filter by whether the agent has any feedback |
400
+ | `includeRevoked` | Include revoked feedback entries in the pool used for filtering |
401
+ | `minValue` / `maxValue` | Threshold on **average value** over feedback matching the other feedback constraints (inclusive) |
402
+ | `minCount` / `maxCount` | Threshold on **count** over feedback matching the other feedback constraints (inclusive) |
403
+ | `fromReviewers` | Only consider feedback from these reviewer wallets |
404
+ | `endpoint` | Only consider feedback whose `endpoint` contains this substring |
405
+ | `hasResponse` | Only consider feedback that has at least one response (if supported) |
406
+ | `tag1` / `tag2` | Only consider feedback matching tag1/tag2 |
407
+ | `tag` | Shorthand: match either tag1 OR tag2 |
408
+
409
+ ### `SearchFilters`
410
+
411
+ ```python
412
+ DateLike = Union[datetime, str, int]
413
+
414
+ @dataclass
415
+ class SearchFilters:
416
+ chains: Optional[Union[List[ChainId], Literal["all"]]] = None
417
+ agentIds: Optional[List[AgentId]] = None
418
+
419
+ name: Optional[str] = None
420
+ description: Optional[str] = None
421
+
422
+ owners: Optional[List[Address]] = None
423
+ operators: Optional[List[Address]] = None
424
+
425
+ hasRegistrationFile: Optional[bool] = None
426
+ hasWeb: Optional[bool] = None
427
+ hasMCP: Optional[bool] = None
428
+ hasA2A: Optional[bool] = None
429
+ hasOASF: Optional[bool] = None
430
+ hasEndpoints: Optional[bool] = None
431
+
432
+ webContains: Optional[str] = None
433
+ mcpContains: Optional[str] = None
434
+ a2aContains: Optional[str] = None
435
+ ensContains: Optional[str] = None
436
+ didContains: Optional[str] = None
437
+
438
+ walletAddress: Optional[Address] = None
439
+
440
+ supportedTrust: Optional[List[str]] = None
441
+ a2aSkills: Optional[List[str]] = None
442
+ mcpTools: Optional[List[str]] = None
443
+ mcpPrompts: Optional[List[str]] = None
444
+ mcpResources: Optional[List[str]] = None
445
+ oasfSkills: Optional[List[str]] = None
446
+ oasfDomains: Optional[List[str]] = None
447
+
448
+ active: Optional[bool] = None
449
+ x402support: Optional[bool] = None
450
+
451
+ registeredAtFrom: Optional[DateLike] = None
452
+ registeredAtTo: Optional[DateLike] = None
453
+ updatedAtFrom: Optional[DateLike] = None
454
+ updatedAtTo: Optional[DateLike] = None
455
+
456
+ hasMetadataKey: Optional[str] = None
457
+ metadataValue: Optional[Dict[str, str]] = None # { key, value }
458
+
459
+ keyword: Optional[str] = None
460
+ feedback: Optional[FeedbackFilters] = None
461
+ ```
462
+
463
+ ### `SearchOptions`
464
+
465
+ ```python
466
+ @dataclass
467
+ class SearchOptions:
468
+ sort: Optional[List[str]] = None
469
+ semanticMinScore: Optional[float] = None
470
+ semanticTopK: Optional[int] = None
471
+ ```
472
+
473
+ ### `AgentSummary` (returned items)
474
+
475
+ ```python
476
+ @dataclass
477
+ class AgentSummary:
478
+ chainId: ChainId
479
+ agentId: AgentId
480
+ name: str
481
+ image: Optional[URI]
482
+ description: str
483
+ owners: List[Address]
484
+ operators: List[Address]
485
+ # Endpoint strings (present when advertised; not booleans)
486
+ mcp: Optional[str] = None
487
+ a2a: Optional[str] = None
488
+ web: Optional[str] = None
489
+ email: Optional[str] = None
490
+ ens: Optional[str] = None
491
+ did: Optional[str] = None
492
+ walletAddress: Optional[Address] = None
493
+ supportedTrusts: List[str] = field(default_factory=list)
494
+ a2aSkills: List[str] = field(default_factory=list)
495
+ mcpTools: List[str] = field(default_factory=list)
496
+ mcpPrompts: List[str] = field(default_factory=list)
497
+ mcpResources: List[str] = field(default_factory=list)
498
+ oasfSkills: List[str] = field(default_factory=list)
499
+ oasfDomains: List[str] = field(default_factory=list)
500
+ active: bool = False
501
+ x402support: bool = False
502
+ createdAt: Optional[int] = None
503
+ updatedAt: Optional[int] = None
504
+ lastActivity: Optional[int] = None
505
+ agentURI: Optional[str] = None
506
+ agentURIType: Optional[str] = None
507
+ feedbackCount: Optional[int] = None
508
+ averageValue: Optional[float] = None
509
+ semanticScore: Optional[float] = None
510
+ extras: Dict[str, Any] = field(default_factory=dict)
511
+ ```
512
+
368
513
  ## Use Cases
369
514
 
370
515
  - **Building agent marketplaces** - Create platforms where developers can discover, evaluate, and integrate agents based on their capabilities and reputation
@@ -374,10 +519,9 @@ Browse these files to find appropriate skill and domain slugs. For more informat
374
519
 
375
520
  ## 🚀 Coming Soon
376
521
 
377
- - More chains (currently Ethereum Sepolia + Ethereum Mainnet)
522
+ - More chains (currently Ethereum Mainnet + Ethereum Sepolia + Polygon Mainnet)
378
523
  - Support for validations
379
524
  - Enhanced x402 payments
380
- - Semantic/Vectorial search
381
525
  - Advanced reputation aggregation
382
526
  - Import/Export to centralized catalogues
383
527
 
@@ -34,10 +34,10 @@ Agent0 SDK enables you to:
34
34
  pip install agent0-sdk
35
35
  ```
36
36
 
37
- To install the **1.5.0 pre-release** explicitly:
37
+ To install the **1.5.1 pre-release** explicitly:
38
38
 
39
39
  ```bash
40
- pip install --pre agent0-sdk==1.5.0b1
40
+ pip install --pre agent0-sdk==1.5.1b1
41
41
  ```
42
42
 
43
43
  ### Install from Source
@@ -142,10 +142,10 @@ results = sdk.searchAgents(
142
142
  "x402support": True,
143
143
  "feedback": {"minValue": 80, "tag": "enterprise", "includeRevoked": False},
144
144
  },
145
- options={"pageSize": 20, "sort": ["updatedAt:desc"]},
145
+ options={"sort": ["updatedAt:desc"]},
146
146
  )
147
147
 
148
- for agent in results['items']:
148
+ for agent in results:
149
149
  print(f"{agent.name}: {agent.description}")
150
150
  print(f" Tools: {agent.mcpTools}")
151
151
  print(f" Skills: {agent.a2aSkills}")
@@ -295,6 +295,151 @@ The SDK includes complete OASF v0.8.0 taxonomy files:
295
295
 
296
296
  Browse these files to find appropriate skill and domain slugs. For more information, see the [OASF specification](https://github.com/agntcy/oasf) and [Release Notes v0.31](RELEASE_NOTES_0.31.md).
297
297
 
298
+ ## Unified Search Reference (Exhaustive)
299
+
300
+ The unified search API is:
301
+
302
+ ```python
303
+ results = sdk.searchAgents(filters: dict | SearchFilters | None = None, options: dict | SearchOptions | None = None)
304
+ # results: list[AgentSummary]
305
+ ```
306
+
307
+ ### `FeedbackFilters` (used as `filters["feedback"]`)
308
+
309
+ ```python
310
+ @dataclass
311
+ class FeedbackFilters:
312
+ hasFeedback: Optional[bool] = None
313
+ hasNoFeedback: Optional[bool] = None
314
+ includeRevoked: Optional[bool] = None
315
+ minValue: Optional[float] = None
316
+ maxValue: Optional[float] = None
317
+ minCount: Optional[int] = None
318
+ maxCount: Optional[int] = None
319
+ fromReviewers: Optional[List[Address]] = None
320
+ endpoint: Optional[str] = None # substring match
321
+ hasResponse: Optional[bool] = None
322
+ tag1: Optional[str] = None
323
+ tag2: Optional[str] = None
324
+ tag: Optional[str] = None # matches tag1 OR tag2
325
+ ```
326
+
327
+ | Field | Semantics |
328
+ | --- | --- |
329
+ | `hasFeedback` / `hasNoFeedback` | Filter by whether the agent has any feedback |
330
+ | `includeRevoked` | Include revoked feedback entries in the pool used for filtering |
331
+ | `minValue` / `maxValue` | Threshold on **average value** over feedback matching the other feedback constraints (inclusive) |
332
+ | `minCount` / `maxCount` | Threshold on **count** over feedback matching the other feedback constraints (inclusive) |
333
+ | `fromReviewers` | Only consider feedback from these reviewer wallets |
334
+ | `endpoint` | Only consider feedback whose `endpoint` contains this substring |
335
+ | `hasResponse` | Only consider feedback that has at least one response (if supported) |
336
+ | `tag1` / `tag2` | Only consider feedback matching tag1/tag2 |
337
+ | `tag` | Shorthand: match either tag1 OR tag2 |
338
+
339
+ ### `SearchFilters`
340
+
341
+ ```python
342
+ DateLike = Union[datetime, str, int]
343
+
344
+ @dataclass
345
+ class SearchFilters:
346
+ chains: Optional[Union[List[ChainId], Literal["all"]]] = None
347
+ agentIds: Optional[List[AgentId]] = None
348
+
349
+ name: Optional[str] = None
350
+ description: Optional[str] = None
351
+
352
+ owners: Optional[List[Address]] = None
353
+ operators: Optional[List[Address]] = None
354
+
355
+ hasRegistrationFile: Optional[bool] = None
356
+ hasWeb: Optional[bool] = None
357
+ hasMCP: Optional[bool] = None
358
+ hasA2A: Optional[bool] = None
359
+ hasOASF: Optional[bool] = None
360
+ hasEndpoints: Optional[bool] = None
361
+
362
+ webContains: Optional[str] = None
363
+ mcpContains: Optional[str] = None
364
+ a2aContains: Optional[str] = None
365
+ ensContains: Optional[str] = None
366
+ didContains: Optional[str] = None
367
+
368
+ walletAddress: Optional[Address] = None
369
+
370
+ supportedTrust: Optional[List[str]] = None
371
+ a2aSkills: Optional[List[str]] = None
372
+ mcpTools: Optional[List[str]] = None
373
+ mcpPrompts: Optional[List[str]] = None
374
+ mcpResources: Optional[List[str]] = None
375
+ oasfSkills: Optional[List[str]] = None
376
+ oasfDomains: Optional[List[str]] = None
377
+
378
+ active: Optional[bool] = None
379
+ x402support: Optional[bool] = None
380
+
381
+ registeredAtFrom: Optional[DateLike] = None
382
+ registeredAtTo: Optional[DateLike] = None
383
+ updatedAtFrom: Optional[DateLike] = None
384
+ updatedAtTo: Optional[DateLike] = None
385
+
386
+ hasMetadataKey: Optional[str] = None
387
+ metadataValue: Optional[Dict[str, str]] = None # { key, value }
388
+
389
+ keyword: Optional[str] = None
390
+ feedback: Optional[FeedbackFilters] = None
391
+ ```
392
+
393
+ ### `SearchOptions`
394
+
395
+ ```python
396
+ @dataclass
397
+ class SearchOptions:
398
+ sort: Optional[List[str]] = None
399
+ semanticMinScore: Optional[float] = None
400
+ semanticTopK: Optional[int] = None
401
+ ```
402
+
403
+ ### `AgentSummary` (returned items)
404
+
405
+ ```python
406
+ @dataclass
407
+ class AgentSummary:
408
+ chainId: ChainId
409
+ agentId: AgentId
410
+ name: str
411
+ image: Optional[URI]
412
+ description: str
413
+ owners: List[Address]
414
+ operators: List[Address]
415
+ # Endpoint strings (present when advertised; not booleans)
416
+ mcp: Optional[str] = None
417
+ a2a: Optional[str] = None
418
+ web: Optional[str] = None
419
+ email: Optional[str] = None
420
+ ens: Optional[str] = None
421
+ did: Optional[str] = None
422
+ walletAddress: Optional[Address] = None
423
+ supportedTrusts: List[str] = field(default_factory=list)
424
+ a2aSkills: List[str] = field(default_factory=list)
425
+ mcpTools: List[str] = field(default_factory=list)
426
+ mcpPrompts: List[str] = field(default_factory=list)
427
+ mcpResources: List[str] = field(default_factory=list)
428
+ oasfSkills: List[str] = field(default_factory=list)
429
+ oasfDomains: List[str] = field(default_factory=list)
430
+ active: bool = False
431
+ x402support: bool = False
432
+ createdAt: Optional[int] = None
433
+ updatedAt: Optional[int] = None
434
+ lastActivity: Optional[int] = None
435
+ agentURI: Optional[str] = None
436
+ agentURIType: Optional[str] = None
437
+ feedbackCount: Optional[int] = None
438
+ averageValue: Optional[float] = None
439
+ semanticScore: Optional[float] = None
440
+ extras: Dict[str, Any] = field(default_factory=dict)
441
+ ```
442
+
298
443
  ## Use Cases
299
444
 
300
445
  - **Building agent marketplaces** - Create platforms where developers can discover, evaluate, and integrate agents based on their capabilities and reputation
@@ -304,10 +449,9 @@ Browse these files to find appropriate skill and domain slugs. For more informat
304
449
 
305
450
  ## 🚀 Coming Soon
306
451
 
307
- - More chains (currently Ethereum Sepolia + Ethereum Mainnet)
452
+ - More chains (currently Ethereum Mainnet + Ethereum Sepolia + Polygon Mainnet)
308
453
  - Support for validations
309
454
  - Enhanced x402 payments
310
- - Semantic/Vectorial search
311
455
  - Advanced reputation aggregation
312
456
  - Import/Export to centralized catalogues
313
457
 
@@ -35,7 +35,7 @@ except ImportError:
35
35
  TransactionMined = None
36
36
  _sdk_available = False
37
37
 
38
- __version__ = "1.5.0b1"
38
+ __version__ = "1.5.1b1"
39
39
  __all__ = [
40
40
  "SDK",
41
41
  "Agent",
@@ -422,8 +422,6 @@ class FeedbackManager:
422
422
  minValue: Optional[float] = None,
423
423
  maxValue: Optional[float] = None,
424
424
  include_revoked: bool = False,
425
- first: int = 100,
426
- skip: int = 0,
427
425
  agents: Optional[List[AgentId]] = None,
428
426
  ) -> List[Feedback]:
429
427
  """Search feedback.
@@ -451,8 +449,6 @@ class FeedbackManager:
451
449
  minValue,
452
450
  maxValue,
453
451
  include_revoked,
454
- first,
455
- skip,
456
452
  agents=agents,
457
453
  )
458
454
 
@@ -469,8 +465,6 @@ class FeedbackManager:
469
465
  minValue,
470
466
  maxValue,
471
467
  include_revoked,
472
- first,
473
- skip,
474
468
  agents=agents,
475
469
  )
476
470
 
@@ -558,8 +552,6 @@ class FeedbackManager:
558
552
  minValue: Optional[float],
559
553
  maxValue: Optional[float],
560
554
  include_revoked: bool,
561
- first: int,
562
- skip: int,
563
555
  agents: Optional[List[AgentId]] = None,
564
556
  ) -> List[Feedback]:
565
557
  """Search feedback using subgraph."""
@@ -583,79 +575,84 @@ class FeedbackManager:
583
575
  includeRevoked=include_revoked
584
576
  )
585
577
 
586
- # Query subgraph
587
- feedbacks_data = self.subgraph_client.search_feedback(
588
- params=params,
589
- first=first,
590
- skip=skip,
591
- order_by="createdAt",
592
- order_direction="desc"
593
- )
594
-
595
- # Map to Feedback objects
596
578
  feedbacks = []
597
- for fb_data in feedbacks_data:
598
- feedback_file = fb_data.get('feedbackFile') or {}
599
- if not isinstance(feedback_file, dict):
600
- feedback_file = {}
601
-
602
- # Map responses
603
- responses_data = fb_data.get('responses', [])
604
- answers = []
605
- for resp in responses_data:
606
- answers.append({
607
- 'responder': resp.get('responder'),
608
- 'responseUri': resp.get('responseUri'),
609
- 'responseHash': resp.get('responseHash'),
610
- 'createdAt': resp.get('createdAt')
611
- })
612
-
613
- # Map tags: rely on whatever the subgraph returns (may be legacy bytes/hash-like values)
614
- tags_list: List[str] = []
615
- tag1 = fb_data.get('tag1') or feedback_file.get('tag1')
616
- tag2 = fb_data.get('tag2') or feedback_file.get('tag2')
617
- if isinstance(tag1, str) and tag1:
579
+ batch = 1000
580
+ skip = 0
581
+ while True:
582
+ feedbacks_data = self.subgraph_client.search_feedback(
583
+ params=params,
584
+ first=batch,
585
+ skip=skip,
586
+ order_by="createdAt",
587
+ order_direction="desc",
588
+ )
589
+
590
+ for fb_data in feedbacks_data:
591
+ feedback_file = fb_data.get('feedbackFile') or {}
592
+ if not isinstance(feedback_file, dict):
593
+ feedback_file = {}
594
+
595
+ # Map responses
596
+ responses_data = fb_data.get('responses', [])
597
+ answers = []
598
+ for resp in responses_data:
599
+ answers.append({
600
+ 'responder': resp.get('responder'),
601
+ 'responseUri': resp.get('responseUri'),
602
+ 'responseHash': resp.get('responseHash'),
603
+ 'createdAt': resp.get('createdAt')
604
+ })
605
+
606
+ # Map tags: rely on whatever the subgraph returns (may be legacy bytes/hash-like values)
607
+ tags_list: List[str] = []
608
+ tag1 = fb_data.get('tag1') or feedback_file.get('tag1')
609
+ tag2 = fb_data.get('tag2') or feedback_file.get('tag2')
610
+ if isinstance(tag1, str) and tag1:
618
611
  tags_list.append(tag1)
619
- if isinstance(tag2, str) and tag2:
612
+ if isinstance(tag2, str) and tag2:
620
613
  tags_list.append(tag2)
621
-
622
- # Parse agentId from feedback ID
623
- feedback_id = fb_data['id']
624
- parts = feedback_id.split(':')
625
- if len(parts) >= 2:
626
- agent_id_str = f"{parts[0]}:{parts[1]}"
627
- client_addr = parts[2] if len(parts) > 2 else ""
628
- feedback_idx = int(parts[3]) if len(parts) > 3 else 1
629
- else:
630
- agent_id_str = feedback_id
631
- client_addr = ""
632
- feedback_idx = 1
633
-
634
- feedback = Feedback(
635
- id=Feedback.create_id(agent_id_str, client_addr, feedback_idx),
636
- agentId=agent_id_str,
637
- reviewer=client_addr,
638
- value=float(fb_data.get("value")) if fb_data.get("value") is not None else None,
639
- tags=tags_list,
640
- text=feedback_file.get('text'),
641
- capability=feedback_file.get('capability'),
642
- context=feedback_file.get('context'),
643
- proofOfPayment={
644
- 'fromAddress': feedback_file.get('proofOfPaymentFromAddress'),
645
- 'toAddress': feedback_file.get('proofOfPaymentToAddress'),
646
- 'chainId': feedback_file.get('proofOfPaymentChainId'),
647
- 'txHash': feedback_file.get('proofOfPaymentTxHash'),
648
- } if feedback_file.get('proofOfPaymentFromAddress') else None,
649
- fileURI=fb_data.get('feedbackURI') or fb_data.get('feedbackUri'), # Handle both old and new field names
650
- endpoint=fb_data.get('endpoint'),
651
- createdAt=fb_data.get('createdAt', int(time.time())),
652
- answers=answers,
653
- isRevoked=fb_data.get('isRevoked', False),
654
- name=feedback_file.get('name'),
655
- skill=feedback_file.get('skill'),
656
- task=feedback_file.get('task'),
657
- )
658
- feedbacks.append(feedback)
614
+
615
+ # Parse agentId from feedback ID
616
+ feedback_id = fb_data['id']
617
+ parts = feedback_id.split(':')
618
+ if len(parts) >= 2:
619
+ agent_id_str = f"{parts[0]}:{parts[1]}"
620
+ client_addr = parts[2] if len(parts) > 2 else ""
621
+ feedback_idx = int(parts[3]) if len(parts) > 3 else 1
622
+ else:
623
+ agent_id_str = feedback_id
624
+ client_addr = ""
625
+ feedback_idx = 1
626
+
627
+ feedback = Feedback(
628
+ id=Feedback.create_id(agent_id_str, client_addr, feedback_idx),
629
+ agentId=agent_id_str,
630
+ reviewer=client_addr,
631
+ value=float(fb_data.get("value")) if fb_data.get("value") is not None else None,
632
+ tags=tags_list,
633
+ text=feedback_file.get('text'),
634
+ capability=feedback_file.get('capability'),
635
+ context=feedback_file.get('context'),
636
+ proofOfPayment={
637
+ 'fromAddress': feedback_file.get('proofOfPaymentFromAddress'),
638
+ 'toAddress': feedback_file.get('proofOfPaymentToAddress'),
639
+ 'chainId': feedback_file.get('proofOfPaymentChainId'),
640
+ 'txHash': feedback_file.get('proofOfPaymentTxHash'),
641
+ } if feedback_file.get('proofOfPaymentFromAddress') else None,
642
+ fileURI=fb_data.get('feedbackURI') or fb_data.get('feedbackUri'), # Handle both old and new field names
643
+ endpoint=fb_data.get('endpoint'),
644
+ createdAt=fb_data.get('createdAt', int(time.time())),
645
+ answers=answers,
646
+ isRevoked=fb_data.get('isRevoked', False),
647
+ name=feedback_file.get('name'),
648
+ skill=feedback_file.get('skill'),
649
+ task=feedback_file.get('task'),
650
+ )
651
+ feedbacks.append(feedback)
652
+
653
+ if len(feedbacks_data) < batch:
654
+ break
655
+ skip += batch
659
656
 
660
657
  return feedbacks
661
658