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.

@@ -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):
@@ -24,9 +26,11 @@ class ScriptRunner:
24
26
  self.stop_current_event = threading.Event()
25
27
  self.is_running = False
26
28
  self.lock = threading.Lock()
29
+ self.paused = False
27
30
 
28
31
  def toggle_pause(self):
29
32
  """Toggles between pausing and resuming the script"""
33
+ self.paused = not self.paused
30
34
  if self.pause_event.is_set():
31
35
  self.pause_event.clear() # Pause the script
32
36
  return "Paused"
@@ -34,10 +38,15 @@ class ScriptRunner:
34
38
  self.pause_event.set() # Resume the script
35
39
  return "Resumed"
36
40
 
41
+ def pause_status(self):
42
+ """Toggles between pausing and resuming the script"""
43
+ return self.paused
44
+
37
45
  def reset_stop_event(self):
38
46
  """Resets the stop event"""
39
47
  self.stop_pending_event.clear()
40
48
  self.stop_current_event.clear()
49
+ self.pause_event.set()
41
50
 
42
51
  def abort_pending(self):
43
52
  """Abort the pending iteration after the current is finished"""
@@ -50,7 +59,8 @@ class ScriptRunner:
50
59
  self.abort_pending()
51
60
 
52
61
  def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
53
- output_path=""):
62
+ output_path="", current_app=None):
63
+ # Get run.id
54
64
  global deck
55
65
  if deck is None:
56
66
  deck = global_config.deck
@@ -64,85 +74,146 @@ class ScriptRunner:
64
74
  self.reset_stop_event()
65
75
 
66
76
  thread = threading.Thread(target=self._run_with_stop_check,
67
- 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))
68
78
  thread.start()
69
79
  return thread
70
80
 
71
- def execute_function_line_by_line(self, lines:list, section_name, logger, socketio, **kwargs):
81
+ def exec_steps(self, script, section_name, logger, socketio, run_id, i_progress, **kwargs):
72
82
  """
73
- Executes a function defined in a string line by line.
74
-
83
+ Executes a function defined in a string line by line
75
84
  :param func_str: The function as a string
76
85
  :param kwargs: Arguments to pass to the function
77
86
  :return: The final result of the function execution
78
87
  """
88
+ _func_str = script.compile()
89
+ step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
79
90
  global deck
91
+ # global deck, registered_workflows
80
92
  if deck is None:
81
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
+ #
82
100
  # func_str = script.compile()
83
101
  # Parse function body from string
84
-
102
+ temp_connections = global_config.defined_variables
85
103
  # Prepare execution environment
86
- exec_globals = {"deck": deck} # Add required global objects
104
+ exec_globals = {"deck": deck, "time":time} # Add required global objects
105
+ # exec_globals = {"deck": deck, "time": time, "registered_workflows":registered_workflows} # Add required global objects
106
+ exec_globals.update(temp_connections)
87
107
  exec_locals = {} # Local execution scope
88
108
 
89
109
  # Define function arguments manually in exec_locals
90
110
  exec_locals.update(kwargs)
111
+ index = 0
91
112
 
92
113
  # Execute each line dynamically
93
- for index, line in enumerate(lines):
114
+ while index < len(step_list):
94
115
  if self.stop_current_event.is_set():
95
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)
96
128
  break
97
- logger.info(f"Executing: {line}") # Debugging output
129
+ line = step_list[index]
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}")
98
141
  socketio.emit('execution', {'section': f"{section_name}-{index}"})
99
142
  # self._emit_progress(socketio, 100)
100
- exec(line, exec_globals, exec_locals)
143
+ # if line.startswith("registered_workflows"):
144
+ # line = line.replace("registered_workflows.", "")
145
+ try:
146
+ exec(line, exec_globals, exec_locals)
147
+ step.run_error = False
148
+ except Exception as e:
149
+ logger.error(f"Error during script execution: {e}")
150
+ socketio.emit('error', {'message': str(e)})
151
+
152
+ step.run_error = True
153
+ self.toggle_pause()
154
+ step.end_time = datetime.now()
101
155
  self.pause_event.wait()
102
156
 
157
+ # todo update script during the run
158
+ # _func_str = script.compile()
159
+ # step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
160
+ db.session.add(step)
161
+ db.session.commit()
162
+ index += 1
103
163
  return exec_locals # Return the 'results' variable
