mech-client 0.2.11__py3-none-any.whl → 0.2.13__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  """Mech client."""
2
2
 
3
- __version__ = "0.2.11"
3
+ __version__ = "0.2.12"
mech_client/cli.py CHANGED
@@ -40,6 +40,11 @@ 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,
@@ -52,11 +57,6 @@ def cli() -> None:
52
57
  help="Extra attribute (key=value) to be included in the request metadata",
53
58
  metavar="KEY=VALUE",
54
59
  )
55
- @click.option(
56
- "--key",
57
- type=click.Path(exists=True, file_okay=True, dir_okay=False),
58
- help="Path to private key to use for request minting",
59
- )
60
60
  @click.option(
61
61
  "--confirm",
62
62
  type=click.Choice(
@@ -79,6 +79,11 @@ def cli() -> None:
79
79
  type=float,
80
80
  help="Amount of sleep before retrying the transaction",
81
81
  )
82
+ @click.option(
83
+ "--chain-config",
84
+ type=str,
85
+ help="Id of the mech's chain configuration (stored configs/mechs.json)",
86
+ )
82
87
  def interact( # pylint: disable=too-many-arguments
83
88
  prompt: str,
84
89
  agent_id: int,
@@ -89,6 +94,7 @@ def interact( # pylint: disable=too-many-arguments
89
94
  retries: Optional[int] = None,
90
95
  timeout: Optional[float] = None,
91
96
  sleep: Optional[float] = None,
97
+ chain_config: Optional[str] = None,
92
98
  ) -> None:
93
99
  """Interact with a mech specifying a prompt and tool."""
94
100
  try:
@@ -112,6 +118,7 @@ def interact( # pylint: disable=too-many-arguments
112
118
  retries=retries,
113
119
  timeout=timeout,
114
120
  sleep=sleep,
121
+ chain_config=chain_config,
115
122
  )
116
123
  except (ValueError, FileNotFoundError) as e:
117
124
  raise click.ClickException(str(e)) from e
@@ -0,0 +1,98 @@
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
+ "transaction_url": "https://gnosisscan.io/tx/{transaction_digest}",
16
+ "subgraph_url": "https://api.studio.thegraph.com/query/57238/mech/version/latest"
17
+ },
18
+ "arbitrum": {
19
+ "agent_registry_contract": "0xa4799B083E0068732456EF45ff9fe5c683658327",
20
+ "rpc_url": "https://arbitrum.llamarpc.com",
21
+ "wss_endpoint": "wss://arbitrum.gateway.tenderly.co",
22
+ "ledger_config": {
23
+ "address": "https://arbitrum.llamarpc.com",
24
+ "chain_id": 42161,
25
+ "poa_chain": false,
26
+ "default_gas_price_strategy": "eip1559",
27
+ "is_gas_estimation_enabled": false
28
+ },
29
+ "gas_limit": 100000,
30
+ "contract_abi_url": "https://api.arbiscan.io/api?module=contract&action=getabi&address={contract_address}",
31
+ "transaction_url": "https://arbiscan.io/tx/{transaction_digest}",
32
+ "subgraph_url": ""
33
+ },
34
+ "polygon": {
35
+ "agent_registry_contract": "0x984cf72FDe8B5aA910e9e508aC5e007ae5BDcC9C",
36
+ "rpc_url": "https://polygon-bor-rpc.publicnode.com",
37
+ "wss_endpoint": "wss://polygon.gateway.tenderly.co",
38
+ "ledger_config": {
39
+ "address": "https://polygon-bor-rpc.publicnode.com",
40
+ "chain_id": 137,
41
+ "poa_chain": true,
42
+ "default_gas_price_strategy": "eip1559",
43
+ "is_gas_estimation_enabled": false
44
+ },
45
+ "gas_limit": 100000,
46
+ "contract_abi_url": "https://api.polygonscan.com/api?module=contract&action=getabi&address={contract_address}",
47
+ "transaction_url": "https://polygonscan.com/tx/{transaction_digest}",
48
+ "subgraph_url": ""
49
+ },
50
+ "base": {
51
+ "agent_registry_contract": "0x88DE734655184a09B70700aE4F72364d1ad23728",
52
+ "rpc_url": "https://base.llamarpc.com",
53
+ "wss_endpoint": "wss://base.gateway.tenderly.co",
54
+ "ledger_config": {
55
+ "address": "https://base.llamarpc.com",
56
+ "chain_id": 8453,
57
+ "poa_chain": false,
58
+ "default_gas_price_strategy": "eip1559",
59
+ "is_gas_estimation_enabled": false
60
+ },
61
+ "gas_limit": 100000,
62
+ "contract_abi_url": "https://api.basescan.org/api?module=contract&action=getabi&address={contract_address}",
63
+ "transaction_url": "https://basescan.org/tx/{transaction_digest}",
64
+ "subgraph_url": ""
65
+ },
66
+ "celo": {
67
+ "agent_registry_contract": "0xE49CB081e8d96920C38aA7AB90cb0294ab4Bc8EA",
68
+ "rpc_url": "https://forno.celo.org",
69
+ "wss_endpoint": "wss://forno.celo.org/ws",
70
+ "ledger_config": {
71
+ "address": "https://forno.celo.org",
72
+ "chain_id": 42220,
73
+ "poa_chain": true,
74
+ "default_gas_price_strategy": "eip1559",
75
+ "is_gas_estimation_enabled": false
76
+ },
77
+ "gas_limit": 250000,
78
+ "contract_abi_url": "https://api.celoscan.io/api?module=contract&action=getabi&address={contract_address}",
79
+ "transaction_url": "https://celoscan.io/tx/{transaction_digest}",
80
+ "subgraph_url": ""
81
+ },
82
+ "optimism": {
83
+ "agent_registry_contract": "0x75D529FAe220bC8db714F0202193726b46881B76",
84
+ "rpc_url": "https://mainnet.optimism.io",
85
+ "wss_endpoint": "wss://optimism.gateway.tenderly.co",
86
+ "ledger_config": {
87
+ "address": "https://mainnet.optimism.io",
88
+ "chain_id": 10,
89
+ "poa_chain": false,
90
+ "default_gas_price_strategy": "eip1559",
91
+ "is_gas_estimation_enabled": false
92
+ },
93
+ "gas_limit": 100000,
94
+ "contract_abi_url": "https://api-optimistic.etherscan.io/api?module=contract&action=getabi&address={contract_address}",
95
+ "transaction_url": "https://optimistic.etherscan.io/tx/{transaction_digest}",
96
+ "subgraph_url": ""
97
+ }
98
+ }
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
@@ -51,33 +54,8 @@ from mech_client.wss import (
51
54
  )
