olas-operate-middleware 0.6.2__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {olas_operate_middleware-0.6.2.dist-info → olas_operate_middleware-0.7.0.dist-info}/METADATA +3 -3
- {olas_operate_middleware-0.6.2.dist-info → olas_operate_middleware-0.7.0.dist-info}/RECORD +20 -19
- operate/bridge/{bridge.py → bridge_manager.py} +99 -62
- operate/bridge/providers/{lifi_bridge_provider.py → lifi_provider.py} +94 -81
- operate/bridge/providers/native_bridge_provider.py +124 -115
- operate/bridge/providers/{bridge_provider.py → provider.py} +132 -150
- operate/bridge/providers/relay_provider.py +442 -0
- operate/cli.py +72 -7
- operate/constants.py +1 -0
- operate/ledger/profiles.py +37 -1
- operate/quickstart/reset_configs.py +109 -0
- operate/quickstart/run_service.py +31 -5
- operate/quickstart/utils.py +6 -2
- operate/services/agent_runner.py +168 -91
- operate/services/manage.py +41 -11
- operate/services/service.py +10 -2
- operate/services/utils/mech.py +6 -4
- operate/data/contracts/service_staking_token/contract.yaml +0 -23
- {olas_operate_middleware-0.6.2.dist-info → olas_operate_middleware-0.7.0.dist-info}/LICENSE +0 -0
- {olas_operate_middleware-0.6.2.dist-info → olas_operate_middleware-0.7.0.dist-info}/WHEEL +0 -0
- {olas_operate_middleware-0.6.2.dist-info → olas_operate_middleware-0.7.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# ------------------------------------------------------------------------------
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2023-2024 Valory AG
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
# ------------------------------------------------------------------------------
|
|
19
|
+
"""Reset configurations."""
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
from typing import Callable, Optional, TYPE_CHECKING, cast
|
|
23
|
+
|
|
24
|
+
from operate.quickstart.run_service import load_local_config
|
|
25
|
+
from operate.quickstart.utils import (
|
|
26
|
+
ask_or_get_from_env,
|
|
27
|
+
ask_yes_or_no,
|
|
28
|
+
check_rpc,
|
|
29
|
+
print_section,
|
|
30
|
+
print_title,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from operate.cli import OperateApp
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _ask_to_change(
|
|
39
|
+
name: str,
|
|
40
|
+
env_var: str,
|
|
41
|
+
old_value: str,
|
|
42
|
+
hidden: bool = False,
|
|
43
|
+
validator: Callable[[Optional[str]], bool] = lambda x: False if x is None else True,
|
|
44
|
+
) -> str:
|
|
45
|
+
"""Ask user if they want to change a configuration value."""
|
|
46
|
+
old_value_str = old_value
|
|
47
|
+
if hidden:
|
|
48
|
+
if len(old_value_str) < 4:
|
|
49
|
+
old_value_str = "*" * len(old_value_str)
|
|
50
|
+
else:
|
|
51
|
+
old_value_str = "*" * len(old_value_str[:-4]) + old_value_str[-4:]
|
|
52
|
+
|
|
53
|
+
print(f"\nCurrent '{name}' is set to: {old_value_str}")
|
|
54
|
+
if ask_yes_or_no(f"Do you want to change the '{name}'?"):
|
|
55
|
+
new_value = None
|
|
56
|
+
while not validator(new_value):
|
|
57
|
+
new_value = ask_or_get_from_env(
|
|
58
|
+
prompt=f"Enter new value for '{name}' {'[hidden]' if hidden else ''}: ",
|
|
59
|
+
env_var_name=env_var,
|
|
60
|
+
is_pass=hidden,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return str(new_value)
|
|
64
|
+
|
|
65
|
+
return old_value
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def reset_configs(operate: "OperateApp", config_path: str) -> None:
|
|
69
|
+
"""Reset configurations."""
|
|
70
|
+
with open(config_path, "r") as config_file:
|
|
71
|
+
template = json.load(config_file)
|
|
72
|
+
|
|
73
|
+
operate.service_manager().migrate_service_configs()
|
|
74
|
+
operate.wallet_manager.migrate_wallet_configs()
|
|
75
|
+
|
|
76
|
+
print_title(f"Reset your {template['name']} configurations")
|
|
77
|
+
|
|
78
|
+
# check if agent was started before
|
|
79
|
+
config = load_local_config(
|
|
80
|
+
operate=operate, service_name=cast(str, template["name"])
|
|
81
|
+
)
|
|
82
|
+
if not config.path.exists():
|
|
83
|
+
print("No previous agent setup found. Exiting.")
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
if config.rpc is None:
|
|
87
|
+
config.rpc = {}
|
|
88
|
+
|
|
89
|
+
for chain_name in config.rpc:
|
|
90
|
+
config.rpc[chain_name] = _ask_to_change(
|
|
91
|
+
name=f"{chain_name.capitalize()} RPC URL",
|
|
92
|
+
env_var=f"{chain_name.upper()}_LEDGER_RPC",
|
|
93
|
+
old_value=config.rpc[chain_name],
|
|
94
|
+
hidden=True,
|
|
95
|
+
validator=check_rpc,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if config.user_provided_args is None:
|
|
99
|
+
config.user_provided_args = {}
|
|
100
|
+
|
|
101
|
+
for env_var in config.user_provided_args:
|
|
102
|
+
config.user_provided_args[env_var] = _ask_to_change(
|
|
103
|
+
name=env_var,
|
|
104
|
+
env_var=env_var,
|
|
105
|
+
old_value=config.user_provided_args[env_var],
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
config.store()
|
|
109
|
+
print_section("Configurations updated")
|
|
@@ -37,7 +37,14 @@ from operate.account.user import UserAccount
|
|
|
37
37
|
from operate.constants import IPFS_ADDRESS, OPERATE_HOME
|
|
38
38
|
from operate.data import DATA_DIR
|
|
39
39
|
from operate.data.contracts.staking_token.contract import StakingTokenContract
|
|
40
|
-
from operate.ledger.profiles import
|
|
40
|
+
from operate.ledger.profiles import (
|
|
41
|
+
DEFAULT_PRIORITY_MECH_ADDRESS,
|
|
42
|
+
DEFAULT_PRIORITY_MECH_SERVICE_ID,
|
|
43
|
+
NO_STAKING_PROGRAM_ID,
|
|
44
|
+
STAKING,
|
|
45
|
+
get_staking_contract,
|
|
46
|
+
get_staking_program_mech_type,
|
|
47
|
+
)
|
|
41
48
|
from operate.operate_types import (
|
|
42
49
|
Chain,
|
|
43
50
|
LedgerType,
|
|
@@ -99,8 +106,6 @@ QS_STAKING_PROGRAMS: t.Dict[Chain, t.Dict[str, str]] = {
|
|
|
99
106
|
"quickstart_beta_mech_marketplace_expert_8": "trader",
|
|
100
107
|
"mech_marketplace": "mech",
|
|
101
108
|
"marketplace_supply_alpha": "mech",
|
|
102
|
-
"marketplace_demand_alpha_1": "mech",
|
|
103
|
-
"marketplace_demand_alpha_2": "mech",
|
|
104
109
|
},
|
|
105
110
|
Chain.OPTIMISTIC: {
|
|
106
111
|
"optimus_alpha_2": "optimus",
|
|
@@ -111,8 +116,6 @@ QS_STAKING_PROGRAMS: t.Dict[Chain, t.Dict[str, str]] = {
|
|
|
111
116
|
Chain.BASE: {
|
|
112
117
|
"meme_base_alpha_2": "memeooorr",
|
|
113
118
|
"marketplace_supply_alpha": "mech",
|
|
114
|
-
"marketplace_demand_alpha_1": "mech",
|
|
115
|
-
"marketplace_demand_alpha_2": "mech",
|
|
116
119
|
"agents_fun_1": "memeooorr",
|
|
117
120
|
"agents_fun_2": "memeooorr",
|
|
118
121
|
"agents_fun_3": "memeooorr",
|
|
@@ -393,8 +396,31 @@ def configure_local_config(
|
|
|
393
396
|
):
|
|
394
397
|
print_section("Please enter the arguments that will be used by the service.")
|
|
395
398
|
|
|
399
|
+
staking_program_mech_type = get_staking_program_mech_type(config.staking_program_id)
|
|
400
|
+
|
|
396
401
|
for env_var_name, env_var_data in template["env_variables"].items():
|
|
397
402
|
if env_var_data["provision_type"] == ServiceEnvProvisionType.USER:
|
|
403
|
+
# PRIORITY_MECH_ADDRESS and PRIORITY_MECH_SERVICE_ID are given dynamic default values
|
|
404
|
+
if env_var_name == "PRIORITY_MECH_ADDRESS":
|
|
405
|
+
env_var_data["value"] = DEFAULT_PRIORITY_MECH_ADDRESS[
|
|
406
|
+
staking_program_mech_type
|
|
407
|
+
]
|
|
408
|
+
if (
|
|
409
|
+
env_var_name in config.user_provided_args
|
|
410
|
+
and env_var_data["value"] != config.user_provided_args[env_var_name]
|
|
411
|
+
):
|
|
412
|
+
del config.user_provided_args[env_var_name]
|
|
413
|
+
|
|
414
|
+
if env_var_name == "PRIORITY_MECH_SERVICE_ID":
|
|
415
|
+
env_var_data["value"] = str(
|
|
416
|
+
DEFAULT_PRIORITY_MECH_SERVICE_ID.get(staking_program_mech_type, 0)
|
|
417
|
+
)
|
|
418
|
+
if (
|
|
419
|
+
env_var_name in config.user_provided_args
|
|
420
|
+
and env_var_data["value"] != config.user_provided_args[env_var_name]
|
|
421
|
+
):
|
|
422
|
+
del config.user_provided_args[env_var_name]
|
|
423
|
+
|
|
398
424
|
if env_var_name not in config.user_provided_args:
|
|
399
425
|
print(f"Description: {env_var_data['description']}")
|
|
400
426
|
if env_var_data["value"]:
|
operate/quickstart/utils.py
CHANGED
|
@@ -188,8 +188,12 @@ def ask_yes_or_no(question: str) -> bool:
|
|
|
188
188
|
"""Ask a yes/no question."""
|
|
189
189
|
if os.environ.get("ATTENDED", "true").lower() != "true":
|
|
190
190
|
return True
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
while True:
|
|
192
|
+
response = input(f"{question} (yes/no): ").strip().lower()
|
|
193
|
+
if response.lower() in ("yes", "y"):
|
|
194
|
+
return True
|
|
195
|
+
if response.lower() in ("no", "n"):
|
|
196
|
+
return False
|
|
193
197
|
|
|
194
198
|
|
|
195
199
|
def ask_or_get_from_env(
|
operate/services/agent_runner.py
CHANGED
|
@@ -18,116 +18,193 @@
|
|
|
18
18
|
#
|
|
19
19
|
# -------------------------------------------------------------
|
|
20
20
|
"""Source dode to download and run agent from the repos."""
|
|
21
|
+
import hashlib
|
|
21
22
|
import os
|
|
22
23
|
import platform
|
|
23
24
|
import shutil
|
|
24
25
|
import stat
|
|
26
|
+
from dataclasses import dataclass
|
|
25
27
|
from pathlib import Path
|
|
26
28
|
from tempfile import TemporaryDirectory
|
|
27
29
|
from typing import Tuple
|
|
28
30
|
|
|
29
31
|
import requests
|
|
30
32
|
from aea.configurations.data_types import PublicId
|
|
33
|
+
from aea.helpers.logging import setup_logger
|
|
31
34
|
|
|
32
35
|
|
|
36
|
+
@dataclass
|
|
37
|
+
class AgentRelease:
|
|
38
|
+
"""Agent release dataclass."""
|
|
39
|
+
|
|
40
|
+
owner: str
|
|
41
|
+
repo: str
|
|
42
|
+
release: str
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def release_url(self) -> str:
|
|
46
|
+
"""Get release api url."""
|
|
47
|
+
return f"https://api.github.com/repos/{self.owner}/{self.repo}/releases/tags/{self.release}"
|
|
48
|
+
|
|
49
|
+
def get_url_and_hash(self, asset_name: str) -> tuple[str, str]:
|
|
50
|
+
"""Get download url and asset sha256 hash."""
|
|
51
|
+
release_data = requests.get(self.release_url).json()
|
|
52
|
+
|
|
53
|
+
assets_filtered = [i for i in release_data["assets"] if i["name"] == asset_name]
|
|
54
|
+
if not assets_filtered:
|
|
55
|
+
raise ValueError(
|
|
56
|
+
f"Asset {asset_name} not found in release {self.release_url}"
|
|
57
|
+
)
|
|
58
|
+
asset = assets_filtered[0]
|
|
59
|
+
file_hash = asset["digest"]
|
|
60
|
+
file_url = asset["browser_download_url"]
|
|
61
|
+
|
|
62
|
+
return file_url, file_hash
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# list of agents releases supported
|
|
33
66
|
AGENTS_SUPPORTED = {
|
|
34
|
-
"valory":
|
|
35
|
-
"trader"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
"valory/trader": AgentRelease(
|
|
68
|
+
owner="valory-xyz", repo="trader", release="v0.0.101"
|
|
69
|
+
),
|
|
70
|
+
"valory/optimus": AgentRelease(
|
|
71
|
+
owner="valory-xyz", repo="optimus", release="v0.0.103"
|
|
72
|
+
),
|
|
73
|
+
"dvilela/memeooorr": AgentRelease(
|
|
74
|
+
owner="valory-xyz", repo="meme-ooorr-test", release="v0.0.101"
|
|
75
|
+
),
|
|
41
76
|
}
|
|
42
77
|
|
|
43
78
|
|
|
44
|
-
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
raise ValueError("
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
79
|
+
class AgentRunnerManager:
|
|
80
|
+
"""Agent Runner Manager."""
|
|
81
|
+
|
|
82
|
+
logger = setup_logger(name="operate.agent_runner_manager")
|
|
83
|
+
AGENTS = AGENTS_SUPPORTED
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def get_agent_runner_executable_name() -> str:
|
|
87
|
+
"""Get runner executable name by platform running."""
|
|
88
|
+
if platform.system() == "Darwin":
|
|
89
|
+
os_name = "macos"
|
|
90
|
+
elif platform.system() == "Windows":
|
|
91
|
+
os_name = "windows"
|
|
92
|
+
else:
|
|
93
|
+
raise ValueError("Platform not supported!")
|
|
94
|
+
|
|
95
|
+
if platform.machine().lower() in ("x86_64", "amd64"):
|
|
96
|
+
arch = "x64"
|
|
97
|
+
elif platform.machine().lower() == "arm64":
|
|
98
|
+
arch = "arm64"
|
|
99
|
+
if os_name == "windows":
|
|
100
|
+
raise ValueError("Windows arm64 is not supported!")
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError(f"unsupported arch: {platform.machine()}")
|
|
103
|
+
|
|
104
|
+
exec_name = f"agent_runner_{os_name}_{arch}"
|
|
105
|
+
if platform.system() == "Windows":
|
|
106
|
+
exec_name += ".exe"
|
|
107
|
+
return exec_name
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def parse_agent(public_id_str: str) -> Tuple[str, str]:
|
|
111
|
+
"""Get authorn and name from agent public string id."""
|
|
112
|
+
public_id = PublicId.from_str(public_id_string=public_id_str)
|
|
113
|
+
return (public_id.author, public_id.name)
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def download_file(cls, url: str, save_path: Path) -> None:
|
|
117
|
+
"""Download file of agent runner."""
|
|
118
|
+
try:
|
|
119
|
+
# Send a GET request to the URL
|
|
120
|
+
response = requests.get(url, stream=True)
|
|
121
|
+
response.raise_for_status() # Raise an error for bad status codes (4xx or 5xx)
|
|
122
|
+
|
|
123
|
+
# Open the file in binary write mode and save the content
|
|
124
|
+
with open(save_path, "wb") as file:
|
|
125
|
+
for chunk in response.iter_content(chunk_size=8192):
|
|
126
|
+
file.write(chunk)
|
|
127
|
+
|
|
128
|
+
cls.logger.info(f"File downloaded and saved to {save_path}")
|
|
129
|
+
except requests.exceptions.RequestException as e:
|
|
130
|
+
cls.logger.error(f"Error downloading file: {e}")
|
|
131
|
+
raise
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def get_agent_release_by_public_id(cls, agent_public_id_str: str) -> AgentRelease:
|
|
135
|
+
"""Get agent release object according to public id."""
|
|
136
|
+
agent_author, agent_name = cls.parse_agent(public_id_str=agent_public_id_str)
|
|
137
|
+
|
|
138
|
+
agent_name = f"{agent_author}/{agent_name}"
|
|
139
|
+
agent_release = cls.AGENTS.get(agent_name, None)
|
|
140
|
+
if agent_release is None:
|
|
141
|
+
raise ValueError(f"{agent_name} is not supported!")
|
|
142
|
+
return agent_release
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def get_local_file_sha256(path: Path) -> str:
|
|
146
|
+
"""Get local file sha256."""
|
|
147
|
+
sha256_hash = hashlib.sha256()
|
|
148
|
+
with open(path, "rb") as f:
|
|
149
|
+
for byte_block in iter(lambda: f.read(4096), b""):
|
|
150
|
+
sha256_hash.update(byte_block)
|
|
151
|
+
return "sha256:" + sha256_hash.hexdigest()
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def update_agent_runner(
|
|
155
|
+
cls, target_path: Path, agent_runner_name: str, agent_release: AgentRelease
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Download agent runner."""
|
|
158
|
+
download_url, remote_file_hash = agent_release.get_url_and_hash(
|
|
159
|
+
agent_runner_name
|
|
102
160
|
)
|
|
103
|
-
repo_url = AGENTS_SUPPORTED[agent_author][agent_name]
|
|
104
|
-
download_url = f"{repo_url}{agent_runner_name}"
|
|
105
|
-
try:
|
|
106
|
-
with TemporaryDirectory() as tmp_dir:
|
|
107
|
-
tmp_file = Path(tmp_dir) / "agent_runner"
|
|
108
|
-
download_file(download_url, tmp_file)
|
|
109
|
-
shutil.copy2(tmp_file, target_path)
|
|
110
|
-
if os.name == "posix":
|
|
111
|
-
target_path.chmod(target_path.stat().st_mode | stat.S_IEXEC)
|
|
112
|
-
except Exception:
|
|
113
|
-
# remove in cae of errors
|
|
114
|
-
if target_path.exists():
|
|
115
|
-
target_path.unlink(missing_ok=True)
|
|
116
|
-
raise
|
|
117
161
|
|
|
162
|
+
if target_path.exists():
|
|
163
|
+
# check sha
|
|
164
|
+
current_file_hash = cls.get_local_file_sha256(target_path)
|
|
165
|
+
if remote_file_hash == current_file_hash:
|
|
166
|
+
cls.logger.info(
|
|
167
|
+
"local and remote files hashes are match, nothing to download"
|
|
168
|
+
)
|
|
169
|
+
return
|
|
170
|
+
cls.logger.info(
|
|
171
|
+
"local and remote files hashes does not match, go to download"
|
|
172
|
+
)
|
|
173
|
+
else:
|
|
174
|
+
cls.logger.info("local file not found, go to download")
|
|
175
|
+
|
|
176
|
+
try:
|
|
177
|
+
with TemporaryDirectory() as tmp_dir:
|
|
178
|
+
tmp_file = Path(tmp_dir) / "agent_runner"
|
|
179
|
+
cls.download_file(download_url, tmp_file)
|
|
180
|
+
shutil.copy2(tmp_file, target_path)
|
|
181
|
+
if os.name == "posix":
|
|
182
|
+
target_path.chmod(target_path.stat().st_mode | stat.S_IEXEC)
|
|
183
|
+
except Exception:
|
|
184
|
+
# remove in caae of errors
|
|
185
|
+
if target_path.exists():
|
|
186
|
+
target_path.unlink(missing_ok=True)
|
|
187
|
+
raise
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def get_agent_runner_path(cls, service_dir: Path, agent_public_id_str: str) -> str:
|
|
191
|
+
"""Get path to the agent runner bin palced."""
|
|
192
|
+
agent_runner_name = cls.get_agent_runner_executable_name()
|
|
193
|
+
agent_runner_path: Path = service_dir / agent_runner_name
|
|
194
|
+
agent_release = cls.get_agent_release_by_public_id(
|
|
195
|
+
agent_public_id_str=agent_public_id_str
|
|
196
|
+
)
|
|
118
197
|
|
|
119
|
-
|
|
120
|
-
"""Get path to the agent runner bin palced."""
|
|
121
|
-
agent_runner_name = get_agent_runner_executable_name()
|
|
122
|
-
agent_runner_path: Path = service_dir / agent_runner_name
|
|
123
|
-
|
|
124
|
-
if agent_runner_path.exists():
|
|
125
|
-
print(f"agent runner {agent_runner_path} already exists. dont download it.")
|
|
126
|
-
else:
|
|
127
|
-
print(f"agent runner {agent_runner_path} does not exists. downloading it.")
|
|
128
|
-
download_agent_runner(
|
|
198
|
+
cls.update_agent_runner(
|
|
129
199
|
target_path=agent_runner_path,
|
|
130
200
|
agent_runner_name=agent_runner_name,
|
|
131
|
-
|
|
201
|
+
agent_release=agent_release,
|
|
132
202
|
)
|
|
133
|
-
|
|
203
|
+
return str(agent_runner_path)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def get_agent_runner_path(service_dir: Path, agent_public_id_str: str) -> str:
|
|
207
|
+
"""Get path to the agent runner bin placed."""
|
|
208
|
+
return AgentRunnerManager.get_agent_runner_path(
|
|
209
|
+
service_dir=service_dir, agent_public_id_str=agent_public_id_str
|
|
210
|
+
)
|
operate/services/manage.py
CHANGED
|
@@ -52,12 +52,14 @@ from operate.ledger import PUBLIC_RPCS, get_currency_denom
|
|
|
52
52
|
from operate.ledger.profiles import (
|
|
53
53
|
CONTRACTS,
|
|
54
54
|
DEFAULT_MASTER_EOA_FUNDS,
|
|
55
|
-
|
|
55
|
+
DEFAULT_PRIORITY_MECH_ADDRESS,
|
|
56
|
+
DEFAULT_PRIORITY_MECH_SERVICE_ID,
|
|
56
57
|
OLAS,
|
|
57
58
|
STAKING,
|
|
58
59
|
USDC,
|
|
59
60
|
WRAPPED_NATIVE_ASSET,
|
|
60
61
|
get_staking_contract,
|
|
62
|
+
get_staking_program_mech_type,
|
|
61
63
|
)
|
|
62
64
|
from operate.operate_types import (
|
|
63
65
|
Chain,
|
|
@@ -106,6 +108,9 @@ HTTP_OK = 200
|
|
|
106
108
|
URI_HASH_POSITION = 7
|
|
107
109
|
IPFS_GATEWAY = "https://gateway.autonolas.tech/ipfs/"
|
|
108
110
|
DEFAULT_TOPUP_THRESHOLD = 0.5
|
|
111
|
+
# At the moment, we only support running one agent per service locally on a machine.
|
|
112
|
+
# If multiple agents are provided in the service.yaml file, only the 0th index config will be used.
|
|
113
|
+
NUM_LOCAL_AGENT_INSTANCES = 1
|
|
109
114
|
|
|
110
115
|
|
|
111
116
|
class ServiceManager:
|
|
@@ -227,7 +232,7 @@ class ServiceManager:
|
|
|
227
232
|
if not service.keys:
|
|
228
233
|
service.keys = [
|
|
229
234
|
self.keys_manager.get(self.keys_manager.create())
|
|
230
|
-
for _ in range(
|
|
235
|
+
for _ in range(NUM_LOCAL_AGENT_INSTANCES)
|
|
231
236
|
]
|
|
232
237
|
service.store()
|
|
233
238
|
|
|
@@ -267,7 +272,7 @@ class ServiceManager:
|
|
|
267
272
|
if not service.keys:
|
|
268
273
|
service.keys = [
|
|
269
274
|
self.keys_manager.get(self.keys_manager.create())
|
|
270
|
-
for _ in range(
|
|
275
|
+
for _ in range(NUM_LOCAL_AGENT_INSTANCES)
|
|
271
276
|
]
|
|
272
277
|
service.store()
|
|
273
278
|
|
|
@@ -437,7 +442,7 @@ class ServiceManager:
|
|
|
437
442
|
ocm.mint(
|
|
438
443
|
package_path=service.package_absolute_path_absolute_path,
|
|
439
444
|
agent_id=staking_params["agent_ids"][0],
|
|
440
|
-
number_of_slots=
|
|
445
|
+
number_of_slots=NUM_LOCAL_AGENT_INSTANCES,
|
|
441
446
|
cost_of_bond=(
|
|
442
447
|
staking_params["min_staking_deposit"]
|
|
443
448
|
if user_params.use_staking
|
|
@@ -551,6 +556,10 @@ class ServiceManager:
|
|
|
551
556
|
self.logger.info(f"Service state: {on_chain_state.name}")
|
|
552
557
|
|
|
553
558
|
current_staking_program = self._get_current_staking_program(service, chain)
|
|
559
|
+
staking_program_mech_type = get_staking_program_mech_type(
|
|
560
|
+
user_params.staking_program_id
|
|
561
|
+
)
|
|
562
|
+
self.logger.info(f"{staking_program_mech_type=}")
|
|
554
563
|
fallback_params = dict( # nosec
|
|
555
564
|
staking_contract=NULL_ADDRESS,
|
|
556
565
|
agent_ids=[user_params.agent_id],
|
|
@@ -599,6 +608,9 @@ class ServiceManager:
|
|
|
599
608
|
use_mech_marketplace = False
|
|
600
609
|
mech_marketplace_address = ZERO_ADDRESS
|
|
601
610
|
priority_mech_address = ZERO_ADDRESS
|
|
611
|
+
priority_mech_service_id = DEFAULT_PRIORITY_MECH_SERVICE_ID.get(
|
|
612
|
+
staking_program_mech_type, 0
|
|
613
|
+
)
|
|
602
614
|
|
|
603
615
|
except Exception: # pylint: disable=broad-except
|
|
604
616
|
# Try if activity checker is a RequesterActivityChecker contract
|
|
@@ -635,17 +647,35 @@ class ServiceManager:
|
|
|
635
647
|
else:
|
|
636
648
|
agent_mech = (
|
|
637
649
|
priority_mech_address
|
|
638
|
-
) =
|
|
650
|
+
) = DEFAULT_PRIORITY_MECH_ADDRESS[staking_program_mech_type]
|
|
651
|
+
|
|
652
|
+
if (
|
|
653
|
+
"PRIORITY_MECH_SERVICE_ID" in service.env_variables
|
|
654
|
+
and service.env_variables["PRIORITY_MECH_SERVICE_ID"][
|
|
655
|
+
"provision_type"
|
|
656
|
+
]
|
|
657
|
+
== ServiceEnvProvisionType.USER
|
|
658
|
+
):
|
|
659
|
+
priority_mech_service_id = service.env_variables[
|
|
660
|
+
"PRIORITY_MECH_SERVICE_ID"
|
|
661
|
+
]["value"]
|
|
662
|
+
else:
|
|
663
|
+
priority_mech_service_id = DEFAULT_PRIORITY_MECH_SERVICE_ID.get(
|
|
664
|
+
staking_program_mech_type, 0
|
|
665
|
+
)
|
|
639
666
|
|
|
640
667
|
except Exception: # pylint: disable=broad-except
|
|
641
668
|
self.logger.warning(
|
|
642
669
|
"Cannot determine type of activity checker contract. Using default parameters. "
|
|
643
670
|
"NOTE: This will be an exception in the future!"
|
|
644
671
|
)
|
|
645
|
-
agent_mech =
|
|
672
|
+
agent_mech = DEFAULT_PRIORITY_MECH_ADDRESS[
|
|
673
|
+
staking_program_mech_type
|
|
674
|
+
]
|
|
646
675
|
use_mech_marketplace = False
|
|
647
676
|
mech_marketplace_address = ZERO_ADDRESS
|
|
648
677
|
priority_mech_address = ZERO_ADDRESS
|
|
678
|
+
priority_mech_service_id = 0
|
|
649
679
|
|
|
650
680
|
env_var_to_value.update(
|
|
651
681
|
{
|
|
@@ -666,7 +696,7 @@ class ServiceManager:
|
|
|
666
696
|
f'{{"mech_marketplace_address":"{mech_marketplace_address}",'
|
|
667
697
|
f'"priority_mech_address":"{priority_mech_address}",'
|
|
668
698
|
f'"priority_mech_staking_instance_address":"0x998dEFafD094817EF329f6dc79c703f1CF18bC90",'
|
|
669
|
-
f'"priority_mech_service_id":{
|
|
699
|
+
f'"priority_mech_service_id":{priority_mech_service_id},'
|
|
670
700
|
f'"requester_staking_instance_address":"{target_staking_params.get("staking_contract")}",'
|
|
671
701
|
f'"response_timeout":300}}'
|
|
672
702
|
),
|
|
@@ -709,7 +739,7 @@ class ServiceManager:
|
|
|
709
739
|
)
|
|
710
740
|
protocol_asset_requirements[target_staking_params["staking_token"]] = (
|
|
711
741
|
target_staking_params["min_staking_deposit"]
|
|
712
|
-
*
|
|
742
|
+
* NUM_LOCAL_AGENT_INSTANCES
|
|
713
743
|
)
|
|
714
744
|
else:
|
|
715
745
|
protocol_asset_requirements = {}
|
|
@@ -791,7 +821,7 @@ class ServiceManager:
|
|
|
791
821
|
sftxb.get_mint_tx_data(
|
|
792
822
|
package_path=service.package_absolute_path,
|
|
793
823
|
agent_id=agent_id,
|
|
794
|
-
number_of_slots=
|
|
824
|
+
number_of_slots=NUM_LOCAL_AGENT_INSTANCES,
|
|
795
825
|
cost_of_bond=(
|
|
796
826
|
target_staking_params["min_staking_deposit"]
|
|
797
827
|
if user_params.use_staking
|
|
@@ -841,7 +871,7 @@ class ServiceManager:
|
|
|
841
871
|
sftxb.get_mint_tx_data(
|
|
842
872
|
package_path=service.package_absolute_path,
|
|
843
873
|
agent_id=agent_id,
|
|
844
|
-
number_of_slots=
|
|
874
|
+
number_of_slots=NUM_LOCAL_AGENT_INSTANCES,
|
|
845
875
|
cost_of_bond=(
|
|
846
876
|
target_staking_params["min_staking_deposit"]
|
|
847
877
|
if user_params.use_staking
|
|
@@ -2504,7 +2534,7 @@ class ServiceManager:
|
|
|
2504
2534
|
chain_config = service.chain_configs[chain]
|
|
2505
2535
|
user_params = chain_config.chain_data.user_params
|
|
2506
2536
|
ledger_config = chain_config.ledger_config
|
|
2507
|
-
number_of_agents =
|
|
2537
|
+
number_of_agents = NUM_LOCAL_AGENT_INSTANCES
|
|
2508
2538
|
os.environ["CUSTOM_CHAIN_RPC"] = ledger_config.rpc
|
|
2509
2539
|
sftxb = self.get_eth_safe_tx_builder(ledger_config=ledger_config)
|
|
2510
2540
|
service_asset_requirements: defaultdict = defaultdict(int)
|
operate/services/service.py
CHANGED
|
@@ -320,13 +320,19 @@ class HostDeploymentGenerator(BaseDeploymentGenerator):
|
|
|
320
320
|
tendermint_executable = str(
|
|
321
321
|
Path(os.path.dirname(sys.executable)) / "tendermint"
|
|
322
322
|
)
|
|
323
|
-
|
|
323
|
+
|
|
324
324
|
if platform.system() == "Windows":
|
|
325
325
|
env["PATH"] = os.path.dirname(sys.executable) + ";" + os.environ["PATH"]
|
|
326
326
|
tendermint_executable = str(
|
|
327
327
|
Path(os.path.dirname(sys.executable)) / "tendermint.exe"
|
|
328
328
|
)
|
|
329
|
-
|
|
329
|
+
|
|
330
|
+
if not (getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")):
|
|
331
|
+
# we dont run inside pyinstaller, mean DEV mode!
|
|
332
|
+
tendermint_executable = "tendermint"
|
|
333
|
+
if platform.system() == "Windows":
|
|
334
|
+
tendermint_executable = "tendermint.exe"
|
|
335
|
+
|
|
330
336
|
subprocess.run( # pylint: disable=subprocess-run-check # nosec
|
|
331
337
|
args=[
|
|
332
338
|
tendermint_executable,
|
|
@@ -1251,6 +1257,8 @@ class Service(LocalResource):
|
|
|
1251
1257
|
config # type: ignore
|
|
1252
1258
|
)
|
|
1253
1259
|
|
|
1260
|
+
self.chain_configs[chain].ledger_config.rpc = config["rpc"]
|
|
1261
|
+
|
|
1254
1262
|
self.store()
|
|
1255
1263
|
|
|
1256
1264
|
def consume_env_variables(self) -> None:
|