ivoryos 1.3.8__py3-none-any.whl → 1.4.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.

Files changed (34) hide show
  1. ivoryos/optimizer/ax_optimizer.py +44 -25
  2. ivoryos/optimizer/base_optimizer.py +15 -1
  3. ivoryos/optimizer/baybe_optimizer.py +24 -17
  4. ivoryos/optimizer/nimo_optimizer.py +26 -24
  5. ivoryos/routes/data/data.py +27 -9
  6. ivoryos/routes/data/templates/components/step_card.html +47 -11
  7. ivoryos/routes/data/templates/workflow_view.html +14 -5
  8. ivoryos/routes/design/design.py +31 -1
  9. ivoryos/routes/design/design_step.py +2 -1
  10. ivoryos/routes/design/templates/components/edit_action_form.html +16 -3
  11. ivoryos/routes/design/templates/components/python_code_overlay.html +27 -10
  12. ivoryos/routes/design/templates/experiment_builder.html +1 -0
  13. ivoryos/routes/execute/execute.py +36 -7
  14. ivoryos/routes/execute/templates/components/run_tabs.html +45 -2
  15. ivoryos/routes/execute/templates/components/tab_bayesian.html +447 -325
  16. ivoryos/routes/execute/templates/components/tab_configuration.html +303 -18
  17. ivoryos/routes/execute/templates/components/tab_repeat.html +6 -2
  18. ivoryos/routes/execute/templates/experiment_run.html +0 -264
  19. ivoryos/socket_handlers.py +1 -1
  20. ivoryos/static/js/action_handlers.js +252 -118
  21. ivoryos/static/js/sortable_design.js +1 -0
  22. ivoryos/utils/bo_campaign.py +17 -16
  23. ivoryos/utils/db_models.py +122 -18
  24. ivoryos/utils/decorators.py +1 -0
  25. ivoryos/utils/form.py +22 -5
  26. ivoryos/utils/nest_script.py +314 -0
  27. ivoryos/utils/script_runner.py +447 -147
  28. ivoryos/utils/utils.py +11 -1
  29. ivoryos/version.py +1 -1
  30. {ivoryos-1.3.8.dist-info → ivoryos-1.4.0.dist-info}/METADATA +3 -2
  31. {ivoryos-1.3.8.dist-info → ivoryos-1.4.0.dist-info}/RECORD +34 -33
  32. {ivoryos-1.3.8.dist-info → ivoryos-1.4.0.dist-info}/WHEEL +0 -0
  33. {ivoryos-1.3.8.dist-info → ivoryos-1.4.0.dist-info}/licenses/LICENSE +0 -0
  34. {ivoryos-1.3.8.dist-info → ivoryos-1.4.0.dist-info}/top_level.txt +0 -0
@@ -23,11 +23,24 @@
23
23
  {% else %}
24
24
  {{ forms.hidden_tag() }}
25
25
  {% for field in forms %}
26
+
26
27
  {% if field.type not in ['CSRFTokenField'] %}
27
28
  <div class="input-group mb-3">
28
- <label class="input-group-text">{{ field.label.text }}</label>
29
- {{ field(class="form-control") }}
30
- <div class="form-text">{{ field.description }} </div>
29
+ <label class="input-group-text">{{ field.label.text | format_name }}</label>
30
+ {% if field.type == "BooleanField" %}
31
+ {{ field(class="form-check-input") }}
32
+ {% elif field.type == "FlexibleEnumField" %}
33
+ <input type="text" id="{{ field.id }}" name="{{ field.name }}" value="{{ field.data }}"
34
+ list="{{ field.id }}_options" placeholder="{{ field.render_kw.placeholder if field.render_kw and field.render_kw.placeholder }}"
35
+ class="form-control">
36
+ <datalist id="{{ field.id }}_options">
37
+ {% for key in field.choices %}
38
+ <option value="{{ key }}">{{ key }}</option>
39
+ {% endfor %}
40
+ </datalist>
41
+ {% else %}
42
+ {{ field(class="form-control") }}
43
+ {% endif %}
31
44
  </div>
32
45
  {% endif %}
33
46
  {% endfor %}
