bohr-agent-sdk 0.1.19__tar.gz → 0.1.21__tar.gz

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.
Files changed (47) hide show
  1. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/PKG-INFO +1 -1
  2. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/setup.py +1 -1
  3. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/bohr_agent_sdk.egg-info/PKG-INFO +1 -1
  4. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/calculation_mcp_server.py +7 -3
  5. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/executor/dispatcher_executor.py +16 -13
  6. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/executor/local_executor.py +59 -4
  7. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/README.md +0 -0
  8. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/pyproject.toml +0 -0
  9. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/setup.cfg +0 -0
  10. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/bohr_agent_sdk.egg-info/SOURCES.txt +0 -0
  11. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/bohr_agent_sdk.egg-info/dependency_links.txt +0 -0
  12. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/bohr_agent_sdk.egg-info/entry_points.txt +0 -0
  13. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/bohr_agent_sdk.egg-info/requires.txt +0 -0
  14. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/bohr_agent_sdk.egg-info/top_level.txt +0 -0
  15. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/__init__.py +0 -0
  16. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/__init__.py +0 -0
  17. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/adapter/adk/__init__.py +0 -0
  18. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/adapter/adk/client/__init__.py +0 -0
  19. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/adapter/adk/client/calculation_mcp_tool.py +0 -0
  20. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/adapter/adk/storage_artifact_service.py +0 -0
  21. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/adapter/adk/utils.py +0 -0
  22. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/adapter/camel/__init__.py +0 -0
  23. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/adapter/camel/client/__init__.py +0 -0
  24. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/adapter/camel/client/calculation_mcp_client.py +0 -0
  25. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/cli/__init__.py +0 -0
  26. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/cli/cli.py +0 -0
  27. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/cloud/__init__.py +0 -0
  28. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/cloud/main.py +0 -0
  29. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/cloud/mcp.py +0 -0
  30. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/cloud/mqtt.py +0 -0
  31. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/device/__init__.py +0 -0
  32. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/device/device/__init__.py +0 -0
  33. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/device/device/device.py +0 -0
  34. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/device/device/types.py +0 -0
  35. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/device/mqtt_device_twin.py +0 -0
  36. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/__init__.py +0 -0
  37. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/executor/__init__.py +0 -0
  38. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/executor/base_executor.py +0 -0
  39. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/preprocessor.py +0 -0
  40. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/storage/__init__.py +0 -0
  41. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/storage/base_storage.py +0 -0
  42. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/storage/bohrium_storage.py +0 -0
  43. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/storage/http_storage.py +0 -0
  44. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/storage/local_storage.py +0 -0
  45. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/storage/oss_storage.py +0 -0
  46. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/src/dp/agent/server/utils.py +0 -0
  47. {bohr_agent_sdk-0.1.19 → bohr_agent_sdk-0.1.21}/tests/test_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bohr-agent-sdk
3
- Version: 0.1.19
3
+ Version: 0.1.21
4
4
  Summary: SDK for scientific agents
5
5
  Home-page: https://github.com/dptech-corp/bohr-agent-sdk/
6
6
  Author: DP Technology
@@ -9,7 +9,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
9
9
 
