olas-operate-middleware 0.6.3__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.3.dist-info → olas_operate_middleware-0.7.0.dist-info}/METADATA +1 -1
- {olas_operate_middleware-0.6.3.dist-info → olas_operate_middleware-0.7.0.dist-info}/RECORD +16 -15
- 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 +28 -7
- operate/constants.py +1 -0
- operate/ledger/profiles.py +37 -1
- operate/quickstart/run_service.py +31 -1
- operate/services/agent_runner.py +168 -91
- operate/services/manage.py +31 -4
- {olas_operate_middleware-0.6.3.dist-info → olas_operate_middleware-0.7.0.dist-info}/LICENSE +0 -0
- {olas_operate_middleware-0.6.3.dist-info → olas_operate_middleware-0.7.0.dist-info}/WHEEL +0 -0
- {olas_operate_middleware-0.6.3.dist-info → olas_operate_middleware-0.7.0.dist-info}/entry_points.txt +0 -0
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,
|
|
@@ -554,6 +556,10 @@ class ServiceManager:
|
|
|
554
556
|
self.logger.info(f"Service state: {on_chain_state.name}")
|
|
555
557
|
|
|
556
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=}")
|
|
557
563
|
fallback_params = dict( # nosec
|
|
558
564
|
staking_contract=NULL_ADDRESS,
|
|
559
565
|
agent_ids=[user_params.agent_id],
|
|
@@ -602,6 +608,9 @@ class ServiceManager:
|
|
|
602
608
|
use_mech_marketplace = False
|
|
603
609
|
mech_marketplace_address = ZERO_ADDRESS
|
|
604
610
|
priority_mech_address = ZERO_ADDRESS
|
|
611
|
+
priority_mech_service_id = DEFAULT_PRIORITY_MECH_SERVICE_ID.get(
|
|
612
|
+
staking_program_mech_type, 0
|
|
613
|
+
)
|
|
605
614
|
|
|
606
615
|
except Exception: # pylint: disable=broad-except
|
|
607
616
|
# Try if activity checker is a RequesterActivityChecker contract
|
|
@@ -638,17 +647,35 @@ class ServiceManager:
|
|
|
638
647
|
else:
|
|
639
648
|
agent_mech = (
|
|
640
649
|
priority_mech_address
|
|
641
|
-
) =
|
|
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
|
+
)
|
|
642
666
|
|
|
643
667
|
except Exception: # pylint: disable=broad-except
|
|
644
668
|
self.logger.warning(
|
|
645
669
|
"Cannot determine type of activity checker contract. Using default parameters. "
|
|
646
670
|
"NOTE: This will be an exception in the future!"
|
|
647
671
|
)
|
|
648
|
-
agent_mech =
|
|
672
|
+
agent_mech = DEFAULT_PRIORITY_MECH_ADDRESS[
|
|
673
|
+
staking_program_mech_type
|
|
674
|
+
]
|
|
649
675
|
use_mech_marketplace = False
|
|
650
676
|
mech_marketplace_address = ZERO_ADDRESS
|
|
651
677
|
priority_mech_address = ZERO_ADDRESS
|
|
678
|
+
priority_mech_service_id = 0
|
|
652
679
|
|
|
653
680
|
env_var_to_value.update(
|
|
654
681
|
{
|
|
@@ -669,7 +696,7 @@ class ServiceManager:
|
|
|
669
696
|
f'{{"mech_marketplace_address":"{mech_marketplace_address}",'
|
|
670
697
|
f'"priority_mech_address":"{priority_mech_address}",'
|
|
671
698
|
f'"priority_mech_staking_instance_address":"0x998dEFafD094817EF329f6dc79c703f1CF18bC90",'
|
|
672
|
-
f'"priority_mech_service_id":{
|
|
699
|
+
f'"priority_mech_service_id":{priority_mech_service_id},'
|
|
673
700
|
f'"requester_staking_instance_address":"{target_staking_params.get("staking_contract")}",'
|
|
674
701
|
f'"response_timeout":300}}'
|
|
675
702
|
),
|
|
File without changes
|
|
File without changes
|
{olas_operate_middleware-0.6.3.dist-info → olas_operate_middleware-0.7.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|