ivoryos 1.2.3__tar.gz → 1.2.5__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.
Potentially problematic release.
This version of ivoryos might be problematic. Click here for more details.
- {ivoryos-1.2.3 → ivoryos-1.2.5}/PKG-INFO +80 -119
- ivoryos-1.2.5/README.md +177 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/__init__.py +18 -18
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/design.py +1 -1
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/js/ui_state.js +1 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/db_models.py +1 -1
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/form.py +6 -1
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/script_runner.py +46 -38
- ivoryos-1.2.5/ivoryos/version.py +1 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos.egg-info/PKG-INFO +80 -119
- ivoryos-1.2.3/README.md +0 -216
- ivoryos-1.2.3/ivoryos/version.py +0 -1
- {ivoryos-1.2.3 → ivoryos-1.2.5}/LICENSE +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/config.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/optimizer/ax_optimizer.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/optimizer/base_optimizer.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/optimizer/baybe_optimizer.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/optimizer/registry.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/api/api.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/auth/auth.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/auth/templates/login.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/auth/templates/signup.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/control/control.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/control/control_file.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/control/control_new_device.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/control/templates/controllers.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/control/templates/controllers_new.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/control/utils.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/data/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/data/data.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/data/templates/components/step_card.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/data/templates/workflow_database.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/data/templates/workflow_view.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/design_file.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/design_step.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/action_form.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/actions_panel.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/autofill_toggle.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/canvas.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/canvas_footer.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/canvas_header.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/canvas_main.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/deck_selector.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/edit_action_form.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/instruments_panel.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/modals/drop_modal.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/modals/json_modal.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/modals/new_script_modal.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/modals/rename_modal.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/modals/saveas_modal.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/modals.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/python_code_overlay.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/sidebar.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/components/text_to_code_panel.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/design/templates/experiment_builder.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/execute.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/execute_file.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/components/error_modal.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/components/logging_panel.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/components/progress_panel.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/components/run_panel.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/components/run_tabs.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/components/tab_bayesian.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/components/tab_configuration.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/components/tab_repeat.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/execute/templates/experiment_run.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/library/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/library/library.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/library/templates/library.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/main/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/main/main.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/main/templates/help.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/routes/main/templates/home.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/socket_handlers.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/js/action_handlers.js +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/js/db_delete.js +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/js/script_metadata.js +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/js/socket_handler.js +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/js/sortable_design.js +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/logo.webp +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/static/style.css +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/templates/base.html +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/__init__.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/bo_campaign.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/client_proxy.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/global_config.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/llm_agent.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/py_to_json.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/serilize.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/task_runner.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos/utils/utils.py +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos.egg-info/SOURCES.txt +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos.egg-info/requires.txt +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/ivoryos.egg-info/top_level.txt +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/pyproject.toml +0 -0
- {ivoryos-1.2.3 → ivoryos-1.2.5}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.5
|
|
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
|
|
@@ -25,142 +25,121 @@ Dynamic: license-file
|
|
|
25
25
|
[](https://ivoryos.readthedocs.io/en/latest/?badge=latest)
|
|
26
26
|
[](https://pypi.org/project/ivoryos/)
|
|
27
27
|

|
|
28
|
-
[](https://youtu.be/dFfJv9I2-1g)
|
|
29
|
+
[](https://youtu.be/flr5ydiE96s)
|
|
29
30
|
[](https://www.nature.com/articles/s41467-025-60514-w)
|
|
30
31
|
[](https://discord.gg/AX5P9EdGVX)
|
|
31
32
|
|
|
32
33
|

|
|
33
34
|
# ivoryOS: interoperable Web UI for self-driving laboratories (SDLs)
|
|
34
|
-
|
|
35
|
+
A **plug-and-play** web interface for flexible SDLs
|
|
36
|
+
|
|
37
|
+
---
|
|
35
38
|
|
|
36
39
|
## Table of Contents
|
|
37
40
|
- [Description](#description)
|
|
38
41
|
- [System requirements](#system-requirements)
|
|
39
42
|
- [Installation](#installation)
|
|
40
|
-
- [
|
|
43
|
+
- [Quick Start](#quick-start)
|
|
44
|
+
- [Features](#features)
|
|
41
45
|
- [Demo](#demo)
|
|
42
46
|
- [Roadmap](#roadmap)
|
|
47
|
+
- [Acknowledgements](#acknowledgements)
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
---
|
|
45
50
|
## Description
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
51
|
+
Building UIs for SDLs is challenging because flexibility and modularity make them unpredictable — yet accessibility is essential for **democratisation** of AI-driven scientific discovery.
|
|
52
|
+
|
|
53
|
+
**IvoryOS** bridges the gap by:
|
|
54
|
+
- Dynamically inspecting initialized Python modules (hardware APIs, high-level functions, or workflows)
|
|
55
|
+
- Automatically displaying functions and parameters in a web UI
|
|
56
|
+
- Allowing users to **design**, **manage**, and **execute** experimental workflows with minimal changes to existing scripts
|
|
57
|
+
- Providing natural language support for workflow design and execution, check [IvoryOS MCP](https://gitlab.com/heingroup/ivoryos-suite/ivoryos-mcp) for more details.
|
|
58
|
+
|
|
59
|
+
----
|
|
60
|
+
## System Requirements
|
|
61
|
+
|
|
62
|
+
**Platforms:** Compatible with Linux, macOS, and Windows (developed/tested on Windows).
|
|
63
|
+
**Python:**
|
|
64
|
+
- Recommended: Python ≥3.10
|
|
65
|
+
- Minimum: Python ≥3.7 (without Ax optimizer support)
|
|
66
|
+
|
|
67
|
+
**Core Dependencies:**
|
|
68
|
+
<details>
|
|
69
|
+
<summary>Click to expand</summary>
|
|
70
|
+
|
|
71
|
+
- bcrypt~=4.0
|
|
72
|
+
- Flask-Login~=0.6
|
|
73
|
+
- Flask-Session~=0.8
|
|
74
|
+
- Flask-SocketIO~=5.3
|
|
75
|
+
- Flask-SQLAlchemy~=3.1
|
|
76
|
+
- SQLAlchemy-Utils~=0.41
|
|
77
|
+
- Flask-WTF~=1.2
|
|
78
|
+
- python-dotenv==1.0.1
|
|
79
|
+
|
|
80
|
+
**Optional:**
|
|
81
|
+
- ax-platform (≥1.0, Python≥3.10)
|
|
82
|
+
- baybe
|
|
83
|
+
</details>
|
|
84
|
+
|
|
85
|
+
---
|
|
68
86
|
|
|
69
87
|
|
|
70
88
|
## Installation
|
|
89
|
+
From PyPI:
|
|
71
90
|
```bash
|
|
72
91
|
pip install ivoryos
|
|
73
92
|
```
|
|
74
|
-
|
|
93
|
+
From source:
|
|
75
94
|
```bash
|
|
76
95
|
git clone https://gitlab.com/heingroup/ivoryos.git
|
|
77
96
|
cd ivoryos
|
|
78
|
-
pip install .
|
|
97
|
+
pip install -e .
|
|
79
98
|
```
|
|
80
99
|
|
|
81
|
-
The installation may take 10 to 30 seconds to install. The installation time may vary and take up to several minutes, depending on the network speed, computer performance, and virtual environment settings.
|
|
82
100
|
|
|
83
|
-
##
|
|
84
|
-
|
|
85
|
-
In your SDL script, use `ivoryos(__name__)`.
|
|
101
|
+
## Quick start
|
|
102
|
+
In your SDL script,
|
|
86
103
|
```python
|
|
87
104
|
import ivoryos
|
|
88
105
|
|
|
89
106
|
ivoryos.run(__name__)
|
|
90
107
|
```
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
Login: Create an account (local DB, bcrypt password)
|
|
109
|
+
|
|
110
|
+
----
|
|
111
|
+
## Features
|
|
112
|
+
### Direct control:
|
|
113
|
+
direct function calling _Devices_ tab
|
|
114
|
+
### Workflows
|
|
96
115
|
- **Design Editor**: drag/add function to canvas in _Design_ tab. click `Compile and Run` button to go to the execution configuration page
|
|
97
116
|
- **Execution Config**: configure iteration methods and parameters in _Compile/Run_ tab.
|
|
98
117
|
- **Design Library**: manage workflow scripts in _Library_ tab.
|
|
99
118
|
- **Workflow Data**: Execution records are in _Data_ tab.
|
|
119
|
+
### Offline mode
|
|
120
|
+
after one successful connection, a blueprint will be automatically saved and made accessible without hardware connection. In a new Python script in the same directory, use `ivoryos.run()` to start offline mode.
|
|
100
121
|
|
|
101
|
-
[//]: # ()
|
|
102
|
-
|
|
103
|
-
[//]: # ()
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
### Additional settings
|
|
107
|
-
[//]: # (#### AI assistant)
|
|
108
|
-
|
|
109
|
-
[//]: # (To streamline the experimental design on SDLs, we also integrate Large Language Models (LLMs) to interpret the inspected functions and generate code according to task descriptions.)
|
|
110
|
-
|
|
111
|
-
[//]: # ()
|
|
112
|
-
[//]: # (#### Enable LLMs with [OpenAI API](https://github.com/openai/openai-python))
|
|
113
|
-
|
|
114
|
-
[//]: # (1. Create a `.env` file for `OPENAI_API_KEY`)
|
|
115
|
-
|
|
116
|
-
[//]: # (```)
|
|
117
|
-
|
|
118
|
-
[//]: # (OPENAI_API_KEY="Your API Key")
|
|
119
|
-
|
|
120
|
-
[//]: # (```)
|
|
121
|
-
|
|
122
|
-
[//]: # (2. In your SDL script, define model, you can use any GPT models.)
|
|
123
|
-
|
|
124
|
-
[//]: # ()
|
|
125
|
-
[//]: # (```python)
|
|
126
|
-
|
|
127
|
-
[//]: # (ivoryos.run(__name__, model="gpt-3.5-turbo"))
|
|
128
122
|
|
|
129
|
-
[//]: # (```)
|
|
130
123
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
[//]: # (1. Download Ollama.)
|
|
135
|
-
|
|
136
|
-
[//]: # (2. pull models from Ollama)
|
|
137
|
-
|
|
138
|
-
[//]: # (3. In your SDL script, define LLM server and model, you can use any models available on Ollama.)
|
|
139
|
-
|
|
140
|
-
[//]: # ()
|
|
141
|
-
[//]: # (```python)
|
|
142
|
-
|
|
143
|
-
[//]: # (ivoryos.run(__name__, llm_server="localhost", model="llama3.1"))
|
|
144
|
-
|
|
145
|
-
[//]: # (```)
|
|
146
|
-
|
|
147
|
-
#### Add additional logger(s)
|
|
124
|
+
### Logging
|
|
125
|
+
Add single or multiple loggers:
|
|
148
126
|
```python
|
|
149
127
|
ivoryos.run(__name__, logger="logger name")
|
|
150
|
-
```
|
|
151
|
-
or
|
|
152
|
-
```python
|
|
153
128
|
ivoryos.run(__name__, logger=["logger 1", "logger 2"])
|
|
154
129
|
```
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
130
|
+
### Directory Structure
|
|
131
|
+
|
|
132
|
+
Created automatically on first run:
|
|
133
|
+
- **`ivoryos_data/`**:
|
|
134
|
+
- **`ivoryos_data/config_csv/`**: Batch configuration `csv`
|
|
135
|
+
- **`ivoryos_data/pseudo_deck/`**: Offline deck `.pkl`
|
|
136
|
+
- **`ivoryos_data/results/`**: Execution results
|
|
137
|
+
- **`ivoryos_data/scripts/`**: Compiled workflows Python scripts
|
|
138
|
+
- **`default.log`**: Application logs
|
|
139
|
+
- **`ivoryos.db`**: Local database
|
|
140
|
+
---
|
|
161
141
|
## Demo
|
|
162
|
-
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py)
|
|
163
|
-
addresses will be available on terminal.
|
|
142
|
+
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py)
|
|
164
143
|
```Python
|
|
165
144
|
ivoryos.run(__name__)
|
|
166
145
|
```
|
|
@@ -169,35 +148,19 @@ ivoryos.run(__name__)
|
|
|
169
148
|
* Running on http://127.0.0.1:8000
|
|
170
149
|
* Running on http://0.0.0.0:8000
|
|
171
150
|
|
|
172
|
-
|
|
173
|
-

|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
### Directory structure
|
|
177
|
-
|
|
178
|
-
When you run the application for the first time, it will automatically create the following folders and files in the same directory:
|
|
179
|
-
|
|
180
|
-
- **`ivoryos_data/`**: Main directory for application-related data.
|
|
181
|
-
- **`ivoryos_data/config_csv/`**: Contains iteration configuration files in CSV format.
|
|
182
|
-
- **`ivoryos_data/llm_output/`**: Stores raw prompt generated for the large language model.
|
|
183
|
-
- **`ivoryos_data/pseudo_deck/`**: Contains pseudo-deck `.pkl` files for offline access.
|
|
184
|
-
- **`ivoryos_data/results/`**: Used for storing results or outputs during workflow execution.
|
|
185
|
-
- **`ivoryos_data/scripts/`**: Holds Python scripts compiled from the visual programming script design.
|
|
186
|
-
|
|
187
|
-
- **`default.log`**: Log file that captures application logs.
|
|
188
|
-
- **`ivoryos.db`**: Database file that stores application data locally.
|
|
189
|
-
|
|
151
|
+
---
|
|
190
152
|
|
|
191
153
|
## Roadmap
|
|
192
154
|
|
|
193
155
|
- [x] Allow plugin pages ✅
|
|
194
156
|
- [x] pause, resume, abort current and pending workflows ✅
|
|
195
|
-
- [
|
|
196
|
-
- [x] show line number option ✅
|
|
157
|
+
- [ ] dropdown input
|
|
197
158
|
- [ ] snapshot version control
|
|
198
159
|
- [ ] optimizer-agnostic
|
|
199
160
|
- [ ] check batch-config file compatibility
|
|
200
161
|
|
|
162
|
+
---
|
|
163
|
+
|
|
201
164
|
## Citing
|
|
202
165
|
|
|
203
166
|
If you find this project useful, please consider citing the following manuscript:
|
|
@@ -233,8 +196,6 @@ For an additional perspective related to the development of the tool, please see
|
|
|
233
196
|
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
234
197
|
}
|
|
235
198
|
```
|
|
236
|
-
|
|
237
|
-
##
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
Authors acknowledge Telescope Innovations, Hein Lab members for their valuable suggestions and contributions.
|
|
199
|
+
---
|
|
200
|
+
## Acknowledgements
|
|
201
|
+
Authors acknowledge Telescope Innovations Corp., Hein Lab members for their valuable suggestions and contributions.
|
ivoryos-1.2.5/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
[](https://ivoryos.readthedocs.io/en/latest/?badge=latest)
|
|
2
|
+
[](https://pypi.org/project/ivoryos/)
|
|
3
|
+

|
|
4
|
+
[](https://youtu.be/dFfJv9I2-1g)
|
|
5
|
+
[](https://youtu.be/flr5ydiE96s)
|
|
6
|
+
[](https://www.nature.com/articles/s41467-025-60514-w)
|
|
7
|
+
[](https://discord.gg/AX5P9EdGVX)
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
# ivoryOS: interoperable Web UI for self-driving laboratories (SDLs)
|
|
11
|
+
A **plug-and-play** web interface for flexible SDLs
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of Contents
|
|
16
|
+
- [Description](#description)
|
|
17
|
+
- [System requirements](#system-requirements)
|
|
18
|
+
- [Installation](#installation)
|
|
19
|
+
- [Quick Start](#quick-start)
|
|
20
|
+
- [Features](#features)
|
|
21
|
+
- [Demo](#demo)
|
|
22
|
+
- [Roadmap](#roadmap)
|
|
23
|
+
- [Acknowledgements](#acknowledgements)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
## Description
|
|
27
|
+
Building UIs for SDLs is challenging because flexibility and modularity make them unpredictable — yet accessibility is essential for **democratisation** of AI-driven scientific discovery.
|
|
28
|
+
|
|
29
|
+
**IvoryOS** bridges the gap by:
|
|
30
|
+
- Dynamically inspecting initialized Python modules (hardware APIs, high-level functions, or workflows)
|
|
31
|
+
- Automatically displaying functions and parameters in a web UI
|
|
32
|
+
- Allowing users to **design**, **manage**, and **execute** experimental workflows with minimal changes to existing scripts
|
|
33
|
+
- Providing natural language support for workflow design and execution, check [IvoryOS MCP](https://gitlab.com/heingroup/ivoryos-suite/ivoryos-mcp) for more details.
|
|
34
|
+
|
|
35
|
+
----
|
|
36
|
+
## System Requirements
|
|
37
|
+
|
|
38
|
+
**Platforms:** Compatible with Linux, macOS, and Windows (developed/tested on Windows).
|
|
39
|
+
**Python:**
|
|
40
|
+
- Recommended: Python ≥3.10
|
|
41
|
+
- Minimum: Python ≥3.7 (without Ax optimizer support)
|
|
42
|
+
|
|
43
|
+
**Core Dependencies:**
|
|
44
|
+
<details>
|
|
45
|
+
<summary>Click to expand</summary>
|
|
46
|
+
|
|
47
|
+
- bcrypt~=4.0
|
|
48
|
+
- Flask-Login~=0.6
|
|
49
|
+
- Flask-Session~=0.8
|
|
50
|
+
- Flask-SocketIO~=5.3
|
|
51
|
+
- Flask-SQLAlchemy~=3.1
|
|
52
|
+
- SQLAlchemy-Utils~=0.41
|
|
53
|
+
- Flask-WTF~=1.2
|
|
54
|
+
- python-dotenv==1.0.1
|
|
55
|
+
|
|
56
|
+
**Optional:**
|
|
57
|
+
- ax-platform (≥1.0, Python≥3.10)
|
|
58
|
+
- baybe
|
|
59
|
+
</details>
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
From PyPI:
|
|
66
|
+
```bash
|
|
67
|
+
pip install ivoryos
|
|
68
|
+
```
|
|
69
|
+
From source:
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://gitlab.com/heingroup/ivoryos.git
|
|
72
|
+
cd ivoryos
|
|
73
|
+
pip install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Quick start
|
|
78
|
+
In your SDL script,
|
|
79
|
+
```python
|
|
80
|
+
import ivoryos
|
|
81
|
+
|
|
82
|
+
ivoryos.run(__name__)
|
|
83
|
+
```
|
|
84
|
+
Login: Create an account (local DB, bcrypt password)
|
|
85
|
+
|
|
86
|
+
----
|
|
87
|
+
## Features
|
|
88
|
+
### Direct control:
|
|
89
|
+
direct function calling _Devices_ tab
|
|
90
|
+
### Workflows
|
|
91
|
+
- **Design Editor**: drag/add function to canvas in _Design_ tab. click `Compile and Run` button to go to the execution configuration page
|
|
92
|
+
- **Execution Config**: configure iteration methods and parameters in _Compile/Run_ tab.
|
|
93
|
+
- **Design Library**: manage workflow scripts in _Library_ tab.
|
|
94
|
+
- **Workflow Data**: Execution records are in _Data_ tab.
|
|
95
|
+
### Offline mode
|
|
96
|
+
after one successful connection, a blueprint will be automatically saved and made accessible without hardware connection. In a new Python script in the same directory, use `ivoryos.run()` to start offline mode.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
### Logging
|
|
101
|
+
Add single or multiple loggers:
|
|
102
|
+
```python
|
|
103
|
+
ivoryos.run(__name__, logger="logger name")
|
|
104
|
+
ivoryos.run(__name__, logger=["logger 1", "logger 2"])
|
|
105
|
+
```
|
|
106
|
+
### Directory Structure
|
|
107
|
+
|
|
108
|
+
Created automatically on first run:
|
|
109
|
+
- **`ivoryos_data/`**:
|
|
110
|
+
- **`ivoryos_data/config_csv/`**: Batch configuration `csv`
|
|
111
|
+
- **`ivoryos_data/pseudo_deck/`**: Offline deck `.pkl`
|
|
112
|
+
- **`ivoryos_data/results/`**: Execution results
|
|
113
|
+
- **`ivoryos_data/scripts/`**: Compiled workflows Python scripts
|
|
114
|
+
- **`default.log`**: Application logs
|
|
115
|
+
- **`ivoryos.db`**: Local database
|
|
116
|
+
---
|
|
117
|
+
## Demo
|
|
118
|
+
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py)
|
|
119
|
+
```Python
|
|
120
|
+
ivoryos.run(__name__)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
* Running on all addresses (0.0.0.0)
|
|
124
|
+
* Running on http://127.0.0.1:8000
|
|
125
|
+
* Running on http://0.0.0.0:8000
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Roadmap
|
|
130
|
+
|
|
131
|
+
- [x] Allow plugin pages ✅
|
|
132
|
+
- [x] pause, resume, abort current and pending workflows ✅
|
|
133
|
+
- [ ] dropdown input
|
|
134
|
+
- [ ] snapshot version control
|
|
135
|
+
- [ ] optimizer-agnostic
|
|
136
|
+
- [ ] check batch-config file compatibility
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Citing
|
|
141
|
+
|
|
142
|
+
If you find this project useful, please consider citing the following manuscript:
|
|
143
|
+
|
|
144
|
+
> 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).
|
|
145
|
+
|
|
146
|
+
```bibtex
|
|
147
|
+
@article{zhang_et_al_2025,
|
|
148
|
+
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},
|
|
149
|
+
title = {{IvoryOS}: an interoperable web interface for orchestrating {Python-based} self-driving laboratories},
|
|
150
|
+
journal = {Nature Communications},
|
|
151
|
+
year = {2025},
|
|
152
|
+
volume = {16},
|
|
153
|
+
number = {1},
|
|
154
|
+
pages = {5182},
|
|
155
|
+
doi = {10.1038/s41467-025-60514-w},
|
|
156
|
+
url = {https://doi.org/10.1038/s41467-025-60514-w}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
For an additional perspective related to the development of the tool, please see:
|
|
161
|
+
|
|
162
|
+
> 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).
|
|
163
|
+
|
|
164
|
+
```bibtex
|
|
165
|
+
@misc{zhang_hein_2025,
|
|
166
|
+
author = {Wenyu Zhang and Jason Hein},
|
|
167
|
+
title = {Behind {IvoryOS}: Empowering Scientists to Harness Self-Driving Labs for Accelerated Discovery},
|
|
168
|
+
howpublished = {Springer Nature Research Communities},
|
|
169
|
+
year = {2025},
|
|
170
|
+
month = {Jun},
|
|
171
|
+
day = {18},
|
|
172
|
+
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
---
|
|
176
|
+
## Acknowledgements
|
|
177
|
+
Authors acknowledge Telescope Innovations Corp., Hein Lab members for their valuable suggestions and contributions.
|
|
@@ -6,7 +6,7 @@ from typing import Union
|
|
|
6
6
|
from flask import Flask, redirect, url_for, g, Blueprint, session
|
|
7
7
|
from flask_login import AnonymousUserMixin
|
|
8
8
|
|
|
9
|
-
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
9
|
+
# sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
10
10
|
|
|
11
11
|
from ivoryos.config import Config, get_config
|
|
12
12
|
from ivoryos.routes.auth.auth import auth, login_manager
|
|
@@ -25,7 +25,7 @@ from ivoryos.utils.global_config import GlobalConfig
|
|
|
25
25
|
from ivoryos.optimizer.registry import OPTIMIZER_REGISTRY
|
|
26
26
|
from ivoryos.utils.script_runner import ScriptRunner
|
|
27
27
|
from ivoryos.version import __version__ as ivoryos_version
|
|
28
|
-
from importlib.metadata import entry_points
|
|
28
|
+
# from importlib.metadata import entry_points
|
|
29
29
|
|
|
30
30
|
global_config = GlobalConfig()
|
|
31
31
|
from sqlalchemy import event
|
|
@@ -210,22 +210,22 @@ def run(module=None, host="0.0.0.0", port=None, debug=None, llm_server=None, mod
|
|
|
210
210
|
# return app
|
|
211
211
|
|
|
212
212
|
|
|
213
|
-
def load_installed_plugins(app, socketio):
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
213
|
+
# def load_installed_plugins(app, socketio):
|
|
214
|
+
# """
|
|
215
|
+
# Dynamically load installed plugins and attach Flask-SocketIO.
|
|
216
|
+
# """
|
|
217
|
+
# plugin_names = []
|
|
218
|
+
# for entry_point in entry_points().get("ivoryos.plugins", []):
|
|
219
|
+
# plugin = entry_point.load()
|
|
220
|
+
#
|
|
221
|
+
# # If the plugin has an `init_socketio()` function, pass socketio
|
|
222
|
+
# if hasattr(plugin, 'init_socketio'):
|
|
223
|
+
# plugin.init_socketio(socketio)
|
|
224
|
+
#
|
|
225
|
+
# plugin_names.append(entry_point.name)
|
|
226
|
+
# app.register_blueprint(getattr(plugin, entry_point.name), url_prefix=f"{url_prefix}/{entry_point.name}")
|
|
227
|
+
#
|
|
228
|
+
# return plugin_names
|
|
229
229
|
|
|
230
230
|
|
|
231
231
|
def load_plugins(blueprints: Union[list, Blueprint], app, socketio):
|
|
@@ -173,7 +173,7 @@ def update_ui_state():
|
|
|
173
173
|
autofill = data.get("autofill", False)
|
|
174
174
|
session['autofill'] = autofill
|
|
175
175
|
_, forms = _create_forms(instrument, script, autofill)
|
|
176
|
-
rendered_html = render_template("components/
|
|
176
|
+
rendered_html = render_template("components/actions_panel.html", forms=forms, script=script, instrument=instrument)
|
|
177
177
|
return jsonify({"html": rendered_html})
|
|
178
178
|
|
|
179
179
|
if "deck_name" in data:
|
|
@@ -474,7 +474,7 @@ class Script(db.Model):
|
|
|
474
474
|
"""
|
|
475
475
|
configure, config_type = self.config(stype)
|
|
476
476
|
|
|
477
|
-
configure = [param + f":{param_type}" if not param_type == "any" else
|
|
477
|
+
configure = [param + f":{param_type}" if not param_type == "any" else param for param, param_type in
|
|
478
478
|
config_type.items()]
|
|
479
479
|
|
|
480
480
|
script_type = f"_{stype}" if stype != "script" else ""
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Union, Any
|
|
3
|
+
try:
|
|
4
|
+
from typing import get_origin, get_args
|
|
5
|
+
except ImportError:
|
|
6
|
+
# For Python versions = 3.7, use typing_extensions
|
|
7
|
+
from typing_extensions import get_origin, get_args
|
|
3
8
|
|
|
4
9
|
from wtforms.fields.choices import SelectField
|
|
5
10
|
from wtforms.fields.core import Field
|
|
@@ -188,44 +188,52 @@ class ScriptRunner:
|
|
|
188
188
|
# _func_str = script.compile()
|
|
189
189
|
# step_list_dict: dict = script.convert_to_lines(_func_str)
|
|
190
190
|
self._emit_progress(socketio, 1)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
191
|
+
filename = None
|
|
192
|
+
error_flag = False
|
|
193
|
+
# create a new run entry in the database
|
|
194
|
+
try:
|
|
195
|
+
with current_app.app_context():
|
|
196
|
+
run = WorkflowRun(name=script.name or "untitled", platform=script.deck or "deck",start_time=datetime.now())
|
|
197
|
+
db.session.add(run)
|
|
198
|
+
db.session.commit()
|
|
199
|
+
run_id = run.id # Save the ID
|
|
200
|
+
global_config.runner_status = {"id":run_id, "type": "workflow"}
|
|
201
|
+
|
|
202
|
+
# Run "prep" section once
|
|
203
|
+
self._run_actions(script, section_name="prep", logger=logger, socketio=socketio, run_id=run_id)
|
|
204
|
+
output_list = []
|
|
205
|
+
_, arg_type = script.config("script")
|
|
206
|
+
_, return_list = script.config_return()
|
|
207
|
+
# Run "script" section multiple times
|
|
208
|
+
if repeat_count:
|
|
209
|
+
self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
|
|
210
|
+
run_name, return_list, compiled, logger, socketio,
|
|
211
|
+
history, output_path, run_id=run_id, optimizer=optimizer)
|
|
212
|
+
elif config:
|
|
213
|
+
self._run_config_section(config, arg_type, output_list, script, run_name, logger,
|
|
214
|
+
socketio, run_id=run_id, compiled=compiled)
|
|
215
|
+
# Run "cleanup" section once
|
|
216
|
+
self._run_actions(script, section_name="cleanup", logger=logger, socketio=socketio,run_id=run_id)
|
|
217
|
+
# Reset the running flag when done
|
|
218
|
+
|
|
219
|
+
# Save results if necessary
|
|
220
|
+
|
|
221
|
+
if not script.python_script and output_list:
|
|
222
|
+
filename = self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
|
|
223
|
+
self._emit_progress(socketio, 100)
|
|
224
|
+
|
|
225
|
+
except Exception as e:
|
|
226
|
+
logger.error(f"Error during script execution: {e.__str__()}")
|
|
227
|
+
error_flag = True
|
|
228
|
+
finally:
|
|
218
229
|
self.lock.release()
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
run.end_time = datetime.now()
|
|
227
|
-
run.data_path = filename
|
|
228
|
-
db.session.commit()
|
|
230
|
+
with current_app.app_context():
|
|
231
|
+
run = db.session.get(WorkflowRun, run_id)
|
|
232
|
+
run.end_time = datetime.now()
|
|
233
|
+
run.output_file = filename
|
|
234
|
+
run.run_error = error_flag
|
|
235
|
+
db.session.commit()
|
|
236
|
+
|
|
229
237
|
|
|
230
238
|
def _run_actions(self, script, section_name="", logger=None, socketio=None, run_id=None):
|
|
231
239
|
_func_str = script.python_script or script.compile()
|
|
@@ -254,7 +262,7 @@ class ScriptRunner:
|
|
|
254
262
|
logger.info(f'Stopping execution during {run_name}: {i + 1}/{len(config)}')
|
|
255
263
|
break
|
|
256
264
|
logger.info(f'Executing {i + 1} of {len(config)} with kwargs = {kwargs}')
|
|
257
|
-
progress = (i + 1) * 100 / len(config)
|
|
265
|
+
progress = ((i + 1) * 100 / len(config)) - 0.1
|
|
258
266
|
self._emit_progress(socketio, progress)
|
|
259
267
|
# fname = f"{run_name}_script"
|
|
260
268
|
# function = self.globals_dict[fname]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.2.5"
|