104
164
 
105
165
  def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
106
- output_path):
166
+ output_path, current_app):
107
167
  time.sleep(1)
108
- func_str = script.compile()
109
- func_str_lines = script.convert_to_lines(func_str)
168
+ # _func_str = script.compile()
169
+ # step_list_dict: dict = script.convert_to_lines(_func_str)
110
170
  self._emit_progress(socketio, 1)
111
- try:
112
- # Run "prep" section once
113
- script_dict = script.script_dict
114
- self._run_actions(script_dict.get("prep", []), func_str_lines.get("prep", []), section_name="prep", logger=logger, socketio=socketio)
171
+
172
+ # Run "prep" section once
173
+ script_dict = script.script_dict
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)
115
181
  output_list = []
116
182
  _, arg_type = script.config("script")
117
183
  _, return_list = script.config_return()
184
+
118
185
  # Run "script" section multiple times
119
186
  if repeat_count:
120
- self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, func_str_lines.get("script", []),
121
- run_name, return_list, logger, socketio)
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)
122
189
  elif config:
123
- self._run_config_section(config, arg_type, output_list, func_str_lines.get("script", []), run_name, logger,
124
- socketio)
190
+ self._run_config_section(config, arg_type, output_list, script, run_name, logger,
191
+ socketio, run_id=run.id)
192
+
125
193
  # Run "cleanup" section once
126
- self._run_actions(script_dict.get("cleanup", []), func_str.get("cleanup", []), section_name="cleanup",
127
- logger=logger, socketio=socketio)
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
128
198
  # Save results if necessary
199
+ filename = None
129
200
  if output_list:
130
- self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
131
- except Exception as e:
132
- logger.error(f"Error during script execution: {e}")
133
- finally:
134
- with self.lock:
135
- self.is_running = False # Reset the running flag when done
201
+ filename = self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
136
202
  self._emit_progress(socketio, 100)
203
+ run.end_time = datetime.now()
204
+ run.data_path = filename
205
+ db.session.commit()
137
206
 
138
- def _run_actions(self, actions, func_str, section_name="", logger=None, socketio=None):
139
- logger.info(f'Executing {section_name} steps') if actions else logger.info(f'No {section_name} steps')
207
+ def _run_actions(self, script, section_name="", logger=None, socketio=None, run_id=None):
208
+ _func_str = script.compile()
209
+ step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
210
+ logger.info(f'Executing {section_name} steps') if step_list else logger.info(f'No {section_name} steps')
140
211
  if self.stop_pending_event.is_set():
141
212
  logger.info(f"Stopping execution during {section_name} section.")
142
213
  return
143
- self.execute_function_line_by_line(func_str, section_name, logger, socketio)
214
+ self.exec_steps(script, section_name, logger, socketio, run_id=run_id, i_progress=0)
144
215
 
145
- def _run_config_section(self, config, arg_type, output_list, func_str, run_name, logger, socketio):
216
+ def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio, run_id):
146
217
  compiled = True
147
218
  for i in config:
148
219
  try:
@@ -162,22 +233,22 @@ class ScriptRunner:
162
233
  self._emit_progress(socketio, progress)
163
234
  # fname = f"{run_name}_script"
164
235
  # function = self.globals_dict[fname]
165
- output = self.execute_function_line_by_line(func_str, "script", logger, socketio, **kwargs)
236
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i, **kwargs)
166
237
  if output:
167
238
  # kwargs.update(output)
168
239
  output_list.append(output)
169
240
 
170
- def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, func_str, run_name, return_list,
171
- logger, socketio):
241
+ def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list,
242
+ logger, socketio, run_id):
172
243
  if bo_args:
173
244
  logger.info('Initializing optimizer...')
174
245
  ax_client = utils.ax_initiation(bo_args, arg_types)
175
- for i in range(int(repeat_count)):
246
+ for i_progress in range(int(repeat_count)):
176
247
  if self.stop_pending_event.is_set():
177
- logger.info(f'Stopping execution during {run_name}: {i + 1}/{int(repeat_count)}')
248
+ logger.info(f'Stopping execution during {run_name}: {i_progress + 1}/{int(repeat_count)}')
178
249
  break
179
- logger.info(f'Executing {run_name} experiment: {i + 1}/{int(repeat_count)}')
180
- progress = (i + 1) * 100 / int(repeat_count) - 0.1
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
181
252
  self._emit_progress(socketio, progress)
