agent0-sdk 0.2.2__py3-none-any.whl → 0.5__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 +303 -29
- agent0_sdk/core/contracts.py +93 -51
- agent0_sdk/core/feedback_manager.py +191 -162
- agent0_sdk/core/indexer.py +787 -37
- agent0_sdk/core/models.py +10 -21
- agent0_sdk/core/oasf_validator.py +98 -0
- agent0_sdk/core/sdk.py +238 -20
- agent0_sdk/core/subgraph_client.py +56 -17
- agent0_sdk/core/web3_client.py +184 -17
- agent0_sdk/taxonomies/all_domains.json +1565 -0
- agent0_sdk/taxonomies/all_skills.json +1030 -0
- {agent0_sdk-0.2.2.dist-info → agent0_sdk-0.5.dist-info}/METADATA +78 -6
- agent0_sdk-0.5.dist-info/RECORD +19 -0
- {agent0_sdk-0.2.2.dist-info → agent0_sdk-0.5.dist-info}/top_level.txt +0 -1
- agent0_sdk-0.2.2.dist-info/RECORD +0 -27
- tests/__init__.py +0 -1
- tests/config.py +0 -46
- tests/conftest.py +0 -22
- tests/test_feedback.py +0 -417
- tests/test_models.py +0 -224
- 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.2.2.dist-info → agent0_sdk-0.5.dist-info}/WHEEL +0 -0
- {agent0_sdk-0.2.2.dist-info → agent0_sdk-0.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent0-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5
|
|
4
4
|
Summary: Python SDK for agent portability, discovery and trust based on ERC-8004
|
|
5
5
|
Author-email: Marco De Rossi <marco.derossi@consensys.net>
|
|
6
6
|
License: MIT License
|
|
@@ -76,10 +76,11 @@ Agent0 is the SDK for agentic economies. It enables agents to register, advertis
|
|
|
76
76
|
|
|
77
77
|
## What Does Agent0 SDK Do?
|
|
78
78
|
|
|
79
|
-
Agent0 SDK v0.
|
|
79
|
+
Agent0 SDK v0.31 enables you to:
|
|
80
80
|
|
|
81
81
|
- **Create and manage agent identities** - Register your AI agent on-chain with a unique identity, configure presentation fields (name, description, image), set wallet addresses, and manage trust models with x402 support
|
|
82
82
|
- **Advertise agent capabilities** - Publish MCP and A2A endpoints, with automated extraction of MCP tools and A2A skills from endpoints
|
|
83
|
+
- **OASF taxonomies** - Advertise standardized skills and domains using the Open Agentic Schema Framework (OASF) taxonomies for better discovery and interoperability
|
|
83
84
|
- **Enable permissionless discovery** - Make your agent discoverable by other agents and platforms using rich search by attributes, capabilities, skills, tools, tasks, and x402 support
|
|
84
85
|
- **Build reputation** - Give and receive feedback, retrieve feedback history, and search agents by reputation with cryptographic authentication
|
|
85
86
|
- **Cross-chain registration** - One-line registration with IPFS nodes, Pinata, Filecoin, or HTTP URIs
|
|
@@ -87,7 +88,7 @@ Agent0 SDK v0.2 enables you to:
|
|
|
87
88
|
|
|
88
89
|
## ⚠️ Alpha Release
|
|
89
90
|
|
|
90
|
-
Agent0 SDK v0.
|
|
91
|
+
Agent0 SDK v0.31 is in **alpha** with bugs and is not production ready. We're actively testing and improving it.
|
|
91
92
|
|
|
92
93
|
**Bug reports & feedback:** GitHub: [Report issues](https://github.com/agent0lab/agent0-py/issues) | Telegram: [@marcoderossi](https://t.me/marcoderossi) | Email: marco.derossi@consensys.net
|
|
93
94
|
|
|
@@ -149,8 +150,17 @@ agent.setMCP("https://mcp.example.com/") # Extracts tools, prompts, resources
|
|
|
149
150
|
agent.setA2A("https://a2a.example.com/agent-card.json") # Extracts skills
|
|
150
151
|
agent.setENS("myagent.eth")
|
|
151
152
|
|
|
153
|
+
# Add OASF skills and domains (standardized taxonomies)
|
|
154
|
+
agent.addSkill("data_engineering/data_transformation_pipeline", validate_oasf=True)
|
|
155
|
+
agent.addSkill("natural_language_processing/natural_language_generation/summarization", validate_oasf=True)
|
|
156
|
+
agent.addDomain("finance_and_business/investment_services", validate_oasf=True)
|
|
157
|
+
agent.addDomain("technology/data_science/data_science", validate_oasf=True)
|
|
158
|
+
|
|
152
159
|
# Configure wallet and trust
|
|
153
|
-
|
|
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.
|
|
154
164
|
agent.setTrust(reputation=True, cryptoEconomic=True)
|
|
155
165
|
|
|
156
166
|
# Add metadata and set status
|
|
@@ -161,6 +171,16 @@ agent.setActive(True)
|
|
|
161
171
|
agent.registerIPFS()
|
|
162
172
|
print(f"Agent registered: {agent.agentId}") # e.g., "11155111:123"
|
|
163
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
|
+
)
|
|
164
184
|
```
|
|
165
185
|
|
|
166
186
|
### 3. Load and Edit Agent
|
|
@@ -206,7 +226,8 @@ agent_summary = sdk.getAgent("11155111:123")
|
|
|
206
226
|
feedback_file = sdk.prepareFeedback(
|
|
207
227
|
agentId="11155111:123",
|
|
208
228
|
score=85, # 0-100 (mandatory)
|
|
209
|
-
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
|
|
210
231
|
capability="tools", # Optional: MCP capability
|
|
211
232
|
name="code_generation", # Optional: MCP tool name
|
|
212
233
|
skill="python" # Optional: A2A skill
|
|
@@ -263,6 +284,58 @@ sdk = SDK(chainId=11155111, rpcUrl="...", signer=private_key)
|
|
|
263
284
|
agent.register("https://example.com/agent-registration.json")
|
|
264
285
|
```
|
|
265
286
|
|
|
287
|
+
## OASF Taxonomies
|
|
288
|
+
|
|
289
|
+
The SDK includes support for the **Open Agentic Schema Framework (OASF)** taxonomies, enabling agents to advertise standardized skills and domains. This improves discoverability and interoperability across agent platforms.
|
|
290
|
+
|
|
291
|
+
### Adding Skills and Domains
|
|
292
|
+
|
|
293
|
+
```python
|
|
294
|
+
# Add OASF skills (with optional validation)
|
|
295
|
+
agent.addSkill("advanced_reasoning_planning/strategic_planning", validate_oasf=True)
|
|
296
|
+
agent.addSkill("data_engineering/data_transformation_pipeline", validate_oasf=True)
|
|
297
|
+
|
|
298
|
+
# Add OASF domains (with optional validation)
|
|
299
|
+
agent.addDomain("finance_and_business/investment_services", validate_oasf=True)
|
|
300
|
+
agent.addDomain("technology/data_science/data_visualization", validate_oasf=True)
|
|
301
|
+
|
|
302
|
+
# Remove skills/domains
|
|
303
|
+
agent.removeSkill("old_skill")
|
|
304
|
+
agent.removeDomain("old_domain")
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### OASF in Registration Files
|
|
308
|
+
|
|
309
|
+
OASF skills and domains appear in your agent's registration file:
|
|
310
|
+
|
|
311
|
+
```json
|
|
312
|
+
{
|
|
313
|
+
"endpoints": [
|
|
314
|
+
{
|
|
315
|
+
"name": "OASF",
|
|
316
|
+
"endpoint": "https://github.com/agntcy/oasf/",
|
|
317
|
+
"version": "v0.8.0",
|
|
318
|
+
"skills": [
|
|
319
|
+
"advanced_reasoning_planning/strategic_planning",
|
|
320
|
+
"data_engineering/data_transformation_pipeline"
|
|
321
|
+
],
|
|
322
|
+
"domains": [
|
|
323
|
+
"finance_and_business/investment_services",
|
|
324
|
+
"technology/data_science/data_science"
|
|
325
|
+
]
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Taxonomy Files
|
|
332
|
+
|
|
333
|
+
The SDK includes complete OASF v0.8.0 taxonomy files:
|
|
334
|
+
- **Skills**: `agent0_sdk/taxonomies/all_skills.json` (136 skills)
|
|
335
|
+
- **Domains**: `agent0_sdk/taxonomies/all_domains.json` (204 domains)
|
|
336
|
+
|
|
337
|
+
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).
|
|
338
|
+
|
|
266
339
|
## Use Cases
|
|
267
340
|
|
|
268
341
|
- **Building agent marketplaces** - Create platforms where developers can discover, evaluate, and integrate agents based on their capabilities and reputation
|
|
@@ -274,7 +347,6 @@ agent.register("https://example.com/agent-registration.json")
|
|
|
274
347
|
|
|
275
348
|
- More chains (currently Ethereum Sepolia only)
|
|
276
349
|
- Support for validations
|
|
277
|
-
- Multi-chain agents search
|
|
278
350
|
- Enhanced x402 payments
|
|
279
351
|
- Semantic/Vectorial search
|
|
280
352
|
- Advanced reputation aggregation
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
agent0_sdk/__init__.py,sha256=2Wl6gXZTVEdQxueLl7YHpYyAm-uh-DaELEVdRson8yg,917
|
|
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-0.5.dist-info/licenses/LICENSE,sha256=rhZZbZm_Ovz4Oa9LNQ-ms8a1tA36wWh90ZkC0OR7WMw,1072
|
|
16
|
+
agent0_sdk-0.5.dist-info/METADATA,sha256=W49qOkBpho8iDVe2OIcZ3Nr8IWT4QzuhTMqpp6OuvKY,14567
|
|
17
|
+
agent0_sdk-0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
agent0_sdk-0.5.dist-info/top_level.txt,sha256=p4520WUKNfhU0lVWJgkrB_jdeUfvHSY3K18k4oYLNfI,11
|
|
19
|
+
agent0_sdk-0.5.dist-info/RECORD,,
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
agent0_sdk/__init__.py,sha256=SOJFcN81--KPhBSpHV0T85Q7Z1qZxxjLedZDCSbfkm0,919
|
|
2
|
-
agent0_sdk/core/agent.py,sha256=RQND8F3Hmmpnt-PqCXBP7T8KfrjTXo0_X2nZoMJh01w,33343
|
|
3
|
-
agent0_sdk/core/contracts.py,sha256=mER1pSae4-fiGBwaVTi7oqJ_QYaDnrtyIF7g6dkdE-0,19889
|
|
4
|
-
agent0_sdk/core/endpoint_crawler.py,sha256=QBkFc3tBSQqHj6PtSTZ5D3_HVB00KJZJdxE3uYpI9po,13611
|
|
5
|
-
agent0_sdk/core/feedback_manager.py,sha256=wkV-P5wgDwOLwhaf22qOl-3S2F_PNI7oCHBBZMAxugI,36017
|
|
6
|
-
agent0_sdk/core/indexer.py,sha256=bl5TqTSytt_qQyGqUsqZvrnXFlmQEuOgm6uKEE-u-BQ,41161
|
|
7
|
-
agent0_sdk/core/ipfs_client.py,sha256=fml1ai1BdBkgb95xAkf-ft8QsahV1HL30hBYRz7rQwI,13929
|
|
8
|
-
agent0_sdk/core/models.py,sha256=bQARVmKETiVL5uJpHxXGN2wY2GFrdzxhPHqjzL8TBlQ,12373
|
|
9
|
-
agent0_sdk/core/sdk.py,sha256=stOurnsSoedUFKaHKhrjaM1sghBzjighizV6AaZD7-U,31560
|
|
10
|
-
agent0_sdk/core/subgraph_client.py,sha256=XXFVFAoHcgEfqxc2w2OksSp8vtbKMtsNIuNbNcNQOzE,27280
|
|
11
|
-
agent0_sdk/core/web3_client.py,sha256=859ntu5dAmNlcJ3YM1w_VV2gI3mpCC9QEr-GN1236zU,6850
|
|
12
|
-
agent0_sdk-0.2.2.dist-info/licenses/LICENSE,sha256=rhZZbZm_Ovz4Oa9LNQ-ms8a1tA36wWh90ZkC0OR7WMw,1072
|
|
13
|
-
tests/__init__.py,sha256=60ffheccPhuMCtwiiKP1X-CJJXKpxJ_Ywa0aXGHR9bY,23
|
|
14
|
-
tests/config.py,sha256=1uePvkLBNubOQsvYkQSno0m007PMD1VACgm33fCYY6s,1429
|
|
15
|
-
tests/conftest.py,sha256=P-HCtVVYwSvscuaJqhrgZcv39XXNnr932ekEamzIqis,589
|
|
16
|
-
tests/test_feedback.py,sha256=7lszWYSmseJE0I4BhKzZdBiIzf2bgpPqZTZvhRrCTjY,14638
|
|
17
|
-
tests/test_models.py,sha256=kCZdoPasIIcOjEw7ToPldqARdbGVK8v8byOhFwVo7OI,7115
|
|
18
|
-
tests/test_real_public_servers.py,sha256=pCo4aLSCG9qv4D6T7jbyVmP1gt3r1jWxdes6z5XSNhU,3433
|
|
19
|
-
tests/test_registration.py,sha256=pYanDPLAFETIfabBUvO34ZDmyD0Rbcv8vecSfgSrQ70,9542
|
|
20
|
-
tests/test_registrationIpfs.py,sha256=9O3IBiN2CVMKzB19bqb-jN-nhqsN22kQINMpe9THqiI,8400
|
|
21
|
-
tests/test_sdk.py,sha256=dALLFm_A6aXx0ec-CNOLGQoadaSPZ08EEeCS6Tgnm0M,9362
|
|
22
|
-
tests/test_search.py,sha256=SiUio8H-M6Za8OXD_h9tUZdln0ayzkPJ3doTrkHv-Fs,18382
|
|
23
|
-
tests/test_transfer.py,sha256=zRBllpoMs6NhagAmaZWmD4ckbYjSvsSUerBK4oS-HlA,9258
|
|
24
|
-
agent0_sdk-0.2.2.dist-info/METADATA,sha256=mw1WxxAWC29tQ-FhDD_c7NJWPwyEGSYJ_w6atYM8CtY,11404
|
|
25
|
-
agent0_sdk-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
-
agent0_sdk-0.2.2.dist-info/top_level.txt,sha256=rgGBfOJlLi1zInQ85jBL2MpDu_ZJNbPjIGz-3Vn5rZs,17
|
|
27
|
-
agent0_sdk-0.2.2.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
|
-
|
tests/test_feedback.py
DELETED
|
@@ -1,417 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Test for Agent Feedback Flow with IPFS Pin
|
|
3
|
-
Submits feedback from a client to an existing agent and verifies data integrity.
|
|
4
|
-
|
|
5
|
-
Flow:
|
|
6
|
-
1. Load existing agent by ID
|
|
7
|
-
2. Client submits multiple feedback entries
|
|
8
|
-
3. Verify feedback data consistency (score, tags, capability, skill)
|
|
9
|
-
4. Wait for blockchain finalization
|
|
10
|
-
5. Verify feedback can be retrieved (if SDK supports it)
|
|
11
|
-
|
|
12
|
-
Usage:
|
|
13
|
-
Update AGENT_ID constant below to point to your existing agent
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
import logging
|
|
17
|
-
import time
|
|
18
|
-
import random
|
|
19
|
-
import sys
|
|
20
|
-
|
|
21
|
-
# Configure logging: root logger at WARNING to suppress noisy dependencies
|
|
22
|
-
logging.basicConfig(
|
|
23
|
-
level=logging.WARNING,
|
|
24
|
-
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
25
|
-
datefmt='%Y-%m-%d %H:%M:%S',
|
|
26
|
-
handlers=[logging.StreamHandler(sys.stdout)]
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
# Set debug level ONLY for agent0_sdk
|
|
30
|
-
logging.getLogger('agent0_sdk').setLevel(logging.DEBUG)
|
|
31
|
-
logging.getLogger('agent0_sdk.core').setLevel(logging.DEBUG)
|
|
32
|
-
|
|
33
|
-
from agent0_sdk import SDK
|
|
34
|
-
from config import CHAIN_ID, RPC_URL, AGENT_PRIVATE_KEY, PINATA_JWT, SUBGRAPH_URL, AGENT_ID, print_config
|
|
35
|
-
|
|
36
|
-
# Client configuration (different wallet)
|
|
37
|
-
CLIENT_PRIVATE_KEY = "f8d368064ccf80769e348a59155f69ec224849bd507a8c26dd85beefa777331a"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def generateFeedbackData(index: int):
|
|
41
|
-
"""Generate random feedback data."""
|
|
42
|
-
scores = [50, 75, 80, 85, 90, 95]
|
|
43
|
-
tags_sets = [
|
|
44
|
-
["data_analysis", "enterprise"],
|
|
45
|
-
["code_generation", "enterprise"],
|
|
46
|
-
["natural_language_understanding", "enterprise"],
|
|
47
|
-
["problem_solving", "enterprise"],
|
|
48
|
-
["communication", "enterprise"],
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
capabilities = [
|
|
52
|
-
"tools",
|
|
53
|
-
"tools",
|
|
54
|
-
"tools",
|
|
55
|
-
"tools",
|
|
56
|
-
"tools"
|
|
57
|
-
]
|
|
58
|
-
|
|
59
|
-
capabilities = [
|
|
60
|
-
"data_analysis",
|
|
61
|
-
"code_generation",
|
|
62
|
-
"natural_language_understanding",
|
|
63
|
-
"problem_solving",
|
|
64
|
-
"communication"
|
|
65
|
-
]
|
|
66
|
-
|
|
67
|
-
skills = [
|
|
68
|
-
"python",
|
|
69
|
-
"javascript",
|
|
70
|
-
"machine_learning",
|
|
71
|
-
"web_development",
|
|
72
|
-
"cloud_computing"
|
|
73
|
-
]
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
'score': random.choice(scores),
|
|
77
|
-
'tags': random.choice(tags_sets),
|
|
78
|
-
'capability': random.choice(capabilities),
|
|
79
|
-
'skill': random.choice(skills),
|
|
80
|
-
'context': 'enterprise'
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def main():
|
|
85
|
-
print("🧪 Testing Agent Feedback Flow with IPFS Pin")
|
|
86
|
-
print_config()
|
|
87
|
-
print("=" * 60)
|
|
88
|
-
|
|
89
|
-
# SDK Configuration
|
|
90
|
-
sdkConfig_pinata = {
|
|
91
|
-
'chainId': CHAIN_ID,
|
|
92
|
-
'rpcUrl': RPC_URL,
|
|
93
|
-
'ipfs': 'pinata',
|
|
94
|
-
'pinataJwt': PINATA_JWT
|
|
95
|
-
# Subgraph URL auto-defaults from DEFAULT_SUBGRAPH_URLS
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
# Step 1: Load existing agent
|
|
99
|
-
print("\n📍 Step 1: Load Existing Agent")
|
|
100
|
-
print("-" * 60)
|
|
101
|
-
print(f"Loading agent: {AGENT_ID}")
|
|
102
|
-
|
|
103
|
-
agentSdk = SDK(**sdkConfig_pinata) # Read-only for loading
|
|
104
|
-
|
|
105
|
-
try:
|
|
106
|
-
agent = agentSdk.loadAgent(AGENT_ID)
|
|
107
|
-
print(f"✅ Agent loaded: {agent.name}")
|
|
108
|
-
print(f" Description: {agent.description[:50]}...")
|
|
109
|
-
print(f" MCP Endpoint: {agent.mcpEndpoint}")
|
|
110
|
-
print(f" A2A Endpoint: {agent.a2aEndpoint}")
|
|
111
|
-
print(f" ENS Endpoint: {agent.ensEndpoint}")
|
|
112
|
-
except Exception as e:
|
|
113
|
-
print(f"❌ Failed to load agent: {e}")
|
|
114
|
-
import traceback
|
|
115
|
-
traceback.print_exc()
|
|
116
|
-
exit(1)
|
|
117
|
-
|
|
118
|
-
# Step 2: Agent (server) signs feedback auth for client
|
|
119
|
-
print("\n📍 Step 2: Agent (Server) Signs Feedback Auth")
|
|
120
|
-
print("-" * 60)
|
|
121
|
-
|
|
122
|
-
clientSdk = SDK(signer=CLIENT_PRIVATE_KEY, **sdkConfig_pinata)
|
|
123
|
-
clientAddress = clientSdk.web3_client.account.address
|
|
124
|
-
print(f"Client address: {clientAddress}")
|
|
125
|
-
|
|
126
|
-
# Agent SDK needs to be initialized with signer for signing feedback auth
|
|
127
|
-
agentSdkWithSigner = SDK(signer=AGENT_PRIVATE_KEY, **sdkConfig_pinata)
|
|
128
|
-
|
|
129
|
-
# Sign feedback authorization
|
|
130
|
-
print("Signing feedback authorization...")
|
|
131
|
-
feedbackAuth = agentSdkWithSigner.signFeedbackAuth(
|
|
132
|
-
agentId=AGENT_ID,
|
|
133
|
-
clientAddress=clientAddress,
|
|
134
|
-
expiryHours=24
|
|
135
|
-
)
|
|
136
|
-
print(f"✅ Feedback auth signed: {len(feedbackAuth)} bytes")
|
|
137
|
-
|
|
138
|
-
# Step 3: Client submits feedback
|
|
139
|
-
print("\n📍 Step 3: Client Submits Feedback")
|
|
140
|
-
print("-" * 60)
|
|
141
|
-
|
|
142
|
-
feedbackEntries = []
|
|
143
|
-
numFeedback = 1
|
|
144
|
-
|
|
145
|
-
for i in range(numFeedback):
|
|
146
|
-
print(f"\n Submitting Feedback #{i+1}:")
|
|
147
|
-
feedbackData = generateFeedbackData(i+1)
|
|
148
|
-
|
|
149
|
-
# Prepare feedback file
|
|
150
|
-
feedbackFile = clientSdk.prepareFeedback(
|
|
151
|
-
agentId=AGENT_ID,
|
|
152
|
-
score=feedbackData['score'],
|
|
153
|
-
tags=feedbackData['tags'],
|
|
154
|
-
capability=feedbackData['capability'],
|
|
155
|
-
skill=feedbackData['skill'],
|
|
156
|
-
context=feedbackData['context']
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
print(f" - Score: {feedbackData['score']}/100")
|
|
160
|
-
print(f" - Tags: {feedbackData['tags']}")
|
|
161
|
-
print(f" - Capability: {feedbackData['capability']}")
|
|
162
|
-
print(f" - Skill: {feedbackData['skill']}")
|
|
163
|
-
|
|
164
|
-
# Submit feedback
|
|
165
|
-
try:
|
|
166
|
-
feedback = clientSdk.giveFeedback(
|
|
167
|
-
agentId=AGENT_ID,
|
|
168
|
-
feedbackFile=feedbackFile,
|
|
169
|
-
feedbackAuth=feedbackAuth
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
# Extract actual feedback index from the returned Feedback object
|
|
173
|
-
# feedback.id is a tuple: (agentId, clientAddress, feedbackIndex)
|
|
174
|
-
actualFeedbackIndex = feedback.id[2]
|
|
175
|
-
|
|
176
|
-
feedbackEntries.append({
|
|
177
|
-
'index': actualFeedbackIndex, # Use actual index from blockchain
|
|
178
|
-
'data': feedbackData,
|
|
179
|
-
'feedback': feedback
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
print(f" ✅ Feedback #{actualFeedbackIndex} submitted successfully (entry #{i+1} in this test)")
|
|
183
|
-
if feedback.fileURI:
|
|
184
|
-
print(f" File URI: {feedback.fileURI}")
|
|
185
|
-
|
|
186
|
-
except Exception as e:
|
|
187
|
-
print(f" ❌ Failed to submit feedback #{i+1}: {e}")
|
|
188
|
-
import traceback
|
|
189
|
-
traceback.print_exc()
|
|
190
|
-
exit(1)
|
|
191
|
-
|
|
192
|
-
time.sleep(2) # Wait between submissions
|
|
193
|
-
|
|
194
|
-
# Step 4: Agent (Server) Responds to Feedback
|
|
195
|
-
print("\n📍 Step 4: Agent (Server) Responds to Feedback")
|
|
196
|
-
print("-" * 60)
|
|
197
|
-
|
|
198
|
-
clientAddress = clientSdk.web3_client.account.address
|
|
199
|
-
|
|
200
|
-
for i, entry in enumerate(feedbackEntries):
|
|
201
|
-
# Use the actual feedback index that was returned when submitting
|
|
202
|
-
feedbackIndex = entry['index']
|
|
203
|
-
print(f"\n Responding to Feedback #{feedbackIndex}:")
|
|
204
|
-
|
|
205
|
-
# Generate response data
|
|
206
|
-
responseData = {
|
|
207
|
-
'text': f"Thank you for your feedback! We appreciate your input.",
|
|
208
|
-
'timestamp': int(time.time()),
|
|
209
|
-
'responder': 'agent'
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
try:
|
|
213
|
-
# Agent responds to the client's feedback
|
|
214
|
-
updatedFeedback = agentSdkWithSigner.appendResponse(
|
|
215
|
-
agentId=AGENT_ID,
|
|
216
|
-
clientAddress=clientAddress,
|
|
217
|
-
feedbackIndex=feedbackIndex,
|
|
218
|
-
response=responseData
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
print(f" ✅ Response submitted to feedback #{feedbackIndex}")
|
|
222
|
-
entry['response'] = responseData
|
|
223
|
-
entry['updatedFeedback'] = updatedFeedback
|
|
224
|
-
except Exception as e:
|
|
225
|
-
print(f" ❌ Failed to submit response: {e}")
|
|
226
|
-
|
|
227
|
-
time.sleep(2) # Wait between responses
|
|
228
|
-
|
|
229
|
-
# Step 5: Wait for blockchain finalization
|
|
230
|
-
print("\n📍 Step 5: Waiting for Blockchain Finalization")
|
|
231
|
-
print("-" * 60)
|
|
232
|
-
print("⏳ Waiting 15 seconds for blockchain to finalize...")
|
|
233
|
-
time.sleep(15)
|
|
234
|
-
|
|
235
|
-
# Step 6: Verify feedback data and responses
|
|
236
|
-
print("\n📍 Step 6: Verify Feedback Data Integrity")
|
|
237
|
-
print("-" * 60)
|
|
238
|
-
|
|
239
|
-
allMatch = True
|
|
240
|
-
|
|
241
|
-
for i, entry in enumerate(feedbackEntries, 1):
|
|
242
|
-
print(f"\n Feedback #{i}:")
|
|
243
|
-
data = entry['data']
|
|
244
|
-
feedback = entry['feedback']
|
|
245
|
-
|
|
246
|
-
# Verify feedback object fields
|
|
247
|
-
checks = [
|
|
248
|
-
('Score', data['score'], feedback.score),
|
|
249
|
-
('Tags', data['tags'], feedback.tags),
|
|
250
|
-
('Capability', data['capability'], feedback.capability),
|
|
251
|
-
('Skill', data['skill'], feedback.skill),
|
|
252
|
-
]
|
|
253
|
-
|
|
254
|
-
for field_name, expected, actual in checks:
|
|
255
|
-
if expected == actual:
|
|
256
|
-
print(f" ✅ {field_name}: {actual}")
|
|
257
|
-
else:
|
|
258
|
-
print(f" ❌ {field_name}: expected={expected}, got={actual}")
|
|
259
|
-
allMatch = False
|
|
260
|
-
|
|
261
|
-
# Verify file URI exists
|
|
262
|
-
if feedback.fileURI:
|
|
263
|
-
print(f" ✅ File URI: {feedback.fileURI}")
|
|
264
|
-
else:
|
|
265
|
-
print(f" ⚠️ No file URI (IPFS storage may have failed)")
|
|
266
|
-
|
|
267
|
-
# Verify server response was added
|
|
268
|
-
if 'response' in entry and entry.get('updatedFeedback'):
|
|
269
|
-
print(f" ✅ Server Response: Recorded successfully")
|
|
270
|
-
|
|
271
|
-
# Step 7: Wait for subgraph indexing
|
|
272
|
-
print("\n📍 Step 7: Waiting for Subgraph to Index")
|
|
273
|
-
print("-" * 60)
|
|
274
|
-
print("⏳ Waiting 60 seconds for subgraph to catch up with blockchain events...")
|
|
275
|
-
print(" (Subgraphs can take up to a minute to index new blocks)")
|
|
276
|
-
time.sleep(60)
|
|
277
|
-
|
|
278
|
-
# Step 8: Test getFeedback (direct access)
|
|
279
|
-
print("\n📍 Step 8: Test getFeedback (Direct Access)")
|
|
280
|
-
print("-" * 60)
|
|
281
|
-
|
|
282
|
-
for i, entry in enumerate(feedbackEntries):
|
|
283
|
-
# Use the actual feedback index that was returned when submitting
|
|
284
|
-
feedbackIndex = entry['index']
|
|
285
|
-
print(f"\n Fetching Feedback #{feedbackIndex} using getFeedback():")
|
|
286
|
-
|
|
287
|
-
try:
|
|
288
|
-
# Use agentSdkWithSigner since agentSdk has no subgraph_client
|
|
289
|
-
retrievedFeedback = agentSdkWithSigner.getFeedback(
|
|
290
|
-
agentId=AGENT_ID,
|
|
291
|
-
clientAddress=clientAddress,
|
|
292
|
-
feedbackIndex=feedbackIndex
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
print(f" ✅ Retrieved feedback successfully")
|
|
296
|
-
print(f" - Score: {retrievedFeedback.score}")
|
|
297
|
-
print(f" - Tags: {retrievedFeedback.tags}")
|
|
298
|
-
print(f" - Capability: {retrievedFeedback.capability}")
|
|
299
|
-
print(f" - Skill: {retrievedFeedback.skill}")
|
|
300
|
-
print(f" - Is Revoked: {retrievedFeedback.isRevoked}")
|
|
301
|
-
print(f" - Has Responses: {len(retrievedFeedback.answers)} response(s)")
|
|
302
|
-
if retrievedFeedback.fileURI:
|
|
303
|
-
print(f" - File URI: {retrievedFeedback.fileURI}")
|
|
304
|
-
|
|
305
|
-
# Verify retrieved feedback matches original
|
|
306
|
-
expected = entry['data']
|
|
307
|
-
if retrievedFeedback.score == expected['score'] and \
|
|
308
|
-
retrievedFeedback.tags == expected['tags'] and \
|
|
309
|
-
retrievedFeedback.capability == expected['capability'] and \
|
|
310
|
-
retrievedFeedback.skill == expected['skill']:
|
|
311
|
-
print(f" ✅ Retrieved feedback matches original submission")
|
|
312
|
-
else:
|
|
313
|
-
print(f" ❌ Retrieved feedback does not match original")
|
|
314
|
-
allMatch = False
|
|
315
|
-
|
|
316
|
-
except Exception as e:
|
|
317
|
-
print(f" ❌ Failed to retrieve feedback: {e}")
|
|
318
|
-
allMatch = False
|
|
319
|
-
|
|
320
|
-
# Step 9: Test searchFeedback (with filters)
|
|
321
|
-
print("\n📍 Step 9: Test searchFeedback (With Filters)")
|
|
322
|
-
print("-" * 60)
|
|
323
|
-
|
|
324
|
-
# Test 1: Search by capability
|
|
325
|
-
print("\n Test 1: Search feedback by capability")
|
|
326
|
-
testCapability = feedbackEntries[0]['data']['capability']
|
|
327
|
-
try:
|
|
328
|
-
results = agentSdkWithSigner.searchFeedback(
|
|
329
|
-
agentId=AGENT_ID,
|
|
330
|
-
capabilities=[testCapability],
|
|
331
|
-
first=10,
|
|
332
|
-
skip=0
|
|
333
|
-
)
|
|
334
|
-
print(f" ✅ Found {len(results)} feedback entry/entries with capability '{testCapability}'")
|
|
335
|
-
if results:
|
|
336
|
-
for fb in results:
|
|
337
|
-
print(f" - Score: {fb.score}, Tags: {fb.tags}")
|
|
338
|
-
except Exception as e:
|
|
339
|
-
print(f" ❌ Failed to search feedback by capability: {e}")
|
|
340
|
-
allMatch = False
|
|
341
|
-
|
|
342
|
-
# Test 2: Search by skill
|
|
343
|
-
print("\n Test 2: Search feedback by skill")
|
|
344
|
-
testSkill = feedbackEntries[0]['data']['skill']
|
|
345
|
-
try:
|
|
346
|
-
results = agentSdkWithSigner.searchFeedback(
|
|
347
|
-
agentId=AGENT_ID,
|
|
348
|
-
skills=[testSkill],
|
|
349
|
-
first=10,
|
|
350
|
-
skip=0
|
|
351
|
-
)
|
|
352
|
-
print(f" ✅ Found {len(results)} feedback entry/entries with skill '{testSkill}'")
|
|
353
|
-
if results:
|
|
354
|
-
for fb in results:
|
|
355
|
-
print(f" - Score: {fb.score}, Tags: {fb.tags}")
|
|
356
|
-
except Exception as e:
|
|
357
|
-
print(f" ❌ Failed to search feedback by skill: {e}")
|
|
358
|
-
allMatch = False
|
|
359
|
-
|
|
360
|
-
# Test 3: Search by tags
|
|
361
|
-
print("\n Test 3: Search feedback by tags")
|
|
362
|
-
testTags = feedbackEntries[0]['data']['tags']
|
|
363
|
-
try:
|
|
364
|
-
results = agentSdkWithSigner.searchFeedback(
|
|
365
|
-
agentId=AGENT_ID,
|
|
366
|
-
tags=testTags,
|
|
367
|
-
first=10,
|
|
368
|
-
skip=0
|
|
369
|
-
)
|
|
370
|
-
print(f" ✅ Found {len(results)} feedback entry/entries with tags {testTags}")
|
|
371
|
-
if results:
|
|
372
|
-
for fb in results:
|
|
373
|
-
print(f" - Score: {fb.score}, Capability: {fb.capability}")
|
|
374
|
-
except Exception as e:
|
|
375
|
-
print(f" ❌ Failed to search feedback by tags: {e}")
|
|
376
|
-
allMatch = False
|
|
377
|
-
|
|
378
|
-
# Test 4: Search by score range
|
|
379
|
-
print("\n Test 4: Search feedback by score range (75-95)")
|
|
380
|
-
try:
|
|
381
|
-
results = agentSdkWithSigner.searchFeedback(
|
|
382
|
-
agentId=AGENT_ID,
|
|
383
|
-
minScore=75,
|
|
384
|
-
maxScore=95,
|
|
385
|
-
first=10,
|
|
386
|
-
skip=0
|
|
387
|
-
)
|
|
388
|
-
print(f" ✅ Found {len(results)} feedback entry/entries with score between 75-95")
|
|
389
|
-
if results:
|
|
390
|
-
scores = sorted([fb.score for fb in results if fb.score])
|
|
391
|
-
print(f" - Scores found: {scores}")
|
|
392
|
-
except Exception as e:
|
|
393
|
-
print(f" ❌ Failed to search feedback by score range: {e}")
|
|
394
|
-
allMatch = False
|
|
395
|
-
|
|
396
|
-
# Final results
|
|
397
|
-
print("\n" + "=" * 60)
|
|
398
|
-
if allMatch:
|
|
399
|
-
print("✅ ALL CHECKS PASSED")
|
|
400
|
-
print("\nSummary:")
|
|
401
|
-
print(f"- Agent ID: {AGENT_ID}")
|
|
402
|
-
print(f"- Agent Name: {agent.name}")
|
|
403
|
-
print(f"- Client address: {clientAddress}")
|
|
404
|
-
print(f"- Feedback entries submitted: {len(feedbackEntries)}")
|
|
405
|
-
print("✅ Feedback flow test complete!")
|
|
406
|
-
else:
|
|
407
|
-
print("❌ SOME CHECKS FAILED")
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if __name__ == "__main__":
|
|
411
|
-
try:
|
|
412
|
-
main()
|
|
413
|
-
except Exception as e:
|
|
414
|
-
print(f"\n❌ Error: {e}")
|
|
415
|
-
import traceback
|
|
416
|
-
traceback.print_exc()
|
|
417
|
-
exit(1)
|