10
10
  setup(
11
11
  name="bohr-agent-sdk",
12
- version="0.1.19",
12
+ version="0.1.21",
13
13
  description="SDK for science agent and mcp tools",
14
14
  long_description=long_description,
15
15
  long_description_content_type="text/markdown",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bohr-agent-sdk
3
- Version: 0.1.19
3
+ Version: 0.1.21
4
4
  Summary: SDK for scientific agents
5
5
  Home-page: https://github.com/dptech-corp/bohr-agent-sdk/
6
6
  Author: DP Technology
@@ -29,7 +29,7 @@ def parse_uri(uri):
29
29
 
30
30
 
31
31
  def init_storage(storage_config: Optional[dict] = None):
32
- if storage_config is None:
32
+ if not storage_config:
33
33
  storage_config = {"type": "local"}
34
34
  storage_config = deepcopy(storage_config)
35
35
  storage_type = storage_config.pop("type")
@@ -38,7 +38,7 @@ def init_storage(storage_config: Optional[dict] = None):
38
38
 
39
39
 
40
40
  def init_executor(executor_config: Optional[dict] = None):
41
- if executor_config is None:
41
+ if not executor_config:
42
42
  executor_config = {"type": "local"}
43
43
  executor_config = deepcopy(executor_config)
44
44
  executor_type = executor_config.pop("type")
@@ -227,7 +227,11 @@ class CalculationMCPServer:
227
227
  exec_id = res["job_id"]
228
228
  job_id = "%s/%s" % (trace_id, exec_id)
229
229
  logger.info("Job submitted (ID: %s)" % job_id)
230
- return convert_to_content({"job_id": job_id}, job_info={
230
+ result = {
231
+ "job_id": job_id,
232
+ "extra_info": res.get("extra_info"),
233
+ }
234
+ return convert_to_content(result, job_info={
231
235
  "trace_id": trace_id,
232
236
  "executor_type": executor_type,
233
237
  "job_id": job_id,
@@ -12,12 +12,15 @@ from dpdispatcher import Machine, Resources, Task, Submission
12
12
 
13
13
  from .base_executor import BaseExecutor
14
14
  from .... import __path__
15
+
15
16
  config = {
16
17
  "username": os.environ.get("BOHRIUM_USERNAME", ""),
17
18
  "password": os.environ.get("BOHRIUM_PASSWORD", ""),
18
19
  "project_id": os.environ.get("BOHRIUM_PROJECT_ID", ""),
19
20
  "access_key": os.environ.get("BOHRIUM_ACCESS_KEY", ""),
20
21
  "app_key": os.environ.get("BOHRIUM_APP_KEY", "agent"),
22
+ "bohrium_url": os.environ.get("BOHRIUM_BOHRIUM_URL",
23
+ "https://bohrium.dp.tech"),
21
24
  }
22
25
  logger = logging.getLogger(__name__)
23
26
 
@@ -26,17 +29,17 @@ def get_source_code(fn):
26
29
  source_lines, start_line = inspect.getsourcelines(fn)
27
30
  source_file = inspect.getsourcefile(fn)
28
31
  with open(source_file, "r", encoding="utf-8") as fd:
29
- pre_lines = fd.readlines()[:start_line-1]
32
+ pre_lines = fd.readlines()[:start_line - 1]
30
33
  return "".join(pre_lines + source_lines) + "\n"
31
34
 
32
35
 
33
36
  class DispatcherExecutor(BaseExecutor):
34
37
  def __init__(
35
- self,
36
- machine=None,
37
- resources=None,
38
- python_packages=None,
39
- python_executable="python3",
38
+ self,
39
+ machine=None,
40
+ resources=None,
41
+ python_packages=None,
42
+ python_executable="python3",
40
43
  ):
41
44
  """Use DPDispatcher to execute the tool
42
45
  Refer to https://docs.deepmodeling.com/projects/dpdispatcher.
@@ -107,7 +110,7 @@ class DispatcherExecutor(BaseExecutor):
107
110
  script += "if __name__ == \"__main__\":\n"
108
111
  script += " cwd = os.getcwd()\n"
109
112
  script += " kwargs = jsonpickle.loads(r'''%s''')\n" % \
110
- jsonpickle.dumps(kwargs)
113
+ jsonpickle.dumps(kwargs)
111
114
  script += " try:\n"
112
115
  if import_func_line is not None:
113
116
  script += " " + import_func_line
@@ -118,8 +121,8 @@ class DispatcherExecutor(BaseExecutor):
118
121
  script += " if isinstance(results, dict):\n"
119
122
  script += " for name in results:\n"
120
123
  script += " if isinstance(results[name], Path):\n"
121
- script += " results[name] = "\
122
- "results[name].absolute().relative_to(cwd)\n"
124
+ script += " results[name] = " \
125
+ "results[name].absolute().relative_to(cwd)\n"
123
126
  script += " except Exception as e:\n"
124
127
  script += " os.chdir(cwd)\n"
125
128
  script += " with open('err', 'w') as f:\n"
@@ -173,8 +176,8 @@ class DispatcherExecutor(BaseExecutor):
173
176
  extra_info = {
174
177
  "bohr_job_id": bohr_job_id,
175
178
  "bohr_group_id": bohr_group_id,
176
- "job_link": "https://bohrium.dp.tech/jobs/detail/%s" %
177
- bohr_job_id,
179
+ "job_link": f"{config['bohrium_url']}/jobs/detail/%s" %
180
+ bohr_job_id,
178
181
  }
179
182
  logger.info(extra_info)
180
183
  res["extra_info"] = extra_info
@@ -182,8 +185,8 @@ class DispatcherExecutor(BaseExecutor):
182
185
  bohr_job_id = submission.belonging_jobs[0].job_id
183
186
  extra_info = {
184
187
  "bohr_job_id": bohr_job_id,
185
- "job_link": "https://bohrium.dp.tech/jobs/detail/%s" %
186
- bohr_job_id,
188
+ "job_link": f"{config['bohrium_url']}/jobs/detail/%s" %
189
+ bohr_job_id,
187
190
  }
188
191
  logger.info(extra_info)
189
192
  res["extra_info"] = extra_info
@@ -1,19 +1,43 @@
1
1
  import asyncio
2
2
  import importlib
3
3
  import inspect
4
+ import io
4
5
  import jsonpickle
5
6
  import os
6
7
  import psutil
8
+ import re
7
9
  import sys
10
+ import time
8
11
  import uuid
9
12
  from multiprocessing import Process
10
13
  from typing import Dict, Optional
11
14
 
12
15
  from .base_executor import BaseExecutor
16
+ from ..utils import get_logger
13
17
 
18
+ DFLOW_ID_PATTERN = r"Workflow has been submitted \(ID: ([^,]*), UID: ([^)]*)\)"
19
+ DFLOW_LINK_PATTERN = r"Workflow link: (.*)"
20
+ logger = get_logger(__name__)
14
21
 
15
- def wrapped_fn(fn, kwargs):
22
+
23
+ class Tee(io.TextIOBase):
24
+ def __init__(self, file, stdout):
25
+ self.file = file
26
+ self.stdout = stdout
27
+
28
+ def write(self, text):
29
+ self.stdout.write(text)
30
+ self.file.write(text)
31
+ self.file.flush()
32
+ return len(text)
33
+
34
+
35
+ def wrapped_fn(fn, kwargs, redirect_file=None):
16
36
  pid = os.getpid()
37
+ if redirect_file:
38
+ stdout = sys.stdout
39
+ flog = open(redirect_file, "w")
40
+ sys.stdout = Tee(flog, stdout)
17
41
  try:
18
42
  if inspect.iscoroutinefunction(fn):
19
43
  result = asyncio.run(fn(**kwargs))
@@ -23,6 +47,10 @@ def wrapped_fn(fn, kwargs):
23
47
  with open("err", "w") as f:
24
48
  f.write(str(e))
25
49
  raise e
50
+ finally:
51
+ if redirect_file:
52
+ sys.stdout = stdout
53
+ flog.close()
26
54
  with open("%s.txt" % pid, "w") as f:
27
55
  f.write(jsonpickle.dumps(result))
28
56
 
@@ -47,13 +75,16 @@ def reload_dflow_config():
47
75
 
48
76
 
49
77
  class LocalExecutor(BaseExecutor):
50
- def __init__(self, env: Optional[Dict[str, str]] = None):
78
+ def __init__(self, env: Optional[Dict[str, str]] = None,
79
+ dflow: bool = False):
51
80
  """
52
81
  Execute the tool locally
53
82
  Args:
54
83
  env: The environmental variables at run time
84
+ dflow: Wait until workflow submitted in submit method
55
85
  """
56
86
  self.env = env or {}
87
+ self.dflow = dflow
57
88
 
58
89
  def set_env(self):
59
90
  old_env = {}
@@ -73,10 +104,34 @@ class LocalExecutor(BaseExecutor):
73
104
  def submit(self, fn, kwargs):
74
105
  os.environ["DP_AGENT_RUNNING_MODE"] = "1"
75
106
  old_env = self.set_env()
76
- p = Process(target=wrapped_fn, kwargs={"fn": fn, "kwargs": kwargs})
107
+ params = {"fn": fn, "kwargs": kwargs}
108
+ if self.dflow:
109
+ params["redirect_file"] = "log.txt"
110
+ p = Process(target=wrapped_fn, kwargs=params)
77
111
  p.start()
112
+ extra_info = {}
113
+ if self.dflow:
114
+ while True:
115
+ alive = p.is_alive()
116
+ if os.path.isfile("log.txt"):
117
+ with open("log.txt", "r") as f:
118
+ log = f.read()
119
+ match_id = re.search(DFLOW_ID_PATTERN, log)
120
+ match_link = re.search(DFLOW_LINK_PATTERN, log)
121
+ if match_id and match_link:
122
+ wf_id = match_id.group(1)
123
+ wf_uid = match_id.group(2)
124
+ wf_link = match_link.group(1)
125
+ extra_info["workflow_id"] = wf_id
126
+ extra_info["workflow_uid"] = wf_uid
127
+ extra_info["workflow_link"] = wf_link
128
+ break
129
+ if not alive:
130
+ break
131
+ logger.info("Waiting workflow to be submitted")
132
+ time.sleep(1)
78
133
  self.recover_env(old_env)
79
- return {"job_id": str(p.pid)}
134
+ return {"job_id": str(p.pid), "extra_info": extra_info}
80
135
 
81
136
  def query_status(self, job_id):
82
137
  try: