ivoryos 1.1.0__py3-none-any.whl → 1.2.0__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 +12 -5
- ivoryos/routes/api/api.py +5 -58
- ivoryos/routes/control/control.py +46 -43
- ivoryos/routes/control/control_file.py +4 -4
- ivoryos/routes/control/control_new_device.py +10 -10
- ivoryos/routes/control/templates/controllers.html +38 -9
- ivoryos/routes/control/templates/controllers_new.html +1 -1
- ivoryos/routes/data/data.py +81 -60
- ivoryos/routes/data/templates/components/step_card.html +9 -3
- ivoryos/routes/data/templates/workflow_database.html +10 -4
- ivoryos/routes/design/design.py +306 -243
- ivoryos/routes/design/design_file.py +42 -31
- ivoryos/routes/design/design_step.py +132 -30
- ivoryos/routes/design/templates/components/action_form.html +4 -3
- ivoryos/routes/design/templates/components/{instrument_panel.html → actions_panel.html} +7 -5
- ivoryos/routes/design/templates/components/autofill_toggle.html +8 -12
- ivoryos/routes/design/templates/components/canvas.html +5 -14
- ivoryos/routes/design/templates/components/canvas_footer.html +5 -1
- ivoryos/routes/design/templates/components/canvas_header.html +36 -15
- ivoryos/routes/design/templates/components/canvas_main.html +34 -0
- ivoryos/routes/design/templates/components/deck_selector.html +8 -10
- ivoryos/routes/design/templates/components/edit_action_form.html +16 -7
- ivoryos/routes/design/templates/components/instruments_panel.html +66 -0
- ivoryos/routes/design/templates/components/modals/drop_modal.html +3 -5
- ivoryos/routes/design/templates/components/modals/new_script_modal.html +1 -2
- ivoryos/routes/design/templates/components/modals/rename_modal.html +5 -5
- ivoryos/routes/design/templates/components/modals/saveas_modal.html +2 -2
- ivoryos/routes/design/templates/components/python_code_overlay.html +26 -4
- ivoryos/routes/design/templates/components/sidebar.html +12 -13
- ivoryos/routes/design/templates/experiment_builder.html +20 -20
- ivoryos/routes/execute/execute.py +157 -13
- ivoryos/routes/execute/execute_file.py +38 -4
- ivoryos/routes/execute/templates/components/tab_bayesian.html +365 -114
- ivoryos/routes/execute/templates/components/tab_configuration.html +1 -1
- ivoryos/routes/library/library.py +70 -115
- ivoryos/routes/library/templates/library.html +27 -19
- ivoryos/static/js/action_handlers.js +213 -0
- ivoryos/static/js/db_delete.js +23 -0
- ivoryos/static/js/script_metadata.js +39 -0
- ivoryos/static/js/sortable_design.js +89 -56
- ivoryos/static/js/ui_state.js +113 -0
- ivoryos/utils/bo_campaign.py +137 -1
- ivoryos/utils/db_models.py +14 -5
- ivoryos/utils/form.py +4 -9
- ivoryos/utils/global_config.py +13 -1
- ivoryos/utils/script_runner.py +24 -5
- ivoryos/utils/serilize.py +203 -0
- ivoryos/utils/task_runner.py +4 -1
- ivoryos/version.py +1 -1
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/METADATA +1 -1
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/RECORD +54 -51
- ivoryos/routes/design/templates/components/action_list.html +0 -15
- ivoryos/routes/design/templates/components/operations_panel.html +0 -43
- ivoryos/routes/design/templates/components/script_info.html +0 -31
- ivoryos/routes/design/templates/components/scripts.html +0 -50
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/LICENSE +0 -0
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/WHEEL +0 -0
- {ivoryos-1.1.0.dist-info → ivoryos-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,16 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Move triggerModal to global scope
|
|
2
|
+
function triggerModal(formHtml, actionName, actionId, dropTargetId) {
|
|
3
|
+
if (formHtml && formHtml.trim() !== "") {
|
|
4
|
+
var $form = $("<div>").html(formHtml);
|
|
5
|
+
|
|
6
|
+
var $hiddenInput = $("<input>")
|
|
7
|
+
.attr("type", "hidden")
|
|
8
|
+
.attr("name", "drop_target_id")
|
|
9
|
+
.attr("id", "dropTargetInput")
|
|
10
|
+
.val(dropTargetId);
|
|
11
|
+
|
|
12
|
+
$form.find("button[type='submit']").before($hiddenInput);
|
|
13
|
+
|
|
14
|
+
$("#modalFormFields").empty().append($form.children());
|
|
15
|
+
|
|
16
|
+
const $modal = $("#dropModal");
|
|
17
|
+
|
|
18
|
+
setTimeout(() => {
|
|
19
|
+
showModal($modal);
|
|
20
|
+
}, 0);
|
|
21
|
+
|
|
22
|
+
$("#modalDropTarget").text(dropTargetId || "N/A");
|
|
23
|
+
$("#modalFormFields")
|
|
24
|
+
.data("action-id", actionId)
|
|
25
|
+
.data("action-name", actionName)
|
|
26
|
+
.data("drop-target-id", dropTargetId);
|
|
27
|
+
} else {
|
|
28
|
+
console.error("Form HTML is undefined or empty!");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function showModal($modal) {
|
|
33
|
+
$modal.modal({
|
|
34
|
+
backdrop: 'static',
|
|
35
|
+
keyboard: true,
|
|
36
|
+
focus: true
|
|
37
|
+
}).modal('show');
|
|
38
|
+
}
|
|
3
39
|
|
|
40
|
+
const state = {
|
|
41
|
+
dropTargetId: ""
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function initializeCanvas() {
|
|
4
45
|
$("#list ul").sortable({
|
|
5
46
|
cancel: ".unsortable",
|
|
6
47
|
opacity: 0.8,
|
|
7
48
|
cursor: "move",
|
|
8
49
|
placeholder: "drop-placeholder",
|
|
9
50
|
update: function () {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
51
|
+
const item_order = $("ul.reorder li").map(function () {
|
|
52
|
+
return this.id;
|
|
53
|
+
}).get();
|
|
14
54
|
var order_string = "order=" + item_order.join(",");
|
|
15
55
|
|
|
16
56
|
$.ajax({
|
|
@@ -19,87 +59,80 @@ $(document).ready(function () {
|
|
|
19
59
|
data: order_string,
|
|
20
60
|
cache: false,
|
|
21
61
|
success: function (data) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
window.location.href = window.location.href;
|
|
62
|
+
// Update the canvas content with the new HTML
|
|
63
|
+
updateActionCanvas(data)
|
|
25
64
|
}
|
|
65
|
+
}).fail(function (jqXHR, textStatus, errorThrown) {
|
|
66
|
+
console.error("Failed to update order:", textStatus, errorThrown);
|
|
26
67
|
});
|
|
27
68
|
}
|
|
28
69
|
});
|
|
29
70
|
|
|
30
71
|
// Make Entire Accordion Item Draggable
|
|
31
|
-
$(".accordion-item").on("dragstart", function (event) {
|
|
32
|
-
let formHtml = $(this).find(".accordion-body").html();
|
|
33
|
-
event.originalEvent.dataTransfer.setData("form", formHtml || "");
|
|
72
|
+
$(".accordion-item").off("dragstart").on("dragstart", function (event) {
|
|
73
|
+
let formHtml = $(this).find(".accordion-body").html();
|
|
74
|
+
event.originalEvent.dataTransfer.setData("form", formHtml || "");
|
|
34
75
|
event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
|
|
35
76
|
event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
|
|
36
|
-
|
|
37
77
|
$(this).addClass("dragging");
|
|
38
78
|
});
|
|
39
79
|
|
|
40
|
-
|
|
41
|
-
$("#list ul, .canvas").on("dragover", function (event) {
|
|
80
|
+
$("#list ul, .canvas").off("dragover").on("dragover", function (event) {
|
|
42
81
|
event.preventDefault();
|
|
43
82
|
let $target = $(event.target).closest("li");
|
|
44
83
|
|
|
45
|
-
// If we're over a valid <li> element in the list
|
|
46
84
|
if ($target.length) {
|
|
47
|
-
dropTargetId = $target.attr("id") || "";
|
|
48
|
-
|
|
49
|
-
$(".drop-placeholder").remove(); // Remove existing placeholders
|
|
50
|
-
$("<li class='drop-placeholder'></li>").insertBefore($target); // Insert before the target element
|
|
85
|
+
state.dropTargetId = $target.attr("id") || "";
|
|
86
|
+
insertDropPlaceholder($target);
|
|
51
87
|
} else if (!$("#list ul").children().length && $(this).hasClass("canvas")) {
|
|
52
|
-
$(".drop-placeholder").remove();
|
|
53
|
-
// $("#list ul").append("<li class='drop-placeholder'></li>"); // Append placeholder to canvas
|
|
88
|
+
$(".drop-placeholder").remove();
|
|
54
89
|
} else {
|
|
55
|
-
dropTargetId = "";
|
|
90
|
+
state.dropTargetId = "";
|
|
56
91
|
}
|
|
57
92
|
});
|
|
58
93
|
|
|
59
|
-
$("#list ul, .canvas").on("dragleave", function () {
|
|
60
|
-
$(".drop-placeholder").remove();
|
|
94
|
+
$("#list ul, .canvas").off("dragleave").on("dragleave", function () {
|
|
95
|
+
$(".drop-placeholder").remove();
|
|
61
96
|
});
|
|
62
97
|
|
|
63
|
-
$("#list ul, .canvas").on("drop", function (event) {
|
|
98
|
+
$("#list ul, .canvas").off("drop").on("drop", function (event) {
|
|
64
99
|
event.preventDefault();
|
|
65
|
-
|
|
66
100
|
var actionName = event.originalEvent.dataTransfer.getData("action");
|
|
67
101
|
var actionId = event.originalEvent.dataTransfer.getData("id");
|
|
68
|
-
var formHtml = event.originalEvent.dataTransfer.getData("form");
|
|
102
|
+
var formHtml = event.originalEvent.dataTransfer.getData("form");
|
|
69
103
|
let listLength = $("ul.reorder li").length;
|
|
70
|
-
dropTargetId = dropTargetId || listLength + 1;
|
|
104
|
+
state.dropTargetId = state.dropTargetId || listLength + 1;
|
|
71
105
|
$(".drop-placeholder").remove();
|
|
72
|
-
|
|
73
|
-
triggerModal(formHtml, actionName, actionId, dropTargetId);
|
|
74
|
-
|
|
106
|
+
document.activeElement?.blur();
|
|
107
|
+
triggerModal(formHtml, actionName, actionId, state.dropTargetId);
|
|
75
108
|
});
|
|
109
|
+
}
|
|
76
110
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
111
|
+
function insertDropPlaceholder($target) {
|
|
112
|
+
$(".drop-placeholder").remove();
|
|
113
|
+
$("<li class='drop-placeholder'></li>").insertBefore($target);
|
|
114
|
+
}
|
|
81
115
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.attr("id", "dropTargetInput")
|
|
87
|
-
.val(dropTargetId);
|
|
116
|
+
// Add this function to sortable_design.js
|
|
117
|
+
function initializeDragHandlers() {
|
|
118
|
+
$(".accordion-item").off("dragstart").on("dragstart", function (event) {
|
|
119
|
+
let formHtml = $(this).find(".accordion-body form").prop('outerHTML');
|
|
88
120
|
|
|
89
|
-
|
|
90
|
-
|
|
121
|
+
if (!formHtml) {
|
|
122
|
+
console.error("Form not found in accordion-body");
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
91
125
|
|
|
92
|
-
|
|
93
|
-
|
|
126
|
+
event.originalEvent.dataTransfer.setData("form", formHtml);
|
|
127
|
+
event.originalEvent.dataTransfer.setData("action", $(this).find(".draggable-action").data("action"));
|
|
128
|
+
event.originalEvent.dataTransfer.setData("id", $(this).find(".draggable-action").attr("id"));
|
|
94
129
|
|
|
95
|
-
|
|
96
|
-
|
|
130
|
+
$(this).addClass("dragging");
|
|
131
|
+
});
|
|
132
|
+
}
|
|
97
133
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
});
|
|
134
|
+
// Make sure it's called in the document ready function
|
|
135
|
+
$(document).ready(function () {
|
|
136
|
+
initializeCanvas();
|
|
137
|
+
initializeDragHandlers(); // Add this line
|
|
138
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Toggle visibility of line numbers
|
|
2
|
+
function toggleLineNumbers(save = true) {
|
|
3
|
+
const show = document.getElementById('toggleLineNumbers').checked;
|
|
4
|
+
document.querySelectorAll('.line-number').forEach(el => {
|
|
5
|
+
el.classList.toggle('d-none', !show);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
if (save) {
|
|
9
|
+
localStorage.setItem('showLineNumbers', show ? 'true' : 'false');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Toggle visibility of Python code overlay
|
|
14
|
+
function toggleCodeOverlay(state = null) {
|
|
15
|
+
const overlay = document.getElementById("pythonCodeOverlay");
|
|
16
|
+
const checkbox = document.getElementById("showPythonCodeSwitch");
|
|
17
|
+
|
|
18
|
+
const isVisible = overlay.classList.contains("show");
|
|
19
|
+
const newState = state !== null ? state : !isVisible;
|
|
20
|
+
|
|
21
|
+
if (newState) {
|
|
22
|
+
overlay.classList.add("show");
|
|
23
|
+
checkbox.checked = true;
|
|
24
|
+
} else {
|
|
25
|
+
overlay.classList.remove("show");
|
|
26
|
+
checkbox.checked = false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Save state to session via PATCH
|
|
30
|
+
fetch(scriptUIStateUrl, {
|
|
31
|
+
method: "PATCH",
|
|
32
|
+
headers: { "Content-Type": "application/json" },
|
|
33
|
+
body: JSON.stringify({ show_code: newState })
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function setScriptPhase(stype) {
|
|
39
|
+
fetch(scriptUIStateUrl, {
|
|
40
|
+
method: "PATCH",
|
|
41
|
+
headers: {
|
|
42
|
+
"Content-Type": "application/json",
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify({ editing_type: stype })
|
|
45
|
+
})
|
|
46
|
+
.then(res => res.json())
|
|
47
|
+
.then(data => {
|
|
48
|
+
if (data.html) {
|
|
49
|
+
document.getElementById("canvas-wrapper").innerHTML = data.html;
|
|
50
|
+
initializeCanvas(); // Reinitialize the canvas functionality
|
|
51
|
+
document.querySelectorAll('#pythonCodeOverlay pre code').forEach((block) => {
|
|
52
|
+
hljs.highlightElement(block);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
.catch(error => console.error("Failed to update editing type", error));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
function changeDeck(deck) {
|
|
62
|
+
fetch(scriptUIStateUrl, {
|
|
63
|
+
method: "PATCH",
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/json",
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify({ deck_name: deck })
|
|
68
|
+
})
|
|
69
|
+
.then(res => res.json())
|
|
70
|
+
.then(data => {
|
|
71
|
+
if (data.html) {
|
|
72
|
+
document.getElementById("sidebar-wrapper").innerHTML = data.html;
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
.catch(error => console.error("Failed to change deck", error));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
function toggleAutoFill() {
|
|
81
|
+
const instrumentValue = document.querySelector('.form-check.form-switch').dataset.instrument;
|
|
82
|
+
|
|
83
|
+
fetch(scriptUIStateUrl, {
|
|
84
|
+
method: "PATCH",
|
|
85
|
+
headers: {
|
|
86
|
+
"Content-Type": "application/json",
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify({
|
|
89
|
+
autofill: document.getElementById("autoFillCheck").checked,
|
|
90
|
+
instrument: instrumentValue
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
.then(res => res.json())
|
|
94
|
+
.then(data => {
|
|
95
|
+
if (data.html) {
|
|
96
|
+
document.getElementById("instrument-panel").innerHTML = data.html;
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
// Restore state on page load
|
|
101
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
102
|
+
const savedState = localStorage.getItem('showLineNumbers');
|
|
103
|
+
const checkbox = document.getElementById('toggleLineNumbers');
|
|
104
|
+
|
|
105
|
+
if (savedState === 'true') {
|
|
106
|
+
checkbox.checked = true;
|
|
107
|
+
}
|
|
108
|
+
if (checkbox) {
|
|
109
|
+
toggleLineNumbers(false); // don't overwrite localStorage on load
|
|
110
|
+
checkbox.addEventListener('change', () => toggleLineNumbers());
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
});
|
ivoryos/utils/bo_campaign.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
import re
|
|
1
3
|
from ivoryos.utils.utils import install_and_import
|
|
2
4
|
|
|
3
5
|
|
|
@@ -124,4 +126,138 @@ def exisitng_data_gs(data_len):
|
|
|
124
126
|
),
|
|
125
127
|
]
|
|
126
128
|
)
|
|
127
|
-
return gs
|
|
129
|
+
return gs
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def parse_optimization_form(form_data: Dict[str, str]):
|
|
133
|
+
"""
|
|
134
|
+
Parse dynamic form data into structured optimization configuration.
|
|
135
|
+
|
|
136
|
+
Expected form field patterns:
|
|
137
|
+
- Objectives: {name}_min, {name}_weight
|
|
138
|
+
- Parameters: {name}_type, {name}_min, {name}_max, {name}_choices, {name}_value_type
|
|
139
|
+
- Config: step{n}_model, step{n}_num_samples
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
objectives = []
|
|
143
|
+
parameters = []
|
|
144
|
+
config = {}
|
|
145
|
+
|
|
146
|
+
# Track processed field names to avoid duplicates
|
|
147
|
+
processed_objectives = set()
|
|
148
|
+
processed_parameters = set()
|
|
149
|
+
|
|
150
|
+
# Parse objectives
|
|
151
|
+
for field_name, value in form_data.items():
|
|
152
|
+
if field_name.endswith('_min') and value:
|
|
153
|
+
# Extract objective name
|
|
154
|
+
obj_name = field_name.replace('_min', '')
|
|
155
|
+
if obj_name in processed_objectives:
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
# Check if corresponding weight exists
|
|
159
|
+
weight_field = f"{obj_name}_weight"
|
|
160
|
+
if weight_field in form_data and form_data[weight_field]:
|
|
161
|
+
objectives.append({
|
|
162
|
+
"name": obj_name,
|
|
163
|
+
"minimize": value == "minimize",
|
|
164
|
+
"weight": float(form_data[weight_field])
|
|
165
|
+
})
|
|
166
|
+
else:
|
|
167
|
+
objectives.append({
|
|
168
|
+
"name": obj_name,
|
|
169
|
+
"minimize": value == "minimize",
|
|
170
|
+
})
|
|
171
|
+
processed_objectives.add(obj_name)
|
|
172
|
+
|
|
173
|
+
# Parse parameters
|
|
174
|
+
for field_name, value in form_data.items():
|
|
175
|
+
if field_name.endswith('_type') and value:
|
|
176
|
+
# Extract parameter name
|
|
177
|
+
param_name = field_name.replace('_type', '')
|
|
178
|
+
if param_name in processed_parameters:
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
parameter = {
|
|
182
|
+
"name": param_name,
|
|
183
|
+
"type": value
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# Get value type (default to float)
|
|
187
|
+
value_type_field = f"{param_name}_value_type"
|
|
188
|
+
value_type = form_data.get(value_type_field, "float")
|
|
189
|
+
parameter["value_type"] = value_type
|
|
190
|
+
|
|
191
|
+
# Handle different parameter types
|
|
192
|
+
if value == "range":
|
|
193
|
+
min_field = f"{param_name}_min"
|
|
194
|
+
max_field = f"{param_name}_max"
|
|
195
|
+
|
|
196
|
+
if min_field in form_data and max_field in form_data:
|
|
197
|
+
min_val = form_data[min_field]
|
|
198
|
+
max_val = form_data[max_field]
|
|
199
|
+
|
|
200
|
+
if min_val and max_val:
|
|
201
|
+
# Convert based on value_type
|
|
202
|
+
if value_type == "int":
|
|
203
|
+
bounds = [int(min_val), int(max_val)]
|
|
204
|
+
elif value_type == "float":
|
|
205
|
+
bounds = [float(min_val), float(max_val)]
|
|
206
|
+
else: # string
|
|
207
|
+
bounds = [str(min_val), str(max_val)]
|
|
208
|
+
|
|
209
|
+
parameter["bounds"] = bounds
|
|
210
|
+
|
|
211
|
+
elif value == "choice":
|
|
212
|
+
choices_field = f"{param_name}_choices"
|
|
213
|
+
if choices_field in form_data and form_data[choices_field]:
|
|
214
|
+
# Split choices by comma and clean whitespace
|
|
215
|
+
choices = [choice.strip() for choice in form_data[choices_field].split(',')]
|
|
216
|
+
|
|
217
|
+
# Convert choices based on value_type
|
|
218
|
+
if value_type == "int":
|
|
219
|
+
choices = [int(choice) for choice in choices if choice.isdigit()]
|
|
220
|
+
elif value_type == "float":
|
|
221
|
+
choices = [float(choice) for choice in choices if
|
|
222
|
+
choice.replace('.', '').replace('-', '').isdigit()]
|
|
223
|
+
# For string, keep as is
|
|
224
|
+
|
|
225
|
+
parameter["bounds"] = choices
|
|
226
|
+
|
|
227
|
+
elif value == "fixed":
|
|
228
|
+
fixed_field = f"{param_name}_value"
|
|
229
|
+
if fixed_field in form_data and form_data[fixed_field]:
|
|
230
|
+
fixed_val = form_data[fixed_field]
|
|
231
|
+
|
|
232
|
+
# Convert based on value_type
|
|
233
|
+
if value_type == "int":
|
|
234
|
+
parameter["value"] = int(fixed_val)
|
|
235
|
+
elif value_type == "float":
|
|
236
|
+
parameter["value"] = float(fixed_val)
|
|
237
|
+
else:
|
|
238
|
+
parameter["value"] = str(fixed_val)
|
|
239
|
+
|
|
240
|
+
parameters.append(parameter)
|
|
241
|
+
processed_parameters.add(param_name)
|
|
242
|
+
|
|
243
|
+
# Parse configuration steps
|
|
244
|
+
step_pattern = re.compile(r'step(\d+)_(.+)')
|
|
245
|
+
steps = {}
|
|
246
|
+
|
|
247
|
+
for field_name, value in form_data.items():
|
|
248
|
+
match = step_pattern.match(field_name)
|
|
249
|
+
if match and value:
|
|
250
|
+
step_num = int(match.group(1))
|
|
251
|
+
step_attr = match.group(2)
|
|
252
|
+
step_key = f"step_{step_num}"
|
|
253
|
+
|
|
254
|
+
if step_key not in steps:
|
|
255
|
+
steps[step_key] = {}
|
|
256
|
+
|
|
257
|
+
# Convert num_samples to int if it's a number field
|
|
258
|
+
if step_attr == "num_samples":
|
|
259
|
+
steps[step_key][step_attr] = int(value)
|
|
260
|
+
else:
|
|
261
|
+
steps[step_key][step_attr] = value
|
|
262
|
+
|
|
263
|
+
return parameters, objectives, steps
|
ivoryos/utils/db_models.py
CHANGED
|
@@ -265,11 +265,20 @@ class Script(db.Model):
|
|
|
265
265
|
output_variables: Dict[str, str] = self.get_variables()
|
|
266
266
|
# print(output_variables)
|
|
267
267
|
for key, value in kwargs.items():
|
|
268
|
-
if
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
if isinstance(value, str):
|
|
269
|
+
if value in output_variables:
|
|
270
|
+
var_type = output_variables[value]
|
|
271
|
+
kwargs[key] = {value: var_type}
|
|
272
|
+
elif value.startswith("#"):
|
|
273
|
+
kwargs[key] = f"#{self.validate_function_name(value[1:])}"
|
|
274
|
+
else:
|
|
275
|
+
# attempt to convert to numerical or bool value for args with no type hint
|
|
276
|
+
try:
|
|
277
|
+
converted = ast.literal_eval(value)
|
|
278
|
+
if isinstance(converted, (int, float, bool)):
|
|
279
|
+
kwargs[key] = converted
|
|
280
|
+
except (ValueError, SyntaxError):
|
|
281
|
+
pass
|
|
273
282
|
return kwargs
|
|
274
283
|
|
|
275
284
|
def add_logic_action(self, logic_type: str, statement, insert_position=None):
|
ivoryos/utils/form.py
CHANGED
|
@@ -214,11 +214,6 @@ class FlexibleEnumField(StringField):
|
|
|
214
214
|
f"Invalid choice: '{key}'. Must match one of {list(self.enum_class.__members__.keys())}")
|
|
215
215
|
|
|
216
216
|
|
|
217
|
-
def format_name(name):
|
|
218
|
-
"""Converts 'example_name' to 'Example Name'."""
|
|
219
|
-
name = name.split(".")[-1]
|
|
220
|
-
text = ' '.join(word for word in name.split('_'))
|
|
221
|
-
return text.capitalize()
|
|
222
217
|
|
|
223
218
|
def parse_annotation(annotation):
|
|
224
219
|
"""
|
|
@@ -264,7 +259,7 @@ def create_form_for_method(method, autofill, script=None, design=True):
|
|
|
264
259
|
for param in sig.parameters.values():
|
|
265
260
|
if param.name == 'self':
|
|
266
261
|
continue
|
|
267
|
-
formatted_param_name = format_name(param.name)
|
|
262
|
+
# formatted_param_name = format_name(param.name)
|
|
268
263
|
|
|
269
264
|
default_value = None
|
|
270
265
|
if autofill:
|
|
@@ -277,7 +272,7 @@ def create_form_for_method(method, autofill, script=None, design=True):
|
|
|
277
272
|
default_value = param.default
|
|
278
273
|
|
|
279
274
|
field_kwargs = {
|
|
280
|
-
"label":
|
|
275
|
+
"label": param.name,
|
|
281
276
|
"default": default_value,
|
|
282
277
|
"validators": [InputRequired()] if param.default is param.empty else [Optional()],
|
|
283
278
|
**({"script": script} if (autofill or design) else {})
|
|
@@ -396,12 +391,12 @@ def create_form_from_action(action: dict, script=None, design=True):
|
|
|
396
391
|
}
|
|
397
392
|
|
|
398
393
|
for name, param_type in arg_types.items():
|
|
399
|
-
formatted_param_name = format_name(name)
|
|
394
|
+
# formatted_param_name = format_name(name)
|
|
400
395
|
value = args.get(name, "")
|
|
401
396
|
if type(value) is dict:
|
|
402
397
|
value = next(iter(value))
|
|
403
398
|
field_kwargs = {
|
|
404
|
-
"label":
|
|
399
|
+
"label": name,
|
|
405
400
|
"default": f'{value}',
|
|
406
401
|
"validators": [InputRequired()],
|
|
407
402
|
**({"script": script})
|
ivoryos/utils/global_config.py
CHANGED
|
@@ -15,6 +15,7 @@ class GlobalConfig:
|
|
|
15
15
|
cls._instance._deck_snapshot = {}
|
|
16
16
|
cls._instance._runner_lock = threading.Lock()
|
|
17
17
|
cls._instance._runner_status = None
|
|
18
|
+
cls._instance._optimizers = {}
|
|
18
19
|
return cls._instance
|
|
19
20
|
|
|
20
21
|
@property
|
|
@@ -84,4 +85,15 @@ class GlobalConfig:
|
|
|
84
85
|
|
|
85
86
|
@runner_status.setter
|
|
86
87
|
def runner_status(self, value):
|
|
87
|
-
self._runner_status = value
|
|
88
|
+
self._runner_status = value
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def optimizers(self):
|
|
92
|
+
return self._optimizers
|
|
93
|
+
|
|
94
|
+
@optimizers.setter
|
|
95
|
+
def optimizers(self, value):
|
|
96
|
+
if isinstance(value, dict):
|
|
97
|
+
self._optimizers = value
|
|
98
|
+
else:
|
|
99
|
+
raise ValueError("Optimizers must be a dictionary.")
|
ivoryos/utils/script_runner.py
CHANGED
|
@@ -62,7 +62,7 @@ class ScriptRunner:
|
|
|
62
62
|
|
|
63
63
|
|
|
64
64
|
def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
|
|
65
|
-
output_path="", compiled=False, current_app=None, history=None):
|
|
65
|
+
output_path="", compiled=False, current_app=None, history=None, optimizer=None):
|
|
66
66
|
global deck
|
|
67
67
|
if deck is None:
|
|
68
68
|
deck = global_config.deck
|
|
@@ -82,7 +82,7 @@ class ScriptRunner:
|
|
|
82
82
|
thread = threading.Thread(
|
|
83
83
|
target=self._run_with_stop_check,
|
|
84
84
|
args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path, current_app, compiled,
|
|
85
|
-
history)
|
|
85
|
+
history, optimizer)
|
|
86
86
|
)
|
|
87
87
|
thread.start()
|
|
88
88
|
return thread
|
|
@@ -183,7 +183,7 @@ class ScriptRunner:
|
|
|
183
183
|
return exec_locals # Return the 'results' variable
|
|
184
184
|
|
|
185
185
|
def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
|
|
186
|
-
output_path, current_app, compiled, history=None):
|
|
186
|
+
output_path, current_app, compiled, history=None, optimizer=None):
|
|
187
187
|
time.sleep(1)
|
|
188
188
|
# _func_str = script.compile()
|
|
189
189
|
# step_list_dict: dict = script.convert_to_lines(_func_str)
|
|
@@ -207,7 +207,7 @@ class ScriptRunner:
|
|
|
207
207
|
if repeat_count:
|
|
208
208
|
self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
|
|
209
209
|
run_name, return_list, compiled, logger, socketio,
|
|
210
|
-
history, output_path, run_id=run_id)
|
|
210
|
+
history, output_path, run_id=run_id, optimizer=optimizer)
|
|
211
211
|
elif config:
|
|
212
212
|
self._run_config_section(config, arg_type, output_list, script, run_name, logger,
|
|
213
213
|
socketio, run_id=run_id, compiled=compiled)
|
|
@@ -264,7 +264,7 @@ class ScriptRunner:
|
|
|
264
264
|
output_list.append(output)
|
|
265
265
|
|
|
266
266
|
def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list, compiled,
|
|
267
|
-
logger, socketio, history, output_path, run_id):
|
|
267
|
+
logger, socketio, history, output_path, run_id, optimizer=None):
|
|
268
268
|
if bo_args:
|
|
269
269
|
logger.info('Initializing optimizer...')
|
|
270
270
|
if compiled:
|
|
@@ -283,6 +283,14 @@ class ScriptRunner:
|
|
|
283
283
|
output_list.append(row)
|
|
284
284
|
else:
|
|
285
285
|
ax_client = bo_campaign.ax_init_form(bo_args, arg_types)
|
|
286
|
+
elif optimizer and history:
|
|
287
|
+
import pandas as pd
|
|
288
|
+
file_path = os.path.join(output_path, history)
|
|
289
|
+
|
|
290
|
+
previous_runs = pd.read_csv(file_path)
|
|
291
|
+
optimizer.append_existing_data(previous_runs)
|
|
292
|
+
for row in previous_runs:
|
|
293
|
+
output_list.append(row)
|
|
286
294
|
|
|
287
295
|
|
|
288
296
|
|
|
@@ -307,6 +315,17 @@ class ScriptRunner:
|
|
|
307
315
|
except Exception as e:
|
|
308
316
|
logger.info(f'Optimization error: {e}')
|
|
309
317
|
break
|
|
318
|
+
elif optimizer:
|
|
319
|
+
try:
|
|
320
|
+
parameters = optimizer.suggest(1)
|
|
321
|
+
logger.info(f'Output value: {parameters}')
|
|
322
|
+
output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress, **parameters)
|
|
323
|
+
if output:
|
|
324
|
+
optimizer.observe(output)
|
|
325
|
+
output.update(parameters)
|
|
326
|
+
except Exception as e:
|
|
327
|
+
logger.info(f'Optimization error: {e}')
|
|
328
|
+
break
|
|
310
329
|
else:
|
|
311
330
|
# fname = f"{run_name}_script"
|
|
312
331
|
# function = self.globals_dict[fname]
|