bohr-agent-sdk 0.1.17__tar.gz → 0.1.19__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.
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/PKG-INFO +1 -1
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/setup.py +1 -1
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/bohr_agent_sdk.egg-info/PKG-INFO +1 -1
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/bohr_agent_sdk.egg-info/SOURCES.txt +1 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/adapter/adk/__init__.py +3 -1
- bohr_agent_sdk-0.1.19/src/dp/agent/adapter/adk/storage_artifact_service.py +184 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/adapter/adk/utils.py +29 -25
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/storage/bohrium_storage.py +3 -2
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/README.md +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/pyproject.toml +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/setup.cfg +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/bohr_agent_sdk.egg-info/dependency_links.txt +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/bohr_agent_sdk.egg-info/entry_points.txt +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/bohr_agent_sdk.egg-info/requires.txt +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/bohr_agent_sdk.egg-info/top_level.txt +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/adapter/adk/client/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/adapter/adk/client/calculation_mcp_tool.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/adapter/camel/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/adapter/camel/client/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/adapter/camel/client/calculation_mcp_client.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/cli/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/cli/cli.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/cloud/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/cloud/main.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/cloud/mcp.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/cloud/mqtt.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/device/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/device/device/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/device/device/device.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/device/device/types.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/device/mqtt_device_twin.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/calculation_mcp_server.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/executor/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/executor/base_executor.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/executor/dispatcher_executor.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/executor/local_executor.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/preprocessor.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/storage/__init__.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/storage/base_storage.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/storage/http_storage.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/storage/local_storage.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/storage/oss_storage.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/utils.py +0 -0
- {bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/tests/test_cli.py +0 -0
|
@@ -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.
|
|
12
|
+
version="0.1.19",
|
|
13
13
|
description="SDK for science agent and mcp tools",
|
|
14
14
|
long_description=long_description,
|
|
15
15
|
long_description_content_type="text/markdown",
|
|
@@ -10,6 +10,7 @@ src/bohr_agent_sdk.egg-info/top_level.txt
|
|
|
10
10
|
src/dp/__init__.py
|
|
11
11
|
src/dp/agent/__init__.py
|
|
12
12
|
src/dp/agent/adapter/adk/__init__.py
|
|
13
|
+
src/dp/agent/adapter/adk/storage_artifact_service.py
|
|
13
14
|
src/dp/agent/adapter/adk/utils.py
|
|
14
15
|
src/dp/agent/adapter/adk/client/__init__.py
|
|
15
16
|
src/dp/agent/adapter/adk/client/calculation_mcp_tool.py
|
|
@@ -3,6 +3,7 @@ from .client import (
|
|
|
3
3
|
CalculationMCPToolset,
|
|
4
4
|
BackgroundJobWatcher,
|
|
5
5
|
)
|
|
6
|
+
from .storage_artifact_service import StorageArtifactService
|
|
6
7
|
from .utils import (
|
|
7
8
|
search_error_in_memory_handler,
|
|
8
9
|
update_session_handler,
|
|
@@ -11,4 +12,5 @@ from .utils import (
|
|
|
11
12
|
|
|
12
13
|
__all__ = ["CalculationMCPTool", "CalculationMCPToolset",
|
|
13
14
|
"update_session_handler", "search_error_in_memory_handler",
|
|
14
|
-
"BackgroundJobWatcher", "extract_job_info"
|
|
15
|
+
"BackgroundJobWatcher", "extract_job_info",
|
|
16
|
+
"StorageArtifactService"]
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
|
|
2
|
+
import logging
|
|
3
|
+
import mimetypes
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from google.adk.artifacts import BaseArtifactService
|
|
9
|
+
from google.genai import types
|
|
10
|
+
from typing_extensions import override
|
|
11
|
+
|
|
12
|
+
from ...server.storage import BaseStorage
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class StorageArtifactService(BaseArtifactService):
|
|
18
|
+
"""An artifact service implementation using storage plugin."""
|
|
19
|
+
def __init__(self, storage: BaseStorage):
|
|
20
|
+
self.storage = storage
|
|
21
|
+
|
|
22
|
+
def _file_has_user_namespace(self, filename: str) -> bool:
|
|
23
|
+
"""Checks if the filename has a user namespace.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
filename: The filename to check.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
True if the filename has a user namespace (starts with "user:"),
|
|
30
|
+
False otherwise.
|
|
31
|
+
"""
|
|
32
|
+
return filename.startswith("user:")
|
|
33
|
+
|
|
34
|
+
def _get_key(
|
|
35
|
+
self,
|
|
36
|
+
app_name: str,
|
|
37
|
+
user_id: str,
|
|
38
|
+
session_id: str,
|
|
39
|
+
filename: str,
|
|
40
|
+
version: int,
|
|
41
|
+
) -> str:
|
|
42
|
+
"""Constructs the key.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
app_name: The name of the application.
|
|
46
|
+
user_id: The ID of the user.
|
|
47
|
+
session_id: The ID of the session.
|
|
48
|
+
filename: The name of the artifact file.
|
|
49
|
+
version: The version of the artifact.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The constructed key.
|
|
53
|
+
"""
|
|
54
|
+
if self._file_has_user_namespace(filename):
|
|
55
|
+
return f"{app_name}/{user_id}/user/{filename}/{version}"
|
|
56
|
+
return f"{app_name}/{user_id}/{session_id}/{filename}/{version}"
|
|
57
|
+
|
|
58
|
+
@override
|
|
59
|
+
async def save_artifact(
|
|
60
|
+
self,
|
|
61
|
+
*,
|
|
62
|
+
app_name: str,
|
|
63
|
+
user_id: str,
|
|
64
|
+
session_id: str,
|
|
65
|
+
filename: str,
|
|
66
|
+
artifact: types.Part,
|
|
67
|
+
) -> int:
|
|
68
|
+
versions = await self.list_versions(
|
|
69
|
+
app_name=app_name,
|
|
70
|
+
user_id=user_id,
|
|
71
|
+
session_id=session_id,
|
|
72
|
+
filename=filename,
|
|
73
|
+
)
|
|
74
|
+
version = 0 if not versions else max(versions) + 1
|
|
75
|
+
|
|
76
|
+
key = self._get_key(
|
|
77
|
+
app_name, user_id, session_id, filename, version
|
|
78
|
+
)
|
|
79
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
80
|
+
path = os.path.join(tmpdir, filename)
|
|
81
|
+
with open(path, "wb") as f:
|
|
82
|
+
f.write(artifact.inline_data.data)
|
|
83
|
+
self.storage._upload(key, path)
|
|
84
|
+
|
|
85
|
+
return version
|
|
86
|
+
|
|
87
|
+
@override
|
|
88
|
+
async def load_artifact(
|
|
89
|
+
self,
|
|
90
|
+
*,
|
|
91
|
+
app_name: str,
|
|
92
|
+
user_id: str,
|
|
93
|
+
session_id: str,
|
|
94
|
+
filename: str,
|
|
95
|
+
version: Optional[int] = None,
|
|
96
|
+
) -> Optional[types.Part]:
|
|
97
|
+
if version is None:
|
|
98
|
+
versions = await self.list_versions(
|
|
99
|
+
app_name=app_name,
|
|
100
|
+
user_id=user_id,
|
|
101
|
+
session_id=session_id,
|
|
102
|
+
filename=filename,
|
|
103
|
+
)
|
|
104
|
+
if not versions:
|
|
105
|
+
return None
|
|
106
|
+
version = max(versions)
|
|
107
|
+
|
|
108
|
+
key = self._get_key(
|
|
109
|
+
app_name, user_id, session_id, filename, version
|
|
110
|
+
)
|
|
111
|
+
with tempfile.TemporaryDirectory() as tmpdir:
|
|
112
|
+
path = os.path.join(tmpdir, filename)
|
|
113
|
+
self.storage._download(key, path)
|
|
114
|
+
with open(path, "rb") as f:
|
|
115
|
+
artifact_bytes = f.read()
|
|
116
|
+
|
|
117
|
+
mime_type, _ = mimetypes.guess_type(filename)
|
|
118
|
+
artifact = types.Part.from_bytes(
|
|
119
|
+
data=artifact_bytes, mime_type=mime_type
|
|
120
|
+
)
|
|
121
|
+
return artifact
|
|
122
|
+
|
|
123
|
+
@override
|
|
124
|
+
async def list_artifact_keys(
|
|
125
|
+
self, *, app_name: str, user_id: str, session_id: str
|
|
126
|
+
) -> list[str]:
|
|
127
|
+
filenames = set()
|
|
128
|
+
|
|
129
|
+
session_prefix = f"{app_name}/{user_id}/{session_id}/"
|
|
130
|
+
keys = self.storage.list(session_prefix)
|
|
131
|
+
for key in keys:
|
|
132
|
+
_, _, _, filename, _ = key.split("/")[-5:]
|
|
133
|
+
filenames.add(filename)
|
|
134
|
+
|
|
135
|
+
user_namespace_prefix = f"{app_name}/{user_id}/user/"
|
|
136
|
+
user_namespace_keys = self.storage.list(user_namespace_prefix)
|
|
137
|
+
for key in user_namespace_keys:
|
|
138
|
+
_, _, _, filename, _ = key.split("/")[-5:]
|
|
139
|
+
filenames.add(filename)
|
|
140
|
+
|
|
141
|
+
return sorted(list(filenames))
|
|
142
|
+
|
|
143
|
+
@override
|
|
144
|
+
async def delete_artifact(
|
|
145
|
+
self, *, app_name: str, user_id: str, session_id: str, filename: str
|
|
146
|
+
) -> None:
|
|
147
|
+
raise NotImplementedError()
|
|
148
|
+
|
|
149
|
+
@override
|
|
150
|
+
async def list_versions(
|
|
151
|
+
self, *, app_name: str, user_id: str, session_id: str, filename: str
|
|
152
|
+
) -> list[int]:
|
|
153
|
+
prefix = self._get_key(app_name, user_id, session_id, filename, "")
|
|
154
|
+
keys = self.storage.list(prefix)
|
|
155
|
+
versions = []
|
|
156
|
+
for key in keys:
|
|
157
|
+
_, _, _, _, version = key.split("/")[-5:]
|
|
158
|
+
versions.append(int(version))
|
|
159
|
+
return versions
|
|
160
|
+
|
|
161
|
+
async def get_permanent_read_url(
|
|
162
|
+
self,
|
|
163
|
+
*,
|
|
164
|
+
app_name: str,
|
|
165
|
+
user_id: str,
|
|
166
|
+
session_id: str,
|
|
167
|
+
filename: str,
|
|
168
|
+
version: Optional[int] = None,
|
|
169
|
+
) -> str:
|
|
170
|
+
if version is None:
|
|
171
|
+
versions = await self.list_versions(
|
|
172
|
+
app_name=app_name,
|
|
173
|
+
user_id=user_id,
|
|
174
|
+
session_id=session_id,
|
|
175
|
+
filename=filename,
|
|
176
|
+
)
|
|
177
|
+
if not versions:
|
|
178
|
+
return None
|
|
179
|
+
version = max(versions)
|
|
180
|
+
|
|
181
|
+
key = self._get_key(
|
|
182
|
+
app_name, user_id, session_id, filename, version
|
|
183
|
+
)
|
|
184
|
+
return self.storage.get_http_url(key)
|
|
@@ -116,6 +116,7 @@ def search_error_in_memory_handler(toolset):
|
|
|
116
116
|
def extract_job_info(events: List[Event]) -> dict:
|
|
117
117
|
jobs = {}
|
|
118
118
|
artifacts = {}
|
|
119
|
+
events.sort(key=lambda event: event.timestamp)
|
|
119
120
|
for event in events:
|
|
120
121
|
if event.content and event.content.parts:
|
|
121
122
|
for part in event.content.parts:
|
|
@@ -135,39 +136,42 @@ def extract_job_info(events: List[Event]) -> dict:
|
|
|
135
136
|
}
|
|
136
137
|
job = jobs[resp.id]
|
|
137
138
|
job["timestamp"] = event.timestamp
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
res = resp.response.get("result")
|
|
140
|
+
if isinstance(res, dict) and "content" in res \
|
|
141
|
+
and "isError" in res:
|
|
142
|
+
res = types.CallToolResult(
|
|
143
|
+
content=res["content"], isError=res["isError"])
|
|
144
|
+
if isinstance(res, types.CallToolResult):
|
|
141
145
|
if res.isError:
|
|
142
146
|
err_msg = res.content[0].text
|
|
143
147
|
if err_msg.startswith("Error executing tool"):
|
|
144
148
|
err_msg = err_msg[err_msg.find(":")+2:]
|
|
145
149
|
job["err_msg"] = err_msg
|
|
146
|
-
|
|
150
|
+
elif hasattr(res.content[0], "text"):
|
|
147
151
|
result = jsonpickle.loads(res.content[0].text)
|
|
148
152
|
if "job_id" not in result:
|
|
149
153
|
job["result"] = result
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
154
|
+
if hasattr(res.content[0], "job_info"):
|
|
155
|
+
job_info = res.content[0].job_info
|
|
156
|
+
job.update(job_info)
|
|
157
|
+
for name, art in job_info.get(
|
|
158
|
+
"input_artifacts", {}).items():
|
|
159
|
+
if art["uri"] not in artifacts:
|
|
160
|
+
artifacts[art["uri"]] = {
|
|
161
|
+
"type": "input",
|
|
162
|
+
"name": name,
|
|
163
|
+
"job_id": job_info["job_id"],
|
|
164
|
+
**art,
|
|
165
|
+
}
|
|
166
|
+
for name, art in job_info.get(
|
|
167
|
+
"output_artifacts", {}).items():
|
|
168
|
+
if art["uri"] not in artifacts:
|
|
169
|
+
artifacts[art["uri"]] = {
|
|
170
|
+
"type": "output",
|
|
171
|
+
"name": name,
|
|
172
|
+
"job_id": job_info["job_id"],
|
|
173
|
+
**art,
|
|
174
|
+
}
|
|
171
175
|
return {
|
|
172
176
|
"jobs": list(jobs.values()),
|
|
173
177
|
"artifacts": list(artifacts.values()),
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/storage/bohrium_storage.py
RENAMED
|
@@ -50,9 +50,9 @@ def login(username=None, phone=None, password=None, bohrium_url=None):
|
|
|
50
50
|
password = config["password"]
|
|
51
51
|
if bohrium_url is None:
|
|
52
52
|
bohrium_url = config["bohrium_url"]
|
|
53
|
-
|
|
53
|
+
authorization = _login(
|
|
54
54
|
bohrium_url + "/account/login", username, phone, password)
|
|
55
|
-
return
|
|
55
|
+
return authorization
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
def _login(login_url=None, username=None, phone=None, password=None):
|
|
@@ -256,6 +256,7 @@ class BohriumStorage(BaseStorage):
|
|
|
256
256
|
return meta["entityTag"] if "entityTag" in meta else ""
|
|
257
257
|
|
|
258
258
|
def get_http_url(self, key):
|
|
259
|
+
key = self.prefixing(key)
|
|
259
260
|
url = self.tiefblue_url + "/api/setacl"
|
|
260
261
|
headers = {
|
|
261
262
|
"Content-type": "application/json",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/bohr_agent_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/bohr_agent_sdk.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/adapter/camel/client/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/calculation_mcp_server.py
RENAMED
|
File without changes
|
|
File without changes
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/executor/base_executor.py
RENAMED
|
File without changes
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/executor/dispatcher_executor.py
RENAMED
|
File without changes
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/executor/local_executor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bohr_agent_sdk-0.1.17 → bohr_agent_sdk-0.1.19}/src/dp/agent/server/storage/local_storage.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|