@@ -1,5 +1,4 @@
1
- {# Python code overlay component #}
2
- {#{% if session.get('show_code') %}#}
1
+ {# Python code overlay component - Pure HTML #}
3
2
  <style>
4
3
  .code-overlay {
5
4
  position: fixed;
@@ -16,7 +15,7 @@
16
15
  transform: translateX(0);
17
16
  }
18
17
  </style>
19
- <script>hljs.highlightAll();</script>
18
+
20
19
  {% if session.get('show_code') %}
21
20
  <div id="pythonCodeOverlay" class="code-overlay bg-light border-start show">
22
21
  {% else %}
@@ -29,11 +28,29 @@
29
28
  </button>
30
29
  </div>
31
30
  <div class="overlay-content p-3">
32
- {% for stype, script in session.get('python_code', {}).items() %}
33
- <pre><code class="language-python">{{ script }}</code></pre>
34
- {% endfor %}
35
- <a href="{{ url_for('design.design_files.download_python', filetype='python') }}">
36
- Download <i class="bi bi-download"></i>
37
- </a>
31
+ <!-- Top toggles (Single / Batch) -->
32
+ <div class="btn-group mb-3">
33
+ <button type="button" class="btn btn-outline-primary mode-toggle active" data-mode="single">Single</button>
34
+ <button type="button" class="btn btn-outline-primary mode-toggle" data-mode="batch">Batch</button>
35
+ </div>
36
+
37
+ {# <!-- Secondary toggles (By Sample / By Step) TODO -->#}
38
+ {# <div class="btn-group mb-3" id="batch-options" style="display: none;">#}
39
+ {# <button type="button" class="btn btn-outline-secondary batch-toggle active" data-batch="sample">By Sample</button>#}
40
+ {# <button type="button" class="btn btn-outline-secondary batch-toggle" data-batch="step">By Step</button>#}
41
+ {# </div>#}
42
+
43
+ <!-- Code display -->
44
+ <pre><code id="python-code" class="language-python">Loading...</code></pre>
45
+
46
+ <!-- Action buttons -->
47
+ <div class="mt-2">
48
+ <button type="button" id="copy-code" class="btn btn-sm btn-outline-success">
49
+ <i class="bi bi-clipboard"></i> Copy
50
+ </button>
51
+ <button type="button" id="download-code" class="btn btn-sm btn-outline-primary">
52
+ <i class="bi bi-download"></i> Download
53
+ </button>
54
+ </div>
38
55
  </div>
39
- </div>
56
+ </div>
@@ -32,6 +32,7 @@
32
32
  const scriptStepUrl = `{{ url_for('design.design_steps.get_step', uuid=0) }}`;
33
33
  const scriptStepDupUrl = `{{ url_for('design.design_steps.duplicate_action', uuid=0) }}`;
34
34
  const scriptDeleteUrl = "{{ url_for('design.clear_draft') }}";
35
+ const scriptCompileUrl = "{{ url_for('design.compile_preview') }}";
35
36
  </script>
36
37
  <script src="{{ url_for('static', filename='js/sortable_design.js') }}"></script>
37
38
  <script src="{{ url_for('static', filename='js/action_handlers.js') }}"></script>
@@ -76,14 +76,17 @@ def experiment_run():
76
76
  if isinstance(exec_string, dict):
77
77
  for key, func_str in exec_string.items():
78
78
  exec(func_str)
79
- line_collection = script.convert_to_lines(exec_string)
79
+
80
80
  else:
81
81
  # Handle string case - you might need to adjust this based on your needs
82
- line_collection = []
82
+ line_collection = {}
83
83
  except Exception:
84
84
  flash(f"Please check syntax!!")
85
85
  return redirect(url_for("design.experiment_builder"))
86
86
 
87
+
88
+ line_collection = script.render_script_lines(script.script_dict)
89
+
87
90
  run_name = script.name if script.name else "untitled"
88
91
 
89
92
  dismiss = session.get("dismiss", None)
@@ -92,8 +95,13 @@ def experiment_run():
92
95
 
93
96
  _, return_list = script.config_return()
94
97
  config_list, config_type_list = script.config("script")
95
- data_list = os.listdir(current_app.config['DATA_FOLDER'])
96
- data_list.remove(".gitkeep") if ".gitkeep" in data_list else data_list
98
+ data_list = [f for f in os.listdir(current_app.config['DATA_FOLDER']) if f.endswith('.csv')]
99
+ # Remove .gitkeep if present
100
+ if ".gitkeep" in data_list:
101
+ data_list.remove(".gitkeep")
102
+
103
+ # Sort by creation time, newest first
104
+ data_list.sort(key=lambda f: os.path.getctime(os.path.join(current_app.config['DATA_FOLDER'], f)), reverse=True)
97
105
 
98
106
  if deck is None:
99
107
  no_deck_warning = True
@@ -115,21 +123,26 @@ def experiment_run():
115
123
  elif "parameters" in payload_json:
116
124
  bo_args = payload_json
117
125
  repeat = payload_json.pop("repeat", None)
126
+ batch_size = payload_json.pop('batch_size', 1)
118
127
  else:
119
128
  if "bo" in request.form:
120
129
  bo_args = request.form.to_dict()
121
130
  existing_data = bo_args.pop("existing_data")
122
131
  if "online-config" in request.form:
123
- config = utils.web_config_entry_wrapper(request.form.to_dict(), config_list)
132
+ config_args = request.form.to_dict()
133
+ config_args.pop("batch_size", None)
134
+ config = utils.web_config_entry_wrapper(config_args, config_list)
135
+ batch_size = int(request.form.get('batch_size', 1))
124
136
  repeat = request.form.get('repeat', None)
125
137
 
126
138
  try:
139
+ # if True:
127
140
  datapath = current_app.config["DATA_FOLDER"]
128
141
  run_name = script.validate_function_name(run_name)
129
142
  runner.run_script(script=script, run_name=run_name, config=config, bo_args=bo_args,
130
143
  logger=g.logger, socketio=g.socketio, repeat_count=repeat,
131
144
  output_path=datapath, compiled=compiled, history=existing_data,
132
- current_app=current_app._get_current_object()
145
+ current_app=current_app._get_current_object(), batch_size=batch_size
133
146
  )
134
147
  if utils.check_config_duplicate(config):
135
148
  flash(f"WARNING: Duplicate in config entries.")
@@ -176,19 +189,35 @@ def run_bo():
176
189
  repeat = payload.pop("repeat", None)
177
190
  optimizer_type = payload.pop("optimizer_type", None)
178
191
  existing_data = payload.pop("existing_data", None)
192
+ batch_mode = payload.pop("batch_mode", None)
193
+ batch_size = payload.pop("batch_size", 1)
194
+
195
+ # Get constraint expressions (new single-line input)
196
+ constraint_exprs = request.form.getlist("constraint_expr")
197
+ constraints = [expr.strip() for expr in constraint_exprs if expr.strip()]
198
+
199
+ # Remove constraint_expr entries from payload before parsing parameters
200
+ for key in list(payload.keys()):
201
+ if key.startswith("constraint_expr"):
202
+ payload.pop(key, None)
203
+
179
204
  parameters, objectives, steps = parse_optimization_form(payload)
180
205
  try:
206
+
207
+ # if True:
181
208
  datapath = current_app.config["DATA_FOLDER"]
182
209
  run_name = script.validate_function_name(run_name)
183
210
  Optimizer = global_config.optimizers.get(optimizer_type, None)
184
211
  if not Optimizer:
185
212
  raise ValueError(f"Optimizer {optimizer_type} is not supported or not found.")
186
213
  optimizer = Optimizer(experiment_name=run_name, parameter_space=parameters, objective_config=objectives,
214
+ parameter_constraints = constraints,
187
215
  optimizer_config=steps, datapath=datapath)
188
216
  runner.run_script(script=script, run_name=run_name, optimizer=optimizer,
189
217
  logger=g.logger, socketio=g.socketio, repeat_count=repeat,
190
218
  output_path=datapath, compiled=False, history=existing_data,
191
- current_app=current_app._get_current_object()
219
+ current_app=current_app._get_current_object(), batch_mode=batch_mode, batch_size=int(batch_size),
220
+ objectives=objectives
192
221
  )
193
222
 
194
223
  except Exception as e:
@@ -1,4 +1,4 @@
1
- {# Run tabs component for experiment run #}
1
+ <!-- Main template structure -->
2
2
  <ul class="nav nav-tabs" id="myTabs" role="tablist">
3
3
  <li class="nav-item" role="presentation">
4
4
  <a class="nav-link {{ 'disabled' if config_list else '' }} {{ 'active' if not config_list else '' }}" id="tab1-tab" data-bs-toggle="tab" href="#tab1" role="tab" aria-controls="tab1" aria-selected="false">Repeat</a>
@@ -14,4 +14,47 @@
14
14
  {% include 'components/tab_repeat.html' %}
15
15
  {% include 'components/tab_configuration.html' %}
16
16
  {% include 'components/tab_bayesian.html' %}
17
- </div>
17
+ </div>
18
+
19
+ <!-- ============================================ -->
20
+ <!-- SWITCH TO THE LAST ACTIVE TAB ON PAGE LOAD -->
21
+ <!-- ============================================ -->
22
+ <script>
23
+ (function() {
24
+ 'use strict';
25
+
26
+ // Store active tab when changed
27
+ document.addEventListener('shown.bs.tab', function(e) {
28
+ const tabId = e.target.id.replace('-tab', '');
29
+ if (tabId === 'tab2' || tabId === 'tab3') {
30
+ localStorage.setItem('ivoryosLastTab', tabId);
31
+ console.log('Saved last active tab:', tabId);
32
+ }
33
+
34
+ // Optional: run specific load logic per tab
35
+ if (tabId === 'tab2' && typeof window.loadConfigData === 'function') {
36
+ window.loadConfigData();
37
+ } else if (tabId === 'tab3' && typeof window.saveBO === 'function') {
38
+ window.saveBO();
39
+ }
40
+ });
41
+
42
+ // Restore tab from last session
43
+ document.addEventListener('DOMContentLoaded', function() {
44
+ const lastTab = localStorage.getItem('ivoryosLastTab');
45
+ const tabElement = lastTab ? document.getElementById(lastTab + '-tab') : null;
46
+
47
+ if (tabElement && !tabElement.classList.contains('disabled')) {
48
+ new bootstrap.Tab(tabElement).show();
49
+ console.log('Restored last active tab:', lastTab);
50
+ } else {
51
+ // Default to first non-disabled tab
52
+ const firstTab = document.querySelector('#myTabs .nav-link:not(.disabled)');
53
+ if (firstTab) {
54
+ new bootstrap.Tab(firstTab).show();
55
+ console.log('Defaulted to first available tab.');
56
+ }
57
+ }
58
+ });
59
+ })();
60
+ </script>