mech-client 0.2.10__py3-none-any.whl → 0.2.12__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.
- mech_client/__init__.py +1 -1
- mech_client/acn.py +0 -17
- mech_client/cli.py +27 -5
- mech_client/configs/mechs.json +92 -0
- mech_client/interact.py +223 -79
- mech_client/prompt_to_ipfs.py +8 -2
- mech_client/subgraph.py +99 -5
- mech_client/wss.py +0 -38
- {mech_client-0.2.10.dist-info → mech_client-0.2.12.dist-info}/METADATA +31 -5
- {mech_client-0.2.10.dist-info → mech_client-0.2.12.dist-info}/RECORD +13 -12
- {mech_client-0.2.10.dist-info → mech_client-0.2.12.dist-info}/LICENSE +0 -0
- {mech_client-0.2.10.dist-info → mech_client-0.2.12.dist-info}/WHEEL +0 -0
- {mech_client-0.2.10.dist-info → mech_client-0.2.12.dist-info}/entry_points.txt +0 -0
mech_client/__init__.py
CHANGED
mech_client/acn.py
CHANGED
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
|
|
20
20
|
"""ACN helpers."""
|
|
21
21
|
|
|
22
|
-
import asyncio
|
|
23
22
|
from pathlib import Path
|
|
24
23
|
from typing import Optional, Type, cast
|
|
25
24
|
|
|
@@ -171,19 +170,3 @@ async def watch_for_data_url_from_mech(crypto: Crypto) -> Optional[str]:
|
|
|
171
170
|
return None
|
|
172
171
|
finally:
|
|
173
172
|
await connection.disconnect()
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
def watch_for_data_url_from_mech_sync(crypto: Crypto) -> Optional[str]:
|
|
177
|
-
"""
|
|
178
|
-
Request and wait for data from agent
|
|
179
|
-
|
|
180
|
-
:param crypto: instance of Crypto
|
|
181
|
-
:type crypto: Crypto
|
|
182
|
-
:return: Data URL
|
|
183
|
-
:rtype: str
|
|
184
|
-
:raises: None
|
|
185
|
-
"""
|
|
186
|
-
loop = asyncio.new_event_loop()
|
|
187
|
-
task = loop.create_task(watch_for_data_url_from_mech(crypto=crypto))
|
|
188
|
-
loop.run_until_complete(task)
|
|
189
|
-
return task.result()
|
mech_client/cli.py
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
"""Mech client CLI module."""
|
|
21
21
|
|
|
22
|
-
from typing import Optional
|
|
22
|
+
from typing import Any, Dict, List, Optional
|
|
23
23
|
|
|
24
24
|
import click
|
|
25
25
|
|
|
@@ -40,15 +40,22 @@ def cli() -> None:
|
|
|
40
40
|
@click.command()
|
|
41
41
|
@click.argument("prompt")
|
|
42
42
|
@click.argument("agent_id", type=int)
|
|
43
|
+
@click.option(
|
|
44
|
+
"--key",
|
|
45
|
+
type=click.Path(exists=True, file_okay=True, dir_okay=False),
|
|
46
|
+
help="Path to private key to use for request minting",
|
|
47
|
+
)
|
|
43
48
|
@click.option(
|
|
44
49
|
"--tool",
|
|
45
50
|
type=str,
|
|
46
51
|
help="Name of the tool to be used",
|
|
47
52
|
)
|
|
48
53
|
@click.option(
|
|
49
|
-
"--
|
|
50
|
-
type=
|
|
51
|
-
|
|
54
|
+
"--extra-attribute",
|
|
55
|
+
type=str,
|
|
56
|
+
multiple=True,
|
|
57
|
+
help="Extra attribute (key=value) to be included in the request metadata",
|
|
58
|
+
metavar="KEY=VALUE",
|
|
52
59
|
)
|
|
53
60
|
@click.option(
|
|
54
61
|
"--confirm",
|
|
@@ -72,23 +79,37 @@ def cli() -> None:
|
|
|
72
79
|
type=float,
|
|
73
80
|
help="Amount of sleep before retrying the transaction",
|
|
74
81
|
)
|
|
82
|
+
@click.option(
|
|
83
|
+
"--chain-config",
|
|
84
|
+
type=str,
|
|
85
|
+
help="Id of the mech's chain configuration (stored configs/mechs.json)",
|
|
86
|
+
)
|
|
75
87
|
def interact( # pylint: disable=too-many-arguments
|
|
76
88
|
prompt: str,
|
|
77
89
|
agent_id: int,
|
|
78
|
-
tool: Optional[str],
|
|
79
90
|
key: Optional[str],
|
|
91
|
+
tool: Optional[str],
|
|
92
|
+
extra_attribute: Optional[List[str]] = None,
|
|
80
93
|
confirm: Optional[str] = None,
|
|
81
94
|
retries: Optional[int] = None,
|
|
82
95
|
timeout: Optional[float] = None,
|
|
83
96
|
sleep: Optional[float] = None,
|
|
97
|
+
chain_config: Optional[str] = None,
|
|
84
98
|
) -> None:
|
|
85
99
|
"""Interact with a mech specifying a prompt and tool."""
|
|
86
100
|
try:
|
|
101
|
+
extra_attributes_dict: Dict[str, Any] = {}
|
|
102
|
+
if extra_attribute:
|
|
103
|
+
for pair in extra_attribute:
|
|
104
|
+
k, v = pair.split("=")
|
|
105
|
+
extra_attributes_dict[k] = v
|
|
106
|
+
|
|
87
107
|
interact_(
|
|
88
108
|
prompt=prompt,
|
|
89
109
|
agent_id=agent_id,
|
|
90
110
|
private_key_path=key,
|
|
91
111
|
tool=tool,
|
|
112
|
+
extra_attributes=extra_attributes_dict,
|
|
92
113
|
confirmation_type=(
|
|
93
114
|
ConfirmationType(confirm)
|
|
94
115
|
if confirm is not None
|
|
@@ -97,6 +118,7 @@ def interact( # pylint: disable=too-many-arguments
|
|
|
97
118
|
retries=retries,
|
|
98
119
|
timeout=timeout,
|
|
99
120
|
sleep=sleep,
|
|
121
|
+
chain_config=chain_config,
|
|
100
122
|
)
|
|
101
123
|
except (ValueError, FileNotFoundError) as e:
|
|
102
124
|
raise click.ClickException(str(e)) from e
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"gnosis": {
|
|
3
|
+
"agent_registry_contract": "0xE49CB081e8d96920C38aA7AB90cb0294ab4Bc8EA",
|
|
4
|
+
"rpc_url": "https://rpc.eu-central-2.gateway.fm/v4/gnosis/non-archival/mainnet",
|
|
5
|
+
"wss_endpoint": "wss://rpc.eu-central-2.gateway.fm/ws/v4/gnosis/non-archival/mainnet",
|
|
6
|
+
"ledger_config": {
|
|
7
|
+
"address": "https://rpc.eu-central-2.gateway.fm/v4/gnosis/non-archival/mainnet",
|
|
8
|
+
"chain_id": 100,
|
|
9
|
+
"poa_chain": false,
|
|
10
|
+
"default_gas_price_strategy": "eip1559",
|
|
11
|
+
"is_gas_estimation_enabled": false
|
|
12
|
+
},
|
|
13
|
+
"gas_limit": 100000,
|
|
14
|
+
"contract_abi_url": "https://gnosis.blockscout.com/api/v2/smart-contracts/{contract_address}",
|
|
15
|
+
"subgraph_url": "https://api.studio.thegraph.com/query/57238/mech/version/latest"
|
|
16
|
+
},
|
|
17
|
+
"arbitrum": {
|
|
18
|
+
"agent_registry_contract": "0xa4799B083E0068732456EF45ff9fe5c683658327",
|
|
19
|
+
"rpc_url": "https://arbitrum.llamarpc.com",
|
|
20
|
+
"wss_endpoint": "wss://arbitrum.gateway.tenderly.co",
|
|
21
|
+
"ledger_config": {
|
|
22
|
+
"address": "https://arbitrum.llamarpc.com",
|
|
23
|
+
"chain_id": 42161,
|
|
24
|
+
"poa_chain": false,
|
|
25
|
+
"default_gas_price_strategy": "eip1559",
|
|
26
|
+
"is_gas_estimation_enabled": false
|
|
27
|
+
},
|
|
28
|
+
"gas_limit": 100000,
|
|
29
|
+
"contract_abi_url": "https://api.arbiscan.io/api?module=contract&action=getabi&address={contract_address}",
|
|
30
|
+
"subgraph_url": ""
|
|
31
|
+
},
|
|
32
|
+
"polygon": {
|
|
33
|
+
"agent_registry_contract": "0x984cf72FDe8B5aA910e9e508aC5e007ae5BDcC9C",
|
|
34
|
+
"rpc_url": "https://polygon-bor-rpc.publicnode.com",
|
|
35
|
+
"wss_endpoint": "wss://polygon.gateway.tenderly.co",
|
|
36
|
+
"ledger_config": {
|
|
37
|
+
"address": "https://polygon-bor-rpc.publicnode.com",
|
|
38
|
+
"chain_id": 137,
|
|
39
|
+
"poa_chain": true,
|
|
40
|
+
"default_gas_price_strategy": "eip1559",
|
|
41
|
+
"is_gas_estimation_enabled": false
|
|
42
|
+
},
|
|
43
|
+
"gas_limit": 100000,
|
|
44
|
+
"contract_abi_url": "https://api.polygonscan.com/api?module=contract&action=getabi&address={contract_address}",
|
|
45
|
+
"subgraph_url": ""
|
|
46
|
+
},
|
|
47
|
+
"base": {
|
|
48
|
+
"agent_registry_contract": "0x88DE734655184a09B70700aE4F72364d1ad23728",
|
|
49
|
+
"rpc_url": "https://base.llamarpc.com",
|
|
50
|
+
"wss_endpoint": "wss://base.gateway.tenderly.co",
|
|
51
|
+
"ledger_config": {
|
|
52
|
+
"address": "https://base.llamarpc.com",
|
|
53
|
+
"chain_id": 8453,
|
|
54
|
+
"poa_chain": false,
|
|
55
|
+
"default_gas_price_strategy": "eip1559",
|
|
56
|
+
"is_gas_estimation_enabled": false
|
|
57
|
+
},
|
|
58
|
+
"gas_limit": 100000,
|
|
59
|
+
"contract_abi_url": "https://api.basescan.org/api?module=contract&action=getabi&address={contract_address}",
|
|
60
|
+
"subgraph_url": ""
|
|
61
|
+
},
|
|
62
|
+
"celo": {
|
|
63
|
+
"agent_registry_contract": "0xE49CB081e8d96920C38aA7AB90cb0294ab4Bc8EA",
|
|
64
|
+
"rpc_url": "https://forno.celo.org",
|
|
65
|
+
"wss_endpoint": "wss://forno.celo.org/ws",
|
|
66
|
+
"ledger_config": {
|
|
67
|
+
"address": "https://forno.celo.org",
|
|
68
|
+
"chain_id": 42220,
|
|
69
|
+
"poa_chain": false,
|
|
70
|
+
"default_gas_price_strategy": "eip1559",
|
|
71
|
+
"is_gas_estimation_enabled": false
|
|
72
|
+
},
|
|
73
|
+
"gas_limit": 100000,
|
|
74
|
+
"contract_abi_url": "https://api.celoscan.io/api?module=contract&action=getabi&address={contract_address}",
|
|
75
|
+
"subgraph_url": ""
|
|
76
|
+
},
|
|
77
|
+
"optimism": {
|
|
78
|
+
"agent_registry_contract": "0x75D529FAe220bC8db714F0202193726b46881B76",
|
|
79
|
+
"rpc_url": "https://mainnet.optimism.io",
|
|
80
|
+
"wss_endpoint": "wss://optimism.gateway.tenderly.co",
|
|
81
|
+
"ledger_config": {
|
|
82
|
+
"address": "https://mainnet.optimism.io",
|
|
83
|
+
"chain_id": 10,
|
|
84
|
+
"poa_chain": false,
|
|
85
|
+
"default_gas_price_strategy": "eip1559",
|
|
86
|
+
"is_gas_estimation_enabled": false
|
|
87
|
+
},
|
|
88
|
+
"gas_limit": 100000,
|
|
89
|
+
"contract_abi_url": "https://api-optimistic.etherscan.io/api?module=contract&action=getabi&address={contract_address}",
|
|
90
|
+
"subgraph_url": ""
|
|
91
|
+
}
|
|
92
|
+
}
|
mech_client/interact.py
CHANGED
|
@@ -26,9 +26,12 @@ python client.py <prompt> <tool>
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
import asyncio
|
|
29
|
+
import json
|
|
29
30
|
import os
|
|
31
|
+
import sys
|
|
30
32
|
import time
|
|
31
33
|
import warnings
|
|
34
|
+
from dataclasses import asdict, dataclass
|
|
32
35
|
from datetime import datetime
|
|
33
36
|
from enum import Enum
|
|
34
37
|
from pathlib import Path
|
|
@@ -41,47 +44,18 @@ from aea_ledger_ethereum import EthereumApi, EthereumCrypto
|
|
|
41
44
|
from web3 import Web3
|
|
42
45
|
from web3.contract import Contract as Web3Contract
|
|
43
46
|
|
|
44
|
-
from mech_client.acn import
|
|
45
|
-
watch_for_data_url_from_mech,
|
|
46
|
-
watch_for_data_url_from_mech_sync,
|
|
47
|
-
)
|
|
47
|
+
from mech_client.acn import watch_for_data_url_from_mech
|
|
48
48
|
from mech_client.prompt_to_ipfs import push_metadata_to_ipfs
|
|
49
|
-
from mech_client.subgraph import query_agent_address
|
|
49
|
+
from mech_client.subgraph import query_agent_address, watch_for_data_url_from_subgraph
|
|
50
50
|
from mech_client.wss import (
|
|
51
51
|
register_event_handlers,
|
|
52
52
|
watch_for_data_url_from_wss,
|
|
53
|
-
watch_for_data_url_from_wss_sync,
|
|
54
53
|
watch_for_request_id,
|
|
55
54
|
)
|
|
56
55
|
|
|
57
56
|
|
|
58
|
-
AGENT_REGISTRY_CONTRACT = "0xE49CB081e8d96920C38aA7AB90cb0294ab4Bc8EA"
|
|
59
|
-
MECHX_CHAIN_RPC = os.environ.get(
|
|
60
|
-
"MECHX_CHAIN_RPC",
|
|
61
|
-
"https://rpc.eu-central-2.gateway.fm/v4/gnosis/non-archival/mainnet",
|
|
62
|
-
)
|
|
63
|
-
LEDGER_CONFIG = {
|
|
64
|
-
"address": MECHX_CHAIN_RPC,
|
|
65
|
-
"chain_id": 100,
|
|
66
|
-
"poa_chain": False,
|
|
67
|
-
"default_gas_price_strategy": "eip1559",
|
|
68
|
-
"is_gas_estimation_enabled": False,
|
|
69
|
-
}
|
|
70
57
|
PRIVATE_KEY_FILE_PATH = "ethereum_private_key.txt"
|
|
71
|
-
|
|
72
|
-
WSS_ENDPOINT = os.getenv(
|
|
73
|
-
"WEBSOCKET_ENDPOINT",
|
|
74
|
-
"wss://rpc.eu-central-2.gateway.fm/ws/v4/gnosis/non-archival/mainnet",
|
|
75
|
-
)
|
|
76
|
-
MANUAL_GAS_LIMIT = int(
|
|
77
|
-
os.getenv(
|
|
78
|
-
"MANUAL_GAS_LIMIT",
|
|
79
|
-
"100_000",
|
|
80
|
-
)
|
|
81
|
-
)
|
|
82
|
-
BLOCKSCOUT_API_URL = (
|
|
83
|
-
"https://gnosis.blockscout.com/api/v2/smart-contracts/{contract_address}"
|
|
84
|
-
)
|
|
58
|
+
MECH_CONFIGS = Path(__file__).parent / "configs" / "mechs.json"
|
|
85
59
|
|
|
86
60
|
MAX_RETRIES = 3
|
|
87
61
|
WAIT_SLEEP = 3.0
|
|
@@ -91,6 +65,80 @@ TIMEOUT = 60.0
|
|
|
91
65
|
warnings.filterwarnings("ignore", "The log with transaction hash.*")
|
|
92
66
|
|
|
93
67
|
|
|
68
|
+
@dataclass
|
|
69
|
+
class LedgerConfig:
|
|
70
|
+
"""Ledger configuration"""
|
|
71
|
+
|
|
72
|
+
address: str
|
|
73
|
+
chain_id: int
|
|
74
|
+
poa_chain: bool
|
|
75
|
+
default_gas_price_strategy: str
|
|
76
|
+
is_gas_estimation_enabled: bool
|
|
77
|
+
|
|
78
|
+
def __post_init__(self) -> None:
|
|
79
|
+
"""Post initialization to override with environment variables."""
|
|
80
|
+
address = os.getenv("MECHX_LEDGER_ADDRESS")
|
|
81
|
+
if address:
|
|
82
|
+
self.address = address
|
|
83
|
+
|
|
84
|
+
chain_id = os.getenv("MECHX_LEDGER_CHAIN_ID")
|
|
85
|
+
if chain_id:
|
|
86
|
+
self.chain_id = int(chain_id)
|
|
87
|
+
|
|
88
|
+
poa_chain = os.getenv("MECHX_LEDGER_POA_CHAIN")
|
|
89
|
+
if poa_chain:
|
|
90
|
+
self.poa_chain = bool(poa_chain)
|
|
91
|
+
|
|
92
|
+
default_gas_price_strategy = os.getenv(
|
|
93
|
+
"MECHX_LEDGER_DEFAULT_GAS_PRICE_STRATEGY"
|
|
94
|
+
)
|
|
95
|
+
if default_gas_price_strategy:
|
|
96
|
+
self.default_gas_price_strategy = default_gas_price_strategy
|
|
97
|
+
|
|
98
|
+
is_gas_estimation_enabled = os.getenv("MECHX_LEDGER_IS_GAS_ESTIMATION_ENABLED")
|
|
99
|
+
if is_gas_estimation_enabled:
|
|
100
|
+
self.is_gas_estimation_enabled = bool(is_gas_estimation_enabled)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class MechConfig:
|
|
105
|
+
"""Mech configuration"""
|
|
106
|
+
|
|
107
|
+
agent_registry_contract: str
|
|
108
|
+
rpc_url: str
|
|
109
|
+
wss_endpoint: str
|
|
110
|
+
ledger_config: LedgerConfig
|
|
111
|
+
gas_limit: int
|
|
112
|
+
contract_abi_url: str
|
|
113
|
+
subgraph_url: str
|
|
114
|
+
|
|
115
|
+
def __post_init__(self) -> None:
|
|
116
|
+
"""Post initialization to override with environment variables."""
|
|
117
|
+
agent_registry_contract = os.getenv("MECHX_AGENT_REGISTRY_CONTRACT")
|
|
118
|
+
if agent_registry_contract:
|
|
119
|
+
self.agent_registry_contract = agent_registry_contract
|
|
120
|
+
|
|
121
|
+
rpc_url = os.getenv("MECHX_CHAIN_RPC")
|
|
122
|
+
if rpc_url:
|
|
123
|
+
self.rpc_url = rpc_url
|
|
124
|
+
|
|
125
|
+
wss_endpoint = os.getenv("MECHX_WSS_ENDPOINT")
|
|
126
|
+
if wss_endpoint:
|
|
127
|
+
self.wss_endpoint = wss_endpoint
|
|
128
|
+
|
|
129
|
+
gas_limit = os.getenv("MECHX_GAS_LIMIT")
|
|
130
|
+
if gas_limit:
|
|
131
|
+
self.gas_limit = int(gas_limit)
|
|
132
|
+
|
|
133
|
+
contract_abi_url = os.getenv("MECHX_CONTRACT_ABI_URL")
|
|
134
|
+
if contract_abi_url:
|
|
135
|
+
self.contract_abi_url = contract_abi_url
|
|
136
|
+
|
|
137
|
+
subgraph_url = os.getenv("MECHX_SUBGRAPH_URL")
|
|
138
|
+
if subgraph_url:
|
|
139
|
+
self.subgraph_url = subgraph_url
|
|
140
|
+
|
|
141
|
+
|
|
94
142
|
class ConfirmationType(Enum):
|
|
95
143
|
"""Verification type."""
|
|
96
144
|
|
|
@@ -99,6 +147,21 @@ class ConfirmationType(Enum):
|
|
|
99
147
|
WAIT_FOR_BOTH = "wait-for-both"
|
|
100
148
|
|
|
101
149
|
|
|
150
|
+
def get_mech_config(chain_config: Optional[str] = None) -> MechConfig:
|
|
151
|
+
"""Get `MechConfig` configuration"""
|
|
152
|
+
with open(MECH_CONFIGS, "r", encoding="UTF-8") as file:
|
|
153
|
+
data = json.load(file)
|
|
154
|
+
|
|
155
|
+
if chain_config is None:
|
|
156
|
+
chain_config = next(iter(data))
|
|
157
|
+
|
|
158
|
+
print(f"Chain configuration: {chain_config}")
|
|
159
|
+
entry = data[chain_config].copy()
|
|
160
|
+
ledger_config = LedgerConfig(**entry.pop("ledger_config"))
|
|
161
|
+
mech_config = MechConfig(**entry, ledger_config=ledger_config)
|
|
162
|
+
return mech_config
|
|
163
|
+
|
|
164
|
+
|
|
102
165
|
def calculate_topic_id(event: Dict) -> str:
|
|
103
166
|
"""Caclulate topic ID"""
|
|
104
167
|
text = event["name"]
|
|
@@ -124,11 +187,22 @@ def get_event_signatures(abi: List) -> Tuple[str, str]:
|
|
|
124
187
|
return request, deliver
|
|
125
188
|
|
|
126
189
|
|
|
127
|
-
def get_abi(contract_address: str) -> List:
|
|
190
|
+
def get_abi(contract_address: str, contract_abi_url: str) -> List:
|
|
128
191
|
"""Get contract abi"""
|
|
129
|
-
abi_request_url =
|
|
192
|
+
abi_request_url = contract_abi_url.format(contract_address=contract_address)
|
|
130
193
|
response = requests.get(abi_request_url).json()
|
|
131
|
-
|
|
194
|
+
|
|
195
|
+
if "result" in response:
|
|
196
|
+
result = response["result"]
|
|
197
|
+
try:
|
|
198
|
+
abi = json.loads(result)
|
|
199
|
+
except json.JSONDecodeError:
|
|
200
|
+
print("Error: Failed to parse 'result' field as JSON")
|
|
201
|
+
sys.exit(1)
|
|
202
|
+
else:
|
|
203
|
+
abi = response.get("abi")
|
|
204
|
+
|
|
205
|
+
return abi if abi else []
|
|
132
206
|
|
|
133
207
|
|
|
134
208
|
def get_contract(
|
|
@@ -194,7 +268,11 @@ def _tool_selector_prompt(available_tools: List[str]) -> str:
|
|
|
194
268
|
|
|
195
269
|
|
|
196
270
|
def verify_or_retrieve_tool(
|
|
197
|
-
agent_id: int,
|
|
271
|
+
agent_id: int,
|
|
272
|
+
ledger_api: EthereumApi,
|
|
273
|
+
agent_registry_contract: str,
|
|
274
|
+
contract_abi_url: str,
|
|
275
|
+
tool: Optional[str] = None,
|
|
198
276
|
) -> str:
|
|
199
277
|
"""
|
|
200
278
|
Checks if the tool is valid and for what agent.
|
|
@@ -203,12 +281,21 @@ def verify_or_retrieve_tool(
|
|
|
203
281
|
:type agent_id: int
|
|
204
282
|
:param ledger_api: The Ethereum API used for interacting with the ledger.
|
|
205
283
|
:type ledger_api: EthereumApi
|
|
284
|
+
:param agent_registry_contract: Agent registry contract address.
|
|
285
|
+
:type agent_registry_contract: str
|
|
286
|
+
:param contract_abi_url: Block explorer URL.
|
|
287
|
+
:type contract_abi_url: str
|
|
206
288
|
:param tool: The tool to verify or retrieve (optional).
|
|
207
289
|
:type tool: Optional[str]
|
|
208
290
|
:return: The result of the verification or retrieval.
|
|
209
291
|
:rtype: str
|
|
210
292
|
"""
|
|
211
|
-
available_tools = fetch_tools(
|
|
293
|
+
available_tools = fetch_tools(
|
|
294
|
+
agent_id=agent_id,
|
|
295
|
+
ledger_api=ledger_api,
|
|
296
|
+
agent_registry_contract=agent_registry_contract,
|
|
297
|
+
contract_abi_url=contract_abi_url,
|
|
298
|
+
)
|
|
212
299
|
if tool is not None and tool not in available_tools:
|
|
213
300
|
raise ValueError(
|
|
214
301
|
f"Provided tool `{tool}` not in the list of available tools; Available tools={available_tools}"
|
|
@@ -218,11 +305,16 @@ def verify_or_retrieve_tool(
|
|
|
218
305
|
return _tool_selector_prompt(available_tools=available_tools)
|
|
219
306
|
|
|
220
307
|
|
|
221
|
-
def fetch_tools(
|
|
308
|
+
def fetch_tools(
|
|
309
|
+
agent_id: int,
|
|
310
|
+
ledger_api: EthereumApi,
|
|
311
|
+
agent_registry_contract: str,
|
|
312
|
+
contract_abi_url: str,
|
|
313
|
+
) -> List[str]:
|
|
222
314
|
"""Fetch tools for specified agent ID."""
|
|
223
315
|
mech_registry = get_contract(
|
|
224
|
-
contract_address=
|
|
225
|
-
abi=get_abi(
|
|
316
|
+
contract_address=agent_registry_contract,
|
|
317
|
+
abi=get_abi(agent_registry_contract, contract_abi_url),
|
|
226
318
|
ledger_api=ledger_api,
|
|
227
319
|
)
|
|
228
320
|
token_uri = mech_registry.functions.tokenURI(agent_id).call()
|
|
@@ -234,13 +326,15 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
234
326
|
crypto: EthereumCrypto,
|
|
235
327
|
ledger_api: EthereumApi,
|
|
236
328
|
mech_contract: Web3Contract,
|
|
329
|
+
gas_limit: int,
|
|
237
330
|
prompt: str,
|
|
238
331
|
tool: str,
|
|
332
|
+
extra_attributes: Optional[Dict[str, Any]] = None,
|
|
239
333
|
price: int = 10_000_000_000_000_000,
|
|
240
334
|
retries: Optional[int] = None,
|
|
241
335
|
timeout: Optional[float] = None,
|
|
242
336
|
sleep: Optional[float] = None,
|
|
243
|
-
) ->
|
|
337
|
+
) -> Optional[str]:
|
|
244
338
|
"""
|
|
245
339
|
Sends a request to the mech.
|
|
246
340
|
|
|
@@ -250,10 +344,14 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
250
344
|
:type ledger_api: EthereumApi
|
|
251
345
|
:param mech_contract: The mech contract instance.
|
|
252
346
|
:type mech_contract: Web3Contract
|
|
347
|
+
:param gas_limit: Gas limit.
|
|
348
|
+
:type gas_limit: int
|
|
253
349
|
:param prompt: The request prompt.
|
|
254
350
|
:type prompt: str
|
|
255
351
|
:param tool: The requested tool.
|
|
256
352
|
:type tool: str
|
|
353
|
+
:param extra_attributes: Extra attributes to be included in the request metadata.
|
|
354
|
+
:type extra_attributes: Optional[Dict[str,Any]]
|
|
257
355
|
:param price: The price for the request (default: 10_000_000_000_000_000).
|
|
258
356
|
:type price: int
|
|
259
357
|
:param retries: Number of retries for sending a transaction
|
|
@@ -262,15 +360,19 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
262
360
|
:type timeout: float
|
|
263
361
|
:param sleep: Amount of sleep before retrying the transaction
|
|
264
362
|
:type sleep: float
|
|
363
|
+
:return: The transaction hash.
|
|
364
|
+
:rtype: Optional[str]
|
|
265
365
|
"""
|
|
266
|
-
v1_file_hash_hex_truncated, v1_file_hash_hex = push_metadata_to_ipfs(
|
|
366
|
+
v1_file_hash_hex_truncated, v1_file_hash_hex = push_metadata_to_ipfs(
|
|
367
|
+
prompt, tool, extra_attributes
|
|
368
|
+
)
|
|
267
369
|
print(f"Prompt uploaded: https://gateway.autonolas.tech/ipfs/{v1_file_hash_hex}")
|
|
268
370
|
method_name = "request"
|
|
269
371
|
methord_args = {"data": v1_file_hash_hex_truncated}
|
|
270
372
|
tx_args = {
|
|
271
373
|
"sender_address": crypto.address,
|
|
272
374
|
"value": price,
|
|
273
|
-
"gas":
|
|
375
|
+
"gas": gas_limit,
|
|
274
376
|
}
|
|
275
377
|
|
|
276
378
|
tries = 0
|
|
@@ -295,21 +397,24 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
295
397
|
raise_on_try=True,
|
|
296
398
|
)
|
|
297
399
|
print(f"Transaction sent: https://gnosisscan.io/tx/{transaction_digest}")
|
|
298
|
-
return
|
|
400
|
+
return transaction_digest
|
|
299
401
|
except Exception as e: # pylint: disable=broad-except
|
|
300
402
|
print(
|
|
301
403
|
f"Error occured while sending the transaction: {e}; Retrying in {sleep}"
|
|
302
404
|
)
|
|
303
405
|
time.sleep(sleep)
|
|
406
|
+
return None
|
|
304
407
|
|
|
305
408
|
|
|
306
409
|
def wait_for_data_url( # pylint: disable=too-many-arguments
|
|
307
410
|
request_id: str,
|
|
308
411
|
wss: websocket.WebSocket,
|
|
309
412
|
mech_contract: Web3Contract,
|
|
413
|
+
subgraph_url: str,
|
|
310
414
|
deliver_signature: str,
|
|
311
415
|
ledger_api: EthereumApi,
|
|
312
416
|
crypto: Crypto,
|
|
417
|
+
confirmation_type: ConfirmationType = ConfirmationType.WAIT_FOR_BOTH,
|
|
313
418
|
) -> Any:
|
|
314
419
|
"""
|
|
315
420
|
Wait for data from on-chain/off-chain.
|
|
@@ -320,32 +425,54 @@ def wait_for_data_url( # pylint: disable=too-many-arguments
|
|
|
320
425
|
:type wss: websocket.WebSocket
|
|
321
426
|
:param mech_contract: The mech contract instance.
|
|
322
427
|
:type mech_contract: Web3Contract
|
|
428
|
+
:param subgraph_url: Subgraph URL.
|
|
429
|
+
:type subgraph_url: str
|
|
323
430
|
:param deliver_signature: Topic signature for Deliver event
|
|
324
431
|
:type deliver_signature: str
|
|
325
432
|
:param ledger_api: The Ethereum API used for interacting with the ledger.
|
|
326
433
|
:type ledger_api: EthereumApi
|
|
327
434
|
:param crypto: The cryptographic object.
|
|
328
435
|
:type crypto: Crypto
|
|
436
|
+
:param confirmation_type: The confirmation type for the interaction (default: ConfirmationType.WAIT_FOR_BOTH).
|
|
437
|
+
:type confirmation_type: ConfirmationType
|
|
329
438
|
:return: The data received from on-chain/off-chain.
|
|
330
439
|
:rtype: Any
|
|
331
440
|
"""
|
|
332
441
|
loop = asyncio.new_event_loop()
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
442
|
+
tasks = []
|
|
443
|
+
|
|
444
|
+
if confirmation_type in (
|
|
445
|
+
ConfirmationType.OFF_CHAIN,
|
|
446
|
+
ConfirmationType.WAIT_FOR_BOTH,
|
|
447
|
+
):
|
|
448
|
+
off_chain_task = loop.create_task(watch_for_data_url_from_mech(crypto=crypto))
|
|
449
|
+
tasks.append(off_chain_task)
|
|
450
|
+
|
|
451
|
+
if confirmation_type in (
|
|
452
|
+
ConfirmationType.ON_CHAIN,
|
|
453
|
+
ConfirmationType.WAIT_FOR_BOTH,
|
|
454
|
+
):
|
|
455
|
+
on_chain_task = loop.create_task(
|
|
456
|
+
watch_for_data_url_from_wss(
|
|
457
|
+
request_id=request_id,
|
|
458
|
+
wss=wss,
|
|
459
|
+
mech_contract=mech_contract,
|
|
460
|
+
deliver_signature=deliver_signature,
|
|
461
|
+
ledger_api=ledger_api,
|
|
462
|
+
loop=loop,
|
|
463
|
+
)
|
|
342
464
|
)
|
|
343
|
-
|
|
465
|
+
mech_task = loop.create_task(
|
|
466
|
+
watch_for_data_url_from_subgraph(request_id=request_id, url=subgraph_url)
|
|
467
|
+
)
|
|
468
|
+
tasks.append(mech_task)
|
|
469
|
+
tasks.append(on_chain_task)
|
|
344
470
|
|
|
345
471
|
async def _wait_for_tasks() -> Any: # type: ignore
|
|
346
472
|
"""Wait for tasks to finish."""
|
|
347
473
|
(finished, *_), unfinished = await asyncio.wait(
|
|
348
|
-
|
|
474
|
+
tasks,
|
|
475
|
+
return_when=asyncio.FIRST_COMPLETED,
|
|
349
476
|
)
|
|
350
477
|
for task in unfinished:
|
|
351
478
|
task.cancel()
|
|
@@ -360,11 +487,13 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
360
487
|
prompt: str,
|
|
361
488
|
agent_id: int,
|
|
362
489
|
tool: Optional[str] = None,
|
|
490
|
+
extra_attributes: Optional[Dict[str, Any]] = None,
|
|
363
491
|
private_key_path: Optional[str] = None,
|
|
364
492
|
confirmation_type: ConfirmationType = ConfirmationType.WAIT_FOR_BOTH,
|
|
365
493
|
retries: Optional[int] = None,
|
|
366
494
|
timeout: Optional[float] = None,
|
|
367
495
|
sleep: Optional[float] = None,
|
|
496
|
+
chain_config: Optional[str] = None,
|
|
368
497
|
) -> Any:
|
|
369
498
|
"""
|
|
370
499
|
Interact with agent mech contract.
|
|
@@ -375,6 +504,8 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
375
504
|
:type agent_id: int
|
|
376
505
|
:param tool: The tool to interact with (optional).
|
|
377
506
|
:type tool: Optional[str]
|
|
507
|
+
:param extra_attributes: Extra attributes to be included in the request metadata (optional).
|
|
508
|
+
:type extra_attributes: Optional[Dict[str, Any]]
|
|
378
509
|
:param private_key_path: The path to the private key file (optional).
|
|
379
510
|
:type private_key_path: Optional[str]
|
|
380
511
|
:param confirmation_type: The confirmation type for the interaction (default: ConfirmationType.WAIT_FOR_BOTH).
|
|
@@ -386,9 +517,18 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
386
517
|
:type timeout: float
|
|
387
518
|
:param sleep: Amount of sleep before retrying the transaction
|
|
388
519
|
:type sleep: float
|
|
520
|
+
:param chain_config: Id of the mech's chain configuration (stored configs/mechs.json)
|
|
521
|
+
:type chain_config: str:
|
|
389
522
|
:rtype: Any
|
|
390
523
|
"""
|
|
391
|
-
|
|
524
|
+
mech_config = get_mech_config(chain_config)
|
|
525
|
+
ledger_config = mech_config.ledger_config
|
|
526
|
+
contract_address = query_agent_address(
|
|
527
|
+
agent_id=agent_id,
|
|
528
|
+
timeout=timeout,
|
|
529
|
+
url=mech_config.subgraph_url,
|
|
530
|
+
chain_config=chain_config,
|
|
531
|
+
)
|
|
392
532
|
if contract_address is None:
|
|
393
533
|
raise ValueError(f"Agent with ID {agent_id} does not exist!")
|
|
394
534
|
|
|
@@ -398,12 +538,21 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
398
538
|
f"Private key file `{private_key_path}` does not exist!"
|
|
399
539
|
)
|
|
400
540
|
|
|
401
|
-
wss = websocket.create_connection(
|
|
541
|
+
wss = websocket.create_connection(mech_config.wss_endpoint)
|
|
402
542
|
crypto = EthereumCrypto(private_key_path=private_key_path)
|
|
403
|
-
ledger_api = EthereumApi(**
|
|
543
|
+
ledger_api = EthereumApi(**asdict(ledger_config))
|
|
404
544
|
|
|
405
|
-
tool = verify_or_retrieve_tool(
|
|
406
|
-
|
|
545
|
+
tool = verify_or_retrieve_tool(
|
|
546
|
+
agent_id=agent_id,
|
|
547
|
+
ledger_api=ledger_api,
|
|
548
|
+
tool=tool,
|
|
549
|
+
agent_registry_contract=mech_config.agent_registry_contract,
|
|
550
|
+
contract_abi_url=mech_config.contract_abi_url,
|
|
551
|
+
)
|
|
552
|
+
abi = get_abi(
|
|
553
|
+
contract_address=contract_address,
|
|
554
|
+
contract_abi_url=mech_config.contract_abi_url,
|
|
555
|
+
)
|
|
407
556
|
mech_contract = get_contract(
|
|
408
557
|
contract_address=contract_address, abi=abi, ledger_api=ledger_api
|
|
409
558
|
)
|
|
@@ -419,12 +568,15 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
419
568
|
crypto=crypto,
|
|
420
569
|
ledger_api=ledger_api,
|
|
421
570
|
mech_contract=mech_contract,
|
|
571
|
+
gas_limit=mech_config.gas_limit,
|
|
422
572
|
prompt=prompt,
|
|
423
573
|
tool=tool,
|
|
574
|
+
extra_attributes=extra_attributes,
|
|
424
575
|
retries=retries,
|
|
425
576
|
timeout=timeout,
|
|
426
577
|
sleep=sleep,
|
|
427
578
|
)
|
|
579
|
+
print("Waiting for transaction receipt...")
|
|
428
580
|
request_id = watch_for_request_id(
|
|
429
581
|
wss=wss,
|
|
430
582
|
mech_contract=mech_contract,
|
|
@@ -432,25 +584,17 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
|
|
|
432
584
|
request_signature=request_event_signature,
|
|
433
585
|
)
|
|
434
586
|
print(f"Created on-chain request with ID {request_id}")
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
data_url = wait_for_data_url(
|
|
447
|
-
request_id=request_id,
|
|
448
|
-
wss=wss,
|
|
449
|
-
mech_contract=mech_contract,
|
|
450
|
-
deliver_signature=deliver_event_signature,
|
|
451
|
-
ledger_api=ledger_api,
|
|
452
|
-
crypto=crypto,
|
|
453
|
-
)
|
|
587
|
+
data_url = wait_for_data_url(
|
|
588
|
+
request_id=request_id,
|
|
589
|
+
wss=wss,
|
|
590
|
+
mech_contract=mech_contract,
|
|
591
|
+
subgraph_url=mech_config.subgraph_url,
|
|
592
|
+
deliver_signature=deliver_event_signature,
|
|
593
|
+
ledger_api=ledger_api,
|
|
594
|
+
crypto=crypto,
|
|
595
|
+
confirmation_type=confirmation_type,
|
|
596
|
+
)
|
|
597
|
+
|
|
454
598
|
print(f"Data arrived: {data_url}")
|
|
455
599
|
data = requests.get(f"{data_url}/{request_id}").json()
|
|
456
600
|
print(f"Data from agent: {data}")
|
mech_client/prompt_to_ipfs.py
CHANGED
|
@@ -29,12 +29,14 @@ import json
|
|
|
29
29
|
import shutil
|
|
30
30
|
import tempfile
|
|
31
31
|
import uuid
|
|
32
|
-
from typing import Tuple
|
|
32
|
+
from typing import Any, Dict, Optional, Tuple
|
|
33
33
|
|
|
34
34
|
from mech_client.push_to_ipfs import push_to_ipfs
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def push_metadata_to_ipfs(
|
|
37
|
+
def push_metadata_to_ipfs(
|
|
38
|
+
prompt: str, tool: str, extra_attributes: Optional[Dict[str, Any]] = None
|
|
39
|
+
) -> Tuple[str, str]:
|
|
38
40
|
"""
|
|
39
41
|
Pushes metadata object to IPFS.
|
|
40
42
|
|
|
@@ -42,10 +44,14 @@ def push_metadata_to_ipfs(prompt: str, tool: str) -> Tuple[str, str]:
|
|
|
42
44
|
:type prompt: str
|
|
43
45
|
:param tool: Tool string.
|
|
44
46
|
:type tool: str
|
|
47
|
+
:param extra_attributes: Extra attributes to be included in the request metadata.
|
|
48
|
+
:type extra_attributes: Optional[Dict[str,Any]]
|
|
45
49
|
:return: Tuple containing the IPFS hash and truncated IPFS hash.
|
|
46
50
|
:rtype: Tuple[str, str]
|
|
47
51
|
"""
|
|
48
52
|
metadata = {"prompt": prompt, "tool": tool, "nonce": str(uuid.uuid4())}
|
|
53
|
+
if extra_attributes:
|
|
54
|
+
metadata.update(extra_attributes)
|
|
49
55
|
dirpath = tempfile.mkdtemp()
|
|
50
56
|
file_name = dirpath + "metadata.json"
|
|
51
57
|
with open(file_name, "w", encoding="utf-8") as f:
|
mech_client/subgraph.py
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
"""Subgraph client for mech."""
|
|
21
21
|
|
|
22
|
+
import asyncio
|
|
22
23
|
from string import Template
|
|
23
24
|
from typing import Optional
|
|
24
25
|
|
|
@@ -26,7 +27,6 @@ from gql import Client, gql
|
|
|
26
27
|
from gql.transport.aiohttp import AIOHTTPTransport
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
MECH_SUBGRAPH_URL = "https://api.studio.thegraph.com/query/57238/mech/version/latest"
|
|
30
30
|
AGENT_QUERY_TEMPLATE = Template(
|
|
31
31
|
"""{
|
|
32
32
|
createMeches(where:{agentId:$agent_id}) {
|
|
@@ -35,22 +35,53 @@ AGENT_QUERY_TEMPLATE = Template(
|
|
|
35
35
|
}
|
|
36
36
|
"""
|
|
37
37
|
)
|
|
38
|
+
DELIVER_QUERY_TEMPLATE = Template(
|
|
39
|
+
"""{
|
|
40
|
+
delivers(
|
|
41
|
+
orderBy: blockTimestamp
|
|
42
|
+
where: {requestId:"$request_id"}
|
|
43
|
+
orderDirection: desc
|
|
44
|
+
) {
|
|
45
|
+
ipfsHash
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
"""
|
|
49
|
+
)
|
|
50
|
+
DEFAULT_TIMEOUT = 600.0
|
|
38
51
|
|
|
39
52
|
|
|
40
|
-
def query_agent_address(
|
|
41
|
-
agent_id: int,
|
|
53
|
+
def query_agent_address( # pylint: disable=too-many-return-statements
|
|
54
|
+
agent_id: int,
|
|
55
|
+
url: str,
|
|
56
|
+
timeout: Optional[float] = None,
|
|
57
|
+
chain_config: Optional[str] = None,
|
|
42
58
|
) -> Optional[str]:
|
|
43
59
|
"""
|
|
44
60
|
Query agent address from subgraph.
|
|
45
61
|
|
|
46
62
|
:param agent_id: The ID of the agent.
|
|
47
|
-
:param timeout: Timeout for the request.
|
|
48
63
|
:type agent_id: int
|
|
64
|
+
:param url: Subgraph URL.
|
|
65
|
+
:type url: str
|
|
66
|
+
:param timeout: Timeout for the request.
|
|
67
|
+
:type timeout: Optional[float]
|
|
68
|
+
:type chain_config: Optional[str]:
|
|
49
69
|
:return: The agent address if found, None otherwise.
|
|
50
70
|
:rtype: Optional[str]
|
|
51
71
|
"""
|
|
72
|
+
# temporary hard coded until subgraph present
|
|
73
|
+
if chain_config == "base" and agent_id == 2:
|
|
74
|
+
return "0x111D7DB1B752AB4D2cC0286983D9bd73a49bac6c"
|
|
75
|
+
if chain_config == "arbitrum" and agent_id == 2:
|
|
76
|
+
return "0x1FDAD3a5af5E96e5a64Fc0662B1814458F114597"
|
|
77
|
+
if chain_config == "polygon" and agent_id == 2:
|
|
78
|
+
return "0xbF92568718982bf65ee4af4F7020205dE2331a8a"
|
|
79
|
+
if chain_config == "celo" and agent_id == 2:
|
|
80
|
+
return "0x230eD015735c0D01EA0AaD2786Ed6Bd3C6e75912"
|
|
81
|
+
if chain_config == "optimism" and agent_id == 2:
|
|
82
|
+
return "0xDd40E7D93c37eFD860Bd53Ab90b2b0a8D05cf71a"
|
|
52
83
|
client = Client(
|
|
53
|
-
transport=AIOHTTPTransport(url=
|
|
84
|
+
transport=AIOHTTPTransport(url=url),
|
|
54
85
|
execute_timeout=timeout or 30.0,
|
|
55
86
|
)
|
|
56
87
|
response = client.execute(
|
|
@@ -64,3 +95,66 @@ def query_agent_address(
|
|
|
64
95
|
|
|
65
96
|
(record,) = mechs
|
|
66
97
|
return record["mech"]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def query_deliver_hash(
|
|
101
|
+
request_id: str, url: str, timeout: Optional[float] = None
|
|
102
|
+
) -> Optional[str]:
|
|
103
|
+
"""
|
|
104
|
+
Query deliver IPFS hash from subgraph.
|
|
105
|
+
|
|
106
|
+
:param request_id: The ID of the mech request.
|
|
107
|
+
:type request_id: str
|
|
108
|
+
:param url: Subgraph URL.
|
|
109
|
+
:type url: str
|
|
110
|
+
:param timeout: Timeout for the request.
|
|
111
|
+
:type timeout: Optional[float]
|
|
112
|
+
:return: The deliver IPFS hash if found, None otherwise.
|
|
113
|
+
:rtype: Optional[str]
|
|
114
|
+
"""
|
|
115
|
+
client = Client(
|
|
116
|
+
transport=AIOHTTPTransport(url=url),
|
|
117
|
+
execute_timeout=timeout or 30.0,
|
|
118
|
+
)
|
|
119
|
+
response = await client.execute_async(
|
|
120
|
+
document=gql(
|
|
121
|
+
request_string=DELIVER_QUERY_TEMPLATE.substitute({"request_id": request_id})
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
delivers = response["delivers"] # pylint: disable=unsubscriptable-object
|
|
125
|
+
if len(delivers) == 0:
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
(record,) = delivers
|
|
129
|
+
return record["ipfsHash"]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
async def watch_for_data_url_from_subgraph(
|
|
133
|
+
request_id: str, url: str, timeout: Optional[float] = None
|
|
134
|
+
) -> Optional[str]:
|
|
135
|
+
"""
|
|
136
|
+
Continuously query for data URL until it's available or timeout is reached.
|
|
137
|
+
|
|
138
|
+
:param request_id: The ID of the mech request.
|
|
139
|
+
:type request_id: str
|
|
140
|
+
:param url: Subgraph URL.
|
|
141
|
+
:type url: str
|
|
142
|
+
:param timeout: Maximum time to wait for the data URL in seconds. Defaults to DEFAULT_TIMEOUT.
|
|
143
|
+
:type timeout: Optional[float]
|
|
144
|
+
:return: Data URL if available within timeout, otherwise None.
|
|
145
|
+
:rtype: Optional[str]
|
|
146
|
+
"""
|
|
147
|
+
timeout = timeout or DEFAULT_TIMEOUT
|
|
148
|
+
start_time = asyncio.get_event_loop().time()
|
|
149
|
+
while True:
|
|
150
|
+
response = await query_deliver_hash(request_id=request_id, url=url)
|
|
151
|
+
if response is not None:
|
|
152
|
+
return f"https://gateway.autonolas.tech/ipfs/{response}"
|
|
153
|
+
|
|
154
|
+
if asyncio.get_event_loop().time() - start_time >= timeout:
|
|
155
|
+
print(f"Error: No response received after {timeout} seconds.")
|
|
156
|
+
break
|
|
157
|
+
|
|
158
|
+
await asyncio.sleep(5)
|
|
159
|
+
|
|
160
|
+
return None
|
mech_client/wss.py
CHANGED
|
@@ -186,41 +186,3 @@ async def watch_for_data_url_from_wss( # pylint: disable=too-many-arguments
|
|
|
186
186
|
if request_id != str(rich_logs[0]["args"]["requestId"]):
|
|
187
187
|
continue
|
|
188
188
|
return f"https://gateway.autonolas.tech/ipfs/f01701220{data.hex()}"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def watch_for_data_url_from_wss_sync(
|
|
192
|
-
request_id: str,
|
|
193
|
-
wss: websocket.WebSocket,
|
|
194
|
-
mech_contract: Web3Contract,
|
|
195
|
-
deliver_signature: str,
|
|
196
|
-
ledger_api: EthereumApi,
|
|
197
|
-
) -> Any:
|
|
198
|
-
"""
|
|
199
|
-
Watches for data on-chain.
|
|
200
|
-
|
|
201
|
-
:param request_id: The ID of the request.
|
|
202
|
-
:type request_id: str
|
|
203
|
-
:param wss: The WebSocket connection object.
|
|
204
|
-
:type wss: websocket.WebSocket
|
|
205
|
-
:param mech_contract: The mech contract instance.
|
|
206
|
-
:type mech_contract: Web3Contract
|
|
207
|
-
:param deliver_signature: Topic signature for Deliver event
|
|
208
|
-
:type deliver_signature: str
|
|
209
|
-
:param ledger_api: The Ethereum API used for interacting with the ledger.
|
|
210
|
-
:type ledger_api: EthereumApi
|
|
211
|
-
:return: The data received from on-chain.
|
|
212
|
-
:rtype: Any
|
|
213
|
-
"""
|
|
214
|
-
loop = asyncio.new_event_loop()
|
|
215
|
-
task = loop.create_task(
|
|
216
|
-
watch_for_data_url_from_wss(
|
|
217
|
-
request_id=request_id,
|
|
218
|
-
wss=wss,
|
|
219
|
-
mech_contract=mech_contract,
|
|
220
|
-
deliver_signature=deliver_signature,
|
|
221
|
-
ledger_api=ledger_api,
|
|
222
|
-
loop=loop,
|
|
223
|
-
)
|
|
224
|
-
)
|
|
225
|
-
loop.run_until_complete(task)
|
|
226
|
-
return task.result()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mech-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
4
4
|
Summary: Basic client to interact with a mech
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: David Minarsch
|
|
@@ -31,10 +31,10 @@ Basic client to interact with a mech
|
|
|
31
31
|
pip install mech-client
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Note: If you encounter an "Out of gas" error when executing the tool, you will need to increase the gas limit by setting, e.g.,
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
export
|
|
37
|
+
export MECHX_GAS_LIMIT=200000
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## CLI:
|
|
@@ -103,6 +103,7 @@ mechx interact <prompt> <agent_id> --key <key_file>
|
|
|
103
103
|
Example output:
|
|
104
104
|
```bash
|
|
105
105
|
mechx interact "write a short poem" 3 --key ~/gnosis_key --tool openai-text-davinci-003
|
|
106
|
+
Chain configuration: gnosis
|
|
106
107
|
Prompt uploaded: https://gateway.autonolas.tech/ipfs/f01701220ad773628911d12e28f005e3f249e990d684e5dba07542259195602f9afed30bf
|
|
107
108
|
Transaction sent: https://gnosisscan.io/tx/0x0d9209e32e965a820b9e80accfcd71ea3b1174b9758dd251c2e627a60ec426a5
|
|
108
109
|
Created on-chain request with ID 111240237160304797537720810617416341148235899500021985333360197012735240803849
|
|
@@ -110,10 +111,11 @@ Data arrived: https://gateway.autonolas.tech/ipfs/bafybeifk2h35ncszlze7t64rpblfo
|
|
|
110
111
|
Data from agent: {'requestId': 111240237160304797537720810617416341148235899500021985333360197012735240803849, 'result': "\n\nI am brave and I'm strong\nI don't hide away my song\nI am here and I'm proud\nMy voice will be heard loud!"}
|
|
111
112
|
```
|
|
112
113
|
|
|
113
|
-
By default the client will wait for data to arrive from on-chain using the websocket subscription and off-chain using the ACN and show you the result which arrives first. You can specify the type of confirmation you want using `--confirm` flag like this
|
|
114
|
+
By default the client will wait for data to arrive from on-chain using the websocket subscription and subgraph, and off-chain using the ACN and show you the result which arrives first. You can specify the type of confirmation you want using `--confirm` flag like this
|
|
114
115
|
|
|
115
116
|
```bash
|
|
116
117
|
mechx interact "write a short poem" 3 --key ~/gnosis_key --tool openai-text-davinci-003 --confirm on-chain
|
|
118
|
+
Chain configuration: gnosis
|
|
117
119
|
Prompt uploaded: https://gateway.autonolas.tech/ipfs/f017012205e37f761221a8ba4005e91c36b94153e9432b8888ff2acae6b101dd5a5de6768
|
|
118
120
|
Transaction sent: https://gnosisscan.io/tx/0xf1ef63f617717bbb8deb09699af99aa39f10155d33796de2fd7eb61c9c1458b6
|
|
119
121
|
Created on-chain request with ID 81653153529124597849081567361606842861262371002932574194580478443414142139857
|
|
@@ -121,6 +123,30 @@ Data arrived: https://gateway.autonolas.tech/ipfs/f0170122069b55e077430a00f3cbc3
|
|
|
121
123
|
Data from agent: {'requestId': 81653153529124597849081567361606842861262371002932574194580478443414142139857, 'result': "\n\nA summer breeze, so sweet,\nA gentle reminder of summer's heat.\nThe sky so blue, no cloud in sight,\nA perfect day, a wondrous sight."}
|
|
122
124
|
```
|
|
123
125
|
|
|
126
|
+
### Chain configuration
|
|
127
|
+
|
|
128
|
+
Configurations for different chains are stored in the file `configs/mechs.json`. By default, `mech interact` will choose the first configuration on the JSON. You can specify which config you want to use using the `--chain-config` flag, for example,
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
mechx interact <prompt> <agent_id> --chain-config gnosis
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Additionally, you can override any configuration parameter by exporting any of the following environment variables:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
MECHX_CHAIN_RPC
|
|
138
|
+
MECHX_WSS_ENDPOINT
|
|
139
|
+
MECHX_GAS_LIMIT
|
|
140
|
+
MECHX_CONTRACT_ABI_URL
|
|
141
|
+
MECHX_SUBGRAPH_URL
|
|
142
|
+
|
|
143
|
+
MECHX_LEDGER_ADDRESS
|
|
144
|
+
MECHX_LEDGER_CHAIN_ID
|
|
145
|
+
MECHX_LEDGER_POA_CHAIN
|
|
146
|
+
MECHX_LEDGER_DEFAULT_GAS_PRICE_STRATEGY
|
|
147
|
+
MECHX_LEDGER_IS_GAS_ESTIMATION_ENABLED
|
|
148
|
+
```
|
|
149
|
+
|
|
124
150
|
## Programmatic Usage:
|
|
125
151
|
|
|
126
152
|
```python
|
|
@@ -149,7 +175,7 @@ poetry install && poetry shell
|
|
|
149
175
|
|
|
150
176
|
## Release guide:
|
|
151
177
|
|
|
152
|
-
- Bump versions in `pyproject.toml
|
|
178
|
+
- Bump versions in `pyproject.toml`, `mech_client/__init__.py` and `SECURITY.md`
|
|
153
179
|
- `poetry lock`
|
|
154
180
|
- `rm -rf dist`
|
|
155
181
|
- `autonomy packages sync --update-packages`
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
mech_client/__init__.py,sha256=
|
|
2
|
-
mech_client/acn.py,sha256=
|
|
3
|
-
mech_client/cli.py,sha256=
|
|
1
|
+
mech_client/__init__.py,sha256=s8E8bTccJTXUKsK6N8pSwD7FabU-dQUps-y98raJYZQ,43
|
|
2
|
+
mech_client/acn.py,sha256=Rj_jLPvJ5loDQfGbu3a_O24cJC4SwIErLceSz_zVYS8,5356
|
|
3
|
+
mech_client/cli.py,sha256=QnC3Z_vth__ZiU12chRl8q5LYDLjyjzaofB41mWlq8c,4608
|
|
4
|
+
mech_client/configs/mechs.json,sha256=ed7mp5Z6qC8QuLIx4iqV9Y_u3KmIOhf3zMfpdZ951cQ,4026
|
|
4
5
|
mech_client/helpers/__init__.py,sha256=f13zpwGDaKQUjML-5Iq66rRfzg8IS5UNK5I8gBr7w54,1028
|
|
5
6
|
mech_client/helpers/acn/README.md,sha256=WMXR2Lk0IpWjr3vpZ8cxcTHk4gwsx4wC06UPkwj9dbQ,1641
|
|
6
7
|
mech_client/helpers/acn/__init__.py,sha256=72WrGEDIuYKvlWQnTajFQ9bNrDy2sFfTYDP62he4doI,1141
|
|
@@ -29,14 +30,14 @@ mech_client/helpers/p2p_libp2p_client/README.md,sha256=6x9s6P7TdKkcvAS1wMFHXRz4a
|
|
|
29
30
|
mech_client/helpers/p2p_libp2p_client/__init__.py,sha256=-GOP3D_JnmXTDomrMLCbnRk7vRQmihIqTYvyIPzx-q4,879
|
|
30
31
|
mech_client/helpers/p2p_libp2p_client/connection.py,sha256=pvfHtI-NhgDbay1wLNit6m8InH4c0p00c3hQo0I2jwQ,27887
|
|
31
32
|
mech_client/helpers/p2p_libp2p_client/connection.yaml,sha256=giYV5FwwugD7ha9IqFHJtvs-Oz1jC5og9rpkstrTqoc,1763
|
|
32
|
-
mech_client/interact.py,sha256=
|
|
33
|
-
mech_client/prompt_to_ipfs.py,sha256=
|
|
33
|
+
mech_client/interact.py,sha256=uRJEcJ8idIm-erGNNBugLGVgYwVO9HmSl2NKPvy248w,19631
|
|
34
|
+
mech_client/prompt_to_ipfs.py,sha256=XqSIBko15MEkpWOQNT97fRI6jNxMF5EDBDEPOJFdhyk,2533
|
|
34
35
|
mech_client/push_to_ipfs.py,sha256=IfvgaPU79N_ZmCPF9d7sPCYz2uduZH0KjT_HQ2LHXoQ,2059
|
|
35
|
-
mech_client/subgraph.py,sha256=
|
|
36
|
+
mech_client/subgraph.py,sha256=4vY6QFyUVs15gS0SvanJbvAxb3aie07IuxQnfMMnStc,4931
|
|
36
37
|
mech_client/to_png.py,sha256=pjUcFJ63MJj_r73eqnfqCWMtlpsrj6H4ZmgvIEmRcFw,2581
|
|
37
|
-
mech_client/wss.py,sha256=
|
|
38
|
-
mech_client-0.2.
|
|
39
|
-
mech_client-0.2.
|
|
40
|
-
mech_client-0.2.
|
|
41
|
-
mech_client-0.2.
|
|
42
|
-
mech_client-0.2.
|
|
38
|
+
mech_client/wss.py,sha256=klvSOMoUbt8-NwVBNH1G3Tlu4Ll9192dgmr6fA1hGNc,6214
|
|
39
|
+
mech_client-0.2.12.dist-info/LICENSE,sha256=mdBDB-mWKV5Cz4ejBzBiKqan6Z8zVLAh9xwM64O2FW4,11339
|
|
40
|
+
mech_client-0.2.12.dist-info/METADATA,sha256=orPKVVfYHtc3tJn0u_FH258fNBiDWpEXddPyn5Buboo,6443
|
|
41
|
+
mech_client-0.2.12.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
|
42
|
+
mech_client-0.2.12.dist-info/entry_points.txt,sha256=SbRMRsayzD8XfNXhgwPuXEqQsdZ5Bw9XDPnUuaDExyY,45
|
|
43
|
+
mech_client-0.2.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|