ivoryos 0.1.18__py3-none-any.whl → 0.1.19__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.

@@ -24,10 +24,22 @@ global_config = GlobalConfig()
24
24
  runner = ScriptRunner()
25
25
 
26
26
 
27
- @socketio.on('abort_action')
28
- def handle_abort_action():
27
+ @socketio.on('abort_pending')
28
+ def handle_abort_pending():
29
+ runner.abort_pending()
30
+ socketio.emit('log', {'message': "aborted pending iterations"})
31
+
32
+
33
+ @socketio.on('abort_current')
34
+ def handle_abort_current():
29
35
  runner.stop_execution()
30
- socketio.emit('log', {'message': "aborted pending tasks"})
36
+ socketio.emit('log', {'message': "stopped next task"})
37
+
38
+
39
+ @socketio.on('pause')
40
+ def handle_pause():
41
+ msg = runner.toggle_pause()
42
+ socketio.emit('log', {'message': msg})
31
43
 
32
44
 
33
45
  @socketio.on('connect')
@@ -266,9 +278,10 @@ def experiment_run():
266
278
  config_preview = config[1:6]
267
279
  arg_type = config.pop(0) # first entry is types
268
280
  try:
269
- exec(exec_string)
281
+ for key, func_str in exec_string.items():
282
+ exec(func_str)
270
283
  except Exception:
271
- flash("Please check syntax!!")
284
+ flash(f"Please check {key} syntax!!")
272
285
  return redirect(url_for("design.experiment_builder"))
273
286
  # runner.globals_dict.update(globals())
274
287
  run_name = script.name if script.name else "untitled"
@@ -313,7 +326,8 @@ def experiment_run():
313
326
  return render_template('experiment_run.html', script=script.script_dict, filename=filename, dot_py=exec_string,
314
327
  return_list=return_list, config_list=config_list, config_file_list=config_file_list,
315
328
  config_preview=config_preview, data_list=data_list, config_type_list=config_type_list,
316
- no_deck_warning=no_deck_warning, dismiss=dismiss, design_buttons=design_buttons, history=deck_list)
329
+ no_deck_warning=no_deck_warning, dismiss=dismiss, design_buttons=design_buttons,
330
+ history=deck_list)
317
331
 
318
332
 
319
333
  @design.route("/toggle_script_type/<stype>")
@@ -547,4 +561,4 @@ def duplicate_action(id: int):
547
561
  script = utils.get_script_file()
548
562
  script.duplicate_action(id)
549
563
  utils.post_script_file(script)
550
- return redirect(back)
564
+ return redirect(back)
@@ -219,10 +219,24 @@
219
219
  </div>
220
220
  <div class="col-lg-6 col-sm-12 logging-panel">
221
221
  <p>
222
- <div class="p d-flex justify-content-between align-items-center">
223
- <h5>Progress:</h5>
224
- <button id="abort" class="btn btn-danger ">Abort Pending Actions</button>
225
- </div>
222
+ <div class="p d-flex justify-content-between align-items-center">
223
+ <h5>Progress:</h5>
224
+ <div class="d-flex gap-2 ms-auto">
225
+ <button id="pause-resume" class="btn btn-info text-white" data-bs-toggle="tooltip" title="Pause execution">
226
+ <i class="bi bi-pause-circle"></i> <!-- Icon for Pause -->
227
+ </button>
228
+ <button id="abort-current" class="btn btn-danger text-white" data-bs-toggle="tooltip" title="Stop execution after current step">
229
+ <i class="bi bi-stop-circle"></i> <!-- Icon for Stop After This Step -->
230
+ </button>
231
+ <button id="abort-pending" class="btn btn-warning text-white" data-bs-toggle="tooltip" title="Stop execution after current iteration">
232
+ <i class="bi bi-hourglass-split"></i> <!-- Icon for Stop After This Iteration -->
233
+ </button>
234
+ </div>
235
+ </div>
236
+ <div class="text-muted mt-2">
237
+ <small><strong>Note:</strong> The current step cannot be paused or stopped until it completes. </small>
238
+ </div>
239
+
226
240
  </p>
227
241
  <div class="progress" role="progressbar" aria-label="Animated striped example" aria-valuenow="10" aria-valuemin="0" aria-valuemax="100">
228
242
  <div id="progress-bar-inner" class="progress-bar progress-bar-striped progress-bar-animated"></div>
@@ -295,7 +309,9 @@
295
309
  </h2>
