ivoryos 1.3.7__tar.gz → 1.4.6__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.
- {ivoryos-1.3.7 → ivoryos-1.4.6}/PKG-INFO +70 -47
- {ivoryos-1.3.7 → ivoryos-1.4.6}/README.md +64 -43
- ivoryos-1.4.6/docs/source/conf.py +84 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/app.py +29 -5
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/optimizer/ax_optimizer.py +52 -25
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/optimizer/base_optimizer.py +19 -1
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/optimizer/baybe_optimizer.py +27 -17
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/optimizer/nimo_optimizer.py +35 -24
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/auth/auth.py +26 -1
- ivoryos-1.4.6/ivoryos/routes/auth/templates/change_password.html +32 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/control/control_new_device.py +21 -11
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/data/data.py +27 -9
- ivoryos-1.4.6/ivoryos/routes/data/templates/components/step_card.html +78 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/data/templates/workflow_view.html +14 -5
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/design.py +31 -1
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/design_step.py +2 -1
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/edit_action_form.html +16 -3
- ivoryos-1.4.6/ivoryos/routes/design/templates/components/info_modal.html +318 -0
- ivoryos-1.4.6/ivoryos/routes/design/templates/components/python_code_overlay.html +56 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/experiment_builder.html +3 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/execute/execute.py +76 -20
- ivoryos-1.4.6/ivoryos/routes/execute/templates/components/logging_panel.html +56 -0
- ivoryos-1.4.6/ivoryos/routes/execute/templates/components/run_tabs.html +60 -0
- ivoryos-1.4.6/ivoryos/routes/execute/templates/components/tab_bayesian.html +520 -0
- ivoryos-1.4.6/ivoryos/routes/execute/templates/components/tab_configuration.html +383 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/execute/templates/components/tab_repeat.html +6 -2
- ivoryos-1.4.6/ivoryos/routes/execute/templates/experiment_run.html +30 -0
- ivoryos-1.4.6/ivoryos/routes/main/main.py +70 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/server.py +1 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/socket_handlers.py +1 -1
- ivoryos-1.4.6/ivoryos/static/ivoryos_logo.png +0 -0
- ivoryos-1.4.6/ivoryos/static/js/action_handlers.js +378 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/js/sortable_design.js +1 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/js/ui_state.js +1 -3
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/templates/base.html +61 -2
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/bo_campaign.py +17 -16
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/client_proxy.py +11 -6
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/db_models.py +171 -37
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/decorators.py +1 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/form.py +32 -12
- ivoryos-1.4.6/ivoryos/utils/nest_script.py +314 -0
- ivoryos-1.4.6/ivoryos/utils/script_runner.py +770 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/task_runner.py +30 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/utils.py +21 -2
- ivoryos-1.4.6/ivoryos/version.py +1 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos.egg-info/PKG-INFO +70 -47
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos.egg-info/SOURCES.txt +17 -2
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos.egg-info/requires.txt +6 -4
- ivoryos-1.4.6/ivoryos.egg-info/top_level.txt +4 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/pyproject.toml +6 -2
- ivoryos-1.4.6/tests/__init__.py +0 -0
- ivoryos-1.4.6/tests/conftest.py +133 -0
- ivoryos-1.4.6/tests/integration/__init__.py +0 -0
- ivoryos-1.4.6/tests/integration/test_route_auth.py +80 -0
- ivoryos-1.4.6/tests/integration/test_route_control.py +94 -0
- ivoryos-1.4.6/tests/integration/test_route_database.py +61 -0
- ivoryos-1.4.6/tests/integration/test_route_design.py +36 -0
- ivoryos-1.4.6/tests/integration/test_route_main.py +35 -0
- ivoryos-1.4.6/tests/integration/test_sockets.py +26 -0
- ivoryos-1.4.6/tests/unit/test_type_conversion.py +42 -0
- ivoryos-1.4.6/tests/unit/test_util.py +3 -0
- ivoryos-1.3.7/ivoryos/routes/api/api.py +0 -57
- ivoryos-1.3.7/ivoryos/routes/data/templates/components/step_card.html +0 -42
- ivoryos-1.3.7/ivoryos/routes/design/templates/components/python_code_overlay.html +0 -39
- ivoryos-1.3.7/ivoryos/routes/execute/templates/components/logging_panel.html +0 -31
- ivoryos-1.3.7/ivoryos/routes/execute/templates/components/run_tabs.html +0 -17
- ivoryos-1.3.7/ivoryos/routes/execute/templates/components/tab_bayesian.html +0 -398
- ivoryos-1.3.7/ivoryos/routes/execute/templates/components/tab_configuration.html +0 -98
- ivoryos-1.3.7/ivoryos/routes/execute/templates/experiment_run.html +0 -294
- ivoryos-1.3.7/ivoryos/routes/main/main.py +0 -42
- ivoryos-1.3.7/ivoryos/static/js/action_handlers.js +0 -250
- ivoryos-1.3.7/ivoryos/utils/script_runner.py +0 -489
- ivoryos-1.3.7/ivoryos/version.py +0 -1
- ivoryos-1.3.7/ivoryos.egg-info/top_level.txt +0 -1
- {ivoryos-1.3.7 → ivoryos-1.4.6}/LICENSE +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/config.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/optimizer/registry.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/auth/templates/login.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/auth/templates/signup.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/control/control.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/control/control_file.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/control/templates/controllers.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/control/templates/controllers_new.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/control/utils.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/data/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/data/templates/workflow_database.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/design_file.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/action_form.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/actions_panel.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/autofill_toggle.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/canvas.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/canvas_footer.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/canvas_header.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/canvas_main.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/deck_selector.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/instruments_panel.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/drop_modal.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/json_modal.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/new_script_modal.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/rename_modal.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/saveas_modal.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/sidebar.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/text_to_code_panel.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/execute/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/execute/execute_file.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/execute/templates/components/error_modal.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/execute/templates/components/progress_panel.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/execute/templates/components/run_panel.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/library/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/library/library.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/library/templates/library.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/main/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/main/templates/help.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/routes/main/templates/home.html +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/js/db_delete.js +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/js/script_metadata.js +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/js/socket_handler.js +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/logo.webp +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/static/style.css +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/__init__.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/global_config.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/llm_agent.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/py_to_json.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos/utils/serilize.py +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-1.3.7 → ivoryos-1.4.6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.6
|
|
4
4
|
Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
|
|
5
5
|
Author-email: Ivory Zhang <ivoryzhang@chem.ubc.ca>
|
|
6
6
|
License: MIT
|
|
@@ -17,6 +17,7 @@ Requires-Dist: Flask-SQLAlchemy
|
|
|
17
17
|
Requires-Dist: Flask-WTF
|
|
18
18
|
Requires-Dist: SQLAlchemy-Utils
|
|
19
19
|
Requires-Dist: python-dotenv
|
|
20
|
+
Requires-Dist: pandas
|
|
20
21
|
Requires-Dist: astor; python_version < "3.9"
|
|
21
22
|
Provides-Extra: optimizer-ax
|
|
22
23
|
Requires-Dist: ax-platform; extra == "optimizer-ax"
|
|
@@ -24,9 +25,10 @@ Provides-Extra: optimizer-baybe
|
|
|
24
25
|
Requires-Dist: baybe; extra == "optimizer-baybe"
|
|
25
26
|
Provides-Extra: optimizer-nimo
|
|
26
27
|
Requires-Dist: nimo; extra == "optimizer-nimo"
|
|
27
|
-
Provides-Extra:
|
|
28
|
-
Requires-Dist: ax-platform>=1.1.2; extra == "
|
|
29
|
-
Requires-Dist: baybe; extra == "
|
|
28
|
+
Provides-Extra: optimizers
|
|
29
|
+
Requires-Dist: ax-platform>=1.1.2; extra == "optimizers"
|
|
30
|
+
Requires-Dist: baybe>=0.14.0; extra == "optimizers"
|
|
31
|
+
Requires-Dist: nimo; extra == "optimizers"
|
|
30
32
|
Provides-Extra: doc
|
|
31
33
|
Requires-Dist: sphinx; extra == "doc"
|
|
32
34
|
Requires-Dist: sphinx-rtd-theme; extra == "doc"
|
|
@@ -41,34 +43,35 @@ Dynamic: license-file
|
|
|
41
43
|
[](https://youtu.be/dFfJv9I2-1g)
|
|
42
44
|
[](https://youtu.be/flr5ydiE96s)
|
|
43
45
|
[](https://www.nature.com/articles/s41467-025-60514-w)
|
|
46
|
+
[](https://discord.gg/AX5P9EdGVX)
|
|
44
47
|
|
|
45
|
-
[
|
|
48
|
+

|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
A **plug-and-play** web interface for flexible SDLs
|
|
50
|
+
# [IvoryOS](https://ivoryos.ai): interoperable orchestrator for self-driving laboratories (SDLs)
|
|
51
|
+
|
|
52
|
+
A **plug-and-play** web interface for flexible, modular SDLs —
|
|
53
|
+
you focus on developing protocols, IvoryOS handles the rest.
|
|
54
|
+
|
|
55
|
+

|
|
50
56
|
|
|
51
57
|
---
|
|
52
58
|
|
|
53
59
|
## Table of Contents
|
|
54
|
-
- [
|
|
60
|
+
- [What IvoryOS does](#what-ivoryos-does)
|
|
55
61
|
- [System requirements](#system-requirements)
|
|
56
62
|
- [Installation](#installation)
|
|
57
|
-
- [Quick Start](#quick-start)
|
|
58
63
|
- [Features](#features)
|
|
59
64
|
- [Demo](#demo)
|
|
60
65
|
- [Roadmap](#roadmap)
|
|
66
|
+
- [Contributing](#contributing)
|
|
61
67
|
- [Acknowledgements](#acknowledgements)
|
|
62
68
|
|
|
63
69
|
---
|
|
64
|
-
##
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
- Automatically displaying functions and parameters in a web UI
|
|
70
|
-
- Allowing users to **design**, **manage**, and **execute** experimental workflows with minimal changes to existing scripts
|
|
71
|
-
- Providing natural language support for workflow design and execution, check [IvoryOS MCP](https://gitlab.com/heingroup/ivoryos-suite/ivoryos-mcp) for more details.
|
|
70
|
+
## What IvoryOS Does
|
|
71
|
+
- Turns Python modules into UIs by dynamically inspecting your hardware APIs, functions, and workflows.
|
|
72
|
+
- Standardizes optimization inputs/outputs, making any optimizer plug-and-play.
|
|
73
|
+
- Provides a visual workflow builder for designing and running experiments.
|
|
74
|
+
- Adds natural-language control for creating and executing workflows, see [IvoryOS MCP](https://gitlab.com/heingroup/ivoryos-suite/ivoryos-mcp) for more details.
|
|
72
75
|
|
|
73
76
|
----
|
|
74
77
|
## System Requirements
|
|
@@ -90,10 +93,13 @@ Building UIs for SDLs is challenging because flexibility and modularity make the
|
|
|
90
93
|
- SQLAlchemy-Utils~=0.41
|
|
91
94
|
- Flask-WTF~=1.2
|
|
92
95
|
- python-dotenv==1.0.1
|
|
96
|
+
- pandas
|
|
93
97
|
|
|
94
98
|
**Optional:**
|
|
95
|
-
- ax-platform
|
|
96
|
-
- baybe
|
|
99
|
+
- ax-platform==1.1.2
|
|
100
|
+
- baybe==0.14.0
|
|
101
|
+
- nimo
|
|
102
|
+
- slack-sdk
|
|
97
103
|
</details>
|
|
98
104
|
|
|
99
105
|
---
|
|
@@ -104,16 +110,22 @@ From PyPI:
|
|
|
104
110
|
```bash
|
|
105
111
|
pip install ivoryos
|
|
106
112
|
```
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
|
|
114
|
+
[//]: # (From source:)
|
|
115
|
+
|
|
116
|
+
[//]: # (```bash)
|
|
117
|
+
|
|
118
|
+
[//]: # (git clone https://gitlab.com/heingroup/ivoryos.git)
|
|
119
|
+
|
|
120
|
+
[//]: # (cd ivoryos)
|
|
121
|
+
|
|
122
|
+
[//]: # (pip install -e .)
|
|
123
|
+
|
|
124
|
+
[//]: # (```)
|
|
113
125
|
|
|
114
126
|
|
|
115
127
|
## Quick start
|
|
116
|
-
In your
|
|
128
|
+
In your script, where you initialize or import your robot:
|
|
117
129
|
```python
|
|
118
130
|
my_robot = Robot()
|
|
119
131
|
|
|
@@ -121,8 +133,8 @@ import ivoryos
|
|
|
121
133
|
|
|
122
134
|
ivoryos.run(__name__)
|
|
123
135
|
```
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
Then run the script and visit `http://localhost:8000` in your browser.
|
|
137
|
+
Use `admin` for both username and password, and start building workflows!
|
|
126
138
|
|
|
127
139
|
----
|
|
128
140
|
## Features
|
|
@@ -145,7 +157,8 @@ ivoryos.run(__name__, logger="logger name")
|
|
|
145
157
|
ivoryos.run(__name__, logger=["logger 1", "logger 2"])
|
|
146
158
|
```
|
|
147
159
|
### Human-in-the-loop
|
|
148
|
-
|
|
160
|
+
Use `pause` in flow control to pause the workflow and send a notification with custom message handler(s).
|
|
161
|
+
When run into `pause`, it will pause, send a message, and wait for human's response. Example of a Slack bot:
|
|
149
162
|
```python
|
|
150
163
|
|
|
151
164
|
def slack_bot(msg: str = "Hi"):
|
|
@@ -168,24 +181,24 @@ ivoryos.run(__name__, notification_handler=slack_bot)
|
|
|
168
181
|
|
|
169
182
|
### Directory Structure
|
|
170
183
|
|
|
171
|
-
Created automatically on first run:
|
|
184
|
+
Created automatically in the same working directory on the first run:
|
|
185
|
+
<details>
|
|
186
|
+
<summary>click to see the data folder structure</summary>
|
|
187
|
+
|
|
172
188
|
- **`ivoryos_data/`**:
|
|
173
|
-
- **`
|
|
174
|
-
- **`
|
|
175
|
-
- **`
|
|
176
|
-
- **`
|
|
177
|
-
- **`default.log`**: Application logs
|
|
178
|
-
- **`ivoryos.db`**: Local database
|
|
189
|
+
- **`config_csv/`**: Batch configuration `csv`
|
|
190
|
+
- **`pseudo_deck/`**: Offline deck `.pkl`
|
|
191
|
+
- **`results/`**: Execution results
|
|
192
|
+
- **`scripts/`**: Compiled workflows Python scripts
|
|
193
|
+
- **`default.log`**: Application logs
|
|
194
|
+
- **`ivoryos.db`**: Local database
|
|
195
|
+
</details>
|
|
196
|
+
|
|
179
197
|
---
|
|
180
|
-
## Demo
|
|
181
|
-
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py)
|
|
182
|
-
```Python
|
|
183
|
-
ivoryos.run(__name__)
|
|
184
|
-
```
|
|
185
198
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
199
|
+
## Demo
|
|
200
|
+
Online demo at [demo.ivoryos.ai](https://demo.ivoryos.ai).
|
|
201
|
+
Local version in [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/community/examples/abstract_sdl_example/abstract_sdl.py)
|
|
189
202
|
|
|
190
203
|
---
|
|
191
204
|
|
|
@@ -193,14 +206,22 @@ ivoryos.run(__name__)
|
|
|
193
206
|
|
|
194
207
|
- [ ] dropdown input
|
|
195
208
|
- [ ] snapshot version control
|
|
196
|
-
- [ ] optimizer-agnostic
|
|
197
|
-
- [ ] prefect compatibility
|
|
198
209
|
- [ ] check batch-config file compatibility
|
|
199
210
|
|
|
200
211
|
---
|
|
201
212
|
|
|
213
|
+
## Contributing
|
|
214
|
+
|
|
215
|
+
We welcome all contributions — from core improvements to new drivers, plugins, and real-world use cases.
|
|
216
|
+
See `CONTRIBUTING.md` for details and let us know you're interested: https://forms.gle/fPSvw5LEGrweUQUH8
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
202
220
|
## Citing
|
|
203
221
|
|
|
222
|
+
<details>
|
|
223
|
+
<summary>Click to see citations</summary>
|
|
224
|
+
|
|
204
225
|
If you find this project useful, please consider citing the following manuscript:
|
|
205
226
|
|
|
206
227
|
> 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).
|
|
@@ -234,6 +255,8 @@ For an additional perspective related to the development of the tool, please see
|
|
|
234
255
|
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
235
256
|
}
|
|
236
257
|
```
|
|
258
|
+
</details>
|
|
259
|
+
|
|
237
260
|
---
|
|
238
261
|
## Acknowledgements
|
|
239
262
|
Authors acknowledge Telescope Innovations Corp., UBC Hein Lab, and Acceleration Consortium members for their valuable suggestions and contributions.
|
|
@@ -4,34 +4,35 @@
|
|
|
4
4
|
[](https://youtu.be/dFfJv9I2-1g)
|
|
5
5
|
[](https://youtu.be/flr5ydiE96s)
|
|
6
6
|
[](https://www.nature.com/articles/s41467-025-60514-w)
|
|
7
|
+
[](https://discord.gg/AX5P9EdGVX)
|
|
7
8
|
|
|
8
|
-
[
|
|
9
|
+

|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
A **plug-and-play** web interface for flexible SDLs
|
|
11
|
+
# [IvoryOS](https://ivoryos.ai): interoperable orchestrator for self-driving laboratories (SDLs)
|
|
12
|
+
|
|
13
|
+
A **plug-and-play** web interface for flexible, modular SDLs —
|
|
14
|
+
you focus on developing protocols, IvoryOS handles the rest.
|
|
15
|
+
|
|
16
|
+

|
|
13
17
|
|
|
14
18
|
---
|
|
15
19
|
|
|
16
20
|
## Table of Contents
|
|
17
|
-
- [
|
|
21
|
+
- [What IvoryOS does](#what-ivoryos-does)
|
|
18
22
|
- [System requirements](#system-requirements)
|
|
19
23
|
- [Installation](#installation)
|
|
20
|
-
- [Quick Start](#quick-start)
|
|
21
24
|
- [Features](#features)
|
|
22
25
|
- [Demo](#demo)
|
|
23
26
|
- [Roadmap](#roadmap)
|
|
27
|
+
- [Contributing](#contributing)
|
|
24
28
|
- [Acknowledgements](#acknowledgements)
|
|
25
29
|
|
|
26
30
|
---
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
- Automatically displaying functions and parameters in a web UI
|
|
33
|
-
- Allowing users to **design**, **manage**, and **execute** experimental workflows with minimal changes to existing scripts
|
|
34
|
-
- Providing natural language support for workflow design and execution, check [IvoryOS MCP](https://gitlab.com/heingroup/ivoryos-suite/ivoryos-mcp) for more details.
|
|
31
|
+
## What IvoryOS Does
|
|
32
|
+
- Turns Python modules into UIs by dynamically inspecting your hardware APIs, functions, and workflows.
|
|
33
|
+
- Standardizes optimization inputs/outputs, making any optimizer plug-and-play.
|
|
34
|
+
- Provides a visual workflow builder for designing and running experiments.
|
|
35
|
+
- Adds natural-language control for creating and executing workflows, see [IvoryOS MCP](https://gitlab.com/heingroup/ivoryos-suite/ivoryos-mcp) for more details.
|
|
35
36
|
|
|
36
37
|
----
|
|
37
38
|
## System Requirements
|
|
@@ -53,10 +54,13 @@ Building UIs for SDLs is challenging because flexibility and modularity make the
|
|
|
53
54
|
- SQLAlchemy-Utils~=0.41
|
|
54
55
|
- Flask-WTF~=1.2
|
|
55
56
|
- python-dotenv==1.0.1
|
|
57
|
+
- pandas
|
|
56
58
|
|
|
57
59
|
**Optional:**
|
|
58
|
-
- ax-platform
|
|
59
|
-
- baybe
|
|
60
|
+
- ax-platform==1.1.2
|
|
61
|
+
- baybe==0.14.0
|
|
62
|
+
- nimo
|
|
63
|
+
- slack-sdk
|
|
60
64
|
</details>
|
|
61
65
|
|
|
62
66
|
---
|
|
@@ -67,16 +71,22 @@ From PyPI:
|
|
|
67
71
|
```bash
|
|
68
72
|
pip install ivoryos
|
|
69
73
|
```
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
|
|
75
|
+
[//]: # (From source:)
|
|
76
|
+
|
|
77
|
+
[//]: # (```bash)
|
|
78
|
+
|
|
79
|
+
[//]: # (git clone https://gitlab.com/heingroup/ivoryos.git)
|
|
80
|
+
|
|
81
|
+
[//]: # (cd ivoryos)
|
|
82
|
+
|
|
83
|
+
[//]: # (pip install -e .)
|
|
84
|
+
|
|
85
|
+
[//]: # (```)
|
|
76
86
|
|
|
77
87
|
|
|
78
88
|
## Quick start
|
|
79
|
-
In your
|
|
89
|
+
In your script, where you initialize or import your robot:
|
|
80
90
|
```python
|
|
81
91
|
my_robot = Robot()
|
|
82
92
|
|
|
@@ -84,8 +94,8 @@ import ivoryos
|
|
|
84
94
|
|
|
85
95
|
ivoryos.run(__name__)
|
|
86
96
|
```
|
|
87
|
-
|
|
88
|
-
|
|
97
|
+
Then run the script and visit `http://localhost:8000` in your browser.
|
|
98
|
+
Use `admin` for both username and password, and start building workflows!
|
|
89
99
|
|
|
90
100
|
----
|
|
91
101
|
## Features
|
|
@@ -108,7 +118,8 @@ ivoryos.run(__name__, logger="logger name")
|
|
|
108
118
|
ivoryos.run(__name__, logger=["logger 1", "logger 2"])
|
|
109
119
|
```
|
|
110
120
|
### Human-in-the-loop
|
|
111
|
-
|
|
121
|
+
Use `pause` in flow control to pause the workflow and send a notification with custom message handler(s).
|
|
122
|
+
When run into `pause`, it will pause, send a message, and wait for human's response. Example of a Slack bot:
|
|
112
123
|
```python
|
|
113
124
|
|
|
114
125
|
def slack_bot(msg: str = "Hi"):
|
|
@@ -131,24 +142,24 @@ ivoryos.run(__name__, notification_handler=slack_bot)
|
|
|
131
142
|
|
|
132
143
|
### Directory Structure
|
|
133
144
|
|
|
134
|
-
Created automatically on first run:
|
|
145
|
+
Created automatically in the same working directory on the first run:
|
|
146
|
+
<details>
|
|
147
|
+
<summary>click to see the data folder structure</summary>
|
|
148
|
+
|
|
135
149
|
- **`ivoryos_data/`**:
|
|
136
|
-
- **`
|
|
137
|
-
- **`
|
|
138
|
-
- **`
|
|
139
|
-
- **`
|
|
140
|
-
- **`default.log`**: Application logs
|
|
141
|
-
- **`ivoryos.db`**: Local database
|
|
150
|
+
- **`config_csv/`**: Batch configuration `csv`
|
|
151
|
+
- **`pseudo_deck/`**: Offline deck `.pkl`
|
|
152
|
+
- **`results/`**: Execution results
|
|
153
|
+
- **`scripts/`**: Compiled workflows Python scripts
|
|
154
|
+
- **`default.log`**: Application logs
|
|
155
|
+
- **`ivoryos.db`**: Local database
|
|
156
|
+
</details>
|
|
157
|
+
|
|
142
158
|
---
|
|
143
|
-
## Demo
|
|
144
|
-
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py)
|
|
145
|
-
```Python
|
|
146
|
-
ivoryos.run(__name__)
|
|
147
|
-
```
|
|
148
159
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
160
|
+
## Demo
|
|
161
|
+
Online demo at [demo.ivoryos.ai](https://demo.ivoryos.ai).
|
|
162
|
+
Local version in [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/community/examples/abstract_sdl_example/abstract_sdl.py)
|
|
152
163
|
|
|
153
164
|
---
|
|
154
165
|
|
|
@@ -156,14 +167,22 @@ ivoryos.run(__name__)
|
|
|
156
167
|
|
|
157
168
|
- [ ] dropdown input
|
|
158
169
|
- [ ] snapshot version control
|
|
159
|
-
- [ ] optimizer-agnostic
|
|
160
|
-
- [ ] prefect compatibility
|
|
161
170
|
- [ ] check batch-config file compatibility
|
|
162
171
|
|
|
163
172
|
---
|
|
164
173
|
|
|
174
|
+
## Contributing
|
|
175
|
+
|
|
176
|
+
We welcome all contributions — from core improvements to new drivers, plugins, and real-world use cases.
|
|
177
|
+
See `CONTRIBUTING.md` for details and let us know you're interested: https://forms.gle/fPSvw5LEGrweUQUH8
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
165
181
|
## Citing
|
|
166
182
|
|
|
183
|
+
<details>
|
|
184
|
+
<summary>Click to see citations</summary>
|
|
185
|
+
|
|
167
186
|
If you find this project useful, please consider citing the following manuscript:
|
|
168
187
|
|
|
169
188
|
> 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).
|
|
@@ -197,6 +216,8 @@ For an additional perspective related to the development of the tool, please see
|
|
|
197
216
|
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
198
217
|
}
|
|
199
218
|
```
|
|
219
|
+
</details>
|
|
220
|
+
|
|
200
221
|
---
|
|
201
222
|
## Acknowledgements
|
|
202
223
|
Authors acknowledge Telescope Innovations Corp., UBC Hein Lab, and Acceleration Consortium members for their valuable suggestions and contributions.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
import urllib
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
# -- General configuration
|
|
9
|
+
sys.path.insert(0, os.path.abspath('../../'))
|
|
10
|
+
from ivoryos.version import __version__
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# appending suite readme.rst to doc
|
|
14
|
+
|
|
15
|
+
external_readme = [
|
|
16
|
+
{
|
|
17
|
+
"name": 'plugin.rst',
|
|
18
|
+
"url": "https://gitlab.com/heingroup/ivoryos-suite/ivoryos-plugin-template/-/raw/main/README.rst"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": 'client.rst',
|
|
22
|
+
"url": "https://gitlab.com/heingroup/ivoryos-suite/ivoryos-client/-/raw/main/README.rst"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": 'mcp.rst',
|
|
26
|
+
"url": "https://gitlab.com/heingroup/ivoryos-suite/ivoryos-mcp/-/raw/main/README.rst"
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
for item in external_readme:
|
|
31
|
+
readme_url = item['url']
|
|
32
|
+
name = item['name']
|
|
33
|
+
output_path = os.path.join(os.path.dirname(__file__), name)
|
|
34
|
+
r = requests.get(readme_url, verify=False)
|
|
35
|
+
if not os.path.exists(output_path):
|
|
36
|
+
with open(output_path, "wb") as f:
|
|
37
|
+
f.write(r.content)
|
|
38
|
+
|
|
39
|
+
# -- Project information
|
|
40
|
+
project = 'ivoryOS'
|
|
41
|
+
copyright = '2024, Ivory Zhang'
|
|
42
|
+
author = 'Ivory Zhang, Lucy Hao'
|
|
43
|
+
version = __version__
|
|
44
|
+
|
|
45
|
+
extensions = [
|
|
46
|
+
'sphinx.ext.duration',
|
|
47
|
+
'sphinx.ext.doctest',
|
|
48
|
+
'sphinx.ext.autosummary',
|
|
49
|
+
'sphinx.ext.intersphinx',
|
|
50
|
+
'sphinxcontrib.httpdomain',
|
|
51
|
+
'sphinxcontrib.autohttp.flask',
|
|
52
|
+
'sphinxcontrib.autohttp.flaskqref'
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
install_requires = [
|
|
56
|
+
'sphinx-autodoc-typehints'
|
|
57
|
+
]
|
|
58
|
+
autodoc_mock_imports = ["flask_sqlalchemy", "another_hard_to_import_lib"]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
intersphinx_mapping = {
|
|
62
|
+
'python': ('https://docs.python.org/3/', None),
|
|
63
|
+
'sphinx': ('https://www.sphinx-doc.org/en/master/', None),
|
|
64
|
+
}
|
|
65
|
+
intersphinx_disabled_domains = ['std']
|
|
66
|
+
|
|
67
|
+
templates_path = ['_templates']
|
|
68
|
+
|
|
69
|
+
html_static_path = ['_static']
|
|
70
|
+
html_css_files = [
|
|
71
|
+
'custom.css',
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
html_allow_raw_html = True
|
|
75
|
+
|
|
76
|
+
# -- Options for HTML output
|
|
77
|
+
|
|
78
|
+
html_theme = 'sphinx_rtd_theme'
|
|
79
|
+
|
|
80
|
+
# -- Options for EPUB output
|
|
81
|
+
epub_show_urls = 'footnote'
|
|
82
|
+
|
|
83
|
+
# The master toctree document.
|
|
84
|
+
master_doc = 'index'
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import uuid
|
|
3
3
|
|
|
4
|
+
import bcrypt
|
|
4
5
|
from flask import Flask, session, g, redirect, url_for
|
|
5
6
|
from flask_login import AnonymousUserMixin
|
|
6
7
|
|
|
7
8
|
from ivoryos.utils import utils
|
|
8
|
-
from ivoryos.utils.db_models import db
|
|
9
|
-
from ivoryos.config import Config, get_config
|
|
9
|
+
from ivoryos.utils.db_models import db, User
|
|
10
10
|
from ivoryos.routes.auth.auth import auth, login_manager
|
|
11
11
|
from ivoryos.routes.control.control import control
|
|
12
12
|
from ivoryos.routes.data.data import data
|
|
13
13
|
from ivoryos.routes.library.library import library
|
|
14
14
|
from ivoryos.routes.design.design import design
|
|
15
15
|
from ivoryos.routes.execute.execute import execute
|
|
16
|
-
from ivoryos.routes.api.api import api
|
|
17
16
|
from ivoryos.socket_handlers import socketio
|
|
18
17
|
from ivoryos.routes.main.main import main
|
|
19
18
|
from ivoryos.version import __version__ as ivoryos_version
|
|
20
19
|
from sqlalchemy import inspect, text
|
|
21
|
-
from flask import current_app
|
|
22
20
|
|
|
23
21
|
url_prefix = os.getenv('URL_PREFIX', "/ivoryos")
|
|
24
22
|
app = Flask(__name__, static_url_path=f'{url_prefix}/static', static_folder='static')
|
|
@@ -29,7 +27,7 @@ app.register_blueprint(control, url_prefix=f'{url_prefix}/instruments')
|
|
|
29
27
|
app.register_blueprint(design, url_prefix=f'{url_prefix}')
|
|
30
28
|
app.register_blueprint(execute, url_prefix=f'{url_prefix}')
|
|
31
29
|
app.register_blueprint(data, url_prefix=f'{url_prefix}')
|
|
32
|
-
app.register_blueprint(api, url_prefix=f'{url_prefix}/{api.name}')
|
|
30
|
+
# app.register_blueprint(api, url_prefix=f'{url_prefix}/{api.name}')
|
|
33
31
|
|
|
34
32
|
def reset_old_schema(engine, db_dir):
|
|
35
33
|
inspector = inspect(engine)
|
|
@@ -60,11 +58,36 @@ def reset_old_schema(engine, db_dir):
|
|
|
60
58
|
conn.execute(text("DROP TABLE IF EXISTS workflow_steps"))
|
|
61
59
|
if old_workflow_run:
|
|
62
60
|
conn.execute(text("DROP TABLE IF EXISTS workflow_runs"))
|
|
61
|
+
with engine.begin() as conn:
|
|
62
|
+
try:
|
|
63
|
+
conn.execute(
|
|
64
|
+
text("ALTER TABLE user ADD COLUMN settings TEXT")
|
|
65
|
+
)
|
|
66
|
+
except Exception:
|
|
67
|
+
pass
|
|
63
68
|
|
|
64
69
|
# Recreate new schema
|
|
65
70
|
db.create_all() # creates workflow_runs, workflow_phases, workflow_steps
|
|
66
71
|
|
|
67
72
|
|
|
73
|
+
def create_admin():
|
|
74
|
+
"""
|
|
75
|
+
Create an admin user with username 'admin' and password 'admin' if it doesn't exist.
|
|
76
|
+
"""
|
|
77
|
+
with app.app_context():
|
|
78
|
+
admin_user = User.query.filter_by(username='admin').first()
|
|
79
|
+
if not admin_user:
|
|
80
|
+
print("Creating default admin user...")
|
|
81
|
+
admin_user = User(
|
|
82
|
+
username='admin',
|
|
83
|
+
password=bcrypt.hashpw("admin".encode('utf-8'), bcrypt.gensalt()),
|
|
84
|
+
)
|
|
85
|
+
db.session.add(admin_user)
|
|
86
|
+
db.session.commit()
|
|
87
|
+
else:
|
|
88
|
+
print("Admin user already exists.")
|
|
89
|
+
|
|
90
|
+
|
|
68
91
|
def create_app(config_class=None):
|
|
69
92
|
"""
|
|
70
93
|
create app, init database
|
|
@@ -82,6 +105,7 @@ def create_app(config_class=None):
|
|
|
82
105
|
with app.app_context():
|
|
83
106
|
# db.create_all()
|
|
84
107
|
reset_old_schema(db.engine, app.config['OUTPUT_FOLDER'])
|
|
108
|
+
create_admin()
|
|
85
109
|
|
|
86
110
|
# Additional setup
|
|
87
111
|
utils.create_gui_dir(app.config['OUTPUT_FOLDER'])
|