agent0-sdk 0.2.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 +52 -0
- agent0_sdk/core/agent.py +860 -0
- agent0_sdk/core/contracts.py +490 -0
- agent0_sdk/core/endpoint_crawler.py +270 -0
- agent0_sdk/core/feedback_manager.py +923 -0
- agent0_sdk/core/indexer.py +1016 -0
- agent0_sdk/core/ipfs_client.py +355 -0
- agent0_sdk/core/models.py +311 -0
- agent0_sdk/core/sdk.py +842 -0
- agent0_sdk/core/subgraph_client.py +813 -0
- agent0_sdk/core/web3_client.py +192 -0
- agent0_sdk-0.2.0.dist-info/METADATA +308 -0
- agent0_sdk-0.2.0.dist-info/RECORD +27 -0
- agent0_sdk-0.2.0.dist-info/WHEEL +5 -0
- agent0_sdk-0.2.0.dist-info/licenses/LICENSE +22 -0
- agent0_sdk-0.2.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/config.py +46 -0
- tests/conftest.py +22 -0
- tests/test_feedback.py +417 -0
- tests/test_models.py +224 -0
- tests/test_real_public_servers.py +103 -0
- tests/test_registration.py +267 -0
- tests/test_registrationIpfs.py +227 -0
- tests/test_sdk.py +238 -0
- tests/test_search.py +271 -0
- tests/test_transfer.py +255 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Web3 integration layer for smart contract interactions.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from web3 import Web3
|
|
12
|
+
from web3.contract import Contract
|
|
13
|
+
from eth_account import Account
|
|
14
|
+
from eth_account.signers.base import BaseAccount
|
|
15
|
+
except ImportError:
|
|
16
|
+
raise ImportError(
|
|
17
|
+
"Web3 dependencies not installed. Install with: pip install web3 eth-account"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Web3Client:
|
|
22
|
+
"""Web3 client for interacting with ERC-8004 smart contracts."""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
rpc_url: str,
|
|
27
|
+
private_key: Optional[str] = None,
|
|
28
|
+
account: Optional[BaseAccount] = None,
|
|
29
|
+
):
|
|
30
|
+
"""Initialize Web3 client."""
|
|
31
|
+
self.rpc_url = rpc_url
|
|
32
|
+
self.w3 = Web3(Web3.HTTPProvider(rpc_url))
|
|
33
|
+
if not self.w3.is_connected():
|
|
34
|
+
raise ConnectionError("Failed to connect to Ethereum node")
|
|
35
|
+
|
|
36
|
+
if account:
|
|
37
|
+
self.account = account
|
|
38
|
+
elif private_key:
|
|
39
|
+
self.account = Account.from_key(private_key)
|
|
40
|
+
else:
|
|
41
|
+
# Read-only mode - no account
|
|
42
|
+
self.account = None
|
|
43
|
+
|
|
44
|
+
self.chain_id = self.w3.eth.chain_id
|
|
45
|
+
|
|
46
|
+
def get_contract(self, address: str, abi: List[Dict[str, Any]]) -> Contract:
|
|
47
|
+
"""Get contract instance."""
|
|
48
|
+
return self.w3.eth.contract(address=address, abi=abi)
|
|
49
|
+
|
|
50
|
+
def call_contract(
|
|
51
|
+
self,
|
|
52
|
+
contract: Contract,
|
|
53
|
+
method_name: str,
|
|
54
|
+
*args,
|
|
55
|
+
**kwargs
|
|
56
|
+
) -> Any:
|
|
57
|
+
"""Call a contract method (view/pure)."""
|
|
58
|
+
method = getattr(contract.functions, method_name)
|
|
59
|
+
return method(*args, **kwargs).call()
|
|
60
|
+
|
|
61
|
+
def transact_contract(
|
|
62
|
+
self,
|
|
63
|
+
contract: Contract,
|
|
64
|
+
method_name: str,
|
|
65
|
+
*args,
|
|
66
|
+
gas_limit: Optional[int] = None,
|
|
67
|
+
gas_price: Optional[int] = None,
|
|
68
|
+
max_fee_per_gas: Optional[int] = None,
|
|
69
|
+
max_priority_fee_per_gas: Optional[int] = None,
|
|
70
|
+
**kwargs
|
|
71
|
+
) -> str:
|
|
72
|
+
"""Execute a contract transaction."""
|
|
73
|
+
if not self.account:
|
|
74
|
+
raise ValueError("Cannot execute transaction: SDK is in read-only mode. Provide a signer to enable write operations.")
|
|
75
|
+
|
|
76
|
+
method = getattr(contract.functions, method_name)
|
|
77
|
+
|
|
78
|
+
# Build transaction with proper nonce management
|
|
79
|
+
# Use 'pending' to get the next nonce including pending transactions
|
|
80
|
+
nonce = self.w3.eth.get_transaction_count(self.account.address, 'pending')
|
|
81
|
+
tx = method(*args, **kwargs).build_transaction({
|
|
82
|
+
'from': self.account.address,
|
|
83
|
+
'nonce': nonce,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
# Add gas settings
|
|
87
|
+
if gas_limit:
|
|
88
|
+
tx['gas'] = gas_limit
|
|
89
|
+
if gas_price:
|
|
90
|
+
tx['gasPrice'] = gas_price
|
|
91
|
+
if max_fee_per_gas:
|
|
92
|
+
tx['maxFeePerGas'] = max_fee_per_gas
|
|
93
|
+
if max_priority_fee_per_gas:
|
|
94
|
+
tx['maxPriorityFeePerGas'] = max_priority_fee_per_gas
|
|
95
|
+
|
|
96
|
+
# Sign and send
|
|
97
|
+
signed_tx = self.w3.eth.account.sign_transaction(tx, self.account.key)
|
|
98
|
+
tx_hash = self.w3.eth.send_raw_transaction(signed_tx.rawTransaction if hasattr(signed_tx, 'rawTransaction') else signed_tx.raw_transaction)
|
|
99
|
+
|
|
100
|
+
return tx_hash.hex()
|
|
101
|
+
|
|
102
|
+
def wait_for_transaction(self, tx_hash: str, timeout: int = 60) -> Dict[str, Any]:
|
|
103
|
+
"""Wait for transaction to be mined."""
|
|
104
|
+
return self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=timeout)
|
|
105
|
+
|
|
106
|
+
def get_events(
|
|
107
|
+
self,
|
|
108
|
+
contract: Contract,
|
|
109
|
+
event_name: str,
|
|
110
|
+
from_block: int = 0,
|
|
111
|
+
to_block: Optional[int] = None,
|
|
112
|
+
argument_filters: Optional[Dict[str, Any]] = None
|
|
113
|
+
) -> List[Dict[str, Any]]:
|
|
114
|
+
"""Get contract events."""
|
|
115
|
+
if to_block is None:
|
|
116
|
+
to_block = self.w3.eth.block_number
|
|
117
|
+
|
|
118
|
+
event_filter = contract.events[event_name].create_filter(
|
|
119
|
+
fromBlock=from_block,
|
|
120
|
+
toBlock=to_block,
|
|
121
|
+
argument_filters=argument_filters or {}
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return event_filter.get_all_entries()
|
|
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
|
+
def signMessage(self, message: bytes) -> bytes:
|
|
143
|
+
"""Sign a message with the account's private key."""
|
|
144
|
+
# Create a SignableMessage from the raw bytes
|
|
145
|
+
from eth_account.messages import encode_defunct
|
|
146
|
+
signableMessage = encode_defunct(message)
|
|
147
|
+
signedMessage = self.account.sign_message(signableMessage)
|
|
148
|
+
return signedMessage.signature
|
|
149
|
+
|
|
150
|
+
def recoverAddress(self, message: bytes, signature: bytes) -> str:
|
|
151
|
+
"""Recover address from message and signature."""
|
|
152
|
+
from eth_account.messages import encode_defunct
|
|
153
|
+
signable_message = encode_defunct(message)
|
|
154
|
+
return self.w3.eth.account.recover_message(signable_message, signature=signature)
|
|
155
|
+
|
|
156
|
+
def keccak256(self, data: bytes) -> bytes:
|
|
157
|
+
"""Compute Keccak-256 hash."""
|
|
158
|
+
return self.w3.keccak(data)
|
|
159
|
+
|
|
160
|
+
def to_checksum_address(self, address: str) -> str:
|
|
161
|
+
"""Convert address to checksum format."""
|
|
162
|
+
return self.w3.to_checksum_address(address)
|
|
163
|
+
|
|
164
|
+
def normalize_address(self, address: str) -> str:
|
|
165
|
+
"""Normalize address to lowercase for consistent storage and comparison.
|
|
166
|
+
|
|
167
|
+
Ethereum addresses are case-insensitive but EIP-55 checksum addresses
|
|
168
|
+
use mixed case. For storage and comparison purposes, we normalize to
|
|
169
|
+
lowercase to avoid case-sensitivity issues.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
address: Ethereum address (with or without checksum)
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Address in lowercase format
|
|
176
|
+
"""
|
|
177
|
+
# Remove 0x prefix if present, convert to lowercase, re-add prefix
|
|
178
|
+
if address.startswith("0x") or address.startswith("0X"):
|
|
179
|
+
return "0x" + address[2:].lower()
|
|
180
|
+
return address.lower()
|
|
181
|
+
|
|
182
|
+
def is_address(self, address: str) -> bool:
|
|
183
|
+
"""Check if string is a valid Ethereum address."""
|
|
184
|
+
return self.w3.is_address(address)
|
|
185
|
+
|
|
186
|
+
def get_balance(self, address: str) -> int:
|
|
187
|
+
"""Get ETH balance of an address."""
|
|
188
|
+
return self.w3.eth.get_balance(address)
|
|
189
|
+
|
|
190
|
+
def get_transaction_count(self, address: str) -> int:
|
|
191
|
+
"""Get transaction count (nonce) of an address."""
|
|
192
|
+
return self.w3.eth.get_transaction_count(address)
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent0-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for agent portability, discovery and trust based on ERC-8004
|
|
5
|
+
Author-email: Marco De Rossi <marco.derossi@consensys.net>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Marco De Rossi
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Project-URL: Homepage, https://sdk.ag0.xyz
|
|
30
|
+
Project-URL: Repository, https://github.com/agent0lab/agent0-py
|
|
31
|
+
Project-URL: Documentation, https://sdk.ag0.xyz
|
|
32
|
+
Project-URL: Source Code, https://github.com/agent0lab/agent0-py
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Requires-Python: >=3.8
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
Requires-Dist: web3>=6.0.0
|
|
46
|
+
Requires-Dist: eth-account>=0.8.0
|
|
47
|
+
Requires-Dist: requests>=2.28.0
|
|
48
|
+
Requires-Dist: pydantic>=2.0.0
|
|
49
|
+
Requires-Dist: ipfshttpclient>=0.8.0a2
|
|
50
|
+
Requires-Dist: numpy>=1.21.0
|
|
51
|
+
Requires-Dist: scikit-learn>=1.0.0
|
|
52
|
+
Requires-Dist: sentence-transformers>=2.2.0
|
|
53
|
+
Requires-Dist: aiohttp>=3.8.0
|
|
54
|
+
Requires-Dist: asyncio-throttle>=1.0.0
|
|
55
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
56
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
57
|
+
Provides-Extra: dev
|
|
58
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
59
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
60
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
61
|
+
Requires-Dist: black>=22.0.0; extra == "dev"
|
|
62
|
+
Requires-Dist: isort>=5.10.0; extra == "dev"
|
|
63
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
64
|
+
Requires-Dist: flake8>=5.0.0; extra == "dev"
|
|
65
|
+
Provides-Extra: test
|
|
66
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
67
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
|
|
68
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
|
|
69
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "test"
|
|
70
|
+
Dynamic: license-file
|
|
71
|
+
|
|
72
|
+
# Agent0 SDK
|
|
73
|
+
|
|
74
|
+
Python SDK for agent portability, discovery and trust based on ERC-8004.
|
|
75
|
+
|
|
76
|
+
Agent0 is the SDK for agentic economies. It enables agents to register, advertise their capabilities and how to communicate with them, and give each other feedback and reputation signals. All this using blockchain infrastructure (ERC-8004) and decentralized storage, enabling permissionless discovery without relying on proprietary catalogues or intermediaries.
|
|
77
|
+
|
|
78
|
+
## What Does Agent0 SDK Do?
|
|
79
|
+
|
|
80
|
+
Agent0 SDK v0.2 enables you to:
|
|
81
|
+
|
|
82
|
+
- **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
|
|
83
|
+
- **Advertise agent capabilities** - Publish MCP and A2A endpoints, with automated extraction of MCP tools and A2A skills from endpoints
|
|
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
|
|
85
|
+
- **Build reputation** - Give and receive feedback, retrieve feedback history, and search agents by reputation with cryptographic authentication
|
|
86
|
+
- **Cross-chain registration** - One-line registration with IPFS nodes, Pinata, Filecoin, or HTTP URIs
|
|
87
|
+
- **Public indexing** - Subgraph indexing both on-chain and IPFS data for fast search and retrieval
|
|
88
|
+
|
|
89
|
+
## ⚠️ Alpha Release
|
|
90
|
+
|
|
91
|
+
Agent0 SDK v0.2 is in **alpha** with bugs and is not production ready. We're actively testing and improving it.
|
|
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
|
|
94
|
+
|
|
95
|
+
## Installation
|
|
96
|
+
|
|
97
|
+
### Prerequisites
|
|
98
|
+
|
|
99
|
+
- Python 3.8 or higher
|
|
100
|
+
- pip package manager
|
|
101
|
+
- Private key for signing transactions (or run in read-only mode)
|
|
102
|
+
- Access to an Ethereum RPC endpoint (e.g., Alchemy, Infura)
|
|
103
|
+
- (Optional) IPFS provider account (Pinata, Filecoin, or local IPFS node)
|
|
104
|
+
|
|
105
|
+
### Install from PyPI
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pip install agent0-sdk
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Install from Source
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git clone https://github.com/agent0lab/agent0-py.git
|
|
115
|
+
cd agent0-py
|
|
116
|
+
pip install -e .
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Quick Start
|
|
120
|
+
|
|
121
|
+
### 1. Initialize SDK
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from agent0_sdk import SDK
|
|
125
|
+
import os
|
|
126
|
+
|
|
127
|
+
# Initialize SDK with IPFS and subgraph
|
|
128
|
+
sdk = SDK(
|
|
129
|
+
chainId=11155111, # Ethereum Sepolia testnet
|
|
130
|
+
rpcUrl=os.getenv("RPC_URL"),
|
|
131
|
+
signer=os.getenv("PRIVATE_KEY"),
|
|
132
|
+
ipfs="pinata", # Options: "pinata", "filecoinPin", "node"
|
|
133
|
+
pinataJwt=os.getenv("PINATA_JWT") # For Pinata
|
|
134
|
+
# Subgraph URL auto-defaults from DEFAULT_SUBGRAPH_URLS
|
|
135
|
+
)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 2. Create and Register Agent
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
# Create agent
|
|
142
|
+
agent = sdk.createAgent(
|
|
143
|
+
name="My AI Agent",
|
|
144
|
+
description="An intelligent assistant for various tasks. Skills: data analysis, code generation.",
|
|
145
|
+
image="https://example.com/agent-image.png"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Configure endpoints (automatically extracts capabilities)
|
|
149
|
+
agent.setMCP("https://mcp.example.com/") # Extracts tools, prompts, resources
|
|
150
|
+
agent.setA2A("https://a2a.example.com/agent-card.json") # Extracts skills
|
|
151
|
+
agent.setENS("myagent.eth")
|
|
152
|
+
|
|
153
|
+
# Configure wallet and trust
|
|
154
|
+
agent.setAgentWallet("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", chainId=11155111)
|
|
155
|
+
agent.setTrust(reputation=True, cryptoEconomic=True)
|
|
156
|
+
|
|
157
|
+
# Add metadata and set status
|
|
158
|
+
agent.setMetadata({"version": "1.0.0", "category": "ai-assistant"})
|
|
159
|
+
agent.setActive(True)
|
|
160
|
+
|
|
161
|
+
# Register on-chain with IPFS
|
|
162
|
+
agent.registerIPFS()
|
|
163
|
+
print(f"Agent registered: {agent.agentId}") # e.g., "11155111:123"
|
|
164
|
+
print(f"Agent URI: {agent.agentURI}") # e.g., "ipfs://Qm..."
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 3. Load and Edit Agent
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
# Load existing agent for editing
|
|
171
|
+
agent = sdk.loadAgent("11155111:123") # Format: "chainId:agentId"
|
|
172
|
+
|
|
173
|
+
# Edit agent properties
|
|
174
|
+
agent.updateInfo(description="Updated description with new capabilities")
|
|
175
|
+
agent.setMCP("https://new-mcp.example.com/")
|
|
176
|
+
|
|
177
|
+
# Re-register to update on-chain
|
|
178
|
+
agent.registerIPFS()
|
|
179
|
+
print(f"Updated: {agent.agentURI}")
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 4. Search Agents
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
# Search by name, capabilities, or attributes
|
|
186
|
+
results = sdk.searchAgents(
|
|
187
|
+
name="AI", # Substring search
|
|
188
|
+
mcpTools=["code_generation"], # Specific MCP tools
|
|
189
|
+
a2aSkills=["python"], # Specific A2A skills
|
|
190
|
+
active=True, # Only active agents
|
|
191
|
+
x402support=True # Payment support
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
for agent in results['items']:
|
|
195
|
+
print(f"{agent.name}: {agent.description}")
|
|
196
|
+
print(f" Tools: {agent.mcpTools}")
|
|
197
|
+
print(f" Skills: {agent.a2aSkills}")
|
|
198
|
+
|
|
199
|
+
# Get single agent (read-only, faster)
|
|
200
|
+
agent_summary = sdk.getAgent("11155111:123")
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 5. Give and Retrieve Feedback
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
# Prepare feedback (only score is mandatory)
|
|
207
|
+
feedback_file = sdk.prepareFeedback(
|
|
208
|
+
agentId="11155111:123",
|
|
209
|
+
score=85, # 0-100 (mandatory)
|
|
210
|
+
tags=["data_analyst", "finance"], # Optional
|
|
211
|
+
capability="tools", # Optional: MCP capability
|
|
212
|
+
name="code_generation", # Optional: MCP tool name
|
|
213
|
+
skill="python" # Optional: A2A skill
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
# Give feedback
|
|
217
|
+
feedback = sdk.giveFeedback(agentId="11155111:123", feedbackFile=feedback_file)
|
|
218
|
+
|
|
219
|
+
# Search feedback
|
|
220
|
+
results = sdk.searchFeedback(
|
|
221
|
+
agentId="11155111:123",
|
|
222
|
+
capabilities=["tools"],
|
|
223
|
+
minScore=80,
|
|
224
|
+
maxScore=100
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Get reputation summary
|
|
228
|
+
summary = sdk.getReputationSummary("11155111:123")
|
|
229
|
+
print(f"Average score: {summary['averageScore']}")
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## IPFS Configuration Options
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
# Option 1: Filecoin Pin (free for ERC-8004 agents)
|
|
236
|
+
sdk = SDK(
|
|
237
|
+
chainId=11155111,
|
|
238
|
+
rpcUrl="...",
|
|
239
|
+
signer=private_key,
|
|
240
|
+
ipfs="filecoinPin",
|
|
241
|
+
filecoinPrivateKey="your-filecoin-private-key"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Option 2: IPFS Node
|
|
245
|
+
sdk = SDK(
|
|
246
|
+
chainId=11155111,
|
|
247
|
+
rpcUrl="...",
|
|
248
|
+
signer=private_key,
|
|
249
|
+
ipfs="node",
|
|
250
|
+
ipfsNodeUrl="https://ipfs.infura.io:5001"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Option 3: Pinata (free for ERC-8004 agents)
|
|
254
|
+
sdk = SDK(
|
|
255
|
+
chainId=11155111,
|
|
256
|
+
rpcUrl="...",
|
|
257
|
+
signer=private_key,
|
|
258
|
+
ipfs="pinata",
|
|
259
|
+
pinataJwt="your-pinata-jwt-token"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Option 4: HTTP registration (no IPFS)
|
|
263
|
+
sdk = SDK(chainId=11155111, rpcUrl="...", signer=private_key)
|
|
264
|
+
agent.register("https://example.com/agent-registration.json")
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Use Cases
|
|
268
|
+
|
|
269
|
+
- **Building agent marketplaces** - Create platforms where developers can discover, evaluate, and integrate agents based on their capabilities and reputation
|
|
270
|
+
- **Agent interoperability** - Discover agents by specific capabilities (skills, tools, tasks), evaluate them through reputation signals, and integrate them via standard protocols (MCP/A2A)
|
|
271
|
+
- **Managing agent reputation** - Track agent performance, collect feedback from users and other agents, and build trust signals for your agent ecosystem
|
|
272
|
+
- **Cross-chain agent operations** - Deploy and manage agents across multiple blockchain networks with consistent identity and reputation
|
|
273
|
+
|
|
274
|
+
## 🚀 Coming Soon
|
|
275
|
+
|
|
276
|
+
- More chains (currently Ethereum Sepolia only)
|
|
277
|
+
- Support for validations
|
|
278
|
+
- Multi-chain agents search
|
|
279
|
+
- Enhanced x402 payments
|
|
280
|
+
- Semantic/Vectorial search
|
|
281
|
+
- Advanced reputation aggregation
|
|
282
|
+
- Import/Export to centralized catalogues
|
|
283
|
+
|
|
284
|
+
## Tests
|
|
285
|
+
|
|
286
|
+
Complete working examples are available in the `tests/` directory:
|
|
287
|
+
|
|
288
|
+
- `test_registration.py` - Agent registration with HTTP URI
|
|
289
|
+
- `test_registrationIpfs.py` - Agent registration with IPFS
|
|
290
|
+
- `test_feedback.py` - Complete feedback flow with IPFS storage
|
|
291
|
+
- `test_search.py` - Agent search and discovery
|
|
292
|
+
- `test_transfer.py` - Agent ownership transfer
|
|
293
|
+
|
|
294
|
+
## Documentation
|
|
295
|
+
|
|
296
|
+
Full documentation is available at [sdk.ag0.xyz](https://sdk.ag0.xyz), including:
|
|
297
|
+
|
|
298
|
+
- [Installation Guide](https://sdk.ag0.xyz/2-usage/2-1-install/)
|
|
299
|
+
- [Agent Configuration](https://sdk.ag0.xyz/2-usage/2-2-configure-agents/)
|
|
300
|
+
- [Registration](https://sdk.ag0.xyz/2-usage/2-3-registration-ipfs/)
|
|
301
|
+
- [Search](https://sdk.ag0.xyz/2-usage/2-5-search/)
|
|
302
|
+
- [Feedback](https://sdk.ag0.xyz/2-usage/2-6-use-feedback/)
|
|
303
|
+
- [Key Concepts](https://sdk.ag0.xyz/1-welcome/1-2-key-concepts/)
|
|
304
|
+
- [API Reference](https://sdk.ag0.xyz/5-reference/5-1-sdk/)
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT License - see LICENSE file for details.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
agent0_sdk/__init__.py,sha256=8afqO4bSRL5QBBIGA3uhvBtbELAlEXBvT3jWMKvYoEs,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=JFLW4-SoQlpyXsUDWeOb6h-u1ilEKNVQF0r6LI-3k68,11026
|
|
5
|
+
agent0_sdk/core/feedback_manager.py,sha256=Z--m_VKP4db0-gaizL_QSe7zKEnID-S1Zqigl2HcrxM,36035
|
|
6
|
+
agent0_sdk/core/indexer.py,sha256=04Iui7BLpp87gy7P585txKLzdRP0fJs7pAVNhvNrTVE,40330
|
|
7
|
+
agent0_sdk/core/ipfs_client.py,sha256=YHZRsgHlxl3bpkaHORunBtVMNEPWKfn_xx92Yi6l1Fk,13907
|
|
8
|
+
agent0_sdk/core/models.py,sha256=tgaixQBcMOJ8V0m_5TY4AQwnQqwGG8Fzf2fzsVmcyIQ,12375
|
|
9
|
+
agent0_sdk/core/sdk.py,sha256=4bq2ooUew39b7Ya9YTtKeCGwQAAnbXpHYUzGSjgDUKU,31570
|
|
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.0.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=4HOtzpzm0OHyzf7rDmCvXQuYbMxRyt8AyLAMd-6uqCk,7114
|
|
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=CRnCgA7ljyuC7L3MfV1w7dtzZk2FrWqCUX0tu5nnca0,9013
|
|
22
|
+
tests/test_search.py,sha256=YC5Zd0c99JhFoy7POwrUkb36I1Q3S9hJPIQrIZIJCwI,11850
|
|
23
|
+
tests/test_transfer.py,sha256=zRBllpoMs6NhagAmaZWmD4ckbYjSvsSUerBK4oS-HlA,9258
|
|
24
|
+
agent0_sdk-0.2.0.dist-info/METADATA,sha256=dMfSUzU9Y6gtP2gaoBcbnaoT37lm9PQLN0T_n-wA-Lo,11174
|
|
25
|
+
agent0_sdk-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
26
|
+
agent0_sdk-0.2.0.dist-info/top_level.txt,sha256=rgGBfOJlLi1zInQ85jBL2MpDu_ZJNbPjIGz-3Vn5rZs,17
|
|
27
|
+
agent0_sdk-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Marco De Rossi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
tests/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Tests for Agent0 SDK
|
tests/config.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
|