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.

@@ -21,7 +21,7 @@ $(document).ready(function () {
21
21
  success: function (data) {
22
22
  $("#response").html(data);
23
23
  $("#response").slideDown("slow");
24
- slideout();
24
+ window.location.href = window.location.href;
25
25
  }
26
26
  });
27
27
  }
@@ -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/backend_control/deck.{class_name}"\n'
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 None,
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
- param.annotation,
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
 
@@ -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"
1
+ __version__ = "1.0.4"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 1.0.1
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=1v6LwuIao8WbmSskifzdfde7E_gH3PWCYaNpLaRtYZk,7341
1
+ ivoryos/__init__.py,sha256=6zTQ5B7Pu99pv33LMpyYb6u0dz_TVSY9TVShXKAkrFs,7959
2
2
  ivoryos/config.py,sha256=3FPBYTIBhQTKDvsEoR8ZeTmg65D-CSFEdGmOuIL4pSI,1311
3
- ivoryos/version.py,sha256=d4QHYmS_30j0hPN8NmNPnQ_Z0TphDRbu4MtQj9cT9e8,22
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=7CdXjGAr1B_xsmwweakTWOoROgsOJf0MNTzlMP_5Nus,3240
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=AbJA1dwWAWZi3eGnsbNObPASVsN7pZbL6la25xB52hI,14209
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=VQ77HRvBlyBrQ3al5fcKF5Y6_vKtU8WeAhilqQQltAo,2997
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=97387fF6tQIfb-BF6krILRP4a4_8jOJmGcE7BoPgjmk,9127
16
- ivoryos/routes/database/templates/database/experiment_database.html,sha256=edlCcKfrS91gGG1dPFQjC9xD7F7nWNNqS3S6Oa7apzs,3460
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/workflow_run_database.html,sha256=MczK9my9u0SyQsMFLbc6CXeZqKaBo5vk1SpwjkcZdqk,3571
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=ameAcgTLlrfOENUa48xxp64psuh2aZ0JuVfIj7zj-LM,28968
22
- ivoryos/routes/design/templates/design/experiment_builder.html,sha256=rEdcHj5onJG_4MejdFBPnJVzsvCMp1KDteqNkpx24kQ,29430
23
- ivoryos/routes/design/templates/design/experiment_run.html,sha256=7VP0Vo98phcYnFennd5vqaMK1M1QBwDmM-b9aZb8jOw,26282
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=wwpKfIzZGDxfX3moNz0cvPvm9YyHmopZK3wmkUdnBiw,4333
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=AzcSQGMqeCqVULP1a7vEKNe135NZYryVX63ke0wgK04,2099
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=b3JKxRc1jN45-bXyfzSJT1lcssUuxT86FhRmNUDv5-U,20973
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=k7bH9AeFGhx2tns-81q0JZFLCM9cvXCpZF7bH80RVzo,14504
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=OBwrRu02yh7pqG_lyl10zWr_RYes3xhMporxIz8lGYI,13579
48
- ivoryos-1.0.1.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
49
- ivoryos-1.0.1.dist-info/METADATA,sha256=KEh9s448whM6VP-Knc5UUjl7DpoME989pKBfwLxFcHM,6992
50
- ivoryos-1.0.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
51
- ivoryos-1.0.1.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
52
- ivoryos-1.0.1.dist-info/RECORD,,
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,,