ivoryos 0.1.20__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 +21 -16
- ivoryos/routes/control/control.py +2 -2
- ivoryos/routes/control/templates/control/controllers_home.html +6 -1
- ivoryos/routes/database/database.py +45 -2
- ivoryos/routes/database/templates/database/experiment_database.html +4 -2
- ivoryos/routes/database/templates/database/workflow_run_database.html +81 -0
- ivoryos/routes/design/design.py +74 -18
- ivoryos/routes/design/templates/design/experiment_builder.html +84 -94
- ivoryos/routes/design/templates/design/experiment_run.html +188 -101
- ivoryos/routes/main/templates/main/home.html +80 -47
- ivoryos/static/js/socket_handler.js +59 -16
- ivoryos/static/js/sortable_design.js +102 -33
- ivoryos/static/style.css +9 -0
- ivoryos/templates/base.html +9 -6
- ivoryos/utils/client_proxy.py +57 -0
- ivoryos/utils/db_models.py +66 -11
- ivoryos/utils/form.py +50 -2
- ivoryos/utils/global_config.py +10 -0
- ivoryos/utils/llm_agent.py +1 -1
- ivoryos/utils/script_runner.py +115 -43
- ivoryos/utils/utils.py +24 -1
- ivoryos/version.py +1 -1
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/METADATA +7 -3
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/RECORD +27 -25
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/LICENSE +0 -0
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/WHEEL +0 -0
- {ivoryos-0.1.20.dist-info → ivoryos-0.1.22.dist-info}/top_level.txt +0 -0
|
@@ -1,36 +1,105 @@
|
|
|
1
|
-
$(document).ready(function(){
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
$(document).ready(function () {
|
|
2
|
+
let dropTargetId = ""; // Store the ID of the drop target
|
|
3
|
+
|
|
4
|
+
$("#list ul").sortable({
|
|
5
|
+
cancel: ".unsortable",
|
|
6
|
+
opacity: 0.8,
|
|
7
|
+
cursor: "move",
|
|
8
|
+
placeholder: "drop-placeholder",
|
|
9
|
+
update: function () {
|
|
10
|
+
var item_order = [];
|
|
11
|
+
$("ul.reorder li").each(function () {
|
|
12
|
+
item_order.push($(this).attr("id"));
|
|
13
|
+
});
|
|
14
|
+
var order_string = "order=" + item_order.join(",");
|
|
15
|
+
|
|
16
|
+
$.ajax({
|
|
17
|
+
method: "POST",
|
|
18
|
+
url: updateListUrl,
|
|
19
|
+
data: order_string,
|
|
20
|
+
cache: false,
|
|
21
|
+
success: function (data) {
|
|
22
|
+
$("#response").html(data);
|
|
23
|
+
$("#response").slideDown("slow");
|
|
24
|
+
slideout();
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Make Entire Accordion Item Draggable
|
|
31
|
+
$(".accordion-item").on("dragstart", function (event) {
|
|
32
|
+
let formHtml = $(this).find(".accordion-body").html(); // Get the correct form
|
|
33
|
+
event.originalEvent.dataTransfer.setData("form", formHtml || ""); // Store form HTML
|
|
34
|
+
event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
|
|
35
|
+
event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
|
|
36
|
+
|
|
37
|
+
$(this).addClass("dragging");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
$("#list ul, .canvas").on("dragover", function (event) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
let $target = $(event.target).closest("li");
|
|
44
|
+
|
|
45
|
+
// If we're over a valid <li> element in the list
|
|
46
|
+
if ($target.length) {
|
|
47
|
+
dropTargetId = $target.attr("id") || ""; // Store the drop target ID
|
|
48
|
+
|
|
49
|
+
$(".drop-placeholder").remove(); // Remove existing placeholders
|
|
50
|
+
$("<li class='drop-placeholder'></li>").insertBefore($target); // Insert before the target element
|
|
51
|
+
} else if (!$("#list ul").children().length && $(this).hasClass("canvas")) {
|
|
52
|
+
$(".drop-placeholder").remove(); // Remove any placeholder
|
|
53
|
+
// $("#list ul").append("<li class='drop-placeholder'></li>"); // Append placeholder to canvas
|
|
54
|
+
} else {
|
|
55
|
+
dropTargetId = ""; // Append placeholder to canvas
|
|
56
|
+
}
|
|
57
|
+
});
|
|
7
58
|
|
|
8
|
-
$("#
|
|
9
|
-
|
|
10
|
-
$(function() {
|
|
11
|
-
$("#list ul").sortable({
|
|
12
|
-
cancel: ".unsortable",
|
|
13
|
-
opacity: 0.8,
|
|
14
|
-
cursor: 'move',
|
|
15
|
-
update: function() {
|
|
16
|
-
var item_order = [];
|
|
17
|
-
$('ul.reorder li').each(function() {
|
|
18
|
-
item_order.push($(this).attr("id"));
|
|
19
|
-
});
|
|
20
|
-
var order_string = 'order=' + item_order.join(',');
|
|
21
|
-
|
|
22
|
-
$.ajax({
|
|
23
|
-
method: "POST",
|
|
24
|
-
url: updateListUrl, // updateListUrl should be set dynamically in your HTML template
|
|
25
|
-
data: order_string,
|
|
26
|
-
cache: false,
|
|
27
|
-
success: function(data){
|
|
28
|
-
$("#response").html(data);
|
|
29
|
-
$("#response").slideDown('slow');
|
|
30
|
-
slideout();
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
});
|
|
59
|
+
$("#list ul, .canvas").on("dragleave", function () {
|
|
60
|
+
$(".drop-placeholder").remove(); // Remove placeholder on leave
|
|
35
61
|
});
|
|
62
|
+
|
|
63
|
+
$("#list ul, .canvas").on("drop", function (event) {
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
|
|
66
|
+
var actionName = event.originalEvent.dataTransfer.getData("action");
|
|
67
|
+
var actionId = event.originalEvent.dataTransfer.getData("id");
|
|
68
|
+
var formHtml = event.originalEvent.dataTransfer.getData("form"); // Retrieve form HTML
|
|
69
|
+
let listLength = $("ul.reorder li").length;
|
|
70
|
+
dropTargetId = dropTargetId || listLength + 1; // Assign a "last" ID or unique identifier
|
|
71
|
+
$(".drop-placeholder").remove();
|
|
72
|
+
// Trigger the modal with the appropriate action
|
|
73
|
+
triggerModal(formHtml, actionName, actionId, dropTargetId);
|
|
74
|
+
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Function to trigger the modal (same for both buttons and accordion items)
|
|
78
|
+
function triggerModal(formHtml, actionName, actionId, dropTargetId) {
|
|
79
|
+
if (formHtml && formHtml.trim() !== "") {
|
|
80
|
+
var $form = $("<div>").html(formHtml); // Convert HTML string to jQuery object
|
|
81
|
+
|
|
82
|
+
// Create a hidden input for the drop target ID
|
|
83
|
+
var $hiddenInput = $("<input>")
|
|
84
|
+
.attr("type", "hidden")
|
|
85
|
+
.attr("name", "drop_target_id")
|
|
86
|
+
.attr("id", "dropTargetInput")
|
|
87
|
+
.val(dropTargetId);
|
|
88
|
+
|
|
89
|
+
// Insert before the submit button
|
|
90
|
+
$form.find("button[type='submit']").before($hiddenInput);
|
|
91
|
+
|
|
92
|
+
$("#modalFormFields").empty().append($form.children());
|
|
93
|
+
$("#dropModal").modal("show"); // Show modal
|
|
94
|
+
|
|
95
|
+
// Store and display drop target ID in the modal
|
|
96
|
+
$("#modalDropTarget").text(dropTargetId || "N/A");
|
|
97
|
+
|
|
98
|
+
$("#modalFormFields").data("action-id", actionId);
|
|
99
|
+
$("#modalFormFields").data("action-name", actionName);
|
|
100
|
+
$("#modalFormFields").data("drop-target-id", dropTargetId);
|
|
101
|
+
} else {
|
|
102
|
+
console.error("Form HTML is undefined or empty!");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
36
105
|
});
|
ivoryos/static/style.css
CHANGED
|
@@ -199,4 +199,13 @@ hr.vertical {
|
|
|
199
199
|
color: white;
|
|
200
200
|
font-size: 24px;
|
|
201
201
|
padding-top: 20%;
|
|
202
|
+
}
|
|
203
|
+
.drop-placeholder {
|
|
204
|
+
height: 2px !important; /* Keep it very thin */
|
|
205
|
+
min-height: 2px !important;
|
|
206
|
+
margin: 0 !important;
|
|
207
|
+
padding: 0 !important;
|
|
208
|
+
background: rgba(0, 0, 0, 0.2); /* Slight visibility */
|
|
209
|
+
border-radius: 2px;
|
|
210
|
+
list-style: none; /* Remove any default list styling */
|
|
202
211
|
}
|
ivoryos/templates/base.html
CHANGED
|
@@ -46,17 +46,20 @@
|
|
|
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('main.help_info') }}">About</a
|
|
59
|
-
</li
|
|
57
|
+
{# <li class="nav-item">#}
|
|
58
|
+
{# <a class="nav-link" href="{{ url_for('control.controllers_home') }}">Temp Devices</a></li>#}
|
|
59
|
+
{# </li>#}
|
|
60
|
+
{# <li class="nav-item">#}
|
|
61
|
+
{# <a class="nav-link" href="{{ url_for('main.help_info') }}">About</a>#}
|
|
62
|
+
{# </li>#}
|
|
60
63
|
{% if plugins %}
|
|
61
64
|
{% for plugin in plugins %}
|
|
62
65
|
<li class="nav-item">
|
|
@@ -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__
|
|
@@ -196,28 +200,37 @@ class Script(db.Model):
|
|
|
196
200
|
for i in self.stypes:
|
|
197
201
|
self._sort(i)
|
|
198
202
|
|
|
199
|
-
def add_action(self, action: dict):
|
|
203
|
+
def add_action(self, action: dict, insert_position=None):
|
|
200
204
|
current_len = len(self.currently_editing_script)
|
|
201
205
|
action_to_add = action.copy()
|
|
202
206
|
action_to_add['id'] = current_len + 1
|
|
203
207
|
action_to_add['uuid'] = uuid.uuid4().fields[-1]
|
|
204
208
|
self.currently_editing_script.append(action_to_add)
|
|
205
|
-
self.
|
|
209
|
+
self._insert_action(insert_position, current_len)
|
|
206
210
|
self.update_time_stamp()
|
|
207
211
|
|
|
208
|
-
def add_variable(self, statement, variable, type):
|
|
212
|
+
def add_variable(self, statement, variable, type, insert_position=None):
|
|
209
213
|
variable = self.validate_function_name(variable)
|
|
210
214
|
convert_type = getattr(builtins, type)
|
|
211
215
|
statement = convert_type(statement)
|
|
212
216
|
current_len = len(self.currently_editing_script)
|
|
213
217
|
uid = uuid.uuid4().fields[-1]
|
|
214
|
-
|
|
218
|
+
action = {"id": current_len + 1, "instrument": 'variable', "action": variable,
|
|
215
219
|
"args": {"statement": 'None' if statement == '' else statement}, "return": '', "uuid": uid,
|
|
216
|
-
"arg_types": {"statement": type}}
|
|
217
|
-
self.currently_editing_script.
|
|
218
|
-
self.
|
|
220
|
+
"arg_types": {"statement": type}}
|
|
221
|
+
self.currently_editing_script.append(action)
|
|
222
|
+
self._insert_action(insert_position, current_len)
|
|
219
223
|
self.update_time_stamp()
|
|
220
224
|
|
|
225
|
+
def _insert_action(self, insert_position, current_len, action_len:int=1):
|
|
226
|
+
|
|
227
|
+
if insert_position is None:
|
|
228
|
+
self.currently_editing_order.extend([str(current_len + i + 1) for i in range(action_len)])
|
|
229
|
+
else:
|
|
230
|
+
index = int(insert_position) - 1
|
|
231
|
+
self.currently_editing_order[index:index] = [str(current_len + i + 1) for i in range(action_len)]
|
|
232
|
+
self.sort_actions()
|
|
233
|
+
|
|
221
234
|
def get_added_variables(self):
|
|
222
235
|
added_variables: Dict[str, str] = {action["action"]: action["arg_types"]["statement"] for action in
|
|
223
236
|
self.currently_editing_script if action["instrument"] == "variable"}
|
|
@@ -251,7 +264,7 @@ class Script(db.Model):
|
|
|
251
264
|
kwargs[key] = f"#{self.validate_function_name(value[1:])}"
|
|
252
265
|
return kwargs
|
|
253
266
|
|
|
254
|
-
def add_logic_action(self, logic_type: str, statement):
|
|
267
|
+
def add_logic_action(self, logic_type: str, statement, insert_position=None):
|
|
255
268
|
current_len = len(self.currently_editing_script)
|
|
256
269
|
uid = uuid.uuid4().fields[-1]
|
|
257
270
|
logic_dict = {
|
|
@@ -291,7 +304,7 @@ class Script(db.Model):
|
|
|
291
304
|
}
|
|
292
305
|
action_list = logic_dict[logic_type]
|
|
293
306
|
self.currently_editing_script.extend(action_list)
|
|
294
|
-
self.
|
|
307
|
+
self._insert_action(insert_position, current_len, len(action_list))
|
|
295
308
|
self.update_time_stamp()
|
|
296
309
|
|
|
297
310
|
def delete_action(self, id: int):
|
|
@@ -446,7 +459,8 @@ class Script(db.Model):
|
|
|
446
459
|
configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in
|
|
447
460
|
config_type.items()]
|
|
448
461
|
|
|
449
|
-
|
|
462
|
+
script_type = f"_{stype}" if stype != "script" else ""
|
|
463
|
+
function_header = f"def {run_name}{script_type}("
|
|
450
464
|
|
|
451
465
|
if stype == "script":
|
|
452
466
|
function_header += ", ".join(configure)
|
|
@@ -492,6 +506,9 @@ class Script(db.Model):
|
|
|
492
506
|
return f"{self.indent(indent_unit)}time.sleep({statement})", indent_unit
|
|
493
507
|
elif instrument == 'repeat':
|
|
494
508
|
return self._process_repeat(indent_unit, action_name, statement, next_action)
|
|
509
|
+
#todo
|
|
510
|
+
# elif instrument == 'registered_workflows':
|
|
511
|
+
# return inspect.getsource(my_function)
|
|
495
512
|
else:
|
|
496
513
|
return self._process_instrument_action(indent_unit, instrument, action_name, args, save_data)
|
|
497
514
|
|
|
@@ -618,6 +635,44 @@ class Script(db.Model):
|
|
|
618
635
|
for i in exec_string.values():
|
|
619
636
|
s.write(f"\n\n\n{i}")
|
|
620
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
|
|
621
676
|
|
|
622
677
|
if __name__ == "__main__":
|
|
623
678
|
a = Script()
|
ivoryos/utils/form.py
CHANGED
|
@@ -7,6 +7,10 @@ from flask_wtf import FlaskForm
|
|
|
7
7
|
from wtforms import StringField, FloatField, HiddenField, BooleanField, IntegerField
|
|
8
8
|
import inspect
|
|
9
9
|
|
|
10
|
+
from ivoryos.utils.db_models import Script
|
|
11
|
+
from ivoryos.utils.global_config import GlobalConfig
|
|
12
|
+
|
|
13
|
+
global_config = GlobalConfig()
|
|
10
14
|
|
|
11
15
|
def find_variable(data, script):
|
|
12
16
|
"""
|
|
@@ -345,6 +349,13 @@ def create_form_from_action(action: dict, script=None, design=True):
|
|
|
345
349
|
setattr(DynamicForm, 'return', return_value)
|
|
346
350
|
return DynamicForm()
|
|
347
351
|
|
|
352
|
+
def create_all_builtin_forms(script):
|
|
353
|
+
all_builtin_forms = {}
|
|
354
|
+
for logic_name in ['if', 'while', 'variable', 'wait', 'repeat']:
|
|
355
|
+
# signature = info.get('signature', {})
|
|
356
|
+
form_class = create_builtin_form(logic_name, script)
|
|
357
|
+
all_builtin_forms[logic_name] = form_class()
|
|
358
|
+
return all_builtin_forms
|
|
348
359
|
|
|
349
360
|
def create_builtin_form(logic_type, script):
|
|
350
361
|
"""
|
|
@@ -379,14 +390,51 @@ def create_builtin_form(logic_type, script):
|
|
|
379
390
|
render_kw=render_kwargs)
|
|
380
391
|
type_field = SelectField(
|
|
381
392
|
'Select Input Type',
|
|
382
|
-
choices=[('int', 'Integer'), ('float', 'Float'), ('str', 'String')],
|
|
393
|
+
choices=[('int', 'Integer'), ('float', 'Float'), ('str', 'String'), ('bool', 'Boolean')],
|
|
383
394
|
default='str' # Optional default value
|
|
384
395
|
)
|
|
385
396
|
setattr(BuiltinFunctionForm, "variable", variable_field)
|
|
386
397
|
setattr(BuiltinFunctionForm, "type", type_field)
|
|
387
398
|
hidden_field = HiddenField(name=f'builtin_name', render_kw={"value": f'{logic_type}'})
|
|
388
399
|
setattr(BuiltinFunctionForm, "builtin_name", hidden_field)
|
|
389
|
-
return BuiltinFunctionForm
|
|
400
|
+
return BuiltinFunctionForm
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def get_method_from_workflow(function_string):
|
|
404
|
+
"""Creates a function from a string and assigns it a new name."""
|
|
405
|
+
|
|
406
|
+
namespace = {}
|
|
407
|
+
exec(function_string, globals(), namespace) # Execute the string in a safe namespace
|
|
408
|
+
func_name = next(iter(namespace))
|
|
409
|
+
# Get the function name dynamically
|
|
410
|
+
return namespace[func_name]
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def create_workflow_forms(script, autofill: bool = False, design: bool = False):
|
|
414
|
+
workflow_forms = {}
|
|
415
|
+
functions = {}
|
|
416
|
+
class RegisteredWorkflows:
|
|
417
|
+
pass
|
|
418
|
+
|
|
419
|
+
deck_name = script.deck
|
|
420
|
+
workflows = Script.query.filter(Script.deck==deck_name, Script.name != script.name).all()
|
|
421
|
+
for workflow in workflows:
|
|
422
|
+
compiled_strs = workflow.compile().get('script', "")
|
|
423
|
+
method = get_method_from_workflow(compiled_strs)
|
|
424
|
+
functions[workflow.name] = dict(signature=inspect.signature(method), docstring=inspect.getdoc(method))
|
|
425
|
+
setattr(RegisteredWorkflows, workflow.name, method)
|
|
426
|
+
|
|
427
|
+
form_class = create_form_for_method(method, autofill, script, design)
|
|
428
|
+
|
|
429
|
+
hidden_method_name = HiddenField(name=f'hidden_name', description="",
|
|
430
|
+
render_kw={"value": f'{workflow.name}'})
|
|
431
|
+
if design:
|
|
432
|
+
return_value = StringField(label='Save value as', render_kw={"placeholder": "Optional"})
|
|
433
|
+
setattr(form_class, 'return', return_value)
|
|
434
|
+
setattr(form_class, 'workflow_name', hidden_method_name)
|
|
435
|
+
workflow_forms[workflow.name] = form_class()
|
|
436
|
+
global_config.registered_workflows = RegisteredWorkflows
|
|
437
|
+
return workflow_forms, functions
|
|
390
438
|
|
|
391
439
|
|
|
392
440
|
def create_action_button(script, stype=None):
|
ivoryos/utils/global_config.py
CHANGED
|
@@ -8,6 +8,7 @@ class GlobalConfig:
|
|
|
8
8
|
if cls._instance is None:
|
|
9
9
|
cls._instance = super(GlobalConfig, cls).__new__(cls, *args, **kwargs)
|
|
10
10
|
cls._instance._deck = None
|
|
11
|
+
cls._instance._registered_workflows = None
|
|
11
12
|
cls._instance._agent = None
|
|
12
13
|
cls._instance._defined_variables = {}
|
|
13
14
|
cls._instance._api_variables = set()
|
|
@@ -24,6 +25,15 @@ class GlobalConfig:
|
|
|
24
25
|
if self._deck is None:
|
|
25
26
|
self._deck = value
|
|
26
27
|
|
|
28
|
+
@property
|
|
29
|
+
def registered_workflows(self):
|
|
30
|
+
return self._registered_workflows
|
|
31
|
+
|
|
32
|
+
@registered_workflows.setter
|
|
33
|
+
def registered_workflows(self, value):
|
|
34
|
+
if self._registered_workflows is None:
|
|
35
|
+
self._registered_workflows = value
|
|
36
|
+
|
|
27
37
|
|
|
28
38
|
@property
|
|
29
39
|
def deck_snapshot(self):
|
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
|
|