olas-operate-middleware 0.12.1__py3-none-any.whl → 0.13.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.
@@ -19,6 +19,7 @@
19
19
  # -------------------------------------------------------------
20
20
  """Source dode to download and run agent from the repos."""
21
21
  import hashlib
22
+ import json
22
23
  import os
23
24
  import platform
24
25
  import shutil
@@ -32,7 +33,7 @@ import requests
32
33
  from aea.configurations.data_types import PublicId
33
34
  from aea.helpers.logging import setup_logger
34
35
 
35
- from operate.constants import AGENT_RUNNER_PREFIX
36
+ from operate.constants import AGENT_RUNNER_PREFIX, CONFIG_JSON
36
37
 
37
38
 
38
39
  @dataclass
@@ -42,6 +43,7 @@ class AgentRelease:
42
43
  owner: str
43
44
  repo: str
44
45
  release: str
46
+ is_aea: bool
45
47
 
46
48
  @property
47
49
  def release_url(self) -> str:
@@ -64,25 +66,10 @@ class AgentRelease:
64
66
  return file_url, file_hash
65
67
 
66
68
 
67
- # list of agents releases supported
68
- AGENTS_SUPPORTED = {
69
- "valory/trader": AgentRelease(
70
- owner="valory-xyz", repo="trader", release="v0.27.2-1-rc.2"
71
- ),
72
- "valory/optimus": AgentRelease(
73
- owner="valory-xyz", repo="optimus", release="v0.6.0-rc.1"
74
- ),
75
- "dvilela/memeooorr": AgentRelease(
76
- owner="valory-xyz", repo="meme-ooorr", release="v0.0.101"
77
- ),
78
- }
79
-
80
-
81
69
  class AgentRunnerManager:
82
70
  """Agent Runner Manager."""
83
71
 
84
72
  logger = setup_logger(name="operate.agent_runner_manager")
85
- AGENTS = AGENTS_SUPPORTED
86
73
 
87
74
  @staticmethod
88
75
  def get_agent_runner_executable_name() -> str:
@@ -133,14 +120,19 @@ class AgentRunnerManager:
133
120
  raise
134
121
 
135
122
  @classmethod
136
- def get_agent_release_by_public_id(cls, agent_public_id_str: str) -> AgentRelease:
123
+ def get_agent_release_from_service_dir(cls, service_dir: Path) -> AgentRelease:
137
124
  """Get agent release object according to public id."""
138
- agent_author, agent_name = cls.parse_agent(public_id_str=agent_public_id_str)
139
-
140
- agent_name = f"{agent_author}/{agent_name}"
141
- agent_release = cls.AGENTS.get(agent_name, None)
142
- if agent_release is None:
143
- raise ValueError(f"{agent_name} is not supported!")
125
+ service_config_file = service_dir / CONFIG_JSON
126
+ service_config = json.loads(service_config_file.read_text())
127
+ if "agent_release" not in service_config:
128
+ raise ValueError(f"Agent release details are not found in {service_config}")
129
+ agent_release_data = service_config["agent_release"]
130
+ agent_release = AgentRelease(
131
+ is_aea=agent_release_data["is_aea"],
132
+ owner=agent_release_data["repository"]["owner"],
133
+ repo=agent_release_data["repository"]["name"],
134
+ release=agent_release_data["repository"]["version"],
135
+ )
144
136
  return agent_release
145
137
 
146
138
  @staticmethod
@@ -189,13 +181,11 @@ class AgentRunnerManager:
189
181
  raise
190
182
 
191
183
  @classmethod
192
- def get_agent_runner_path(cls, service_dir: Path, agent_public_id_str: str) -> str:
184
+ def get_agent_runner_path(cls, service_dir: Path) -> str:
193
185
  """Get path to the agent runner bin palced."""
194
186
  agent_runner_name = cls.get_agent_runner_executable_name()
195
187
  agent_runner_path: Path = service_dir / agent_runner_name
196
- agent_release = cls.get_agent_release_by_public_id(
197
- agent_public_id_str=agent_public_id_str
198
- )
188
+ agent_release = cls.get_agent_release_from_service_dir(service_dir=service_dir)
199
189
 