296
310
  <div id="python" class="accordion-collapse collapse">
297
311
  <div class="accordion-body">
298
- <pre><code class="python" >{{dot_py}}</code></pre>
312
+ {% for stype, script in dot_py.items() %}
313
+ <pre><code class="python" >{{script}}</code></pre>
314
+ {% endfor %}
299
315
  <a href="{{ url_for('design.download', filetype='python') }}">Download <i class="bi bi-download"></i></a>
300
316
  </div>
301
317
  </div>
@@ -24,11 +24,39 @@ document.addEventListener("DOMContentLoaded", function() {
24
24
  $('#logging-panel').scrollTop($('#logging-panel')[0].scrollHeight);
25
25
  });
26
26
 
27
- document.getElementById('abort').addEventListener('click', function() {
28
- var confirmation = confirm("Are you sure you want to abort pending actions?");
27
+ document.getElementById('abort-pending').addEventListener('click', function() {
28
+ var confirmation = confirm("Are you sure you want to stop after this iteration?");
29
29
  if (confirmation) {
30
- socket.emit('abort_action');
30
+ socket.emit('abort_pending');
31
31
  console.log('Abort action sent to server.');
32
32
  }
33
33
  });
34
+ document.getElementById('abort-current').addEventListener('click', function() {
35
+ var confirmation = confirm("Are you sure you want to stop after this step?");
36
+ if (confirmation) {
37
+ socket.emit('abort_current');
38
+ console.log('Stop action sent to server.');
39
+ }
40
+ });
41
+
42
+ document.getElementById('pause-resume').addEventListener('click', function() {
43
+
44
+ socket.emit('pause');
45
+ console.log('Pause/Resume is toggled.');
46
+ var button = this;
47
+ var icon = button.querySelector("i");
48
+
49
+ // Toggle between Pause and Resume
50
+ if (icon.classList.contains("bi-pause-circle")) {
51
+ icon.classList.remove("bi-pause-circle");
52
+ icon.classList.add("bi-play-circle");
53
+ button.innerHTML = '<i class="bi bi-play-circle"></i>';
54
+ button.setAttribute("title", "Resume execution");
55
+ } else {
56
+ icon.classList.remove("bi-play-circle");
57
+ icon.classList.add("bi-pause-circle");
58
+ button.innerHTML = '<i class="bi bi-pause-circle"></i>';
59
+ button.setAttribute("title", "Pause execution");
60
+ }
61
+ });
34
62
  });
@@ -398,35 +398,16 @@ class Script(db.Model):
398
398
  self.sort_actions()
399
399
  run_name = self.name if self.name else "untitled"
400
400
  run_name = self.validate_function_name(run_name)
401
- exec_string = ''
401
+ exec_str_collection = {}
402
402
 
403
403
  for i in self.stypes:
404
- exec_string += self._generate_function_header(run_name, i)
405
- exec_string += self._generate_function_body(i)
406
-
404
+ func_str = self._generate_function_header(run_name, i) + self._generate_function_body(i)
405
+ exec_str_collection[i] = func_str
407
406
  if script_path:
408
- self._write_to_file(script_path, run_name, exec_string)
407
+ self._write_to_file(script_path, run_name, exec_str_collection)
409
408
 
410
- return exec_string
409
+ return exec_str_collection
411
410
 
412
- def compile_steps(self, script_path=None):
413
- """
414
- Compile the current script to steps.
415
- :return: {"prep":[], "script":[], "cleanup":[],}.
416
- """
417
- self.sort_actions()
418
- run_name = self.name if self.name else "untitled"
419
- run_name = self.validate_function_name(run_name)
420
- exec_string = ''
421
- steps = {}
422
- for i in self.stypes:
423
- # exec_string += self._generate_function_header(run_name, i)
424
- exec_string += self._generate_function_body(i)
425
-
426
- if script_path:
427
- self._write_to_file(script_path, run_name, exec_string)
428
-
429
- return exec_string
430
411
 
431
412
 
432
413
  @staticmethod
@@ -447,7 +428,7 @@ class Script(db.Model):
447
428
  configure = [param + f":{param_type}" if not param_type == "any" else "" for param, param_type in
448
429
  config_type.items()]
449
430
 
450
- function_header = f"\n\ndef {run_name}_{stype}("
431
+ function_header = f"def {run_name}_{stype}("
451
432
 
452
433
  if stype == "script":
453
434
  function_header += ", ".join(configure)