182
253
  if bo_args:
183
254
  try:
@@ -185,7 +256,7 @@ class ScriptRunner:
185
256
  logger.info(f'Output value: {parameters}')
186
257
  # fname = f"{run_name}_script"
187
258
  # function = self.globals_dict[fname]
188
- output = self.execute_function_line_by_line(func_str, "script", logger, socketio, **parameters)
259
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress, **parameters)
189
260
 
190
261
  _output = {key: value for key, value in output.items() if key in return_list}
191
262
  ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
@@ -196,7 +267,7 @@ class ScriptRunner:
196
267
  else:
197
268
  # fname = f"{run_name}_script"
198
269
  # function = self.globals_dict[fname]
199
- output = self.execute_function_line_by_line(func_str, "script", logger, socketio)
270
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress)
200
271
 
201
272
  if output:
202
273
  output_list.append(output)
@@ -214,6 +285,7 @@ class ScriptRunner:
214
285
  writer.writeheader()
215
286
  writer.writerows(output_list)
216
287
  logger.info(f'Results saved to {file_path}')
288
+ return filename
217
289
 
218
290
  @staticmethod
219
291
  def _emit_progress(socketio, progress):
ivoryos/utils/utils.py CHANGED
@@ -97,7 +97,7 @@ def _inspect_class(class_object=None, debug=False):
97
97
  under_score = "_"
98
98
  if debug:
99
99
  under_score = "__"
100
- for function, method in inspect.getmembers(type(class_object), predicate=inspect.isfunction):
100
+ for function, method in inspect.getmembers(type(class_object), predicate=callable):
101
101
  if not function.startswith(under_score) and not function.isupper():
102
102
  try:
103
103
  annotation = inspect.signature(method)