52
55
 
53
56
 
54
- AGENT_REGISTRY_CONTRACT = "0xE49CB081e8d96920C38aA7AB90cb0294ab4Bc8EA"
55
- MECHX_CHAIN_RPC = os.environ.get(
56
- "MECHX_CHAIN_RPC",
57
- "https://rpc.eu-central-2.gateway.fm/v4/gnosis/non-archival/mainnet",
58
- )
59
- LEDGER_CONFIG = {
60
- "address": MECHX_CHAIN_RPC,
61
- "chain_id": 100,
62
- "poa_chain": False,
63
- "default_gas_price_strategy": "eip1559",
64
- "is_gas_estimation_enabled": False,
65
- }
66
57
  PRIVATE_KEY_FILE_PATH = "ethereum_private_key.txt"
67
-
68
- WSS_ENDPOINT = os.getenv(
69
- "WEBSOCKET_ENDPOINT",
70
- "wss://rpc.eu-central-2.gateway.fm/ws/v4/gnosis/non-archival/mainnet",
71
- )
72
- MANUAL_GAS_LIMIT = int(
73
- os.getenv(
74
- "MANUAL_GAS_LIMIT",
75
- "100_000",
76
- )
77
- )
78
- BLOCKSCOUT_API_URL = (
79
- "https://gnosis.blockscout.com/api/v2/smart-contracts/{contract_address}"
80
- )
58
+ MECH_CONFIGS = Path(__file__).parent / "configs" / "mechs.json"
81
59
 
82
60
  MAX_RETRIES = 3
83
61
  WAIT_SLEEP = 3.0
@@ -87,6 +65,85 @@ TIMEOUT = 60.0
87
65
  warnings.filterwarnings("ignore", "The log with transaction hash.*")
88
66
 
89
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: # pylint: disable=too-many-instance-attributes
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
+ transaction_url: str
114
+ subgraph_url: str
115
+
116
+ def __post_init__(self) -> None:
117
+ """Post initialization to override with environment variables."""
118
+ agent_registry_contract = os.getenv("MECHX_AGENT_REGISTRY_CONTRACT")
119
+ if agent_registry_contract:
120
+ self.agent_registry_contract = agent_registry_contract
121
+
122
+ rpc_url = os.getenv("MECHX_CHAIN_RPC")
123
+ if rpc_url:
124
+ self.rpc_url = rpc_url
125
+
126
+ wss_endpoint = os.getenv("MECHX_WSS_ENDPOINT")
127
+ if wss_endpoint:
128
+ self.wss_endpoint = wss_endpoint
129
+
130
+ gas_limit = os.getenv("MECHX_GAS_LIMIT")
131
+ if gas_limit:
132
+ self.gas_limit = int(gas_limit)
133
+
134
+ contract_abi_url = os.getenv("MECHX_CONTRACT_ABI_URL")
135
+ if contract_abi_url:
136
+ self.contract_abi_url = contract_abi_url
137
+
138
+ transaction_url = os.getenv("MECHX_TRANSACTION_URL")
139
+ if transaction_url:
140
+ self.transaction_url = transaction_url
141
+
142
+ subgraph_url = os.getenv("MECHX_SUBGRAPH_URL")
143
+ if subgraph_url:
144
+ self.subgraph_url = subgraph_url
145
+
146
+
90
147
  class ConfirmationType(Enum):
91
148
  """Verification type."""
92
149
 
@@ -95,6 +152,21 @@ class ConfirmationType(Enum):
95
152
  WAIT_FOR_BOTH = "wait-for-both"
96
153
 
97
154
 
155
+ def get_mech_config(chain_config: Optional[str] = None) -> MechConfig:
156
+ """Get `MechConfig` configuration"""
157
+ with open(MECH_CONFIGS, "r", encoding="UTF-8") as file:
158
+ data = json.load(file)
159
+
160
+ if chain_config is None:
161
+ chain_config = next(iter(data))
162
+
163
+ print(f"Chain configuration: {chain_config}")
164
+ entry = data[chain_config].copy()
165
+ ledger_config = LedgerConfig(**entry.pop("ledger_config"))
166
+ mech_config = MechConfig(**entry, ledger_config=ledger_config)
167
+ return mech_config
168
+
169
+
98
170
  def calculate_topic_id(event: Dict) -> str:
99
171
  """Caclulate topic ID"""
100
172
  text = event["name"]
@@ -120,11 +192,22 @@ def get_event_signatures(abi: List) -> Tuple[str, str]:
120
192
  return request, deliver
121
193
 
122
194
 
123
- def get_abi(contract_address: str) -> List:
195
+ def get_abi(contract_address: str, contract_abi_url: str) -> List:
124
196
  """Get contract abi"""
125
- abi_request_url = BLOCKSCOUT_API_URL.format(contract_address=contract_address)
197
+ abi_request_url = contract_abi_url.format(contract_address=contract_address)
126
198
  response = requests.get(abi_request_url).json()
127
- return response["abi"]
199
+
200
+ if "result" in response:
201
+ result = response["result"]
202
+ try:
203
+ abi = json.loads(result)
204
+ except json.JSONDecodeError:
205
+ print("Error: Failed to parse 'result' field as JSON")
206
+ sys.exit(1)
207
+ else:
208
+ abi = response.get("abi")
209
+
210
+ return abi if abi else []
128
211
 
129
212
 
