ivoryos 1.0.1__py3-none-any.whl → 1.0.4__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.
Potentially problematic release.
This version of ivoryos might be problematic. Click here for more details.
- ivoryos/__init__.py +22 -6
- ivoryos/routes/auth/auth.py +15 -18
- ivoryos/routes/control/control.py +78 -43
- ivoryos/routes/control/templates/control/controllers_home.html +7 -7
- ivoryos/routes/database/database.py +64 -44
- ivoryos/routes/database/templates/database/{experiment_database.html → scripts_database.html} +27 -18
- ivoryos/routes/database/templates/database/{workflow_run_database.html → workflow_database.html} +27 -5
- ivoryos/routes/design/design.py +135 -79
- ivoryos/routes/design/templates/design/experiment_builder.html +100 -45
- ivoryos/routes/design/templates/design/experiment_run.html +491 -328
- ivoryos/static/js/sortable_design.js +1 -1
- ivoryos/utils/client_proxy.py +1 -1
- ivoryos/utils/form.py +30 -3
- ivoryos/utils/script_runner.py +2 -1
- ivoryos/utils/utils.py +13 -0
- ivoryos/version.py +1 -1
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/METADATA +37 -1
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/RECORD +22 -22
- /ivoryos/routes/database/templates/database/{experiment_step_view.html → workflow_view.html} +0 -0
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/LICENSE +0 -0
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/WHEEL +0 -0
- {ivoryos-1.0.1.dist-info → ivoryos-1.0.4.dist-info}/top_level.txt +0 -0
ivoryos/utils/client_proxy.py
CHANGED
|
@@ -8,7 +8,7 @@ import os
|
|
|
8
8
|
|
|
9
9
|
# Function to create class and methods dynamically
|
|
10
10
|
def create_function(url, class_name, functions):
|
|
11
|
-
class_template = f'class {class_name.capitalize()}:\n url = "{url}ivoryos/
|
|
11
|
+
class_template = f'class {class_name.capitalize()}:\n url = "{url}ivoryos/api/control/deck.{class_name}"\n'
|
|
12
12
|
|
|
13
13
|
for function_name, details in functions.items():
|
|
14
14
|
signature = details['signature']
|
ivoryos/utils/form.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
+
from typing import get_origin, get_args, Union, Any
|
|
2
3
|
|
|
3
4
|
from wtforms.fields.choices import SelectField
|
|
4
5
|
from wtforms.fields.core import Field
|
|
5
|
-
from wtforms.validators import InputRequired, ValidationError
|
|
6
|
+
from wtforms.validators import InputRequired, ValidationError, Optional
|
|
6
7
|
from wtforms.widgets.core import TextInput
|
|
7
8
|
|
|
8
9
|
from flask_wtf import FlaskForm
|
|
@@ -219,6 +220,26 @@ def format_name(name):
|
|
|
219
220
|
text = ' '.join(word for word in name.split('_'))
|
|
220
221
|
return text.capitalize()
|
|
221
222
|
|
|
223
|
+
def parse_annotation(annotation):
|
|
224
|
+
"""
|
|
225
|
+
Given a type annotation, return:
|
|
226
|
+
- a list of all valid types (excluding NoneType)
|
|
227
|
+
- a boolean indicating if the value can be None (optional)
|
|
228
|
+
"""
|
|
229
|
+
origin = get_origin(annotation)
|
|
230
|
+
args = get_args(annotation)
|
|
231
|
+
|
|
232
|
+
if annotation is Any:
|
|
233
|
+
return [str], True # fallback: accept any string, optional
|
|
234
|
+
|
|
235
|
+
if origin is Union:
|
|
236
|
+
types = list(set(args))
|
|
237
|
+
is_optional = type(None) in types
|
|
238
|
+
non_none_types = [t for t in types if t is not type(None)]
|
|
239
|
+
return non_none_types, is_optional
|
|
240
|
+
|
|
241
|
+
# Not a Union, just a regular type
|
|
242
|
+
return [annotation], False
|
|
222
243
|
|
|
223
244
|
def create_form_for_method(method, autofill, script=None, design=True):
|
|
224
245
|
"""
|
|
@@ -258,7 +279,7 @@ def create_form_for_method(method, autofill, script=None, design=True):
|
|
|
258
279
|
field_kwargs = {
|
|
259
280
|
"label": formatted_param_name,
|
|
260
281
|
"default": default_value,
|
|
261
|
-
"validators": [InputRequired()] if param.default is param.empty else
|
|
282
|
+
"validators": [InputRequired()] if param.default is param.empty else [Optional()],
|
|
262
283
|
**({"script": script} if (autofill or design) else {})
|
|
263
284
|
}
|
|
264
285
|
if isinstance(param.annotation, type) and issubclass(param.annotation, Enum):
|
|
@@ -267,11 +288,17 @@ def create_form_for_method(method, autofill, script=None, design=True):
|
|
|
267
288
|
placeholder_text = f"Choose or type a value for {param.annotation.__name__} (start with # for custom)"
|
|
268
289
|
extra_kwargs = {"choices": param.annotation}
|
|
269
290
|
else:
|
|
291
|
+
# print(param.annotation)
|
|
292
|
+
annotation, optional = parse_annotation(param.annotation)
|
|
293
|
+
annotation = annotation[0]
|
|
270
294
|
field_class, placeholder_text = annotation_mapping.get(
|
|
271
|
-
|
|
295
|
+
annotation,
|
|
272
296
|
(VariableOrStringField if design else StringField, f'Enter {param.annotation} value')
|
|
273
297
|
)
|
|
274
298
|
extra_kwargs = {}
|
|
299
|
+
if optional:
|
|
300
|
+
field_kwargs["filters"] = [lambda x: x if x != '' else None]
|
|
301
|
+
|
|
275
302
|
|
|
276
303
|
render_kwargs = {"placeholder": placeholder_text}
|
|
277
304
|
|
ivoryos/utils/script_runner.py
CHANGED
|
@@ -69,7 +69,7 @@ class ScriptRunner:
|
|
|
69
69
|
|
|
70
70
|
if self.current_app is None:
|
|
71
71
|
self.current_app = current_app
|
|
72
|
-
time.sleep(1) # Optional: may help ensure deck readiness
|
|
72
|
+
# time.sleep(1) # Optional: may help ensure deck readiness
|
|
73
73
|
|
|
74
74
|
# Try to acquire lock without blocking
|
|
75
75
|
if not self.lock.acquire(blocking=False):
|
|
@@ -240,6 +240,7 @@ class ScriptRunner:
|
|
|
240
240
|
for i in config:
|
|
241
241
|
try:
|
|
242
242
|
i = utils.convert_config_type(i, arg_type)
|
|
243
|
+
compiled = True
|
|
243
244
|
except Exception as e:
|
|
244
245
|
logger.info(e)
|
|
245
246
|
compiled = False
|
ivoryos/utils/utils.py
CHANGED
|
@@ -4,6 +4,7 @@ import inspect
|
|
|
4
4
|
import logging
|
|
5
5
|
import os
|
|
6
6
|
import pickle
|
|
7
|
+
import socket
|
|
7
8
|
import subprocess
|
|
8
9
|
import sys
|
|
9
10
|
from collections import Counter
|
|
@@ -407,3 +408,15 @@ def get_method_from_workflow(function_string, func_name="workflow"):
|
|
|
407
408
|
# method = get_method_from_workflow(compiled_strs, func_name=workflow.name)
|
|
408
409
|
# setattr(RegisteredWorkflows, workflow.name, staticmethod(method))
|
|
409
410
|
# global_config.registered_workflows = RegisteredWorkflows()
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def get_local_ip():
|
|
414
|
+
try:
|
|
415
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
416
|
+
s.connect(('10.255.255.255', 1)) # Dummy address to get interface IP
|
|
417
|
+
ip = s.getsockname()[0]
|
|
418
|
+
except Exception:
|
|
419
|
+
ip = '127.0.0.1'
|
|
420
|
+
finally:
|
|
421
|
+
s.close()
|
|
422
|
+
return ip
|
ivoryos/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "1.0.
|
|
1
|
+
__version__ = "1.0.4"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
|
|
5
5
|
Home-page: https://gitlab.com/heingroup/ivoryos
|
|
6
6
|
Author: Ivory Zhang
|
|
@@ -176,6 +176,42 @@ When you run the application for the first time, it will automatically create th
|
|
|
176
176
|
- [x] show line number option ✅
|
|
177
177
|
- [ ] snapshot version control
|
|
178
178
|
|
|
179
|
+
## Citing
|
|
180
|
+
|
|
181
|
+
If you find this project useful, please consider citing the following manuscript:
|
|
182
|
+
|
|
183
|
+
> Zhang, W., Hao, L., Lai, V. et al. [IvoryOS: an interoperable web interface for orchestrating Python-based self-driving laboratories.](https://www.nature.com/articles/s41467-025-60514-w) Nat Commun 16, 5182 (2025).
|
|
184
|
+
|
|
185
|
+
```bibtex
|
|
186
|
+
@article{zhang_et_al_2025,
|
|
187
|
+
author = {Wenyu Zhang and Lucy Hao and Veronica Lai and Ryan Corkery and Jacob Jessiman and Jiayu Zhang and Junliang Liu and Yusuke Sato and Maria Politi and Matthew E. Reish and Rebekah Greenwood and Noah Depner and Jiyoon Min and Rama El-khawaldeh and Paloma Prieto and Ekaterina Trushina and Jason E. Hein},
|
|
188
|
+
title = {{IvoryOS}: an interoperable web interface for orchestrating {Python-based} self-driving laboratories},
|
|
189
|
+
journal = {Nature Communications},
|
|
190
|
+
year = {2025},
|
|
191
|
+
volume = {16},
|
|
192
|
+
number = {1},
|
|
193
|
+
pages = {5182},
|
|
194
|
+
doi = {10.1038/s41467-025-60514-w},
|
|
195
|
+
url = {https://doi.org/10.1038/s41467-025-60514-w}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
For an additional perspective related to the development of the tool, please see:
|
|
200
|
+
|
|
201
|
+
> Zhang, W., Hein, J. [Behind IvoryOS: Empowering Scientists to Harness Self-Driving Labs for Accelerated Discovery](https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery). Springer Nature Research Communities (2025).
|
|
202
|
+
|
|
203
|
+
```bibtex
|
|
204
|
+
@misc{zhang_hein_2025,
|
|
205
|
+
author = {Wenyu Zhang and Jason Hein},
|
|
206
|
+
title = {Behind {IvoryOS}: Empowering Scientists to Harness Self-Driving Labs for Accelerated Discovery},
|
|
207
|
+
howpublished = {Springer Nature Research Communities},
|
|
208
|
+
year = {2025},
|
|
209
|
+
month = {Jun},
|
|
210
|
+
day = {18},
|
|
211
|
+
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
179
215
|
## Authors and Acknowledgement
|
|
180
216
|
Ivory Zhang, Lucy Hao
|
|
181
217
|
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
ivoryos/__init__.py,sha256=
|
|
1
|
+
ivoryos/__init__.py,sha256=6zTQ5B7Pu99pv33LMpyYb6u0dz_TVSY9TVShXKAkrFs,7959
|
|
2
2
|
ivoryos/config.py,sha256=3FPBYTIBhQTKDvsEoR8ZeTmg65D-CSFEdGmOuIL4pSI,1311
|
|
3
|
-
ivoryos/version.py,sha256=
|
|
3
|
+
ivoryos/version.py,sha256=acuR_XSJzp4OrQ5T8-Ac5gYe48mUwObuwjRmisFmZ7k,22
|
|
4
4
|
ivoryos/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
ivoryos/routes/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
-
ivoryos/routes/auth/auth.py,sha256=
|
|
6
|
+
ivoryos/routes/auth/auth.py,sha256=pvrYQN2XqH2OpZMxirFQZr9-c8yO29CpvQg9VR6JFsE,3256
|
|
7
7
|
ivoryos/routes/auth/templates/auth/login.html,sha256=WSRrKbdM_oobqSXFRTo-j9UlOgp6sYzS9tm7TqqPULI,1207
|
|
8
8
|
ivoryos/routes/auth/templates/auth/signup.html,sha256=b5LTXtpfTSkSS7X8u1ldwQbbgEFTk6UNMAediA5BwBY,1465
|
|
9
9
|
ivoryos/routes/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
ivoryos/routes/control/control.py,sha256=
|
|
10
|
+
ivoryos/routes/control/control.py,sha256=AaXqXqaIIed9xBVQsk52CNnNDTSofje5qQAfDGWd1Q0,16404
|
|
11
11
|
ivoryos/routes/control/templates/control/controllers.html,sha256=iIp0h6WA68gQj9OsoiB7dU1BqH8CGomTueR73F4C8eY,4274
|
|
12
|
-
ivoryos/routes/control/templates/control/controllers_home.html,sha256=
|
|
12
|
+
ivoryos/routes/control/templates/control/controllers_home.html,sha256=qAM4iZBEuXvSgGUWWVVIe2E9MPJOeG7U214hYM84jIE,2976
|
|
13
13
|
ivoryos/routes/control/templates/control/controllers_new.html,sha256=uOQo9kYmwX2jk3KZDkMUF_ylfNUIs_oIWb_kk_MMVDM,4921
|
|
14
14
|
ivoryos/routes/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
ivoryos/routes/database/database.py,sha256=
|
|
16
|
-
ivoryos/routes/database/templates/database/
|
|
17
|
-
ivoryos/routes/database/templates/database/experiment_step_view.html,sha256=u8_XYhiZ98PzglMzFEkuM1Tk9hVWf79xXIrpHVDxKa0,3618
|
|
15
|
+
ivoryos/routes/database/database.py,sha256=JwFuWf9WHju0CepkuSz6OaOwV6O2f76Vv_0myCFMPFc,9833
|
|
16
|
+
ivoryos/routes/database/templates/database/scripts_database.html,sha256=tEpKOj1UGz7mcDq11guwP48XJxwawjxjPvxRhKiIG2I,3640
|
|
18
17
|
ivoryos/routes/database/templates/database/step_card.html,sha256=F4JRfacrEQfk2rrEbcI_i7G84nzKKDmCrMSmStLb4W4,290
|
|
19
|
-
ivoryos/routes/database/templates/database/
|
|
18
|
+
ivoryos/routes/database/templates/database/workflow_database.html,sha256=fsJHrYeEHGBKRn1pIxWITE6e93tdZsXH3zRs9Ob5FX0,4467
|
|
19
|
+
ivoryos/routes/database/templates/database/workflow_view.html,sha256=u8_XYhiZ98PzglMzFEkuM1Tk9hVWf79xXIrpHVDxKa0,3618
|
|
20
20
|
ivoryos/routes/design/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
ivoryos/routes/design/design.py,sha256=
|
|
22
|
-
ivoryos/routes/design/templates/design/experiment_builder.html,sha256=
|
|
23
|
-
ivoryos/routes/design/templates/design/experiment_run.html,sha256=
|
|
21
|
+
ivoryos/routes/design/design.py,sha256=ui9GvLoJ-0XNZT6q13KPgDh4NEEbVxaHKYcBtDqEkao,29866
|
|
22
|
+
ivoryos/routes/design/templates/design/experiment_builder.html,sha256=4JjkwdMAWZA1XrGtoqBKykDikf-Qg7RbjqedqY_J-fU,32326
|
|
23
|
+
ivoryos/routes/design/templates/design/experiment_run.html,sha256=UFqg6GMgkyYJfc1Ifg1jhyJggdZkCYpgQrNotiXWzwc,25582
|
|
24
24
|
ivoryos/routes/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
ivoryos/routes/main/main.py,sha256=yuVJzXAob1kc1dfflkTBIZQ0tdf6kChfuq-uQlN1e9Q,957
|
|
26
26
|
ivoryos/routes/main/templates/main/help.html,sha256=IOktMEsOPk0SCiMBXZ4mpffClERAyX8W82fel71M3M0,9370
|
|
@@ -33,20 +33,20 @@ ivoryos/static/gui_annotation/Slide2.PNG,sha256=z3wQ9oVgg4JTWVLQGKK_KhtepRHUYP1e
|
|
|
33
33
|
ivoryos/static/js/overlay.js,sha256=dPxop19es0E0ZUSY3d_4exIk7CJuQEnlW5uTt5fZfzI,483
|
|
34
34
|
ivoryos/static/js/socket_handler.js,sha256=2Iyv_3METjhSlSavs_L9FE3PKY4xDEpfzJpd2FywY9o,5300
|
|
35
35
|
ivoryos/static/js/sortable_card.js,sha256=ifmlGe3yy0U_KzMphV4ClRhK2DLOvkELYMlq1vECuac,807
|
|
36
|
-
ivoryos/static/js/sortable_design.js,sha256=
|
|
36
|
+
ivoryos/static/js/sortable_design.js,sha256=d55AEz8LpPX_j-hREom-I19i4l-SOG2RVSR4CnozRtY,4366
|
|
37
37
|
ivoryos/templates/base.html,sha256=sDdwqOIUP2Get-py4E59PkieoGWLFpX6wAJe93s4aRo,8518
|
|
38
38
|
ivoryos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
39
|
ivoryos/utils/bo_campaign.py,sha256=CVs7q15Pm2SRuJNaCvZKIxOFuv1xibM2yymtpAMAWOk,3285
|
|
40
|
-
ivoryos/utils/client_proxy.py,sha256=
|
|
40
|
+
ivoryos/utils/client_proxy.py,sha256=0OT2xTMkqh_2ybgCxMV_71ZVUThWwrsnAhTIBY5vDR8,2095
|
|
41
41
|
ivoryos/utils/db_models.py,sha256=zlmmD2600CYyn79gQq8k0Vra7BDBKJBAyNLYclIWdvs,27382
|
|
42
|
-
ivoryos/utils/form.py,sha256=
|
|
42
|
+
ivoryos/utils/form.py,sha256=k9d4AZz74FeOh4gkhDsZx3g4LZti_aJm9ZPRBkpOsE0,21933
|
|
43
43
|
ivoryos/utils/global_config.py,sha256=OqfDrPgOzRdIUMD4V3pA9t6b-BATMjGZl8Jn7nkI56k,2138
|
|
44
44
|
ivoryos/utils/llm_agent.py,sha256=-lVCkjPlpLues9sNTmaT7bT4sdhWvV2DiojNwzB2Lcw,6422
|
|
45
|
-
ivoryos/utils/script_runner.py,sha256=
|
|
45
|
+
ivoryos/utils/script_runner.py,sha256=0b5hLKAF2o0SQKiArhUsG8-4MA-eniAcjwi8gCNVwtY,14542
|
|
46
46
|
ivoryos/utils/task_runner.py,sha256=u4nF0wOADu_HVlGYVTOXnUm1woWGgYAccr-ZCzgtb6Q,2899
|
|
47
|
-
ivoryos/utils/utils.py,sha256=
|
|
48
|
-
ivoryos-1.0.
|
|
49
|
-
ivoryos-1.0.
|
|
50
|
-
ivoryos-1.0.
|
|
51
|
-
ivoryos-1.0.
|
|
52
|
-
ivoryos-1.0.
|
|
47
|
+
ivoryos/utils/utils.py,sha256=nGSw3vHkxXedX4T-AFEWGzDIU7h0NjrAf0wBt0Og9pk,13886
|
|
48
|
+
ivoryos-1.0.4.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
|
|
49
|
+
ivoryos-1.0.4.dist-info/METADATA,sha256=Z1w1rUuHTx3MWyDwSYo3dIj6bdoUhcSHgUvVYnSVU74,8906
|
|
50
|
+
ivoryos-1.0.4.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
51
|
+
ivoryos-1.0.4.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
|
|
52
|
+
ivoryos-1.0.4.dist-info/RECORD,,
|
/ivoryos/routes/database/templates/database/{experiment_step_view.html → workflow_view.html}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|