@@ -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.20"
1
+ __version__ = "0.1.22"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 0.1.20
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
- - [License](#license)
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
+ [//]: # (![Discord]&#40;https://img.shields.io/discord/1313641159356059770&#41;)
97
+
98
+ [//]: # (![PyPI - Downloads]&#40;https://img.shields.io/pypi/dm/ivoryos&#41;)
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/sdl_example/abstract_sdl.py), where instances of `AbstractSDL` is created as `sdl`,
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__)
@@ -1,46 +1,48 @@
1
- ivoryos/__init__.py,sha256=F3KenDZX1zqei3PZ3tx1U77yXsbeytgMMhVJyJMkmJ0,6071
1
+ ivoryos/__init__.py,sha256=MVzYifnYAyHZvr0PfMs9ck3Nf4Xfj3fJz4djCq3rcC4,6081
2
2
  ivoryos/config.py,sha256=3FPBYTIBhQTKDvsEoR8ZeTmg65D-CSFEdGmOuIL4pSI,1311
3
- ivoryos/version.py,sha256=8XalsVoLEfXslFvdtUEmkNOuYShzOzYOcFbgmOz1oSk,23
3
+ ivoryos/version.py,sha256=zmP2TRnzKPjZJ1eiBcT-cRInsji6FW-OVD3FafQFCc4,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
7
7
  ivoryos/routes/auth/templates/auth/login.html,sha256=WSRrKbdM_oobqSXFRTo-j9UlOgp6sYzS9tm7TqqPULI,1207
8
8
  ivoryos/routes/auth/templates/auth/signup.html,sha256=b5LTXtpfTSkSS7X8u1ldwQbbgEFTk6UNMAediA5BwBY,1465
9
9
  ivoryos/routes/control/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- ivoryos/routes/control/control.py,sha256=MmrcKrkjSKS589XhDdvPc7kWO0ApCNVZCtrgfzTAAN8,14163
10
+ ivoryos/routes/control/control.py,sha256=bxNYR-VAFA7tSswDt5k130AnIx1f64_R9N2B9kMridI,14160
11
11
  ivoryos/routes/control/templates/control/controllers.html,sha256=iIp0h6WA68gQj9OsoiB7dU1BqH8CGomTueR73F4C8eY,4274
12
- ivoryos/routes/control/templates/control/controllers_home.html,sha256=xFnBXTWMlyi2naMSsCEqFlX-Z1HJJDDtzITPvq-3nng,2733
12
+ ivoryos/routes/control/templates/control/controllers_home.html,sha256=VQ77HRvBlyBrQ3al5fcKF5Y6_vKtU8WeAhilqQQltAo,2997
13
13
  ivoryos/routes/control/templates/control/controllers_new.html,sha256=uOQo9kYmwX2jk3KZDkMUF_ylfNUIs_oIWb_kk_MMVDM,4921
14
14
  ivoryos/routes/database/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- ivoryos/routes/database/database.py,sha256=IOrdtdsZ28bV7R1ZQfcwAeS5JDCkX-QYsP9i8BseD38,5684
16
- ivoryos/routes/database/templates/database/experiment_database.html,sha256=ABchwHYDovzwECc7UD4Mh8C_9JUl0YZpAAF9sFjNCs0,3434
15
+ ivoryos/routes/database/database.py,sha256=UY-6tHKPZaDch0whXVwgu201SQ231tPCE8ej374GOZ8,7140
16
+ ivoryos/routes/database/templates/database/experiment_database.html,sha256=edlCcKfrS91gGG1dPFQjC9xD7F7nWNNqS3S6Oa7apzs,3460
17
+ ivoryos/routes/database/templates/database/workflow_run_database.html,sha256=MczK9my9u0SyQsMFLbc6CXeZqKaBo5vk1SpwjkcZdqk,3571
17
18
  ivoryos/routes/design/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- ivoryos/routes/design/design.py,sha256=ckI5QMf9WR7T4qio9IoYNRm4UbwXa3TOl5eFTJMAWhE,21484
19
- ivoryos/routes/design/templates/design/experiment_builder.html,sha256=-C84nHcK1grC8b_B5Lgbg6StJcP8x1oj2XAjRPj1oU0,28239
20
- ivoryos/routes/design/templates/design/experiment_run.html,sha256=e49CTbmFFUXYy0dCcdJ3ysM-sTMg5qQDm5MkRTTcyDE,25210
19
+ ivoryos/routes/design/design.py,sha256=HxEGPL5KTG2-roOB7UB9Ph28haY37IS5irKfloNEld0,25054
20
+ ivoryos/routes/design/templates/design/experiment_builder.html,sha256=2cT25URI6NoRDKuU6xl8Vsv4pqyQhhx-Fu8JOfPIP8k,27110
21
+ ivoryos/routes/design/templates/design/experiment_run.html,sha256=2CWbHx2M4h4EW7E0EapGPtwza56VEqhZDAIX4IDzYjQ,30266
21
22
  ivoryos/routes/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
23
  ivoryos/routes/main/main.py,sha256=yuVJzXAob1kc1dfflkTBIZQ0tdf6kChfuq-uQlN1e9Q,957
23
24
  ivoryos/routes/main/templates/main/help.html,sha256=IOktMEsOPk0SCiMBXZ4mpffClERAyX8W82fel71M3M0,9370
24
- ivoryos/routes/main/templates/main/home.html,sha256=ujM0YC0yrHhCfkuprNnZZZd8XEEguS_6NjrY5ktfctg,3356
25
+ ivoryos/routes/main/templates/main/home.html,sha256=8AhQOWakD-1TyWA0s-B9ZK96L_f8ywYUEGHW1UzMGZA,4634
25
26
  ivoryos/static/favicon.ico,sha256=RhlrPtfITOkzC9BjP1UB1V5L9Oyp6NwNtWeMcGOnpyc,15406
26
27
  ivoryos/static/logo.webp,sha256=lXgfQR-4mHTH83k7VV9iB54-oC2ipe6uZvbwdOnLETc,14974
27
- ivoryos/static/style.css,sha256=cbQ8T8h42smwuyF5qQ_pNhlptDXGcuyK2u4sUppqnyI,3717
28
+ ivoryos/static/style.css,sha256=zQVx35A5g6JMJ-K84-6fSKtzXGjp_p5ZVG6KLHPM2IE,4021
28
29
  ivoryos/static/gui_annotation/Slide1.png,sha256=Lm4gdOkUF5HIUFaB94tl6koQVkzpitKj43GXV_XYMMc,121727
29
30
  ivoryos/static/gui_annotation/Slide2.PNG,sha256=z3wQ9oVgg4JTWVLQGKK_KhtepRHUYP1e05XUWGT2A0I,118761
30
31
  ivoryos/static/js/overlay.js,sha256=dPxop19es0E0ZUSY3d_4exIk7CJuQEnlW5uTt5fZfzI,483
31
- ivoryos/static/js/socket_handler.js,sha256=G0bro6WwZBWeGsS26eg-xuwoS-I7SiqDzkf7mfuvy9U,3149
32
+ ivoryos/static/js/socket_handler.js,sha256=fJ_SKCfS2ZejnECPKZuD8DTEPhBxmm0m-yCvxf2UtIQ,4885
32
33
  ivoryos/static/js/sortable_card.js,sha256=ifmlGe3yy0U_KzMphV4ClRhK2DLOvkELYMlq1vECuac,807
33
- ivoryos/static/js/sortable_design.js,sha256=1lI1I8FbL66tv6n-SX2FkbHNDYo36xVo2qDBKVLmxnQ,1120
34
- ivoryos/templates/base.html,sha256=8HDi7I74ugcCAV4c3ha4C__-7xopt4ZsC0OQ2E_AsQ8,8313
34
+ ivoryos/static/js/sortable_design.js,sha256=wwpKfIzZGDxfX3moNz0cvPvm9YyHmopZK3wmkUdnBiw,4333
35
+ ivoryos/templates/base.html,sha256=sDdwqOIUP2Get-py4E59PkieoGWLFpX6wAJe93s4aRo,8518
35
36
  ivoryos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- ivoryos/utils/db_models.py,sha256=voA-pteIFvlfV0U10H5O18aDwiAuAe6VhX8qEd2rjJs,24602
37
- ivoryos/utils/form.py,sha256=1h6eytHz1tv5_CIc_C6KGYn5JEPYniIOxo49yzM37o0,17149
38
- ivoryos/utils/global_config.py,sha256=Ojcz6xKATSbMLnTT0kiKqSnu_bNqCMyIAnZyHaKxJns,1589
39
- ivoryos/utils/llm_agent.py,sha256=DTf-AF56vy1Em1fUKagl5NURKittmNoxTKIw1PlyC2o,6413
40
- ivoryos/utils/script_runner.py,sha256=OZ3RU6hvS82PubSx-RZqRvHyKuf1pxN5neziUBNR5iU,9622
41
- ivoryos/utils/utils.py,sha256=nQbGSR_FmlZyBb9lwKy3ws4FDAWWfDyu96hZg2DVIeI,14081
42
- ivoryos-0.1.20.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
43
- ivoryos-0.1.20.dist-info/METADATA,sha256=1k_i_szTZR_zgRVZauNtb1gpxNqgZp0s1VGaeAQATmw,6697
44
- ivoryos-0.1.20.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
45
- ivoryos-0.1.20.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
46
- ivoryos-0.1.20.dist-info/RECORD,,
37
+ ivoryos/utils/client_proxy.py,sha256=AzcSQGMqeCqVULP1a7vEKNe135NZYryVX63ke0wgK04,2099
38
+ ivoryos/utils/db_models.py,sha256=1G2De1nLRCGwuY4zqgMeIQ-p1XJ_PkBxH1cd0fJ9YgY,26740
39
+ ivoryos/utils/form.py,sha256=Eqrg4G44hoG6TizxTEP4DNd-rEoyN77t9Y7cB1WdrcQ,19205
40
+ ivoryos/utils/global_config.py,sha256=P0xs_33bZfNQ-D71lCkq7HJyT4ngQWPqUKnkoMrmM8c,1908
41
+ ivoryos/utils/llm_agent.py,sha256=-lVCkjPlpLues9sNTmaT7bT4sdhWvV2DiojNwzB2Lcw,6422
42
+ ivoryos/utils/script_runner.py,sha256=jVQzZQXmeVBNREBr5yP2grAKrO_1PXIlN5Uny87-PfI,12547
43
+ ivoryos/utils/utils.py,sha256=VP-4Wf-slK2pMe659K5TJLbZXVNvyCD1-3kdfp_COxI,14990
44
+ ivoryos-0.1.22.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
45
+ ivoryos-0.1.22.dist-info/METADATA,sha256=13bJFUatG0fTMy8ZfocdZvmOdqZvQZYhwu-vL3Q2kEo,6846
46
+ ivoryos-0.1.22.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
47
+ ivoryos-0.1.22.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
48
+ ivoryos-0.1.22.dist-info/RECORD,,