130
213
  def get_contract(
@@ -190,7 +273,11 @@ def _tool_selector_prompt(available_tools: List[str]) -> str:
190
273
 
191
274
 
192
275
  def verify_or_retrieve_tool(
193
- agent_id: int, ledger_api: EthereumApi, tool: Optional[str] = None
276
+ agent_id: int,
277
+ ledger_api: EthereumApi,
278
+ agent_registry_contract: str,
279
+ contract_abi_url: str,
280
+ tool: Optional[str] = None,
194
281
  ) -> str:
195
282
  """
196
283
  Checks if the tool is valid and for what agent.
@@ -199,12 +286,21 @@ def verify_or_retrieve_tool(
199
286
  :type agent_id: int
200
287
  :param ledger_api: The Ethereum API used for interacting with the ledger.
201
288
  :type ledger_api: EthereumApi
289
+ :param agent_registry_contract: Agent registry contract address.
290
+ :type agent_registry_contract: str
291
+ :param contract_abi_url: Block explorer URL.
292
+ :type contract_abi_url: str
202
293
  :param tool: The tool to verify or retrieve (optional).
203
294
  :type tool: Optional[str]
204
295
  :return: The result of the verification or retrieval.
205
296
  :rtype: str
206
297
  """
207
- available_tools = fetch_tools(agent_id=agent_id, ledger_api=ledger_api)
298
+ available_tools = fetch_tools(
299
+ agent_id=agent_id,
300
+ ledger_api=ledger_api,
301
+ agent_registry_contract=agent_registry_contract,
302
+ contract_abi_url=contract_abi_url,
303
+ )
208
304
  if tool is not None and tool not in available_tools:
209
305
  raise ValueError(
210
306
  f"Provided tool `{tool}` not in the list of available tools; Available tools={available_tools}"
@@ -214,11 +310,16 @@ def verify_or_retrieve_tool(
214
310
  return _tool_selector_prompt(available_tools=available_tools)
215
311
 
216
312
 
217
- def fetch_tools(agent_id: int, ledger_api: EthereumApi) -> List[str]:
313
+ def fetch_tools(
314
+ agent_id: int,
315
+ ledger_api: EthereumApi,
316
+ agent_registry_contract: str,
317
+ contract_abi_url: str,
318
+ ) -> List[str]:
218
319
  """Fetch tools for specified agent ID."""
219
320
  mech_registry = get_contract(
220
- contract_address=AGENT_REGISTRY_CONTRACT,
221
- abi=get_abi(AGENT_REGISTRY_CONTRACT),
321
+ contract_address=agent_registry_contract,
322
+ abi=get_abi(agent_registry_contract, contract_abi_url),
222
323
  ledger_api=ledger_api,
223
324
  )
224
325
  token_uri = mech_registry.functions.tokenURI(agent_id).call()
@@ -230,6 +331,7 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
230
331
  crypto: EthereumCrypto,
231
332
  ledger_api: EthereumApi,
232
333
  mech_contract: Web3Contract,
334
+ gas_limit: int,
233
335
  prompt: str,
234
336
  tool: str,
235
337
  extra_attributes: Optional[Dict[str, Any]] = None,
@@ -237,7 +339,7 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
237
339
  retries: Optional[int] = None,
238
340
  timeout: Optional[float] = None,
239
341
  sleep: Optional[float] = None,
240
- ) -> None:
342
+ ) -> Optional[str]:
241
343
  """
242
344
  Sends a request to the mech.
243
345
 
@@ -247,6 +349,8 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
247
349
  :type ledger_api: EthereumApi
248
350
  :param mech_contract: The mech contract instance.
249
351
  :type mech_contract: Web3Contract
352
+ :param gas_limit: Gas limit.
353
+ :type gas_limit: int
250
354
  :param prompt: The request prompt.
251
355
  :type prompt: str
252
356
  :param tool: The requested tool.
@@ -261,6 +365,8 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
261
365
  :type timeout: float
262
366
  :param sleep: Amount of sleep before retrying the transaction
263
367
  :type sleep: float
368
+ :return: The transaction hash.
369
+ :rtype: Optional[str]
264
370
  """
265
371
  v1_file_hash_hex_truncated, v1_file_hash_hex = push_metadata_to_ipfs(
266
372
  prompt, tool, extra_attributes
@@ -271,7 +377,7 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
271
377
  tx_args = {
272
378
  "sender_address": crypto.address,
273
379
  "value": price,
274
- "gas": MANUAL_GAS_LIMIT,
380
+ "gas": gas_limit,
275
381
  }
276
382
 
277
383
  tries = 0
@@ -295,19 +401,20 @@ def send_request( # pylint: disable=too-many-arguments,too-many-locals
295
401
  signed_transaction,
296
402
  raise_on_try=True,
297
403
  )
298
- print(f"Transaction sent: https://gnosisscan.io/tx/{transaction_digest}")
299
- return
404
+ return transaction_digest
300
405
  except Exception as e: # pylint: disable=broad-except
301
406
  print(
302
407
  f"Error occured while sending the transaction: {e}; Retrying in {sleep}"
303
408
  )
304
409
  time.sleep(sleep)
410
+ return None
305
411
 
306
412
 
307
413
  def wait_for_data_url( # pylint: disable=too-many-arguments
308
414
  request_id: str,
309
415
  wss: websocket.WebSocket,
310
416
  mech_contract: Web3Contract,
417
+ subgraph_url: str,
311
418
  deliver_signature: str,
312
419
  ledger_api: EthereumApi,
313
420
  crypto: Crypto,
@@ -322,6 +429,8 @@ def wait_for_data_url( # pylint: disable=too-many-arguments
322
429
  :type wss: websocket.WebSocket
323
430
  :param mech_contract: The mech contract instance.
324
431
  :type mech_contract: Web3Contract
432
+ :param subgraph_url: Subgraph URL.
433
+ :type subgraph_url: str
325
434
  :param deliver_signature: Topic signature for Deliver event
326
435
  :type deliver_signature: str
327
436
  :param ledger_api: The Ethereum API used for interacting with the ledger.
@@ -357,12 +466,16 @@ def wait_for_data_url( # pylint: disable=too-many-arguments
357
466
  loop=loop,
358
467
  )
359
468
  )
360
- mech_task = loop.create_task(
361
- watch_for_data_url_from_subgraph(request_id=request_id)
362
- )
363
- tasks.append(mech_task)
364
469
  tasks.append(on_chain_task)
365
470
 
471
+ if subgraph_url:
472
+ mech_task = loop.create_task(
473
+ watch_for_data_url_from_subgraph(
474
+ request_id=request_id, url=subgraph_url
475
+ )
476
+ )
477
+ tasks.append(mech_task)
478
+
366
479
  async def _wait_for_tasks() -> Any: # type: ignore
367
480
  """Wait for tasks to finish."""
368
481
  (finished, *_), unfinished = await asyncio.wait(
@@ -371,7 +484,8 @@ def wait_for_data_url( # pylint: disable=too-many-arguments
371
484
  )
372
485
  for task in unfinished:
373
486
  task.cancel()
374
- await asyncio.wait(unfinished)
487
+ if unfinished:
488
+ await asyncio.wait(unfinished)
375
489
  return finished.result()
376
490
 
377
491
  result = loop.run_until_complete(_wait_for_tasks())
@@ -388,6 +502,7 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
388
502
  retries: Optional[int] = None,
389
503
  timeout: Optional[float] = None,
390
504
  sleep: Optional[float] = None,
505
+ chain_config: Optional[str] = None,
391
506
  ) -> Any:
392
507
  """
393
508
  Interact with agent mech contract.
@@ -411,9 +526,18 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
411
526
  :type timeout: float
412
527
  :param sleep: Amount of sleep before retrying the transaction
413
528
  :type sleep: float
529
+ :param chain_config: Id of the mech's chain configuration (stored configs/mechs.json)
530
+ :type chain_config: str:
414
531
  :rtype: Any
415
532
  """
416
- contract_address = query_agent_address(agent_id=agent_id, timeout=timeout)
533
+ mech_config = get_mech_config(chain_config)
534
+ ledger_config = mech_config.ledger_config
535
+ contract_address = query_agent_address(
536
+ agent_id=agent_id,
537
+ timeout=timeout,
538
+ url=mech_config.subgraph_url,
539
+ chain_config=chain_config,
540
+ )
417
541
  if contract_address is None:
418
542
  raise ValueError(f"Agent with ID {agent_id} does not exist!")
419
543
 
@@ -423,12 +547,21 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
423
547
  f"Private key file `{private_key_path}` does not exist!"
424
548
  )
425
549
 
426
- wss = websocket.create_connection(WSS_ENDPOINT)
550
+ wss = websocket.create_connection(mech_config.wss_endpoint)
427
551
  crypto = EthereumCrypto(private_key_path=private_key_path)
428
- ledger_api = EthereumApi(**LEDGER_CONFIG)
552
+ ledger_api = EthereumApi(**asdict(ledger_config))
429
553
 
430
- tool = verify_or_retrieve_tool(agent_id=agent_id, ledger_api=ledger_api, tool=tool)
431
- abi = get_abi(contract_address=contract_address)
554
+ tool = verify_or_retrieve_tool(
555
+ agent_id=agent_id,
556
+ ledger_api=ledger_api,
557
+ tool=tool,
558
+ agent_registry_contract=mech_config.agent_registry_contract,
559
+ contract_abi_url=mech_config.contract_abi_url,
560
+ )
561
+ abi = get_abi(
562
+ contract_address=contract_address,
563
+ contract_abi_url=mech_config.contract_abi_url,
564
+ )
432
565
  mech_contract = get_contract(
433
566
  contract_address=contract_address, abi=abi, ledger_api=ledger_api
434
567
  )
@@ -440,10 +573,12 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
440
573
  request_signature=request_event_signature,
441
574
  deliver_signature=deliver_event_signature,
442
575
  )
443
- send_request(
576
+ print("Sending request...")
577
+ transaction_digest = send_request(
444
578
  crypto=crypto,
445
579
  ledger_api=ledger_api,
446
580
  mech_contract=mech_contract,
581
+ gas_limit=mech_config.gas_limit,
447
582
  prompt=prompt,
448
583
  tool=tool,
449
584
  extra_attributes=extra_attributes,
@@ -451,6 +586,11 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
451
586
  timeout=timeout,
452
587
  sleep=sleep,
453
588
  )
589
+ transaction_url_formatted = mech_config.transaction_url.format(
590
+ transaction_digest=transaction_digest
591
+ )
592
+ print(f"Transaction sent: {transaction_url_formatted}")
593
+ print("Waiting for transaction receipt...")
454
594
  request_id = watch_for_request_id(
455
595
  wss=wss,
456
596
  mech_contract=mech_contract,
@@ -458,17 +598,20 @@ def interact( # pylint: disable=too-many-arguments,too-many-locals
458
598
  request_signature=request_event_signature,
459
599
  )
460
600
  print(f"Created on-chain request with ID {request_id}")
601
+ print("Waiting for Mech response...")
461
602
  data_url = wait_for_data_url(
462
603
  request_id=request_id,
463
604
  wss=wss,
464
605
  mech_contract=mech_contract,
606
+ subgraph_url=mech_config.subgraph_url,
465
607
  deliver_signature=deliver_event_signature,
466
608
  ledger_api=ledger_api,
467
609
  crypto=crypto,
468
610
  confirmation_type=confirmation_type,
469
611
  )
470
-
471
- print(f"Data arrived: {data_url}")
472
- data = requests.get(f"{data_url}/{request_id}").json()
473
- print(f"Data from agent: {data}")
474
- return data
612
+ if data_url:
613
+ print(f"Data arrived: {data_url}")
614
+ data = requests.get(f"{data_url}/{request_id}").json()
615
+ print(f"Data from agent: {data}")
616
+ return data
617
+ return None
mech_client/subgraph.py CHANGED
@@ -27,7 +27,6 @@ from gql import Client, gql
27
27
  from gql.transport.aiohttp import AIOHTTPTransport
28
28
 
29
29
 
30
- MECH_SUBGRAPH_URL = "https://api.studio.thegraph.com/query/57238/mech/version/latest"
31
30
  AGENT_QUERY_TEMPLATE = Template(
32
31
  """{
33
32
  createMeches(where:{agentId:$agent_id}) {
@@ -51,20 +50,38 @@ DELIVER_QUERY_TEMPLATE = Template(
51
50
  DEFAULT_TIMEOUT = 600.0
52
51
 
53
52
 
54
- def query_agent_address(
55
- agent_id: int, timeout: Optional[float] = None
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,
56
58
  ) -> Optional[str]:
57
59
  """
58
60
  Query agent address from subgraph.
59
61
 
60
62
  :param agent_id: The ID of the agent.
61
- :param timeout: Timeout for the request.
62
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]:
63
69
  :return: The agent address if found, None otherwise.
64
70
  :rtype: Optional[str]
65
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"
66
83
  client = Client(
67
- transport=AIOHTTPTransport(url=MECH_SUBGRAPH_URL),
84
+ transport=AIOHTTPTransport(url=url),
68
85
  execute_timeout=timeout or 30.0,
69
86
  )
70
87
  response = client.execute(
@@ -81,19 +98,22 @@ def query_agent_address(
81
98
 
82
99
 
83
100
  async def query_deliver_hash(
84
- request_id: str, timeout: Optional[float] = None
101
+ request_id: str, url: str, timeout: Optional[float] = None
85
102
  ) -> Optional[str]:
86
103
  """
87
104
  Query deliver IPFS hash from subgraph.
88
105
 
89
106
  :param request_id: The ID of the mech request.
90
- :param timeout: Timeout for the request.
91
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]
92
112
  :return: The deliver IPFS hash if found, None otherwise.
93
113
  :rtype: Optional[str]
94
114
  """
95
115
  client = Client(
96
- transport=AIOHTTPTransport(url=MECH_SUBGRAPH_URL),
116
+ transport=AIOHTTPTransport(url=url),
97
117
  execute_timeout=timeout or 30.0,
98
118
  )
99
119
  response = await client.execute_async(
@@ -110,13 +130,15 @@ async def query_deliver_hash(
110
130
 
111
131
 
112
132
  async def watch_for_data_url_from_subgraph(
113
- request_id: str, timeout: Optional[float] = None
133
+ request_id: str, url: str, timeout: Optional[float] = None
114
134
  ) -> Optional[str]:
115
135
  """
116
136
  Continuously query for data URL until it's available or timeout is reached.
117
137
 
118
138
  :param request_id: The ID of the mech request.
119
139
  :type request_id: str
140
+ :param url: Subgraph URL.
141
+ :type url: str
120
142
  :param timeout: Maximum time to wait for the data URL in seconds. Defaults to DEFAULT_TIMEOUT.
121
143
  :type timeout: Optional[float]
122
144
  :return: Data URL if available within timeout, otherwise None.
@@ -125,7 +147,7 @@ async def watch_for_data_url_from_subgraph(
125
147
  timeout = timeout or DEFAULT_TIMEOUT
126
148
  start_time = asyncio.get_event_loop().time()
127
149
  while True:
128
- response = await query_deliver_hash(request_id=request_id)
150
+ response = await query_deliver_hash(request_id=request_id, url=url)
129
151
  if response is not None:
130
152
  return f"https://gateway.autonolas.tech/ipfs/{response}"
131
153
 
mech_client/wss.py CHANGED
@@ -170,19 +170,26 @@ async def watch_for_data_url_from_wss( # pylint: disable=too-many-arguments
170
170
  :rtype: Any
171
171
  """
172
172
  with ThreadPoolExecutor() as executor:
173
- while True:
174
- msg = await loop.run_in_executor(executor=executor, func=wss.recv)
175
- data = json.loads(msg)
176
- tx_hash = data["params"]["result"]["transactionHash"]
177
- tx_receipt = await loop.run_in_executor(
178
- executor, wait_for_receipt, tx_hash, ledger_api
173
+ try:
174
+ while True:
175
+ msg = await loop.run_in_executor(executor=executor, func=wss.recv)
176
+ data = json.loads(msg)
177
+ tx_hash = data["params"]["result"]["transactionHash"]
178
+ tx_receipt = await loop.run_in_executor(
179
+ executor, wait_for_receipt, tx_hash, ledger_api
180
+ )
181
+ event_signature = tx_receipt["logs"][0]["topics"][0].hex()
182
+ if event_signature != deliver_signature:
183
+ continue
184
+
185
+ rich_logs = mech_contract.events.Deliver().process_receipt(tx_receipt)
186
+ data = cast(bytes, rich_logs[0]["args"]["data"])
187
+ if request_id != str(rich_logs[0]["args"]["requestId"]):
188
+ continue
189
+ return f"https://gateway.autonolas.tech/ipfs/f01701220{data.hex()}"
190
+ except websocket.WebSocketConnectionClosedException as e:
191
+ print(f"WebSocketConnectionClosedException {repr(e)}")
192
+ print(
193
+ "Error: The WSS connection was likely closed by the remote party. Please, try using another WSS provider."
179
194
  )
180
- event_signature = tx_receipt["logs"][0]["topics"][0].hex()
181
- if event_signature != deliver_signature:
182
- continue
183
-
184
- rich_logs = mech_contract.events.Deliver().process_receipt(tx_receipt)
185
- data = cast(bytes, rich_logs[0]["args"]["data"])
186
- if request_id != str(rich_logs[0]["args"]["requestId"]):
187
- continue
188
- return f"https://gateway.autonolas.tech/ipfs/f01701220{data.hex()}"
195
+ return None
@@ -0,0 +1,293 @@
1
+ Metadata-Version: 2.1
2
+ Name: mech-client
3
+ Version: 0.2.13
4
+ Summary: Basic client to interact with a mech
5
+ License: Apache-2.0
6
+ Author: David Minarsch
7
+ Author-email: david.minarsch@googlemail.com
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: License :: OSI Approved :: Apache Software License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Requires-Dist: asn1crypto (>=1.4.0,<1.5.0)
14
+ Requires-Dist: gql (>=3.4.1)
15
+ Requires-Dist: open-aea-cli-ipfs (==1.50.0)
16
+ Requires-Dist: open-aea-ledger-cosmos (==1.50.0)
17
+ Requires-Dist: open-aea-ledger-ethereum (==1.50.0)
18
+ Requires-Dist: open-aea[cli] (==1.50.0)
19
+ Requires-Dist: websocket-client (>=0.32.0,<1)
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Mech Client
23
+
24
+ A basic client to interact with an AI Mech. [AI Mechs](https://github.com/valory-xyz/mech) allow users to post requests for AI tasks on-chain, and get their result delivered.
25
+
26
+ > **:warning: Warning** <br />
27
+ > **This is a *hacky* alpha version of the client. Don't rely on it as production software.**
28
+
29
+ ## Requirements
30
+
31
+ - Python >=3.10
32
+
33
+ ## Installation
34
+
35
+ Find the latest available release on [PyPi](https://pypi.org/project/mech-client/#description).
36
+
37
+ We recommend that you create a virtual Python environment using [Poetry](https://python-poetry.org/). Set up your virtual environment as follows:
38
+
39
+ ```bash
40
+ poetry new my_project
41
+ cd my_project
42
+ poetry shell
43
+ poetry add mech-client
44
+ ```
45
+
46
+ Alternatively, you can also install the Mech Client in your local Python installation:
47
+
48
+ ```bash
49
+ pip install mech-client
50
+ ```
51
+
52
+ If you require to use the Mech Client programmatically, please see [this section](#programmatic-usage) below.
53
+
54
+ ## CLI Usage
55
+
56
+ Display the available options:
57
+
58
+ ```bash
59
+ mechx --help
60
+ ```
61
+
62
+ ```bash
63
+ Usage: mechx [OPTIONS] COMMAND [ARGS]...
64
+
65
+ Command-line tool for interacting with mechs.
66
+
67
+ Options:
68
+ --version Show the version and exit.
69
+ --help Show this message and exit.
70
+
71
+ Commands:
72
+ interact Interact with a mech specifying a prompt and tool.
73
+ prompt-to-ipfs Upload a prompt and tool to IPFS as metadata.
74
+ push-to-ipfs Upload a file to IPFS.
75
+ to-png Convert a stability AI API's diffusion model output...
76
+ ```
77
+
78
+ ### Set up the EOA and private key
79
+
80
+ To use the Mech Client you need an EOA account and its associated private key stored in a text file `ethereum_private_key.txt`. You can set it up in two ways:
81
+
82
+ - Use any software of your choice (e.g., [Metamask](https://metamask.io/)) and copy the private key:
83
+
84
+ ```bash
85
+ echo -n YOUR_PRIVATE_KEY > ethereum_private_key.txt
86
+ ```
87
+
88
+ Do not include any leading or trailing spaces, tabs or newlines, or any other character in the file `ethereum_private_key.txt`.
89
+
90
+ - Alternatively, use the Open AEA command `generate-key` (you'll need to install [Open AEA](https://pypi.org/project/open-aea/) and its [Ethereum ledger plugin](https://pypi.org/project/open-aea-ledger-ethereum/)):
91
+
92
+ ```bash
93
+ aea generate-key ethereum
94
+ ```
95
+
96
+ and display the corresponding EOA:
97
+
98
+ ```bash
99
+ python -c "from web3 import Web3; print(Web3().eth.account.from_key(open('ethereum_private_key.txt').read()).address)"
100
+ ```
101
+
102
+ The EOA you use must have enough funds to pay for the Mech requests, or alternatively, use a Nevermined subscription.
103
+
104
+ > **:warning: Warning** <br />
105
+ > * **If the generated EOA account is for development purposes, make sure it does not contain large amounts of funds.**
106
+ >
107
+ > * **If you store the key file in a local Git repository, we recommend that you add it to `.gitignore` in order to avoid publishing it unintentionally:**
108
+ >
109
+ > ```bash
110
+ > echo ethereum_private_key.txt >> .gitignore
111
+ > ```
112
+
113
+ ### Select the mech you are going to send requests to
114
+
115
+ Mechs are deployed to several networks. Find the list of supported networks and corresponging mech addresses [here](https://github.com/valory-xyz/mech?tab=readme-ov-file#examples-of-deployed-mechs).
116
+
117
+ ### Generate Mech requests
118
+
119
+ The basic usage of the Mech Client is as follows:
120
+
121
+ ```bash
122
+ mechx interact <prompt> <agent_id>
123
+ ```
124
+
125
+ where agent with `<agent_id>` will process `<prompt>` with the default options. Each chain has its own set of Mech agents. You can find the agent IDs for each chain on the [Mech Hub](https://aimechs.autonolas.network/registry) or on the [Mech repository](https://github.com/valory-xyz/mech?tab=readme-ov-file#examples-of-deployed-mechs).
126
+
127
+ Some useful options:
128
+
129
+ - `--key <private_key_path>`: Specifies the path of the private key. The default value is `./ethereum_private_key.txt`.
130
+ - `--tool <name>`: Name of the tool to process the prompt. If you are aware about the tools that are provided by an agent you can directly provide its name using this option. If not provided, it will show a list of available tools for the agent so that you can select which one you want to use:
131
+
132
+ ```text
133
+ Select prompting tool
134
+ |--------------------------------------------------|
135
+ | ID | Tool |
136
+ |--------------------------------------------------|
137
+ | 0 | openai-text-davinci-002 |
138
+ | ...| ... |
139
+ |--------------------------------------------------|
140
+ Tool ID >
141
+ ```
142
+
143
+ - `--chain-config <name>`: Use default chain configuration parameters (RPC, WSS, ...). [See below](#chain-configuration) for more details. Available values are
144
+ - `arbitrum`
145
+ - `base`
146
+ - `celo`
147
+ - `gnosis` (Default)
148
+ - `optimism`
149
+ - `polygon`
150
+
151
+ - `--confirm <type>`: Specify how to wait for the result of your request:
152
+ - `off-chain`: Wait for the result using the ACN.
153
+ - `on-chain`: Wait for the result using the Subgraph and the Websocket subscription (whichever arrives first).
154
+ - `wait-for-both` (Default): Wait for the result using both `off-chain` and `on-chain` (whichever arrives first).
155
+
156
+ ### Example
157
+
158
+ Example of a request specifying a key file and tool:
159
+
160
+ ```bash
161
+ mechx interact "write a short poem" 6 --key ~/ethereum_private_key.txt --tool openai-gpt-3.5-turbo --chain-config gnosis --confirm on-chain
162
+ ```
163
+
164
+ You will see an output like this:
165
+
166
+ ```bash
167
+ Chain configuration: gnosis
168
+ Prompt uploaded: https://gateway.autonolas.tech/ipfs/f01701220af9e4e8b4bd62d76394064f493081917bcc0b9c34a4aff60f82623b717617279
169
+ Transaction sent: https://gnosisscan.io/tx/0x61359f9cc6a1debb07d34ce1038f6aa30d25257c17edeb2b161741805e43e8d0
170
+ Waiting for transaction receipt...
171
+ Created on-chain request with ID 100407405856633966395081711430940962809568685031934329025999216833965518452765
172
+ Data arrived: https://gateway.autonolas.tech/ipfs/f01701220a462120d5bb03f406fa5ef3573df77184a20ab6343d7bade76bd321654aa7251
173
+ Data from agent: {'requestId': 100407405856633966395081711430940962809568685031934329025999216833965518452765, 'result': "In a world of chaos and strife,\nThere's beauty in the simplest of life.\nA gentle breeze whispers through the trees,\nAnd birds sing melodies with ease.\n\nThe sun sets in a fiery hue,\nPainting the sky in shades of blue.\nStars twinkle in the darkness above,\nGuiding us with their light and love.\n\nSo take a moment to pause and see,\nThe wonders of this world so free.\nEmbrace the joy that each day brings,\nAnd let your heart soar on gentle wings.", 'prompt': 'write a short poem', 'cost_dict': {}, 'metadata': {'model': None, 'tool': 'openai-gpt-3.5-turbo'}}
174
+ ```
175
+
176
+ > **:pencil2: Note** <br />
177
+ > **If you encounter an "Out of gas" error when executing the Mech Client, you will need to increase the gas limit, e.g.,**
178
+ >
179
+ > ```bash
180
+ > export MECHX_GAS_LIMIT=200000
181
+ > ```
182
+
183
+ ### Chain configuration
184
+
185
+ Default configurations for different chains are stored in the file [configs/mechs.json](./mech_client/configs/mechs.json). If `--chain-config` parameter is not specified, the Mech Client will choose the first configuration on the JSON.
186
+
187
+ Additionally, you can override any configuration parameter by exporting any of the following environment variables:
188
+
189
+ ```bash
190
+ MECHX_CHAIN_RPC
191
+ MECHX_WSS_ENDPOINT
192
+ MECHX_GAS_LIMIT
193
+ MECHX_CONTRACT_ABI_URL
194
+ MECHX_TRANSACTION_URL
195
+ MECHX_SUBGRAPH_URL
196
+
197
+ MECHX_LEDGER_ADDRESS
198
+ MECHX_LEDGER_CHAIN_ID
199
+ MECHX_LEDGER_POA_CHAIN
200
+ MECHX_LEDGER_DEFAULT_GAS_PRICE_STRATEGY
201
+ MECHX_LEDGER_IS_GAS_ESTIMATION_ENABLED
202
+ ```
203
+
204
+ ## Programmatic usage
205
+
206
+ You can also use the Mech Client as a library on your Python project.
207
+
208
+ 1. Set up the private key as specified [above](#set-up-the-private-key). Store the resulting key file (e.g., `ethereum_private_key.txt`) in a convenient and secure location.
209
+
210
+ 2. Create Python script `my_script.py`:
211
+
212
+ ```bash
213
+ touch my_script.py
214
+ ```
215
+
216
+ 3. Edit `my_script.py` as follows:
217
+
218
+ ```python
219
+ from mech_client.interact import interact, ConfirmationType
220
+
221
+ prompt_text = 'Will Gnosis pay reach 100k cards in 2024?'
222
+ agent_id = 6
223
+ tool_name = "prediction-online"
224
+ chain_config = "gnosis"
225
+ private_key_path="ethereum_private_key.txt"
226
+
227
+ result = interact(
228
+ prompt=prompt_text,
229
+ agent_id=agent_id,
230
+ tool=tool_name,
231
+ chain_config=chain_config,
232
+ confirmation_type=ConfirmationType.ON_CHAIN,
233
+ private_key_path=private_key_path
234
+ )
235
+ print(result)
236
+ ```
237
+
238
+ ## Developer installation
239
+
240
+ To setup the development environment for this project, clone the repository and run the following commands:
241
+
242
+ ```bash
243
+ poetry install
244
+ poetry shell
245
+ ```
246
+
247
+ ## Release guide
248
+
249
+ - Bump versions in `pyproject.toml`.`mech_client/__init__.py` and `SECURITY.md`
250
+ - `poetry lock`
251
+ - `rm -rf dist`
252
+ - `autonomy packages sync --update-packages`
253
+ - `make eject-packages`
254
+ - Then, create a release PR and tag the release.
255
+
256
+ ## FAQ
257
+
258
+ <details>
259
+
260
+ <summary><b>On which chains are AI Mechs deployed?</b></summary>
261
+
262
+ The [Mech repository](https://github.com/valory-xyz/mech?tab=readme-ov-file#examples-of-deployed-mechs) contains the latest information on deployed Mechs.
263
+
264
+ </details>
265
+
266
+ <details>
267
+
268
+ <summary><b>Are AI Mechs deployed on testnets?</b></summary>
269
+
270
+ No. AI Mechs are currently deployed only on mainnets.
271
+
272
+ </details>
273
+
274
+ <details>
275
+
276
+ <summary><b>Where can I find the agent ID?</b></summary>
277
+
278
+ You can find the agent IDs for each chain on the [Mech Hub](https://aimechs.autonolas.network/registry) or on the [Mech repository](https://github.com/valory-xyz/mech?tab=readme-ov-file#examples-of-deployed-mechs).
279
+
280
+ </details>
281
+
282
+ <details>
283
+
284
+ <summary><b>How do I access an AI Mech on a different chain?</b></summary>
285
+
286
+ Use the `--chain-config <name>` parameter together with a valid `<agent_id>`, for example:
287
+
288
+ ```bash
289
+ mechx interact "write a short poem" 2 --key ./ethereum_private_key.txt --tool openai-gpt-4 --chain-config celo --confirm on-chain
290
+ ```
291
+
292
+ </details>
293
+
@@ -1,6 +1,7 @@
1
- mech_client/__init__.py,sha256=gBjxKCL17t0uinR0QxPEduX_ZxmuNvsWEzEKtUJGnXA,43
1
+ mech_client/__init__.py,sha256=s8E8bTccJTXUKsK6N8pSwD7FabU-dQUps-y98raJYZQ,43
2
2
  mech_client/acn.py,sha256=Rj_jLPvJ5loDQfGbu3a_O24cJC4SwIErLceSz_zVYS8,5356
3
- mech_client/cli.py,sha256=mhvNPLFTdtRlDNCFD0LMQxJCchR1itRCmmGhJHwCgrg,4399
3
+ mech_client/cli.py,sha256=QnC3Z_vth__ZiU12chRl8q5LYDLjyjzaofB41mWlq8c,4608
4
+ mech_client/configs/mechs.json,sha256=IABnne1lWi62l84Hzqh3XrP659uPKme2k32yAI0HBrg,4488
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=fMF1ggphSHF3lJAGAX8d5sWJHdI8uvYUEEXZgrIF7ac,15648
33
+ mech_client/interact.py,sha256=E9AW_gRMkz8fRDeQSl9Dd97X_405GodA8Xylv5ZavzQ,20189
33
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=rkMkIY2Ovz6KdMT5Ip2_hnPkZVJdXKfgh_Eiw0p_4fU,4049
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=klvSOMoUbt8-NwVBNH1G3Tlu4Ll9192dgmr6fA1hGNc,6214
38
- mech_client-0.2.11.dist-info/LICENSE,sha256=mdBDB-mWKV5Cz4ejBzBiKqan6Z8zVLAh9xwM64O2FW4,11339
39
- mech_client-0.2.11.dist-info/METADATA,sha256=O-X6H6niKPFbS6NSNpmO6RHwm8Zsohbya7cghFMbt_E,5771
40
- mech_client-0.2.11.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
41
- mech_client-0.2.11.dist-info/entry_points.txt,sha256=SbRMRsayzD8XfNXhgwPuXEqQsdZ5Bw9XDPnUuaDExyY,45
42
- mech_client-0.2.11.dist-info/RECORD,,
38
+ mech_client/wss.py,sha256=hRInQjjsyOrs8dmgBb2VpJvpNt6Tx0aEiY3OhOPQvIs,6600
39
+ mech_client-0.2.13.dist-info/LICENSE,sha256=mdBDB-mWKV5Cz4ejBzBiKqan6Z8zVLAh9xwM64O2FW4,11339
40
+ mech_client-0.2.13.dist-info/METADATA,sha256=cTHX-evOL7lNFFgPhDA0Iyt3jFEW-Fd6zB-IjNyPlRU,10544
41
+ mech_client-0.2.13.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
42
+ mech_client-0.2.13.dist-info/entry_points.txt,sha256=SbRMRsayzD8XfNXhgwPuXEqQsdZ5Bw9XDPnUuaDExyY,45
43
+ mech_client-0.2.13.dist-info/RECORD,,
@@ -1,164 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: mech-client
3
- Version: 0.2.11
4
- Summary: Basic client to interact with a mech
5
- License: Apache-2.0
6
- Author: David Minarsch
7
- Author-email: david.minarsch@googlemail.com
8
- Requires-Python: >=3.10,<4.0
9
- Classifier: License :: OSI Approved :: Apache Software License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.11
13
- Requires-Dist: asn1crypto (>=1.4.0,<1.5.0)
14
- Requires-Dist: gql (>=3.4.1)
15
- Requires-Dist: open-aea-cli-ipfs (==1.50.0)
16
- Requires-Dist: open-aea-ledger-cosmos (==1.50.0)
17
- Requires-Dist: open-aea-ledger-ethereum (==1.50.0)
18
- Requires-Dist: open-aea[cli] (==1.50.0)
19
- Requires-Dist: websocket-client (>=0.32.0,<1)
20
- Description-Content-Type: text/markdown
21
-
22
- # mech-client
23
- Basic client to interact with a mech
24
-
25
- > **Warning**<br />
26
- > **This is a hacky alpha version of the client - don't rely on it as production software.**
27
-
28
- ## Installation
29
-
30
- ```bash
31
- pip install mech-client
32
- ```
33
-
34
- Then, set a websocket endpoint for Gnosis RPC like so:
35
-
36
- ```bash
37
- export WEBSOCKET_ENDPOINT=<YOUR ENDPOINT>
38
- ```
39
-
40
- 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.,
41
-
42
- ```bash
43
- export MANUAL_GAS_LIMIT=200000
44
- ```
45
-
46
- ## CLI:
47
-
48
- ```bash
49
- Usage: mechx [OPTIONS] COMMAND [ARGS]...
50
-
51
- Command-line tool for interacting with mechs.
52
-
53
- Options:
54
- --version Show the version and exit.
55
- --help Show this message and exit.
56
-
57
- Commands:
58
- interact Interact with a mech specifying a prompt and tool.
59
- prompt-to-ipfs Upload a prompt and tool to IPFS as metadata.
60
- push-to-ipfs Upload a file to IPFS.
61
- ```
62
-
63
- ## CLI Usage:
64
-
65
- First, create a private key in file `ethereum_private_key.txt` with this command:
66
-
67
- ```bash
68
- aea generate-key ethereum
69
- ```
70
- Ensure the private key carries funds on Gnosis Chain.
71
-
72
- A keyfile is just a file with your ethereum private key as a hex-string, example:
73
- ```
74
- 0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd
75
- ```
76
- In case you add your own, make sure you don't have any extra characters in the file, like newlines or spaces.
77
-
78
- Second, run the following command to instruct the mech with `<prompt>` and `<agent_id>`:
79
-
80
- ```bash
81
- mechx interact <prompt> <agent_id>
82
- ```
83
-
84
- The command will prompt you with all available tools for the agent and you can select which tool you want to use
85
-
86
- ```
87
- Select prompting tool
88
- |--------------------------------------------------|
89
- | ID | Tool |
90
- |--------------------------------------------------|
91
- | 0 | openai-text-davinci-002 |
92
- | ...| ... |
93
- |--------------------------------------------------|
94
- Tool ID >
95
- ```
96
-
97
- If you are aware about the tools that are provided by an agent you can directly provide tool as a command line argument
98
-
99
- ```bash
100
- mechx interact <prompt> <agent_id> --tool <tool>
101
- ```
102
-
103
- If you already have a funded key file on locally you can provide path the key using `--key` flag.
104
-
105
- ```bash
106
- mechx interact <prompt> <agent_id> --key <key_file>
107
- ```
108
-
109
- Example output:
110
- ```bash
111
- mechx interact "write a short poem" 3 --key ~/gnosis_key --tool openai-text-davinci-003
112
- Prompt uploaded: https://gateway.autonolas.tech/ipfs/f01701220ad773628911d12e28f005e3f249e990d684e5dba07542259195602f9afed30bf
113
- Transaction sent: https://gnosisscan.io/tx/0x0d9209e32e965a820b9e80accfcd71ea3b1174b9758dd251c2e627a60ec426a5
114
- Created on-chain request with ID 111240237160304797537720810617416341148235899500021985333360197012735240803849
115
- Data arrived: https://gateway.autonolas.tech/ipfs/bafybeifk2h35ncszlze7t64rpblfo45rezc33xzbya3cjiyumtaioyat3e
116
- 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!"}
117
- ```
118
-
119
- 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
120
-
121
- ```bash
122
- mechx interact "write a short poem" 3 --key ~/gnosis_key --tool openai-text-davinci-003 --confirm on-chain
123
- Prompt uploaded: https://gateway.autonolas.tech/ipfs/f017012205e37f761221a8ba4005e91c36b94153e9432b8888ff2acae6b101dd5a5de6768
124
- Transaction sent: https://gnosisscan.io/tx/0xf1ef63f617717bbb8deb09699af99aa39f10155d33796de2fd7eb61c9c1458b6
125
- Created on-chain request with ID 81653153529124597849081567361606842861262371002932574194580478443414142139857
126
- Data arrived: https://gateway.autonolas.tech/ipfs/f0170122069b55e077430a00f3cbc3b069347e901396f978ff160eb2b0a947872be1848b7
127
- 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."}
128
- ```
129
-
130
- ## Programmatic Usage:
131
-
132
- ```python
133
- from mech_client.interact import interact, ConfirmationType
134
-
135
- prompt_text = 'Will gnosis pay reach 100k cards in 2024?'
136
- agent_id = 3
137
- tool_name = "prediction-online"
138
-
139
- result = interact(
140
- prompt=prompt_text,
141
- agent_id=agent_id,
142
- tool=tool_name,
143
- confirmation_type=ConfirmationType.ON_CHAIN,
144
- private_key_path='PATH_HERE'
145
- )
146
- print(result)
147
- ```
148
-
149
- # Developer installation
150
- To setup the development environment, run the following commands:
151
-
152
- ```bash
153
- poetry install && poetry shell
154
- ```
155
-
156
- ## Release guide:
157
-
158
- - Bump versions in `pyproject.toml`, `mech_client/__init__.py` and `SECURITY.md`
159
- - `poetry lock`
160
- - `rm -rf dist`
161
- - `autonomy packages sync --update-packages`
162
- - `make eject-packages`
163
- - then create release PR and tag release
164
-