ivoryos 1.3.2__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.2/ivoryos.egg-info → ivoryos-1.4.6}/PKG-INFO +108 -47
- ivoryos-1.3.2/PKG-INFO → ivoryos-1.4.6/README.md +89 -67
- ivoryos-1.4.6/docs/source/conf.py +84 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/__init__.py +6 -2
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/app.py +31 -6
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/optimizer/ax_optimizer.py +55 -28
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/optimizer/base_optimizer.py +20 -1
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/optimizer/baybe_optimizer.py +27 -17
- ivoryos-1.4.6/ivoryos/optimizer/nimo_optimizer.py +173 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/optimizer/registry.py +3 -1
- {ivoryos-1.3.2 → 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.2 → ivoryos-1.4.6}/ivoryos/routes/control/control.py +2 -2
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/control/control_new_device.py +21 -11
- {ivoryos-1.3.2 → 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.2 → ivoryos-1.4.6}/ivoryos/routes/data/templates/workflow_view.html +14 -5
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/design.py +46 -4
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/design_step.py +40 -14
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/canvas_main.html +6 -1
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/edit_action_form.html +18 -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.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/experiment_builder.html +3 -0
- {ivoryos-1.3.2 → 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.2 → 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.2 → ivoryos-1.4.6}/ivoryos/server.py +35 -23
- {ivoryos-1.3.2 → 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.2 → ivoryos-1.4.6}/ivoryos/static/js/sortable_design.js +1 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/js/ui_state.js +1 -3
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/templates/base.html +61 -2
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/bo_campaign.py +18 -17
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/client_proxy.py +11 -6
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/db_models.py +204 -49
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/decorators.py +1 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/form.py +40 -16
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/global_config.py +11 -0
- ivoryos-1.4.6/ivoryos/utils/nest_script.py +314 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/py_to_json.py +54 -10
- ivoryos-1.4.6/ivoryos/utils/script_runner.py +770 -0
- ivoryos-1.4.6/ivoryos/utils/task_runner.py +131 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/utils.py +24 -3
- ivoryos-1.4.6/ivoryos/version.py +1 -0
- ivoryos-1.4.6/ivoryos.egg-info/PKG-INFO +262 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos.egg-info/SOURCES.txt +18 -2
- ivoryos-1.4.6/ivoryos.egg-info/requires.txt +35 -0
- ivoryos-1.4.6/ivoryos.egg-info/top_level.txt +4 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/pyproject.toml +18 -1
- 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.2/README.md +0 -177
- ivoryos-1.3.2/ivoryos/routes/api/api.py +0 -57
- ivoryos-1.3.2/ivoryos/routes/data/templates/components/step_card.html +0 -42
- ivoryos-1.3.2/ivoryos/routes/design/templates/components/python_code_overlay.html +0 -39
- ivoryos-1.3.2/ivoryos/routes/execute/templates/components/logging_panel.html +0 -31
- ivoryos-1.3.2/ivoryos/routes/execute/templates/components/run_tabs.html +0 -17
- ivoryos-1.3.2/ivoryos/routes/execute/templates/components/tab_bayesian.html +0 -398
- ivoryos-1.3.2/ivoryos/routes/execute/templates/components/tab_configuration.html +0 -98
- ivoryos-1.3.2/ivoryos/routes/execute/templates/experiment_run.html +0 -294
- ivoryos-1.3.2/ivoryos/routes/main/main.py +0 -42
- ivoryos-1.3.2/ivoryos/static/js/action_handlers.js +0 -213
- ivoryos-1.3.2/ivoryos/utils/script_runner.py +0 -460
- ivoryos-1.3.2/ivoryos/utils/task_runner.py +0 -89
- ivoryos-1.3.2/ivoryos/version.py +0 -1
- ivoryos-1.3.2/ivoryos.egg-info/requires.txt +0 -15
- ivoryos-1.3.2/ivoryos.egg-info/top_level.txt +0 -1
- {ivoryos-1.3.2 → ivoryos-1.4.6}/LICENSE +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/config.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/auth/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/auth/templates/login.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/auth/templates/signup.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/control/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/control/control_file.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/control/templates/controllers.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/control/templates/controllers_new.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/control/utils.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/data/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/data/templates/workflow_database.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/design_file.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/action_form.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/actions_panel.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/autofill_toggle.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/canvas.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/canvas_footer.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/canvas_header.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/deck_selector.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/instruments_panel.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/drop_modal.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/json_modal.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/new_script_modal.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/rename_modal.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals/saveas_modal.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/modals.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/sidebar.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/design/templates/components/text_to_code_panel.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/execute/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/execute/execute_file.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/execute/templates/components/error_modal.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/execute/templates/components/progress_panel.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/execute/templates/components/run_panel.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/library/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/library/library.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/library/templates/library.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/main/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/main/templates/help.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/routes/main/templates/home.html +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/favicon.ico +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/gui_annotation/Slide1.png +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/gui_annotation/Slide2.PNG +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/js/db_delete.js +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/js/overlay.js +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/js/script_metadata.js +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/js/socket_handler.js +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/js/sortable_card.js +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/logo.webp +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/static/style.css +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/__init__.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/llm_agent.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos/utils/serilize.py +0 -0
- {ivoryos-1.3.2 → ivoryos-1.4.6}/ivoryos.egg-info/dependency_links.txt +0 -0
- {ivoryos-1.3.2 → 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
|
|
@@ -9,6 +9,7 @@ Requires-Python: >=3.7
|
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Requires-Dist: bcrypt
|
|
12
|
+
Requires-Dist: Flask[async]
|
|
12
13
|
Requires-Dist: Flask-Login
|
|
13
14
|
Requires-Dist: Flask-Session
|
|
14
15
|
Requires-Dist: Flask-SocketIO
|
|
@@ -16,10 +17,24 @@ Requires-Dist: Flask-SQLAlchemy
|
|
|
16
17
|
Requires-Dist: Flask-WTF
|
|
17
18
|
Requires-Dist: SQLAlchemy-Utils
|
|
18
19
|
Requires-Dist: python-dotenv
|
|
20
|
+
Requires-Dist: pandas
|
|
19
21
|
Requires-Dist: astor; python_version < "3.9"
|
|
20
|
-
Provides-Extra: optimizer
|
|
21
|
-
Requires-Dist: ax-platform; extra == "optimizer"
|
|
22
|
-
|
|
22
|
+
Provides-Extra: optimizer-ax
|
|
23
|
+
Requires-Dist: ax-platform; extra == "optimizer-ax"
|
|
24
|
+
Provides-Extra: optimizer-baybe
|
|
25
|
+
Requires-Dist: baybe; extra == "optimizer-baybe"
|
|
26
|
+
Provides-Extra: optimizer-nimo
|
|
27
|
+
Requires-Dist: nimo; extra == "optimizer-nimo"
|
|
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"
|
|
32
|
+
Provides-Extra: doc
|
|
33
|
+
Requires-Dist: sphinx; extra == "doc"
|
|
34
|
+
Requires-Dist: sphinx-rtd-theme; extra == "doc"
|
|
35
|
+
Requires-Dist: sphinxcontrib-httpdomain; extra == "doc"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest; extra == "dev"
|
|
23
38
|
Dynamic: license-file
|
|
24
39
|
|
|
25
40
|
[](https://ivoryos.readthedocs.io/en/latest/?badge=latest)
|
|
@@ -28,33 +43,35 @@ Dynamic: license-file
|
|
|
28
43
|
[](https://youtu.be/dFfJv9I2-1g)
|
|
29
44
|
[](https://youtu.be/flr5ydiE96s)
|
|
30
45
|
[](https://www.nature.com/articles/s41467-025-60514-w)
|
|
31
|
-
[](https://discord.gg/AX5P9EdGVX)
|
|
32
47
|
|
|
33
|
-

|
|
49
|
+
|
|
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
|
+

|
|
36
56
|
|
|
37
57
|
---
|
|
38
58
|
|
|
39
59
|
## Table of Contents
|
|
40
|
-
- [
|
|
60
|
+
- [What IvoryOS does](#what-ivoryos-does)
|
|
41
61
|
- [System requirements](#system-requirements)
|
|
42
62
|
- [Installation](#installation)
|
|
43
|
-
- [Quick Start](#quick-start)
|
|
44
63
|
- [Features](#features)
|
|
45
64
|
- [Demo](#demo)
|
|
46
65
|
- [Roadmap](#roadmap)
|
|
66
|
+
- [Contributing](#contributing)
|
|
47
67
|
- [Acknowledgements](#acknowledgements)
|
|
48
68
|
|
|
49
69
|
---
|
|
50
|
-
##
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
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.
|
|
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.
|
|
58
75
|
|
|
59
76
|
----
|
|
60
77
|
## System Requirements
|
|
@@ -76,10 +93,13 @@ Building UIs for SDLs is challenging because flexibility and modularity make the
|
|
|
76
93
|
- SQLAlchemy-Utils~=0.41
|
|
77
94
|
- Flask-WTF~=1.2
|
|
78
95
|
- python-dotenv==1.0.1
|
|
96
|
+
- pandas
|
|
79
97
|
|
|
80
98
|
**Optional:**
|
|
81
|
-
- ax-platform
|
|
82
|
-
- baybe
|
|
99
|
+
- ax-platform==1.1.2
|
|
100
|
+
- baybe==0.14.0
|
|
101
|
+
- nimo
|
|
102
|
+
- slack-sdk
|
|
83
103
|
</details>
|
|
84
104
|
|
|
85
105
|
---
|
|
@@ -90,22 +110,31 @@ From PyPI:
|
|
|
90
110
|
```bash
|
|
91
111
|
pip install ivoryos
|
|
92
112
|
```
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
[//]: # (```)
|
|
99
125
|
|
|
100
126
|
|
|
101
127
|
## Quick start
|
|
102
|
-
In your
|
|
128
|
+
In your script, where you initialize or import your robot:
|
|
103
129
|
```python
|
|
130
|
+
my_robot = Robot()
|
|
131
|
+
|
|
104
132
|
import ivoryos
|
|
105
133
|
|
|
106
134
|
ivoryos.run(__name__)
|
|
107
135
|
```
|
|
108
|
-
|
|
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!
|
|
109
138
|
|
|
110
139
|
----
|
|
111
140
|
## Features
|
|
@@ -127,42 +156,72 @@ Add single or multiple loggers:
|
|
|
127
156
|
ivoryos.run(__name__, logger="logger name")
|
|
128
157
|
ivoryos.run(__name__, logger=["logger 1", "logger 2"])
|
|
129
158
|
```
|
|
159
|
+
### Human-in-the-loop
|
|
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:
|
|
162
|
+
```python
|
|
163
|
+
|
|
164
|
+
def slack_bot(msg: str = "Hi"):
|
|
165
|
+
"""
|
|
166
|
+
a function that can be used as a notification handler function("msg")
|
|
167
|
+
:param msg: message to send
|
|
168
|
+
"""
|
|
169
|
+
from slack_sdk import WebClient
|
|
170
|
+
|
|
171
|
+
slack_token = "your slack token"
|
|
172
|
+
client = WebClient(token=slack_token)
|
|
173
|
+
|
|
174
|
+
my_user_id = "your user id" # replace with your actual Slack user ID
|
|
175
|
+
|
|
176
|
+
client.chat_postMessage(channel=my_user_id, text=msg)
|
|
177
|
+
|
|
178
|
+
import ivoryos
|
|
179
|
+
ivoryos.run(__name__, notification_handler=slack_bot)
|
|
180
|
+
```
|
|
181
|
+
|
|
130
182
|
### Directory Structure
|
|
131
183
|
|
|
132
|
-
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
|
+
|
|
133
188
|
- **`ivoryos_data/`**:
|
|
134
|
-
- **`
|
|
135
|
-
- **`
|
|
136
|
-
- **`
|
|
137
|
-
- **`
|
|
138
|
-
- **`default.log`**: Application logs
|
|
139
|
-
- **`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
|
+
|
|
140
197
|
---
|
|
141
|
-
## Demo
|
|
142
|
-
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py)
|
|
143
|
-
```Python
|
|
144
|
-
ivoryos.run(__name__)
|
|
145
|
-
```
|
|
146
198
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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)
|
|
150
202
|
|
|
151
203
|
---
|
|
152
204
|
|
|
153
205
|
## Roadmap
|
|
154
206
|
|
|
155
|
-
- [x] Allow plugin pages ✅
|
|
156
|
-
- [x] pause, resume, abort current and pending workflows ✅
|
|
157
207
|
- [ ] dropdown input
|
|
158
208
|
- [ ] snapshot version control
|
|
159
|
-
- [ ] optimizer-agnostic
|
|
160
209
|
- [ ] check batch-config file compatibility
|
|
161
210
|
|
|
162
211
|
---
|
|
163
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
|
+
|
|
164
220
|
## Citing
|
|
165
221
|
|
|
222
|
+
<details>
|
|
223
|
+
<summary>Click to see citations</summary>
|
|
224
|
+
|
|
166
225
|
If you find this project useful, please consider citing the following manuscript:
|
|
167
226
|
|
|
168
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).
|
|
@@ -196,6 +255,8 @@ For an additional perspective related to the development of the tool, please see
|
|
|
196
255
|
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
197
256
|
}
|
|
198
257
|
```
|
|
258
|
+
</details>
|
|
259
|
+
|
|
199
260
|
---
|
|
200
261
|
## Acknowledgements
|
|
201
|
-
Authors acknowledge Telescope Innovations Corp., Hein Lab members for their valuable suggestions and contributions.
|
|
262
|
+
Authors acknowledge Telescope Innovations Corp., UBC Hein Lab, and Acceleration Consortium members for their valuable suggestions and contributions.
|
|
@@ -1,60 +1,38 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ivoryos
|
|
3
|
-
Version: 1.3.2
|
|
4
|
-
Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
|
|
5
|
-
Author-email: Ivory Zhang <ivoryzhang@chem.ubc.ca>
|
|
6
|
-
License: MIT
|
|
7
|
-
Project-URL: Homepage, https://gitlab.com/heingroup/ivoryos
|
|
8
|
-
Requires-Python: >=3.7
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Dist: bcrypt
|
|
12
|
-
Requires-Dist: Flask-Login
|
|
13
|
-
Requires-Dist: Flask-Session
|
|
14
|
-
Requires-Dist: Flask-SocketIO
|
|
15
|
-
Requires-Dist: Flask-SQLAlchemy
|
|
16
|
-
Requires-Dist: Flask-WTF
|
|
17
|
-
Requires-Dist: SQLAlchemy-Utils
|
|
18
|
-
Requires-Dist: python-dotenv
|
|
19
|
-
Requires-Dist: astor; python_version < "3.9"
|
|
20
|
-
Provides-Extra: optimizer
|
|
21
|
-
Requires-Dist: ax-platform; extra == "optimizer"
|
|
22
|
-
Requires-Dist: baybe; extra == "optimizer"
|
|
23
|
-
Dynamic: license-file
|
|
24
|
-
|
|
25
1
|
[](https://ivoryos.readthedocs.io/en/latest/?badge=latest)
|
|
26
2
|
[](https://pypi.org/project/ivoryos/)
|
|
27
3
|

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

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

|
|
36
17
|
|
|
37
18
|
---
|
|
38
19
|
|
|
39
20
|
## Table of Contents
|
|
40
|
-
- [
|
|
21
|
+
- [What IvoryOS does](#what-ivoryos-does)
|
|
41
22
|
- [System requirements](#system-requirements)
|
|
42
23
|
- [Installation](#installation)
|
|
43
|
-
- [Quick Start](#quick-start)
|
|
44
24
|
- [Features](#features)
|
|
45
25
|
- [Demo](#demo)
|
|
46
26
|
- [Roadmap](#roadmap)
|
|
27
|
+
- [Contributing](#contributing)
|
|
47
28
|
- [Acknowledgements](#acknowledgements)
|
|
48
29
|
|
|
49
30
|
---
|
|
50
|
-
##
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
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.
|
|
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.
|
|
58
36
|
|
|
59
37
|
----
|
|
60
38
|
## System Requirements
|
|
@@ -76,10 +54,13 @@ Building UIs for SDLs is challenging because flexibility and modularity make the
|
|
|
76
54
|
- SQLAlchemy-Utils~=0.41
|
|
77
55
|
- Flask-WTF~=1.2
|
|
78
56
|
- python-dotenv==1.0.1
|
|
57
|
+
- pandas
|
|
79
58
|
|
|
80
59
|
**Optional:**
|
|
81
|
-
- ax-platform
|
|
82
|
-
- baybe
|
|
60
|
+
- ax-platform==1.1.2
|
|
61
|
+
- baybe==0.14.0
|
|
62
|
+
- nimo
|
|
63
|
+
- slack-sdk
|
|
83
64
|
</details>
|
|
84
65
|
|
|
85
66
|
---
|
|
@@ -90,22 +71,31 @@ From PyPI:
|
|
|
90
71
|
```bash
|
|
91
72
|
pip install ivoryos
|
|
92
73
|
```
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
[//]: # (```)
|
|
99
86
|
|
|
100
87
|
|
|
101
88
|
## Quick start
|
|
102
|
-
In your
|
|
89
|
+
In your script, where you initialize or import your robot:
|
|
103
90
|
```python
|
|
91
|
+
my_robot = Robot()
|
|
92
|
+
|
|
104
93
|
import ivoryos
|
|
105
94
|
|
|
106
95
|
ivoryos.run(__name__)
|
|
107
96
|
```
|
|
108
|
-
|
|
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!
|
|
109
99
|
|
|
110
100
|
----
|
|
111
101
|
## Features
|
|
@@ -127,42 +117,72 @@ Add single or multiple loggers:
|
|
|
127
117
|
ivoryos.run(__name__, logger="logger name")
|
|
128
118
|
ivoryos.run(__name__, logger=["logger 1", "logger 2"])
|
|
129
119
|
```
|
|
120
|
+
### Human-in-the-loop
|
|
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:
|
|
123
|
+
```python
|
|
124
|
+
|
|
125
|
+
def slack_bot(msg: str = "Hi"):
|
|
126
|
+
"""
|
|
127
|
+
a function that can be used as a notification handler function("msg")
|
|
128
|
+
:param msg: message to send
|
|
129
|
+
"""
|
|
130
|
+
from slack_sdk import WebClient
|
|
131
|
+
|
|
132
|
+
slack_token = "your slack token"
|
|
133
|
+
client = WebClient(token=slack_token)
|
|
134
|
+
|
|
135
|
+
my_user_id = "your user id" # replace with your actual Slack user ID
|
|
136
|
+
|
|
137
|
+
client.chat_postMessage(channel=my_user_id, text=msg)
|
|
138
|
+
|
|
139
|
+
import ivoryos
|
|
140
|
+
ivoryos.run(__name__, notification_handler=slack_bot)
|
|
141
|
+
```
|
|
142
|
+
|
|
130
143
|
### Directory Structure
|
|
131
144
|
|
|
132
|
-
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
|
+
|
|
133
149
|
- **`ivoryos_data/`**:
|
|
134
|
-
- **`
|
|
135
|
-
- **`
|
|
136
|
-
- **`
|
|
137
|
-
- **`
|
|
138
|
-
- **`default.log`**: Application logs
|
|
139
|
-
- **`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
|
+
|
|
140
158
|
---
|
|
141
|
-
## Demo
|
|
142
|
-
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py)
|
|
143
|
-
```Python
|
|
144
|
-
ivoryos.run(__name__)
|
|
145
|
-
```
|
|
146
159
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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)
|
|
150
163
|
|
|
151
164
|
---
|
|
152
165
|
|
|
153
166
|
## Roadmap
|
|
154
167
|
|
|
155
|
-
- [x] Allow plugin pages ✅
|
|
156
|
-
- [x] pause, resume, abort current and pending workflows ✅
|
|
157
168
|
- [ ] dropdown input
|
|
158
169
|
- [ ] snapshot version control
|
|
159
|
-
- [ ] optimizer-agnostic
|
|
160
170
|
- [ ] check batch-config file compatibility
|
|
161
171
|
|
|
162
172
|
---
|
|
163
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
|
+
|
|
164
181
|
## Citing
|
|
165
182
|
|
|
183
|
+
<details>
|
|
184
|
+
<summary>Click to see citations</summary>
|
|
185
|
+
|
|
166
186
|
If you find this project useful, please consider citing the following manuscript:
|
|
167
187
|
|
|
168
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).
|
|
@@ -196,6 +216,8 @@ For an additional perspective related to the development of the tool, please see
|
|
|
196
216
|
url = {https://communities.springernature.com/posts/behind-ivoryos-empowering-scientists-to-harness-self-driving-labs-for-accelerated-discovery}
|
|
197
217
|
}
|
|
198
218
|
```
|
|
219
|
+
</details>
|
|
220
|
+
|
|
199
221
|
---
|
|
200
222
|
## Acknowledgements
|
|
201
|
-
Authors acknowledge Telescope Innovations Corp., Hein Lab members for their valuable suggestions and contributions.
|
|
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,8 +1,8 @@
|
|
|
1
|
-
from ivoryos.server import run
|
|
1
|
+
from ivoryos.server import run, global_config
|
|
2
2
|
from ivoryos.optimizer.registry import OPTIMIZER_REGISTRY
|
|
3
3
|
from ivoryos.version import __version__ as ivoryos_version
|
|
4
4
|
from ivoryos.utils.decorators import block, BUILDING_BLOCKS
|
|
5
|
-
from ivoryos.app import app
|
|
5
|
+
from ivoryos.app import app, create_app, socketio, db
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"block",
|
|
@@ -11,4 +11,8 @@ __all__ = [
|
|
|
11
11
|
"run",
|
|
12
12
|
"app",
|
|
13
13
|
"ivoryos_version",
|
|
14
|
+
"create_app",
|
|
15
|
+
"socketio",
|
|
16
|
+
"global_config",
|
|
17
|
+
"db"
|
|
14
18
|
]
|
|
@@ -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)
|
|
@@ -41,7 +39,8 @@ def reset_old_schema(engine, db_dir):
|
|
|
41
39
|
old_workflow_run = 'workflow_runs' in tables
|
|
42
40
|
old_workflow_step = 'workflow_steps' in tables
|
|
43
41
|
|
|
44
|
-
|
|
42
|
+
# v1.3.4 only delete and backup when there is runs but no phases
|
|
43
|
+
if not has_workflow_phase and old_workflow_run:
|
|
45
44
|
print("⚠️ Old workflow database detected! All previous workflows have been reset to support the new schema.")
|
|
46
45
|
# Backup old DB
|
|
47
46
|
db_path = os.path.join(db_dir, "ivoryos.db")
|
|
@@ -59,11 +58,36 @@ def reset_old_schema(engine, db_dir):
|
|
|
59
58
|
conn.execute(text("DROP TABLE IF EXISTS workflow_steps"))
|
|
60
59
|
if old_workflow_run:
|
|
61
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
|
|
62
68
|
|
|
63
69
|
# Recreate new schema
|
|
64
70
|
db.create_all() # creates workflow_runs, workflow_phases, workflow_steps
|
|
65
71
|
|
|
66
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
|
+
|
|
67
91
|
def create_app(config_class=None):
|
|
68
92
|
"""
|
|
69
93
|
create app, init database
|
|
@@ -81,6 +105,7 @@ def create_app(config_class=None):
|
|
|
81
105
|
with app.app_context():
|
|
82
106
|
# db.create_all()
|
|
83
107
|
reset_old_schema(db.engine, app.config['OUTPUT_FOLDER'])
|
|
108
|
+
create_admin()
|
|
84
109
|
|
|
85
110
|
# Additional setup
|
|
86
111
|
utils.create_gui_dir(app.config['OUTPUT_FOLDER'])
|