@@ -471,21 +452,6 @@ class Script(db.Model):
471
452
  body += self.indent(indent_unit) + return_str
472
453
  return body
473
454
 
474
- # def _generate_function_body(self, stype):
475
- # """
476
- # Generate the function body for each type in stypes.
477
- # """
478
- # steps = []
479
- # indent_unit = 1
480
- #
481
- # for index, action in enumerate(self.script_dict[stype]):
482
- # text, indent_unit = self._process_action(indent_unit, action, index, stype)
483
- # body += text
484
- # return_str, return_list = self.config_return()
485
- # if return_list and stype == "script":
486
- # body += self.indent(indent_unit) + return_str
487
- # return body
488
-
489
455
  def _process_action(self, indent_unit, action, index, stype):
490
456
  """
491
457
  Process each action within the script dictionary.
@@ -631,7 +597,8 @@ class Script(db.Model):
631
597
  else:
632
598
  s.write("deck = None")
633
599
  s.write("\nimport time")
634
- s.write(exec_string)
600
+ for i in exec_string.values():
601
+ s.write(f"\n\n\n{i}")
635
602
 
636
603
 
637
604
  if __name__ == "__main__":
@@ -1,3 +1,4 @@
1
+ import ast
1
2
  import os
2
3
  import csv
3
4
  import threading
@@ -17,25 +18,40 @@ class ScriptRunner:
17
18
  if globals_dict is None:
18
19
  globals_dict = globals()
19
20
  self.globals_dict = globals_dict
20
-
21
- self.stop_event = threading.Event()
21
+ self.pause_event = threading.Event() # A threading event to manage pause/resume
22
+ self.pause_event.set()
23
+ self.stop_pending_event = threading.Event()
24
+ self.stop_current_event = threading.Event()
22
25
  self.is_running = False
23
26
  self.lock = threading.Lock()
24
27
 
28
+ def toggle_pause(self):
29
+ """Toggles between pausing and resuming the script"""
30
+ if self.pause_event.is_set():
31
+ self.pause_event.clear() # Pause the script
32
+ return "Paused"
33
+ else:
34
+ self.pause_event.set() # Resume the script
35
+ return "Resumed"
36
+
25
37
  def reset_stop_event(self):
26
- self.stop_event.clear()
38
+ self.stop_pending_event.clear()
39
+ self.stop_current_event.clear()
27
40
 
28
- def stop_execution(self):
29
- self.stop_event.set()
41
+ def abort_pending(self):
42
+ self.stop_pending_event.set()
30
43
  # print("Stop pending tasks")
31
44
 
45
+ def stop_execution(self):
46
+ """Force stop everything, including ongoing tasks."""
47
+ self.stop_current_event.set()
48
+ self.abort_pending()
49
+
32
50
  def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
33
51
  output_path=""):
34
52
  global deck
35
53
  if deck is None:
36
54
  deck = global_config.deck
37
- exec_string = script.compile()
38
- exec(exec_string)
39
55
  time.sleep(1)
40
56
  with self.lock:
41
57
  if self.is_running:
@@ -50,24 +66,61 @@ class ScriptRunner:
50
66
  thread.start()
51
67
  return thread
52
68
 
69
+ def execute_function_line_by_line(self, script, section_name, logger, **kwargs):
70
+ """
71
+ Executes a function defined in a string line by line.
72
+
73
+ :param func_str: The function as a string
74
+ :param kwargs: Arguments to pass to the function
75
+ :return: The final result of the function execution
76
+ """
77
+ global deck
78
+ if deck is None:
79
+ deck = global_config.deck
80
+ # func_str = script.compile()
81
+ # Parse function body from string
82
+ module = ast.parse(script)
83
+ func_def = next(node for node in module.body if isinstance(node, ast.FunctionDef))
84
+
85
+ # Extract function body as source lines
86
+ lines = [ast.unparse(node) for node in func_def.body if not isinstance(node, ast.Return)]
87
+ # Prepare execution environment
88
+ exec_globals = {"deck": deck} # Add required global objects
89
+ exec_locals = {} # Local execution scope
90
+
91
+ # Define function arguments manually in exec_locals
92
+ exec_locals.update(kwargs)
93
+
94
+ # Execute each line dynamically
95
+ for line in lines:
96
+ if self.stop_current_event.is_set():
97
+ logger.info(f'Stopping execution during {section_name}')
98
+ break
99
+ logger.info(f"Executing: {line}") # Debugging output
100
+ exec(line, exec_globals, exec_locals)
101
+ self.pause_event.wait()
102
+
103
+ return exec_locals # Return the 'results' variable
104
+
53
105
  def _run_with_stop_check(self, script: Script, repeat_count, run_name, logger, socketio, config, bo_args,
54
106
  output_path):
55
107
  time.sleep(1)
108
+ func_str = script.compile()
56
109
  self._emit_progress(socketio, 1)
57
110
  try:
58
111
  # Run "prep" section once
59
112
  script_dict = script.script_dict
60
- self._run_actions(script_dict.get("prep", []), section_name="prep", run_name=run_name, logger=logger)
113
+ self._run_actions(script_dict.get("prep", []), func_str.get("prep", ''), section_name="prep", logger=logger)
61
114
  output_list = []
62
115
  _, arg_type = script.config("script")
63
116
  _, return_list = script.config_return()
64
117
  # Run "script" section multiple times
65
118
  if repeat_count:
66
- self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, return_list, run_name, logger, socketio)
119
+ self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, func_str.get("script", ''), run_name, return_list, logger, socketio)
67
120
  elif config:
68
- self._run_config_section(config, arg_type, output_list, return_list, run_name, logger, socketio)
121
+ self._run_config_section(config, arg_type, output_list, func_str.get("script", ''), run_name, logger, socketio)
69
122
  # Run "cleanup" section once
70
- self._run_actions(script_dict.get("cleanup", []), section_name="cleanup", run_name=run_name, logger=logger)
123
+ self._run_actions(script_dict.get("cleanup", []), func_str.get("cleanup", ''), section_name="cleanup", logger=logger)
71
124
  # Save results if necessary
72
125
  if output_list:
73
126
  self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
@@ -78,21 +131,14 @@ class ScriptRunner:
78
131
  self.is_running = False # Reset the running flag when done
79
132
  self._emit_progress(socketio, 100)
80
133
 
81
- def _run_actions(self, actions, section_name="", run_name=None, logger=None):
134
+ def _run_actions(self, actions, func_str, section_name="", logger=None):
82
135
  logger.info(f'Executing {section_name} steps') if actions else logger.info(f'No {section_name} steps')
83
- if self.stop_event.is_set():
136
+ if self.stop_pending_event.is_set():
84
137
  logger.info(f"Stopping execution during {section_name} section.")
85
138
  return
86
- # for action in actions:
87
- # if self.stop_event.is_set():
88
- # logger.info(f"Stopping execution during {section_name} section.")
89
- # break
90
- # logger.info(f'Executing {action.get("action", "")} action')
91
- fname = f"{run_name}_{section_name}"
92
- function = self.globals_dict[fname]
93
- function()
94
-
95
- def _run_config_section(self, config, arg_type, output_list, return_list, run_name, logger, socketio):
139
+ self.execute_function_line_by_line(func_str, section_name, logger)
140
+
141
+ def _run_config_section(self, config, arg_type, output_list, func_str, run_name, logger, socketio):
96
142
  compiled = True
97
143
  for i in config:
98
144
  try:
@@ -104,25 +150,25 @@ class ScriptRunner:
104
150
  if compiled:
105
151
  for i, kwargs in enumerate(config):
106
152
  kwargs = dict(kwargs)
107
- if self.stop_event.is_set():
153
+ if self.stop_pending_event.is_set():
108
154
  logger.info(f'Stopping execution during {run_name}: {i + 1}/{len(config)}')
109
155
  break
110
156
  logger.info(f'Executing {i + 1} of {len(config)} with kwargs = {kwargs}')
111
157
  progress = (i + 1) * 100 / len(config)
112
158
  self._emit_progress(socketio, progress)
113
- fname = f"{run_name}_script"
114
- function = self.globals_dict[fname]
115
- output = function(**kwargs)
159
+ # fname = f"{run_name}_script"
160
+ # function = self.globals_dict[fname]
161
+ output = self.execute_function_line_by_line(func_str, "script", logger, **kwargs)
116
162
  if output:
117
- kwargs.update(output)
118
- output_list.append(kwargs)
163
+ # kwargs.update(output)
164
+ output_list.append(output)
119
165
 
120
- def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, return_list, run_name, logger, socketio):
166
+ def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, func_str, run_name, return_list, logger, socketio):
121
167
  if bo_args:
122
168
  logger.info('Initializing optimizer...')
123
169
  ax_client = utils.ax_initiation(bo_args, arg_types)
124
170
  for i in range(int(repeat_count)):
125
- if self.stop_event.is_set():
171
+ if self.stop_pending_event.is_set():
126
172
  logger.info(f'Stopping execution during {run_name}: {i + 1}/{int(repeat_count)}')
127
173
  break
128
174
  logger.info(f'Executing {run_name} experiment: {i + 1}/{int(repeat_count)}')
@@ -132,20 +178,20 @@ class ScriptRunner:
132
178
  try:
133
179
  parameters, trial_index = ax_client.get_next_trial()
134
180
  logger.info(f'Output value: {parameters}')
135
- fname = f"{run_name}_script"
136
- function = self.globals_dict[fname]
137
- output = function(**parameters)
138
- # output = eval(f"{run_name}_script(**{parameters})")
139
- _output = output.copy()
181
+ # fname = f"{run_name}_script"
182
+ # function = self.globals_dict[fname]
183
+ output = self.execute_function_line_by_line(func_str, "script", logger, **parameters)
184
+
185
+ _output = {key: value for key, value in output.items() if key in return_list}
140
186
  ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
141
187
  output.update(parameters)
142
188
  except Exception as e:
143
189
  logger.info(f'Optimization error: {e}')
144
190
  break
145
191
  else:
146
- fname = f"{run_name}_script"
147
- function = self.globals_dict[fname]
148
- output = function()
192
+ # fname = f"{run_name}_script"
193
+ # function = self.globals_dict[fname]
194
+ output = self.execute_function_line_by_line(func_str, "script", logger)
149
195
 
150
196
  if output:
151
197
  output_list.append(output)
ivoryos/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.18"
1
+ __version__ = "0.1.19"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 0.1.18
3
+ Version: 0.1.19
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
@@ -165,6 +165,15 @@ Intro + Tutorial + Demo with PurPOSE platform
165
165
  https://youtu.be/dFfJv9I2-1g
166
166
 
167
167
 
168
+ ## Roadmap
169
+
170
+ - [x] Allow plugin pages ✅
171
+ - [ ] pause, resume, abort current and pending workflows
172
+ - [ ] snapshot version control
173
+ - [ ] dropdown input
174
+ - [ ] show line number option
175
+
176
+
168
177
  ## Authors and Acknowledgement
169
178
  Ivory Zhang, Lucy Hao
170
179
 
@@ -1,6 +1,6 @@
1
1
  ivoryos/__init__.py,sha256=F3KenDZX1zqei3PZ3tx1U77yXsbeytgMMhVJyJMkmJ0,6071
2
2
  ivoryos/config.py,sha256=3FPBYTIBhQTKDvsEoR8ZeTmg65D-CSFEdGmOuIL4pSI,1311
3
- ivoryos/version.py,sha256=6BiuMUkhwQp6bzUZSF8np8F1NwCltEtK0sPBF__tepU,23
3
+ ivoryos/version.py,sha256=cAJAbAh288a9AL-3yxwFzEM1L26izSJ6wma5aiml_9Y,23
4
4
  ivoryos/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  ivoryos/routes/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  ivoryos/routes/auth/auth.py,sha256=7CdXjGAr1B_xsmwweakTWOoROgsOJf0MNTzlMP_5Nus,3240
@@ -15,9 +15,9 @@ ivoryos/routes/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
15
15
  ivoryos/routes/database/database.py,sha256=IOrdtdsZ28bV7R1ZQfcwAeS5JDCkX-QYsP9i8BseD38,5684
16
16
  ivoryos/routes/database/templates/database/experiment_database.html,sha256=ABchwHYDovzwECc7UD4Mh8C_9JUl0YZpAAF9sFjNCs0,3434
17
17
  ivoryos/routes/design/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- ivoryos/routes/design/design.py,sha256=489QCfjcCZuL7HKAvZVrxIL1wK4IhE0zUef_mmbNuMs,21003
18
+ ivoryos/routes/design/design.py,sha256=hV8rtyIgbw5HsDY9h_Xtq96NIoaZB8XXLKyxaxbnYEA,21361
19
19
  ivoryos/routes/design/templates/design/experiment_builder.html,sha256=-C84nHcK1grC8b_B5Lgbg6StJcP8x1oj2XAjRPj1oU0,28239
20
- ivoryos/routes/design/templates/design/experiment_run.html,sha256=JxYEbJqeJH8vZaSVI25EtNYmI_W5GSFdc02Y8y3kZGc,22543
20
+ ivoryos/routes/design/templates/design/experiment_run.html,sha256=JY44cyWyi470Z_GeDGROU6cyFsTlbTYtlFVXmPjf4XA,23944
21
21
  ivoryos/routes/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  ivoryos/routes/main/main.py,sha256=yuVJzXAob1kc1dfflkTBIZQ0tdf6kChfuq-uQlN1e9Q,957
23
23
  ivoryos/routes/main/templates/main/help.html,sha256=IOktMEsOPk0SCiMBXZ4mpffClERAyX8W82fel71M3M0,9370
@@ -28,19 +28,19 @@ ivoryos/static/style.css,sha256=cbQ8T8h42smwuyF5qQ_pNhlptDXGcuyK2u4sUppqnyI,3717
28
28
  ivoryos/static/gui_annotation/Slide1.png,sha256=Lm4gdOkUF5HIUFaB94tl6koQVkzpitKj43GXV_XYMMc,121727
29
29
  ivoryos/static/gui_annotation/Slide2.PNG,sha256=z3wQ9oVgg4JTWVLQGKK_KhtepRHUYP1e05XUWGT2A0I,118761
30
30
  ivoryos/static/js/overlay.js,sha256=dPxop19es0E0ZUSY3d_4exIk7CJuQEnlW5uTt5fZfzI,483
31
- ivoryos/static/js/socket_handler.js,sha256=O7r33couafOE9QCvUK9mP0kJRiBFw2K-Qk1cjtMI5Vg,1415
31
+ ivoryos/static/js/socket_handler.js,sha256=psIz61RgTzFk7B9SBZWtHWpG9vJjaRxEDTueFDKQcO4,2580
32
32
  ivoryos/static/js/sortable_card.js,sha256=ifmlGe3yy0U_KzMphV4ClRhK2DLOvkELYMlq1vECuac,807
33
33
  ivoryos/static/js/sortable_design.js,sha256=1lI1I8FbL66tv6n-SX2FkbHNDYo36xVo2qDBKVLmxnQ,1120
34
34
  ivoryos/templates/base.html,sha256=8HDi7I74ugcCAV4c3ha4C__-7xopt4ZsC0OQ2E_AsQ8,8313
35
35
  ivoryos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- ivoryos/utils/db_models.py,sha256=Y0KymXvb5uHwXwDrtUpc9DNxCBH9pHCmU39Hf4Hwlv4,24835
36
+ ivoryos/utils/db_models.py,sha256=GiBvsQqleXa7sXjh-118Q1rDTWYKrBsz25fben3aKC4,23712
37
37
  ivoryos/utils/form.py,sha256=1h6eytHz1tv5_CIc_C6KGYn5JEPYniIOxo49yzM37o0,17149
38
38
  ivoryos/utils/global_config.py,sha256=Ojcz6xKATSbMLnTT0kiKqSnu_bNqCMyIAnZyHaKxJns,1589
39
39
  ivoryos/utils/llm_agent.py,sha256=DTf-AF56vy1Em1fUKagl5NURKittmNoxTKIw1PlyC2o,6413
40
- ivoryos/utils/script_runner.py,sha256=DImUy5jfv64lrF8CnQf0HGJjnesi7D8uqh4a939ihEQ,7231
40
+ ivoryos/utils/script_runner.py,sha256=fa35LmNHUXdU0R-_e2Wadok4GjQ_MaYNMvOFJfxSdCI,9308
41
41
  ivoryos/utils/utils.py,sha256=nQbGSR_FmlZyBb9lwKy3ws4FDAWWfDyu96hZg2DVIeI,14081
42
- ivoryos-0.1.18.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
43
- ivoryos-0.1.18.dist-info/METADATA,sha256=ldWGQwTTe0M5hF4qi-kWrB0NJUCFvHjRL7F0cOwxq4g,6355
44
- ivoryos-0.1.18.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
45
- ivoryos-0.1.18.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
46
- ivoryos-0.1.18.dist-info/RECORD,,
42
+ ivoryos-0.1.19.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
43
+ ivoryos-0.1.19.dist-info/METADATA,sha256=z55ToyL0h0YAkAS9pEeaXrG2TFT-fSWeJBsaKto3Xc0,6539
44
+ ivoryos-0.1.19.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
45
+ ivoryos-0.1.19.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
46
+ ivoryos-0.1.19.dist-info/RECORD,,