200
190
  cls.update_agent_runner(
201
191
  target_path=agent_runner_path,
@@ -205,8 +195,8 @@ class AgentRunnerManager:
205
195
  return str(agent_runner_path)
206
196
 
207
197
 
208
- def get_agent_runner_path(service_dir: Path, agent_public_id_str: str) -> str:
198
+ def get_agent_runner_path(service_dir: Path) -> str:
209
199
  """Get path to the agent runner bin placed."""
210
200
  return AgentRunnerManager.get_agent_runner_path(
211
- service_dir=service_dir, agent_public_id_str=agent_public_id_str
201
+ service_dir=service_dir,
212
202
  )
@@ -105,6 +105,11 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
105
105
  START_TRIES = constants.DEPLOYMENT_START_TRIES_NUM
106
106
  logger = setup_logger(name="operate.base_deployment_runner")
107
107
 
108
+ def __init__(self, work_directory: Path, is_aea: bool) -> None:
109
+ """Initialize the deployment runner."""
110
+ super().__init__(work_directory)
111
+ self._is_aea = is_aea
112
+
108
113
  def _open_agent_runner_log_file(self) -> TextIOWrapper:
109
114
  """Open agent_runner.log file."""
110
115
  return (
@@ -186,6 +191,21 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
186
191
  """Setup agent."""
187
192
  working_dir = self._work_directory
188
193
  env = self._prepare_agent_env()
194
+ agent_alias_name = "agent"
195
+ agent_dir_full_path = Path(working_dir) / agent_alias_name
196
+ if not self._is_aea:
197
+ if agent_dir_full_path.exists():
198
+ # remove if exists before fetching! can have issues with retry mechanism of multiple start attempts
199
+ with suppress(Exception):
200
+ shutil.rmtree(agent_dir_full_path, ignore_errors=True)
201
+
202
+ # Add keys
203
+ agent_dir_full_path.mkdir(exist_ok=True, parents=True)
204
+ shutil.copy(
205
+ working_dir / "ethereum_private_key.txt",
206
+ working_dir / "agent" / "ethereum_private_key.txt",
207
+ )
208
+ return
189
209
 
190
210
  self._run_aea_command(
191
211
  "init",
@@ -199,10 +219,6 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
199
219
  cwd=working_dir,
200
220
  )
201
221
 
202
- agent_alias_name = "agent"
203
-
204
- agent_dir_full_path = Path(working_dir) / agent_alias_name
205
-
206
222
  if agent_dir_full_path.exists():
207
223
  # remove if exists before fetching! can have issues with retry mechanism of multiple start attempts
208
224
  with suppress(Exception):
@@ -264,13 +280,16 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
264
280
  def _start(self, password: str) -> None:
265
281
  """Start the deployment."""
266
282
  self._setup_agent(password=password)
267
- self._start_tendermint()
283
+ if self._is_aea:
284
+ self._start_tendermint()
285
+
268
286
  self._start_agent(password=password)
269
287
 
270
288
  def stop(self) -> None:
271
289
  """Stop the deployment."""
272
290
  self._stop_agent()
273
- self._stop_tendermint()
291
+ if self._is_aea:
292
+ self._stop_tendermint()
274
293
 
275
294
  def _stop_agent(self) -> None:
276
295
  """Start process."""
@@ -313,6 +332,24 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
313
332
  """Return aea_bin path."""
314
333
  raise NotImplementedError
315
334
 
335
+ def get_agent_start_args(self, password: str) -> List[str]:
336
+ """Return agent start arguments."""
337
+ return (
338
+ [self._agent_runner_bin]
339
+ + (
340
+ [
341
+ "-s",
342
+ "run",
343
+ ]
344
+ if self._is_aea
345
+ else []
346
+ )
347
+ + [
348
+ "--password",
349
+ password,
350
+ ]
351
+ )
352
+
316
353
 
317
354
  class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
318
355
  """Deployment runner within pyinstaller env."""
@@ -320,16 +357,8 @@ class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
320
357
  @property
321
358
  def _agent_runner_bin(self) -> str:
322
359
  """Return aea_bin path."""
323
- env = json.loads(
324
- (self._work_directory / "agent.json").read_text(encoding="utf-8")
325
- )
326
-
327
- agent_publicid_str = env["AEA_AGENT"]
328
360
  service_dir = self._work_directory.parent
329
-
330
- agent_runner_bin = get_agent_runner_path(
331
- service_dir=service_dir, agent_public_id_str=agent_publicid_str
332
- )
361
+ agent_runner_bin = get_agent_runner_path(service_dir=service_dir)
333
362
  return str(agent_runner_bin)
334
363
 
335
364
  @property
@@ -393,13 +422,7 @@ class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
393
422
  """Start agent process."""
394
423
  agent_runner_log_file = self._open_agent_runner_log_file()
395
424
  process = subprocess.Popen( # pylint: disable=consider-using-with,subprocess-popen-preexec-fn # nosec
396
- args=[
397
- self._agent_runner_bin,
398
- "-s",
399
- "run",
400
- "--password",
401
- password,
402
- ],
425
+ args=self.get_agent_start_args(password=password),
403
426
  cwd=working_dir / "agent",
404
427
  stdout=agent_runner_log_file,
405
428
  stderr=agent_runner_log_file,
@@ -431,9 +454,9 @@ class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
431
454
  class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
432
455
  """Windows deployment runner."""
433
456
 
434
- def __init__(self, work_directory: Path) -> None:
457
+ def __init__(self, work_directory: Path, is_aea: bool) -> None:
435
458
  """Init the runner."""
436
- super().__init__(work_directory)
459
+ super().__init__(work_directory, is_aea=is_aea)
437
460
  self._job = self.set_windows_object_job()
438
461
 
439
462
  @staticmethod
@@ -519,13 +542,7 @@ class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
519
542
  """Start agent process."""
520
543
  agent_runner_log_file = self._open_agent_runner_log_file()
521
544
  process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
522
- args=[
523
- self._agent_runner_bin,
524
- "-s",
525
- "run",
526
- "--password",
527
- password,
528
- ], # TODO: Patch for Windows failing hash
545
+ args=self.get_agent_start_args(password=password),
529
546
  cwd=working_dir / "agent",
530
547
  stdout=agent_runner_log_file,
531
548
  stderr=agent_runner_log_file,
@@ -562,7 +579,12 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
562
579
  @property
563
580
  def _agent_runner_bin(self) -> str:
564
581
  """Return aea_bin path."""
565
- return str(self._venv_dir / "bin" / "aea")
582
+ if self._is_aea:
583
+ return str(self._venv_dir / "bin" / "aea")
584
+
585
+ service_dir = self._work_directory.parent
586
+ agent_runner_bin = get_agent_runner_path(service_dir=service_dir)
587
+ return str(agent_runner_bin)
566
588
 
567
589
  def _start_agent(self, password: str) -> None:
568
590
  """Start agent process."""
@@ -573,13 +595,7 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
573
595
  agent_runner_log_file = self._open_agent_runner_log_file()
574
596
 
575
597
  process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
576
- args=[
577
- self._agent_runner_bin,
578
- "-s",
579
- "run",
580
- "--password",
581
- password,
582
- ], # TODO: Patch for Windows failing hash
598
+ args=self.get_agent_start_args(password=password),
583
599
  cwd=str(working_dir / "agent"),
584
600
  env={**os.environ, **env},
585
601
  stdout=agent_runner_log_file,
@@ -629,6 +645,9 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
629
645
 
630
646
  def _setup_venv(self) -> None:
631
647
  """Perform venv setup, install deps."""
648
+ if not self._is_aea:
649
+ return
650
+
632
651
  self._venv_dir.mkdir(exist_ok=True)
633
652
  venv_cli(args=[str(self._venv_dir)])
634
653
  pbin = str(self._venv_dir / "bin" / "python")
@@ -655,6 +674,9 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
655
674
  multiprocessing.set_start_method("spawn")
656
675
  self._setup_venv()
657
676
  super()._setup_agent(password=password)
677
+ if not self._is_aea:
678
+ return
679
+
658
680
  # Install agent dependencies
659
681
  self._run_cmd(
660
682
  args=[
@@ -690,9 +712,11 @@ class DeploymentManager:
690
712
  self.logger = setup_logger(name="operate.deployment_manager")
691
713
  self._states: Dict[Path, States] = {}
692
714
 
693
- def _get_deployment_runner(self, build_dir: Path) -> BaseDeploymentRunner:
715
+ def _get_deployment_runner(
716
+ self, build_dir: Path, is_aea: bool
717
+ ) -> BaseDeploymentRunner:
694
718
  """Get deploymnent runner instance."""
695
- return self._deployment_runner_class(build_dir)
719
+ return self._deployment_runner_class(build_dir, is_aea=is_aea)
696
720
 
697
721
  @staticmethod
698
722
  def _get_host_deployment_runner_class() -> Type[BaseDeploymentRunner]:
@@ -741,7 +765,9 @@ class DeploymentManager:
741
765
  "Failed to perform test connection to ipfs to check network connection!"
742
766
  )
743
767
 
744
- def run_deployment(self, build_dir: Path, password: str) -> None:
768
+ def run_deployment(
769
+ self, build_dir: Path, password: str, is_aea: bool = True
770
+ ) -> None:
745
771
  """Run deployment."""
746
772
  if self._is_stopping:
747
773
  raise RuntimeError("deployment manager stopped")
@@ -754,7 +780,9 @@ class DeploymentManager:
754
780
  self.logger.info(f"Starting deployment {build_dir}...")
755
781
  self._states[build_dir] = States.STARTING
756
782
  try:
757
- deployment_runner = self._get_deployment_runner(build_dir=build_dir)
783
+ deployment_runner = self._get_deployment_runner(
784
+ build_dir=build_dir, is_aea=is_aea
785
+ )
758
786
  deployment_runner.start(password=password)
759
787
  self.logger.info(f"Started deployment {build_dir}")
760
788
  self._states[build_dir] = States.STARTED
@@ -771,7 +799,9 @@ class DeploymentManager:
771
799
  )
772
800
  self.stop_deployment(build_dir=build_dir, force=True)
773
801
 
774
- def stop_deployment(self, build_dir: Path, force: bool = False) -> None:
802
+ def stop_deployment(
803
+ self, build_dir: Path, force: bool = False, is_aea: bool = True
804
+ ) -> None:
775
805
  """Stop the deployment."""
776
806
  if (
777
807
  self.get_state(build_dir=build_dir) in [States.STARTING, States.STOPPING]
@@ -780,7 +810,9 @@ class DeploymentManager:
780
810
  raise ValueError("Service already in transition")
781
811
  self.logger.info(f"Stopping deployment {build_dir}...")
782
812
  self._states[build_dir] = States.STOPPING
783
- deployment_runner = self._get_deployment_runner(build_dir=build_dir)
813
+ deployment_runner = self._get_deployment_runner(
814
+ build_dir=build_dir, is_aea=is_aea
815
+ )
784
816
  try:
785
817
  deployment_runner.stop()
786
818
  self.logger.info(f"Stopped deployment {build_dir}...")
@@ -794,14 +826,16 @@ class DeploymentManager:
794
826
  deployment_manager = DeploymentManager()
795
827
 
796
828
 
797
- def run_host_deployment(build_dir: Path, password: str) -> None:
829
+ def run_host_deployment(build_dir: Path, password: str, is_aea: bool = True) -> None:
798
830
  """Run host deployment."""
799
- deployment_manager.run_deployment(build_dir=build_dir, password=password)
831
+ deployment_manager.run_deployment(
832
+ build_dir=build_dir, password=password, is_aea=is_aea
833
+ )
800
834
 
801
835
 
802
- def stop_host_deployment(build_dir: Path) -> None:
836
+ def stop_host_deployment(build_dir: Path, is_aea: bool = True) -> None:
803
837
  """Stop host deployment."""
804
- deployment_manager.stop_deployment(build_dir=build_dir)
838
+ deployment_manager.stop_deployment(build_dir=build_dir, is_aea=is_aea)
805
839
 
806
840
 
807
841
  def stop_deployment_manager() -> None:
@@ -79,11 +79,13 @@ class FundingManager:
79
79
 
80
80
  def __init__(
81
81
  self,
82
+ keys_manager: KeysManager,
82
83
  wallet_manager: MasterWalletManager,
83
84
  logger: Logger,
84
85
  funding_requests_cooldown_seconds: int = DEFAULT_FUNDING_REQUESTS_COOLDOWN_SECONDS,
85
86
  ) -> None:
86
87
  """Initialize funding manager."""
88
+ self.keys_manager = keys_manager
87
89
  self.wallet_manager = wallet_manager
88
90
  self.logger = logger
89
91
  self.funding_requests_cooldown_seconds = funding_requests_cooldown_seconds
@@ -102,7 +104,7 @@ class FundingManager:
102
104
  f"Draining service agents {service.name} ({service_config_id=})"
103
105
  )
104
106
  for agent_address in service.agent_addresses:
105
- ethereum_crypto = KeysManager().get_crypto_instance(agent_address)
107
+ ethereum_crypto = self.keys_manager.get_crypto_instance(agent_address)
106
108
  balance = ledger_api.get_balance(agent_address)
107
109
  self.logger.info(
108
110
  f"Draining {balance} (approx) {get_currency_denom(chain)} from {agent_address} (agent) to {withdrawal_address}"
@@ -167,7 +169,7 @@ class FundingManager:
167
169
 
168
170
  # Safe not swapped
169
171
  if set(owners) == set(service.agent_addresses):
170
- ethereum_crypto = KeysManager().get_crypto_instance(
172
+ ethereum_crypto = self.keys_manager.get_crypto_instance(
171
173
  service.agent_addresses[0]
172
174
  )
173
175
  transfer_erc20_from_safe(
@@ -207,7 +209,7 @@ class FundingManager:
207
209
  )
208
210
 
209
211
  if set(owners) == set(service.agent_addresses):
210
- ethereum_crypto = KeysManager().get_crypto_instance(
212
+ ethereum_crypto = self.keys_manager.get_crypto_instance(
211
213
  service.agent_addresses[0]
212
214
  )
213
215
  transfer_from_safe(
@@ -109,9 +109,10 @@ NUM_LOCAL_AGENT_INSTANCES = 1
109
109
  class ServiceManager:
110
110
  """Service manager."""
111
111
 
112
- def __init__(
112
+ def __init__( # pylint: disable=too-many-arguments
113
113
  self,
114
114
  path: Path,
115
+ keys_manager: KeysManager,
115
116
  wallet_manager: MasterWalletManager,
116
117
  funding_manager: FundingManager,
117
118
  logger: logging.Logger,
@@ -126,7 +127,7 @@ class ServiceManager:
126
127
  :param logger: logging.Logger object.
127
128
  """
128
129
  self.path = path
129
- self.keys_manager = KeysManager()
130
+ self.keys_manager = keys_manager
130
131
  self.wallet_manager = wallet_manager
131
132
  self.funding_manager = funding_manager
132
133
  self.logger = logger
@@ -812,6 +813,9 @@ class ServiceManager:
812
813
  on_chain_metadata = self._get_on_chain_metadata(chain_config=chain_config)
813
814
  on_chain_hash = on_chain_metadata.get("code_uri", "")[len(IPFS_URI_PREFIX) :]
814
815
  on_chain_description = on_chain_metadata.get("description")
816
+ needs_update_agent_addresses = set(chain_data.instances) != set(
817
+ service.agent_addresses
818
+ )
815
819
 
816
820
  current_agent_bond = sftxb.get_agent_bond(
817
821
  service_id=chain_data.token, agent_id=target_staking_params["agent_ids"][0]
@@ -840,6 +844,7 @@ class ServiceManager:
840
844
  or current_staking_params["staking_token"]
841
845
  != target_staking_params["staking_token"]
842
846
  or on_chain_description != service.description
847
+ or needs_update_agent_addresses
843
848
  )
844
849
  )
845
850
 
@@ -1923,7 +1928,9 @@ class ServiceManager:
1923
1928
  # TODO: remove after staking contract directly starts sending the rewards to master safe
1924
1929
  amount_claimed = int(receipt["logs"][0]["data"].hex(), 16)
1925
1930
  self.logger.info(f"Claimed amount: {amount_claimed}")
1926
- ethereum_crypto = KeysManager().get_crypto_instance(service.agent_addresses[0])
1931
+ ethereum_crypto = self.keys_manager.get_crypto_instance(
1932
+ service.agent_addresses[0]
1933
+ )
1927
1934
  transfer_erc20_from_safe(
1928
1935
  ledger_api=ledger_api,
1929
1936
  crypto=ethereum_crypto,
@@ -2206,7 +2213,7 @@ class ServiceManager:
2206
2213
  chain=chain,
2207
2214
  )
2208
2215
 
2209
- def deploy_service_locally(
2216
+ def deploy_service_locally( # pylint: disable=too-many-arguments
2210
2217
  self,
2211
2218
  service_config_id: str,
2212
2219
  chain: t.Optional[str] = None,
@@ -2232,11 +2239,15 @@ class ServiceManager:
2232
2239
  use_kubernetes=use_kubernetes,
2233
2240
  force=True,
2234
2241
  chain=chain or service.home_chain,
2235
- password=self.wallet_manager.password,
2242
+ keys_manager=self.keys_manager,
2236
2243
  )
2237
2244
  if build_only:
2238
2245
  return deployment
2239
- deployment.start(password=self.wallet_manager.password, use_docker=use_docker)
2246
+ deployment.start(
2247
+ password=self.wallet_manager.password,
2248
+ use_docker=use_docker,
2249
+ is_aea=service.agent_release["is_aea"],
2250
+ )
2240
2251
  return deployment
2241
2252
 
2242
2253
  def stop_service_locally(
@@ -2256,7 +2267,11 @@ class ServiceManager:
2256
2267
  service = self.load(service_config_id=service_config_id)
2257
2268
  service.remove_latest_healthcheck()
2258
2269
  deployment = service.deployment
2259
- deployment.stop(use_docker=use_docker, force=force)
2270
+ deployment.stop(
2271
+ use_docker=use_docker,
2272
+ force=force,
2273
+ is_aea=service.agent_release["is_aea"],
2274
+ )
2260
2275
  if delete:
2261
2276
  deployment.delete()
2262
2277
  return deployment