ivoryos 0.1.21__py3-none-any.whl → 0.1.22__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ivoryos might be problematic. Click here for more details.
- ivoryos/__init__.py +13 -5
- ivoryos/routes/control/control.py +2 -2
- ivoryos/routes/control/templates/control/controllers_home.html +6 -1
- ivoryos/routes/database/database.py +43 -2
- ivoryos/routes/database/templates/database/experiment_database.html +3 -3
- ivoryos/routes/database/templates/database/workflow_run_database.html +81 -0
- ivoryos/routes/design/design.py +16 -2
- ivoryos/routes/design/templates/design/experiment_run.html +159 -81
- ivoryos/routes/main/templates/main/home.html +80 -47
- ivoryos/templates/base.html +6 -3
- ivoryos/utils/client_proxy.py +57 -0
- ivoryos/utils/db_models.py +43 -1
- ivoryos/utils/llm_agent.py +1 -1
- ivoryos/utils/script_runner.py +91 -42
- ivoryos/utils/utils.py +23 -0
- ivoryos/version.py +1 -1
- {ivoryos-0.1.21.dist-info → ivoryos-0.1.22.dist-info}/METADATA +7 -3
- {ivoryos-0.1.21.dist-info → ivoryos-0.1.22.dist-info}/RECORD +21 -19
- {ivoryos-0.1.21.dist-info → ivoryos-0.1.22.dist-info}/LICENSE +0 -0
- {ivoryos-0.1.21.dist-info → ivoryos-0.1.22.dist-info}/WHEEL +0 -0
- {ivoryos-0.1.21.dist-info → ivoryos-0.1.22.dist-info}/top_level.txt +0 -0
|
@@ -1,70 +1,103 @@
|
|
|
1
1
|
{% extends 'base.html' %}
|
|
2
|
-
{% block title %}IvoryOS | Welcome
|
|
3
|
-
|
|
2
|
+
{% block title %}IvoryOS | Welcome{% endblock %}
|
|
4
3
|
|
|
5
4
|
{% block body %}
|
|
6
|
-
<div class="p-4"
|
|
7
|
-
<h1 style="
|
|
5
|
+
<div class="p-4">
|
|
6
|
+
<h1 class="mb-4" style="font-size: 3rem; font-weight: bold; color: #343a40;">
|
|
8
7
|
Welcome
|
|
9
8
|
</h1>
|
|
10
|
-
<p>Version: {{ version }}</p>
|
|
9
|
+
<p class="mb-5">Version: {{ version }}</p>
|
|
10
|
+
|
|
11
|
+
{% if enable_design %}
|
|
12
|
+
<!-- Workflow Design Section -->
|
|
13
|
+
<h4 class="mb-3">Workflow Design</h4>
|
|
11
14
|
<div class="row">
|
|
12
|
-
|
|
13
|
-
<div class="
|
|
14
|
-
<div class="card
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
<div class="col-lg-6 mb-3 d-flex align-items-stretch">
|
|
16
|
+
<div class="card rounded shadow-sm flex-fill">
|
|
17
|
+
<div class="card-body">
|
|
18
|
+
<h5 class="card-title">
|
|
19
|
+
<i class="bi bi-folder2-open me-2"></i>Browse designs
|
|
20
|
+
</h5>
|
|
21
|
+
<p class="card-text">View all saved workflows from the database.</p>
|
|
22
|
+
<a href="{{ url_for('database.load_from_database') }}" class="stretched-link"></a>
|
|
20
23
|
</div>
|
|
21
24
|
</div>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<
|
|
28
|
-
</
|
|
25
|
+
</div>
|
|
26
|
+
<div class="col-lg-6 mb-3 d-flex align-items-stretch">
|
|
27
|
+
<div class="card rounded shadow-sm flex-fill">
|
|
28
|
+
<div class="card-body">
|
|
29
|
+
<h5 class="card-title">
|
|
30
|
+
<i class="bi bi-pencil-square me-2"></i>Edit designs
|
|
31
|
+
</h5>
|
|
32
|
+
<p class="card-text">Create or modify workflows using available functions.</p>
|
|
33
|
+
<a href="{{ url_for('design.experiment_builder') }}" class="stretched-link"></a>
|
|
29
34
|
</div>
|
|
30
35
|
</div>
|
|
31
|
-
|
|
36
|
+
</div>
|
|
32
37
|
</div>
|
|
38
|
+
{% endif %}
|
|
33
39
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
<!-- Workflow Control and Monitor Section -->
|
|
41
|
+
<h4 class="mt-5 mb-3">Workflow Control & Monitoring</h4>
|
|
42
|
+
<div class="row">
|
|
43
|
+
<!-- Always visible: Experiment data -->
|
|
44
|
+
<div class="col-lg-6 mb-3 d-flex align-items-stretch">
|
|
45
|
+
<div class="card rounded shadow-sm flex-fill">
|
|
46
|
+
<div class="card-body">
|
|
47
|
+
<h5 class="card-title">
|
|
48
|
+
<i class="bi bi-graph-up-arrow me-2"></i>Experiment data
|
|
49
|
+
</h5>
|
|
50
|
+
<p class="card-text">Browse workflow logs and output data.</p>
|
|
51
|
+
<a href="{{ url_for('database.list_workflows') }}" class="stretched-link"></a>
|
|
45
52
|
</div>
|
|
46
53
|
</div>
|
|
47
|
-
|
|
54
|
+
</div>
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
<!-- Conditionally visible: Run current workflow -->
|
|
57
|
+
{% if not off_line %}
|
|
58
|
+
<div class="col-lg-6 mb-3 d-flex align-items-stretch">
|
|
59
|
+
<div class="card rounded shadow-sm flex-fill">
|
|
60
|
+
<div class="card-body">
|
|
61
|
+
<h5 class="card-title">
|
|
62
|
+
<i class="bi bi-play-circle me-2"></i>Run current workflow
|
|
63
|
+
</h5>
|
|
64
|
+
<p class="card-text">Execute workflows with configurable parameters.</p>
|
|
65
|
+
<a href="{{ url_for('design.experiment_run') }}" class="stretched-link"></a>
|
|
56
66
|
</div>
|
|
57
67
|
</div>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
</div>
|
|
69
|
+
{% endif %}
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
{% if not off_line %}
|
|
75
|
+
<!-- Direct Control Section -->
|
|
76
|
+
<h4 class="mt-5 mb-3">Direct Control</h4>
|
|
77
|
+
<div class="row">
|
|
78
|
+
<div class="col-lg-6 mb-3 d-flex align-items-stretch">
|
|
79
|
+
<div class="card rounded shadow-sm flex-fill">
|
|
80
|
+
<div class="card-body">
|
|
81
|
+
<h5 class="card-title">
|
|
82
|
+
<i class="bi bi-toggle-on me-2"></i>Direct control
|
|
83
|
+
</h5>
|
|
84
|
+
<p class="card-text">Manually control individual components.</p>
|
|
85
|
+
<a href="{{ url_for('control.deck_controllers') }}" class="stretched-link"></a>
|
|
65
86
|
</div>
|
|
66
87
|
</div>
|
|
67
88
|
</div>
|
|
89
|
+
<div class="col-lg-6 mb-3 d-flex align-items-stretch">
|
|
90
|
+
<div class="card rounded shadow-sm flex-fill">
|
|
91
|
+
<div class="card-body">
|
|
92
|
+
<h5 class="card-title">
|
|
93
|
+
<i class="bi bi-usb-plug me-2"></i>Connect a new device
|
|
94
|
+
</h5>
|
|
95
|
+
<p class="card-text">Add new hardware temporarily or for testing purposes.</p>
|
|
96
|
+
<a href="{{ url_for('control.controllers_home') }}" class="stretched-link"></a>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
68
101
|
{% endif %}
|
|
69
102
|
</div>
|
|
70
103
|
{% endblock %}
|
ivoryos/templates/base.html
CHANGED
|
@@ -46,14 +46,17 @@
|
|
|
46
46
|
<li class="nav-item">
|
|
47
47
|
<a class="nav-link" href="{{ url_for('design.experiment_run') }}">Compile/Run</a>
|
|
48
48
|
</li>
|
|
49
|
+
<li class="nav-item">
|
|
50
|
+
<a class="nav-link" href="{{ url_for('database.list_workflows') }}">Data</a>
|
|
51
|
+
</li>
|
|
49
52
|
{% endif %}
|
|
50
53
|
|
|
51
54
|
<li class="nav-item">
|
|
52
55
|
<a class="nav-link" href="{{ url_for('control.deck_controllers') }}">Devices</a></li>
|
|
53
56
|
</li>
|
|
54
|
-
<li class="nav-item"
|
|
55
|
-
<a class="nav-link" href="{{ url_for('control.controllers_home') }}">Temp Devices</a></li
|
|
56
|
-
</li
|
|
57
|
+
{# <li class="nav-item">#}
|
|
58
|
+
{# <a class="nav-link" href="{{ url_for('control.controllers_home') }}">Temp Devices</a></li>#}
|
|
59
|
+
{# </li>#}
|
|
57
60
|
{# <li class="nav-item">#}
|
|
58
61
|
{# <a class="nav-link" href="{{ url_for('main.help_info') }}">About</a>#}
|
|
59
62
|
{# </li>#}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# import argparse
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
# import requests
|
|
5
|
+
|
|
6
|
+
# session = requests.Session()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Function to create class and methods dynamically
|
|
10
|
+
def create_function(url, class_name, functions):
|
|
11
|
+
class_template = f'class {class_name.capitalize()}:\n url = "{url}ivoryos/backend_control/deck.{class_name}"\n'
|
|
12
|
+
|
|
13
|
+
for function_name, details in functions.items():
|
|
14
|
+
signature = details['signature']
|
|
15
|
+
docstring = details.get('docstring', '')
|
|
16
|
+
|
|
17
|
+
# Creating the function definition
|
|
18
|
+
method = f' def {function_name}{signature}:\n'
|
|
19
|
+
if docstring:
|
|
20
|
+
method += f' """{docstring}"""\n'
|
|
21
|
+
|
|
22
|
+
# Generating the session.post code for sending data
|
|
23
|
+
method += ' return session.post(self.url, data={'
|
|
24
|
+
method += f'"hidden_name": "{function_name}"'
|
|
25
|
+
|
|
26
|
+
# Extracting the parameters from the signature string for the data payload
|
|
27
|
+
param_str = signature[6:-1] # Remove the "(self" and final ")"
|
|
28
|
+
params = [param.strip() for param in param_str.split(',')] if param_str else []
|
|
29
|
+
|
|
30
|
+
for param in params:
|
|
31
|
+
param_name = param.split(':')[0].strip() # Split on ':' and get parameter name
|
|
32
|
+
method += f', "{param_name}": {param_name}'
|
|
33
|
+
|
|
34
|
+
method += '}).json()\n'
|
|
35
|
+
class_template += method + '\n'
|
|
36
|
+
|
|
37
|
+
return class_template
|
|
38
|
+
|
|
39
|
+
# Function to export the generated classes to a Python script
|
|
40
|
+
def export_to_python(class_definitions, path):
|
|
41
|
+
with open(os.path.join(path, "generated_proxy.py"), 'w') as f:
|
|
42
|
+
# Writing the imports at the top of the script
|
|
43
|
+
f.write('import requests\n\n')
|
|
44
|
+
f.write('session = requests.Session()\n\n')
|
|
45
|
+
|
|
46
|
+
# Writing each class definition to the file
|
|
47
|
+
for class_name, class_def in class_definitions.items():
|
|
48
|
+
f.write(class_def)
|
|
49
|
+
f.write('\n')
|
|
50
|
+
|
|
51
|
+
# Creating instances of the dynamically generated classes
|
|
52
|
+
for class_name in class_definitions.keys():
|
|
53
|
+
instance_name = class_name.lower() # Using lowercase for instance names
|
|
54
|
+
f.write(f'{instance_name} = {class_name.capitalize()}()\n')
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
ivoryos/utils/db_models.py
CHANGED
|
@@ -44,9 +44,12 @@ class Script(db.Model):
|
|
|
44
44
|
id_order = db.Column(JSONType, nullable=True)
|
|
45
45
|
editing_type = db.Column(db.String(50), nullable=True)
|
|
46
46
|
author = db.Column(db.String(50), nullable=False)
|
|
47
|
+
# registered = db.Column(db.Boolean, nullable=True, default=False)
|
|
47
48
|
|
|
48
49
|
def __init__(self, name=None, deck=None, status=None, script_dict: dict = None, id_order: dict = None,
|
|
49
|
-
time_created=None, last_modified=None, editing_type=None, author: str = None
|
|
50
|
+
time_created=None, last_modified=None, editing_type=None, author: str = None,
|
|
51
|
+
registered:bool=False,
|
|
52
|
+
):
|
|
50
53
|
if script_dict is None:
|
|
51
54
|
script_dict = {"prep": [], "script": [], "cleanup": []}
|
|
52
55
|
elif type(script_dict) is not dict:
|
|
@@ -73,6 +76,7 @@ class Script(db.Model):
|
|
|
73
76
|
self.id_order = id_order
|
|
74
77
|
self.editing_type = editing_type
|
|
75
78
|
self.author = author
|
|
79
|
+
# self.r = registered
|
|
76
80
|
|
|
77
81
|
def as_dict(self):
|
|
78
82
|
dict = self.__dict__
|
|
@@ -631,6 +635,44 @@ class Script(db.Model):
|
|
|
631
635
|
for i in exec_string.values():
|
|
632
636
|
s.write(f"\n\n\n{i}")
|
|
633
637
|
|
|
638
|
+
class WorkflowRun(db.Model):
|
|
639
|
+
__tablename__ = 'workflow_runs'
|
|
640
|
+
|
|
641
|
+
id = db.Column(db.Integer, primary_key=True)
|
|
642
|
+
name = db.Column(db.String(128), nullable=False)
|
|
643
|
+
platform = db.Column(db.String(128), nullable=False)
|
|
644
|
+
start_time = db.Column(db.DateTime, default=datetime.now())
|
|
645
|
+
end_time = db.Column(db.DateTime)
|
|
646
|
+
data_path = db.Column(db.String(256))
|
|
647
|
+
steps = db.relationship(
|
|
648
|
+
'WorkflowStep',
|
|
649
|
+
backref='workflow_runs',
|
|
650
|
+
cascade='all, delete-orphan',
|
|
651
|
+
passive_deletes=True
|
|
652
|
+
)
|
|
653
|
+
def as_dict(self):
|
|
654
|
+
dict = self.__dict__
|
|
655
|
+
dict.pop('_sa_instance_state', None)
|
|
656
|
+
return dict
|
|
657
|
+
|
|
658
|
+
class WorkflowStep(db.Model):
|
|
659
|
+
__tablename__ = 'workflow_steps'
|
|
660
|
+
|
|
661
|
+
id = db.Column(db.Integer, primary_key=True)
|
|
662
|
+
workflow_id = db.Column(db.Integer, db.ForeignKey('workflow_runs.id', ondelete='CASCADE'), nullable=False)
|
|
663
|
+
|
|
664
|
+
phase = db.Column(db.String(64), nullable=False) # 'prep', 'main', 'cleanup'
|
|
665
|
+
repeat_index = db.Column(db.Integer, default=0) # Only applies to 'main' phase
|
|
666
|
+
step_index = db.Column(db.Integer, default=0)
|
|
667
|
+
method_name = db.Column(db.String(128), nullable=False)
|
|
668
|
+
start_time = db.Column(db.DateTime)
|
|
669
|
+
end_time = db.Column(db.DateTime)
|
|
670
|
+
run_error = db.Column(db.Boolean, default=False)
|
|
671
|
+
|
|
672
|
+
def as_dict(self):
|
|
673
|
+
dict = self.__dict__
|
|
674
|
+
dict.pop('_sa_instance_state', None)
|
|
675
|
+
return dict
|
|
634
676
|
|
|
635
677
|
if __name__ == "__main__":
|
|
636
678
|
a = Script()
|
ivoryos/utils/llm_agent.py
CHANGED
|
@@ -162,7 +162,7 @@ can you also help find the default value you can't find the info from my request
|
|
|
162
162
|
|
|
163
163
|
if __name__ == "__main__":
|
|
164
164
|
from pprint import pprint
|
|
165
|
-
from example.
|
|
165
|
+
from example.abstract_sdl_example.abstract_sdl import deck
|
|
166
166
|
|
|
167
167
|
from utils import parse_functions
|
|
168
168
|
|
ivoryos/utils/script_runner.py
CHANGED
|
@@ -6,12 +6,14 @@ import time
|
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
|
|
8
8
|
from ivoryos.utils import utils
|
|
9
|
-
from ivoryos.utils.db_models import Script
|
|
9
|
+
from ivoryos.utils.db_models import Script, WorkflowRun, WorkflowStep, db
|
|
10
10
|
from ivoryos.utils.global_config import GlobalConfig
|
|
11
11
|
|
|
12
12
|
global_config = GlobalConfig()
|
|
13
13
|
global deck
|
|
14
14
|
deck = None
|
|
15
|
+
# global deck, registered_workflows
|
|
16
|
+
# deck, registered_workflows = None, None
|
|
15
17
|
|
|
16
18
|
class ScriptRunner:
|
|
17
19
|
def __init__(self, globals_dict=None):
|
|
@@ -57,7 +59,8 @@ class ScriptRunner:
|
|
|
57
59
|
self.abort_pending()
|
|
58
60
|
|
|
59
61
|
def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
|
|
60
|
-
output_path=""):
|
|
62
|
+
output_path="", current_app=None):
|
|
63
|
+
# Get run.id
|
|
61
64
|
global deck
|
|
62
65
|
if deck is None:
|
|
63
66
|
deck = global_config.deck
|
|
@@ -71,14 +74,13 @@ class ScriptRunner:
|
|
|
71
74
|
self.reset_stop_event()
|
|
72
75
|
|
|
73
76
|
thread = threading.Thread(target=self._run_with_stop_check,
|
|
74
|
-
args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path))
|
|
77
|
+
args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path, current_app))
|
|
75
78
|
thread.start()
|
|
76
79
|
return thread
|
|
77
80
|
|
|
78
|
-
def
|
|
81
|
+
def exec_steps(self, script, section_name, logger, socketio, run_id, i_progress, **kwargs):
|
|
79
82
|
"""
|
|
80
|
-
Executes a function defined in a string line by line
|
|
81
|
-
|
|
83
|
+
Executes a function defined in a string line by line
|
|
82
84
|
:param func_str: The function as a string
|
|
83
85
|
:param kwargs: Arguments to pass to the function
|
|
84
86
|
:return: The final result of the function execution
|
|
@@ -86,8 +88,15 @@ class ScriptRunner:
|
|
|
86
88
|
_func_str = script.compile()
|
|
87
89
|
step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
|
|
88
90
|
global deck
|
|
91
|
+
# global deck, registered_workflows
|
|
89
92
|
if deck is None:
|
|
90
93
|
deck = global_config.deck
|
|
94
|
+
# if registered_workflows is None:
|
|
95
|
+
# registered_workflows = global_config.registered_workflows
|
|
96
|
+
|
|
97
|
+
# for i, line in enumerate(step_list):
|
|
98
|
+
# if line.startswith("registered_workflows"):
|
|
99
|
+
#
|
|
91
100
|
# func_str = script.compile()
|
|
92
101
|
# Parse function body from string
|
|
93
102
|
temp_connections = global_config.defined_variables
|
|
@@ -105,27 +114,56 @@ class ScriptRunner:
|
|
|
105
114
|
while index < len(step_list):
|
|
106
115
|
if self.stop_current_event.is_set():
|
|
107
116
|
logger.info(f'Stopping execution during {section_name}')
|
|
117
|
+
step = WorkflowStep(
|
|
118
|
+
workflow_id=run_id,
|
|
119
|
+
phase=section_name,
|
|
120
|
+
repeat_index=i_progress,
|
|
121
|
+
step_index=index,
|
|
122
|
+
method_name="stop",
|
|
123
|
+
start_time=datetime.now(),
|
|
124
|
+
end_time=datetime.now(),
|
|
125
|
+
run_error=False,
|
|
126
|
+
)
|
|
127
|
+
db.session.add(step)
|
|
108
128
|
break
|
|
109
129
|
line = step_list[index]
|
|
110
|
-
|
|
130
|
+
method_name = line.strip().split("(")[0] if "(" in line else line.strip()
|
|
131
|
+
start_time = datetime.now()
|
|
132
|
+
step = WorkflowStep(
|
|
133
|
+
workflow_id=run_id,
|
|
134
|
+
phase=section_name,
|
|
135
|
+
repeat_index=i_progress,
|
|
136
|
+
step_index=index,
|
|
137
|
+
method_name=method_name,
|
|
138
|
+
start_time=start_time,
|
|
139
|
+
)
|
|
140
|
+
logger.info(f"Executing: {line}")
|
|
111
141
|
socketio.emit('execution', {'section': f"{section_name}-{index}"})
|
|
112
142
|
# self._emit_progress(socketio, 100)
|
|
143
|
+
# if line.startswith("registered_workflows"):
|
|
144
|
+
# line = line.replace("registered_workflows.", "")
|
|
113
145
|
try:
|
|
114
146
|
exec(line, exec_globals, exec_locals)
|
|
147
|
+
step.run_error = False
|
|
115
148
|
except Exception as e:
|
|
116
149
|
logger.error(f"Error during script execution: {e}")
|
|
117
|
-
socketio.emit('error', {'message': e
|
|
150
|
+
socketio.emit('error', {'message': str(e)})
|
|
151
|
+
|
|
152
|
+
step.run_error = True
|
|
118
153
|
self.toggle_pause()
|
|
154
|
+
step.end_time = datetime.now()
|
|
119
155
|
self.pause_event.wait()
|
|
120
156
|
|
|
121
157
|
# todo update script during the run
|
|
122
158
|
# _func_str = script.compile()
|
|
123
159
|
# step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
|
|
160
|
+
db.session.add(step)
|
|
161
|
+
db.session.commit()
|
|
124
162
|
index += 1
|
|
125
163
|
return exec_locals # Return the 'results' variable
|
|
126
164
|
|
|
127
165
|
def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
|
|
128
|
-
output_path):
|
|
166
|
+
output_path, current_app):
|
|
129
167
|
time.sleep(1)
|
|
130
168
|
# _func_str = script.compile()
|
|
131
169
|
# step_list_dict: dict = script.convert_to_lines(_func_str)
|
|
@@ -133,39 +171,49 @@ class ScriptRunner:
|
|
|
133
171
|
|
|
134
172
|
# Run "prep" section once
|
|
135
173
|
script_dict = script.script_dict
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
self.
|
|
157
|
-
|
|
174
|
+
with current_app.app_context():
|
|
175
|
+
|
|
176
|
+
run = WorkflowRun(name=script.name or "untitled", platform=script.deck,start_time=datetime.now())
|
|
177
|
+
db.session.add(run)
|
|
178
|
+
db.session.flush()
|
|
179
|
+
|
|
180
|
+
self._run_actions(script, section_name="prep", logger=logger, socketio=socketio, run_id=run.id)
|
|
181
|
+
output_list = []
|
|
182
|
+
_, arg_type = script.config("script")
|
|
183
|
+
_, return_list = script.config_return()
|
|
184
|
+
|
|
185
|
+
# Run "script" section multiple times
|
|
186
|
+
if repeat_count:
|
|
187
|
+
self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
|
|
188
|
+
run_name, return_list, logger, socketio, run_id=run.id)
|
|
189
|
+
elif config:
|
|
190
|
+
self._run_config_section(config, arg_type, output_list, script, run_name, logger,
|
|
191
|
+
socketio, run_id=run.id)
|
|
192
|
+
|
|
193
|
+
# Run "cleanup" section once
|
|
194
|
+
self._run_actions(script, section_name="cleanup", logger=logger, socketio=socketio,run_id=run.id)
|
|
195
|
+
# Reset the running flag when done
|
|
196
|
+
with self.lock:
|
|
197
|
+
self.is_running = False
|
|
198
|
+
# Save results if necessary
|
|
199
|
+
filename = None
|
|
200
|
+
if output_list:
|
|
201
|
+
filename = self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
|
|
202
|
+
self._emit_progress(socketio, 100)
|
|
203
|
+
run.end_time = datetime.now()
|
|
204
|
+
run.data_path = filename
|
|
205
|
+
db.session.commit()
|
|
158
206
|
|
|
159
|
-
def _run_actions(self, script, section_name="", logger=None, socketio=None):
|
|
207
|
+
def _run_actions(self, script, section_name="", logger=None, socketio=None, run_id=None):
|
|
160
208
|
_func_str = script.compile()
|
|
161
209
|
step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
|
|
162
210
|
logger.info(f'Executing {section_name} steps') if step_list else logger.info(f'No {section_name} steps')
|
|
163
211
|
if self.stop_pending_event.is_set():
|
|
164
212
|
logger.info(f"Stopping execution during {section_name} section.")
|
|
165
213
|
return
|
|
166
|
-
self.
|
|
214
|
+
self.exec_steps(script, section_name, logger, socketio, run_id=run_id, i_progress=0)
|
|
167
215
|
|
|
168
|
-
def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio):
|
|
216
|
+
def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio, run_id):
|
|
169
217
|
compiled = True
|
|
170
218
|
for i in config:
|
|
171
219
|
try:
|
|
@@ -185,22 +233,22 @@ class ScriptRunner:
|
|
|
185
233
|
self._emit_progress(socketio, progress)
|
|
186
234
|
# fname = f"{run_name}_script"
|
|
187
235
|
# function = self.globals_dict[fname]
|
|
188
|
-
output = self.
|
|
236
|
+
output = self.exec_steps(script, "script", logger, socketio, run_id, i, **kwargs)
|
|
189
237
|
if output:
|
|
190
238
|
# kwargs.update(output)
|
|
191
239
|
output_list.append(output)
|
|
192
240
|
|
|
193
241
|
def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list,
|
|
194
|
-
logger, socketio):
|
|
242
|
+
logger, socketio, run_id):
|
|
195
243
|
if bo_args:
|
|
196
244
|
logger.info('Initializing optimizer...')
|
|
197
245
|
ax_client = utils.ax_initiation(bo_args, arg_types)
|
|
198
|
-
for
|
|
246
|
+
for i_progress in range(int(repeat_count)):
|
|
199
247
|
if self.stop_pending_event.is_set():
|
|
200
|
-
logger.info(f'Stopping execution during {run_name}: {
|
|
248
|
+
logger.info(f'Stopping execution during {run_name}: {i_progress + 1}/{int(repeat_count)}')
|
|
201
249
|
break
|
|
202
|
-
logger.info(f'Executing {run_name} experiment: {
|
|
203
|
-
progress = (
|
|
250
|
+
logger.info(f'Executing {run_name} experiment: {i_progress + 1}/{int(repeat_count)}')
|
|
251
|
+
progress = (i_progress + 1) * 100 / int(repeat_count) - 0.1
|
|
204
252
|
self._emit_progress(socketio, progress)
|
|
205
253
|
if bo_args:
|
|
206
254
|
try:
|
|
@@ -208,7 +256,7 @@ class ScriptRunner:
|
|
|
208
256
|
logger.info(f'Output value: {parameters}')
|
|
209
257
|
# fname = f"{run_name}_script"
|
|
210
258
|
# function = self.globals_dict[fname]
|
|
211
|
-
output = self.
|
|
259
|
+
output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress, **parameters)
|
|
212
260
|
|
|
213
261
|
_output = {key: value for key, value in output.items() if key in return_list}
|
|
214
262
|
ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
|
|
@@ -219,7 +267,7 @@ class ScriptRunner:
|
|
|
219
267
|
else:
|
|
220
268
|
# fname = f"{run_name}_script"
|
|
221
269
|
# function = self.globals_dict[fname]
|
|
222
|
-
output = self.
|
|
270
|
+
output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress)
|
|
223
271
|
|
|
224
272
|
if output:
|
|
225
273
|
output_list.append(output)
|
|
@@ -237,6 +285,7 @@ class ScriptRunner:
|
|
|
237
285
|
writer.writeheader()
|
|
238
286
|
writer.writerows(output_list)
|
|
239
287
|
logger.info(f'Results saved to {file_path}')
|
|
288
|
+
return filename
|
|
240
289
|
|
|
241
290
|
@staticmethod
|
|
242
291
|
def _emit_progress(socketio, progress):
|
ivoryos/utils/utils.py
CHANGED
|
@@ -407,3 +407,26 @@ def check_config_duplicate(config):
|
|
|
407
407
|
"""
|
|
408
408
|
hashable_data = [tuple(sorted(d.items())) for d in config]
|
|
409
409
|
return any(count > 1 for count in Counter(hashable_data).values())
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def get_method_from_workflow(function_string, func_name="workflow"):
|
|
413
|
+
"""Creates a function from a string and assigns it a new name."""
|
|
414
|
+
|
|
415
|
+
namespace = {}
|
|
416
|
+
exec(function_string, globals(), namespace) # Execute the string in a safe namespace
|
|
417
|
+
# func_name = next(iter(namespace))
|
|
418
|
+
# Get the function name dynamically
|
|
419
|
+
return namespace[func_name]
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
# def load_workflows(script):
|
|
423
|
+
#
|
|
424
|
+
# class RegisteredWorkflows:
|
|
425
|
+
# pass
|
|
426
|
+
# deck_name = script.deck
|
|
427
|
+
# workflows = Script.query.filter(Script.deck == deck_name, Script.name != script.name, Script.registered==True).all()
|
|
428
|
+
# for workflow in workflows:
|
|
429
|
+
# compiled_strs = workflow.compile().get('script', "")
|
|
430
|
+
# method = get_method_from_workflow(compiled_strs, func_name=workflow.name)
|
|
431
|
+
# setattr(RegisteredWorkflows, workflow.name, staticmethod(method))
|
|
432
|
+
# global_config.registered_workflows = RegisteredWorkflows()
|
ivoryos/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.22"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ivoryos
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.22
|
|
4
4
|
Summary: an open-source Python package enabling Self-Driving Labs (SDLs) interoperability
|
|
5
5
|
Home-page: https://gitlab.com/heingroup/ivoryos
|
|
6
6
|
Author: Ivory Zhang
|
|
@@ -36,7 +36,7 @@ Requires-Dist: python-dotenv
|
|
|
36
36
|
- [Instructions for use](#instructions-for-use)
|
|
37
37
|
- [Demo](#demo)
|
|
38
38
|
- [Roadmap](#roadmap)
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
|
|
41
41
|
## Description
|
|
42
42
|
Granting SDLs flexibility and modularity makes it almost impossible to design a UI, yet it's a necessity for allowing more people to interact with it (democratisation).
|
|
@@ -93,6 +93,10 @@ Create an account and login (local database)
|
|
|
93
93
|
- **Database**: manage workflows in _Library_ tab.
|
|
94
94
|
- **Info page**: additional info in _About_ tab.
|
|
95
95
|
|
|
96
|
+
[//]: # ()
|
|
97
|
+
|
|
98
|
+
[//]: # ()
|
|
99
|
+
|
|
96
100
|
|
|
97
101
|
### Additional settings
|
|
98
102
|
#### AI assistant
|
|
@@ -133,7 +137,7 @@ After one successful connection, a blueprint will be automatically saved and mad
|
|
|
133
137
|
ivoryos.run()
|
|
134
138
|
```
|
|
135
139
|
## Demo
|
|
136
|
-
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/
|
|
140
|
+
In the [abstract_sdl.py](https://gitlab.com/heingroup/ivoryos/-/blob/main/example/abstract_sdl_example/abstract_sdl.py), where instances of `AbstractSDL` is created as `sdl`,
|
|
137
141
|
addresses will be available on terminal.
|
|
138
142
|
```Python
|
|
139
143
|
ivoryos.run(__name__)
|