ivoryos 0.1.21__py3-none-any.whl → 0.1.23__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/utils/form.py CHANGED
@@ -1,6 +1,8 @@
1
+ from enum import Enum
2
+
1
3
  from wtforms.fields.choices import SelectField
2
4
  from wtforms.fields.core import Field
3
- from wtforms.validators import InputRequired
5
+ from wtforms.validators import InputRequired, ValidationError
4
6
  from wtforms.widgets.core import TextInput
5
7
 
6
8
  from flask_wtf import FlaskForm
@@ -187,6 +189,30 @@ class VariableOrBoolField(BooleanField):
187
189
  return "y"
188
190
 
189
191
 
192
+ class FlexibleEnumField(StringField):
193
+ def __init__(self, label=None, validators=None, choices=None, script=None, **kwargs):
194
+ super().__init__(label, validators, **kwargs)
195
+ self.script = script
196
+ self.enum_class = choices
197
+ self.choices = [e.name for e in self.enum_class]
198
+ # self.value_list = [e.name for e in self.enum_class]
199
+
200
+
201
+ def process_formdata(self, valuelist):
202
+ if valuelist:
203
+ key = valuelist[0]
204
+ if key in self.choices:
205
+ # Convert the string key to Enum instance
206
+ self.data = self.enum_class[key].value
207
+ elif self.data.startswith("#"):
208
+ if not self.script.editing_type == "script":
209
+ raise ValueError(self.gettext("Variable is not supported in prep/cleanup"))
210
+ self.data = self.data
211
+ else:
212
+ raise ValidationError(
213
+ f"Invalid choice: '{key}'. Must match one of {list(self.enum_class.__members__.keys())}")
214
+
215
+
190
216
  def format_name(name):
191
217
  """Converts 'example_name' to 'Example Name'."""
192
218
  name = name.split(".")[-1]
@@ -218,20 +244,39 @@ def create_form_for_method(method, autofill, script=None, design=True):
218
244
  if param.name == 'self':
219
245
  continue
220
246
  formatted_param_name = format_name(param.name)
247
+
248
+ default_value = None
249
+ if autofill:
250
+ default_value = f'#{param.name}'
251
+ else:
252
+ if param.default is not param.empty:
253
+ if isinstance(param.default, Enum):
254
+ default_value = param.default.name
255
+ else:
256
+ default_value = param.default
257
+
221
258
  field_kwargs = {
222
259
  "label": formatted_param_name,
223
- "default": f'#{param.name}' if autofill else (param.default if param.default is not param.empty else None),
260
+ "default": default_value,
224
261
  "validators": [InputRequired()] if param.default is param.empty else None,
225
262
  **({"script": script} if (autofill or design) else {})
226
263
  }
227
- field_class, placeholder_text = annotation_mapping.get(
228
- param.annotation,
229
- (VariableOrStringField if design else StringField, f'Enter {param.annotation} value')
230
- )
264
+ if isinstance(param.annotation, type) and issubclass(param.annotation, Enum):
265
+ # enum_class = [(e.name, e.value) for e in param.annotation]
266
+ field_class = FlexibleEnumField
267
+ placeholder_text = f"Choose or type a value for {param.annotation.__name__} (start with # for custom)"
268
+ extra_kwargs = {"choices": param.annotation}
269
+ else:
270
+ field_class, placeholder_text = annotation_mapping.get(
271
+ param.annotation,
272
+ (VariableOrStringField if design else StringField, f'Enter {param.annotation} value')
273
+ )
274
+ extra_kwargs = {}
275
+
231
276
  render_kwargs = {"placeholder": placeholder_text}
232
277
 
233
278
  # Create the field with additional rendering kwargs for placeholder text
234
- field = field_class(**field_kwargs, render_kw=render_kwargs)
279
+ field = field_class(**field_kwargs, render_kw=render_kwargs, **extra_kwargs)
235
280
  setattr(DynamicForm, param.name, field)
236
281
 
237
282
  # setattr(DynamicForm, f'add', fname)
@@ -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.sdl_example.abstract_sdl import deck
165
+ from example.abstract_sdl_example.abstract_sdl import deck
166
166
 
167
167
  from utils import parse_functions
168
168
 
@@ -6,15 +6,18 @@ 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):
20
+ self.retry = False
18
21
  if globals_dict is None:
19
22
  globals_dict = globals()
20
23
  self.globals_dict = globals_dict
@@ -57,7 +60,8 @@ class ScriptRunner:
57
60
  self.abort_pending()
58
61
 
59
62
  def run_script(self, script, repeat_count=1, run_name=None, logger=None, socketio=None, config=None, bo_args=None,
60
- output_path=""):
63
+ output_path="", current_app=None):
64
+ # Get run.id
61
65
  global deck
62
66
  if deck is None:
63
67
  deck = global_config.deck
@@ -71,14 +75,13 @@ class ScriptRunner:
71
75
  self.reset_stop_event()
72
76
 
73
77
  thread = threading.Thread(target=self._run_with_stop_check,
74
- args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path))
78
+ args=(script, repeat_count, run_name, logger, socketio, config, bo_args, output_path, current_app))
75
79
  thread.start()
76
80
  return thread
77
81
 
78
- def exex_steps(self, script, section_name, logger, socketio, **kwargs):
82
+ def exec_steps(self, script, section_name, logger, socketio, run_id, i_progress, **kwargs):
79
83
  """
80
- Executes a function defined in a string line by line.
81
-
84
+ Executes a function defined in a string line by line
82
85
  :param func_str: The function as a string
83
86
  :param kwargs: Arguments to pass to the function
84
87
  :return: The final result of the function execution
@@ -86,8 +89,15 @@ class ScriptRunner:
86
89
  _func_str = script.compile()
87
90
  step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
88
91
  global deck
92
+ # global deck, registered_workflows
89
93
  if deck is None:
90
94
  deck = global_config.deck
95
+ # if registered_workflows is None:
96
+ # registered_workflows = global_config.registered_workflows
97
+
98
+ # for i, line in enumerate(step_list):
99
+ # if line.startswith("registered_workflows"):
100
+ #
91
101
  # func_str = script.compile()
92
102
  # Parse function body from string
93
103
  temp_connections = global_config.defined_variables
@@ -105,27 +115,65 @@ class ScriptRunner:
105
115
  while index < len(step_list):
106
116
  if self.stop_current_event.is_set():
107
117
  logger.info(f'Stopping execution during {section_name}')
118
+ step = WorkflowStep(
119
+ workflow_id=run_id,
120
+ phase=section_name,
121
+ repeat_index=i_progress,
122
+ step_index=index,
123
+ method_name="stop",
124
+ start_time=datetime.now(),
125
+ end_time=datetime.now(),
126
+ run_error=False,
127
+ )
128
+ db.session.add(step)
108
129
  break
109
130
  line = step_list[index]
110
- logger.info(f"Executing: {line}") # Debugging output
131
+ method_name = line.strip().split("(")[0] if "(" in line else line.strip()
132
+ start_time = datetime.now()
133
+ step = WorkflowStep(
134
+ workflow_id=run_id,
135
+ phase=section_name,
136
+ repeat_index=i_progress,
137
+ step_index=index,
138
+ method_name=method_name,
139
+ start_time=start_time,
140
+ )
141
+ logger.info(f"Executing: {line}")
111
142
  socketio.emit('execution', {'section': f"{section_name}-{index}"})
112
143
  # self._emit_progress(socketio, 100)
144
+ # if line.startswith("registered_workflows"):
145
+ # line = line.replace("registered_workflows.", "")
113
146
  try:
114
- exec(line, exec_globals, exec_locals)
147
+ if line.startswith("time.sleep("): # add safe sleep for time.sleep lines
148
+ duration_str = line[len("time.sleep("):-1]
149
+ duration = float(duration_str)
150
+ self.safe_sleep(duration)
151
+ else:
152
+ exec(line, exec_globals, exec_locals)
153
+ step.run_error = False
115
154
  except Exception as e:
116
155
  logger.error(f"Error during script execution: {e}")
117
- socketio.emit('error', {'message': e.__str__()})
156
+ socketio.emit('error', {'message': str(e)})
157
+
158
+ step.run_error = True
118
159
  self.toggle_pause()
160
+ step.end_time = datetime.now()
161
+ db.session.add(step)
162
+ db.session.commit()
163
+
119
164
  self.pause_event.wait()
120
165
 
121
166
  # todo update script during the run
122
167
  # _func_str = script.compile()
123
168
  # step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
124
- index += 1
169
+ if not step.run_error:
170
+ index += 1
171
+ elif not self.retry:
172
+ index += 1
125
173
  return exec_locals # Return the 'results' variable
126
174
 
127
175
  def _run_with_stop_check(self, script: Script, repeat_count: int, run_name: str, logger, socketio, config, bo_args,
128
- output_path):
176
+ output_path, current_app):
129
177
  time.sleep(1)
130
178
  # _func_str = script.compile()
131
179
  # step_list_dict: dict = script.convert_to_lines(_func_str)
@@ -133,39 +181,49 @@ class ScriptRunner:
133
181
 
134
182
  # Run "prep" section once
135
183
  script_dict = script.script_dict
136
- self._run_actions(script, section_name="prep", logger=logger, socketio=socketio)
137
- output_list = []
138
- _, arg_type = script.config("script")
139
- _, return_list = script.config_return()
140
-
141
- # Run "script" section multiple times
142
- if repeat_count:
143
- self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
144
- run_name, return_list, logger, socketio)
145
- elif config:
146
- self._run_config_section(config, arg_type, output_list, script, run_name, logger,
147
- socketio)
148
-
149
- # Run "cleanup" section once
150
- self._run_actions(script, section_name="cleanup", logger=logger, socketio=socketio)
151
- # Reset the running flag when done
152
- with self.lock:
153
- self.is_running = False
154
- # Save results if necessary
155
- if output_list:
156
- self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
157
- self._emit_progress(socketio, 100)
184
+ with current_app.app_context():
185
+
186
+ run = WorkflowRun(name=script.name or "untitled", platform=script.deck,start_time=datetime.now())
187
+ db.session.add(run)
188
+ db.session.flush()
158
189
 
159
- def _run_actions(self, script, section_name="", logger=None, socketio=None):
190
+ self._run_actions(script, section_name="prep", logger=logger, socketio=socketio, run_id=run.id)
191
+ output_list = []
192
+ _, arg_type = script.config("script")
193
+ _, return_list = script.config_return()
194
+
195
+ # Run "script" section multiple times
196
+ if repeat_count:
197
+ self._run_repeat_section(repeat_count, arg_type, bo_args, output_list, script,
198
+ run_name, return_list, logger, socketio, run_id=run.id)
199
+ elif config:
200
+ self._run_config_section(config, arg_type, output_list, script, run_name, logger,
201
+ socketio, run_id=run.id)
202
+
203
+ # Run "cleanup" section once
204
+ self._run_actions(script, section_name="cleanup", logger=logger, socketio=socketio,run_id=run.id)
205
+ # Reset the running flag when done
206
+ with self.lock:
207
+ self.is_running = False
208
+ # Save results if necessary
209
+ filename = None
210
+ if output_list:
211
+ filename = self._save_results(run_name, arg_type, return_list, output_list, logger, output_path)
212
+ self._emit_progress(socketio, 100)
213
+ run.end_time = datetime.now()
214
+ run.data_path = filename
215
+ db.session.commit()
216
+
217
+ def _run_actions(self, script, section_name="", logger=None, socketio=None, run_id=None):
160
218
  _func_str = script.compile()
161
219
  step_list: list = script.convert_to_lines(_func_str).get(section_name, [])
162
220
  logger.info(f'Executing {section_name} steps') if step_list else logger.info(f'No {section_name} steps')
163
221
  if self.stop_pending_event.is_set():
164
222
  logger.info(f"Stopping execution during {section_name} section.")
165
223
  return
166
- self.exex_steps(script, section_name, logger, socketio)
224
+ self.exec_steps(script, section_name, logger, socketio, run_id=run_id, i_progress=0)
167
225
 
168
- def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio):
226
+ def _run_config_section(self, config, arg_type, output_list, script, run_name, logger, socketio, run_id):
169
227
  compiled = True
170
228
  for i in config:
171
229
  try:
@@ -185,22 +243,22 @@ class ScriptRunner:
185
243
  self._emit_progress(socketio, progress)
186
244
  # fname = f"{run_name}_script"
187
245
  # function = self.globals_dict[fname]
188
- output = self.exex_steps(script, "script", logger, socketio, **kwargs)
246
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i, **kwargs)
189
247
  if output:
190
248
  # kwargs.update(output)
191
249
  output_list.append(output)
192
250
 
193
251
  def _run_repeat_section(self, repeat_count, arg_types, bo_args, output_list, script, run_name, return_list,
194
- logger, socketio):
252
+ logger, socketio, run_id):
195
253
  if bo_args:
196
254
  logger.info('Initializing optimizer...')
197
255
  ax_client = utils.ax_initiation(bo_args, arg_types)
198
- for i in range(int(repeat_count)):
256
+ for i_progress in range(int(repeat_count)):
199
257
  if self.stop_pending_event.is_set():
200
- logger.info(f'Stopping execution during {run_name}: {i + 1}/{int(repeat_count)}')
258
+ logger.info(f'Stopping execution during {run_name}: {i_progress + 1}/{int(repeat_count)}')
201
259
  break
202
- logger.info(f'Executing {run_name} experiment: {i + 1}/{int(repeat_count)}')
203
- progress = (i + 1) * 100 / int(repeat_count) - 0.1
260
+ logger.info(f'Executing {run_name} experiment: {i_progress + 1}/{int(repeat_count)}')
261
+ progress = (i_progress + 1) * 100 / int(repeat_count) - 0.1
204
262
  self._emit_progress(socketio, progress)
205
263
  if bo_args:
206
264
  try:
@@ -208,7 +266,7 @@ class ScriptRunner:
208
266
  logger.info(f'Output value: {parameters}')
209
267
  # fname = f"{run_name}_script"
210
268
  # function = self.globals_dict[fname]
211
- output = self.exex_steps(script, "script", logger, socketio, **parameters)
269
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress, **parameters)
212
270
 
213
271
  _output = {key: value for key, value in output.items() if key in return_list}
214
272
  ax_client.complete_trial(trial_index=trial_index, raw_data=_output)
@@ -219,7 +277,7 @@ class ScriptRunner:
219
277
  else:
220
278
  # fname = f"{run_name}_script"
221
279
  # function = self.globals_dict[fname]
222
- output = self.exex_steps(script, "script", logger, socketio)
280
+ output = self.exec_steps(script, "script", logger, socketio, run_id, i_progress)
223
281
 
224
282
  if output:
225
283
  output_list.append(output)
@@ -237,7 +295,16 @@ class ScriptRunner:
237
295
  writer.writeheader()
238
296
  writer.writerows(output_list)
239
297
  logger.info(f'Results saved to {file_path}')
298
+ return filename
240
299
 
241
300
  @staticmethod
242
301
  def _emit_progress(socketio, progress):
243
302
  socketio.emit('progress', {'progress': progress})
303
+
304
+ def safe_sleep(self, duration: float):
305
+ interval = 1 # check every 1 second
306
+ end_time = time.time() + duration
307
+ while time.time() < end_time:
308
+ if self.stop_current_event.is_set():
309
+ return # Exit early if stop is requested
310
+ time.sleep(min(interval, end_time - time.time()))
ivoryos/utils/utils.py CHANGED
@@ -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.21"
1
+ __version__ = "0.1.23"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ivoryos
3
- Version: 0.1.21
3
+ Version: 0.1.23
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,50 @@
1
- ivoryos/__init__.py,sha256=bx8EuJ5ObYwuwlW3KnWQwXG-AdWWyi-F0kaOLe7WeUo,5978
1
+ ivoryos/__init__.py,sha256=pvQVtXOuDSaOwxMkcfgE00kEVXMFUsOsvjv49-Hy0Z4,6877
2
2
  ivoryos/config.py,sha256=3FPBYTIBhQTKDvsEoR8ZeTmg65D-CSFEdGmOuIL4pSI,1311
3
- ivoryos/version.py,sha256=qEmNtjnOwhDYQ0cHPPtUkUaghzD2xl0thJEznl4giYw,23
3
+ ivoryos/version.py,sha256=0byemO6n6WCv41u9vBG2AIsOkVbxLvok7puvwy8EhfU,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=h4b5r1K5W3psqze0v2QF_jX0ADppy8XdFBQLJb41LOU,5808
16
- ivoryos/routes/database/templates/database/experiment_database.html,sha256=GeZjx220PdAt_dVN6kigsaNzvaruPlrSSii1cka56NE,3530
15
+ ivoryos/routes/database/database.py,sha256=uchoLXRx8ShJZIu20NZya6FJ4LMg2b9yvn6hjEq3dlU,7687
16
+ ivoryos/routes/database/templates/database/experiment_database.html,sha256=edlCcKfrS91gGG1dPFQjC9xD7F7nWNNqS3S6Oa7apzs,3460
17
+ ivoryos/routes/database/templates/database/experiment_step_view.html,sha256=u8_XYhiZ98PzglMzFEkuM1Tk9hVWf79xXIrpHVDxKa0,3618
18
+ ivoryos/routes/database/templates/database/step_card.html,sha256=F4JRfacrEQfk2rrEbcI_i7G84nzKKDmCrMSmStLb4W4,290
19
+ ivoryos/routes/database/templates/database/workflow_run_database.html,sha256=MczK9my9u0SyQsMFLbc6CXeZqKaBo5vk1SpwjkcZdqk,3571
17
20
  ivoryos/routes/design/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- ivoryos/routes/design/design.py,sha256=rIy6LCMw6ck4bxuPqoxIV6F1vZxY8aZ3Au5pm74dfr8,23934
19
- ivoryos/routes/design/templates/design/experiment_builder.html,sha256=2cT25URI6NoRDKuU6xl8Vsv4pqyQhhx-Fu8JOfPIP8k,27110
20
- ivoryos/routes/design/templates/design/experiment_run.html,sha256=t4Pq6uIltm3sq5cDb4zQa9i98iOQFNumkSa24vzCZrY,26001
21
+ ivoryos/routes/design/design.py,sha256=gO8FKuSdGD6EiN3cbspk7jv6y2Ar25A-ZdrCyUTInpE,25221
22
+ ivoryos/routes/design/templates/design/experiment_builder.html,sha256=rEdcHj5onJG_4MejdFBPnJVzsvCMp1KDteqNkpx24kQ,29430
23
+ ivoryos/routes/design/templates/design/experiment_run.html,sha256=Q7cYYTgvZ8SBzqkDEhAwR634-LLcYq4Gof4bpH_adt0,30397
21
24
  ivoryos/routes/main/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
25
  ivoryos/routes/main/main.py,sha256=yuVJzXAob1kc1dfflkTBIZQ0tdf6kChfuq-uQlN1e9Q,957
23
26
  ivoryos/routes/main/templates/main/help.html,sha256=IOktMEsOPk0SCiMBXZ4mpffClERAyX8W82fel71M3M0,9370
24
- ivoryos/routes/main/templates/main/home.html,sha256=ujM0YC0yrHhCfkuprNnZZZd8XEEguS_6NjrY5ktfctg,3356
27
+ ivoryos/routes/main/templates/main/home.html,sha256=8AhQOWakD-1TyWA0s-B9ZK96L_f8ywYUEGHW1UzMGZA,4634
25
28
  ivoryos/static/favicon.ico,sha256=RhlrPtfITOkzC9BjP1UB1V5L9Oyp6NwNtWeMcGOnpyc,15406
26
29
  ivoryos/static/logo.webp,sha256=lXgfQR-4mHTH83k7VV9iB54-oC2ipe6uZvbwdOnLETc,14974
27
30
  ivoryos/static/style.css,sha256=zQVx35A5g6JMJ-K84-6fSKtzXGjp_p5ZVG6KLHPM2IE,4021
28
31
  ivoryos/static/gui_annotation/Slide1.png,sha256=Lm4gdOkUF5HIUFaB94tl6koQVkzpitKj43GXV_XYMMc,121727
29
32
  ivoryos/static/gui_annotation/Slide2.PNG,sha256=z3wQ9oVgg4JTWVLQGKK_KhtepRHUYP1e05XUWGT2A0I,118761
30
33
  ivoryos/static/js/overlay.js,sha256=dPxop19es0E0ZUSY3d_4exIk7CJuQEnlW5uTt5fZfzI,483
31
- ivoryos/static/js/socket_handler.js,sha256=fJ_SKCfS2ZejnECPKZuD8DTEPhBxmm0m-yCvxf2UtIQ,4885
34
+ ivoryos/static/js/socket_handler.js,sha256=YGwWlT8TqNBvvIzs2G9g1g7nM2-vUPZjCwmQt4Yv0Uw,5078
32
35
  ivoryos/static/js/sortable_card.js,sha256=ifmlGe3yy0U_KzMphV4ClRhK2DLOvkELYMlq1vECuac,807
33
36
  ivoryos/static/js/sortable_design.js,sha256=wwpKfIzZGDxfX3moNz0cvPvm9YyHmopZK3wmkUdnBiw,4333
34
- ivoryos/templates/base.html,sha256=OM4D_1A_RawOLBRhiCbKVn7a1UgPxji0BZlnCURl5WM,8325
37
+ ivoryos/templates/base.html,sha256=sDdwqOIUP2Get-py4E59PkieoGWLFpX6wAJe93s4aRo,8518
35
38
  ivoryos/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- ivoryos/utils/db_models.py,sha256=LG78TSc9VAEELH9pgTVRRpmyEsCjtxkQOM176N0HAw8,25179
37
- ivoryos/utils/form.py,sha256=Eqrg4G44hoG6TizxTEP4DNd-rEoyN77t9Y7cB1WdrcQ,19205
39
+ ivoryos/utils/client_proxy.py,sha256=AzcSQGMqeCqVULP1a7vEKNe135NZYryVX63ke0wgK04,2099
40
+ ivoryos/utils/db_models.py,sha256=1G2De1nLRCGwuY4zqgMeIQ-p1XJ_PkBxH1cd0fJ9YgY,26740
41
+ ivoryos/utils/form.py,sha256=b3JKxRc1jN45-bXyfzSJT1lcssUuxT86FhRmNUDv5-U,20973
38
42
  ivoryos/utils/global_config.py,sha256=P0xs_33bZfNQ-D71lCkq7HJyT4ngQWPqUKnkoMrmM8c,1908
39
- ivoryos/utils/llm_agent.py,sha256=DTf-AF56vy1Em1fUKagl5NURKittmNoxTKIw1PlyC2o,6413
40
- ivoryos/utils/script_runner.py,sha256=1B6iImRvJ--VSD036p5YSjl76yxMRqDa1OCoRi9ZMM4,10304
41
- ivoryos/utils/utils.py,sha256=SWVlMjpcUnQ2hgVasXwKugX781eiq78Dv6oZa21lg5Q,14071
42
- ivoryos-0.1.21.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
43
- ivoryos-0.1.21.dist-info/METADATA,sha256=3eE6xsyC4fYuUeXA5OsAhWbdrRPCGXaxAFbTTLbCCNA,6697
44
- ivoryos-0.1.21.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
45
- ivoryos-0.1.21.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
46
- ivoryos-0.1.21.dist-info/RECORD,,
43
+ ivoryos/utils/llm_agent.py,sha256=-lVCkjPlpLues9sNTmaT7bT4sdhWvV2DiojNwzB2Lcw,6422
44
+ ivoryos/utils/script_runner.py,sha256=iyeYCTPWnukW3F8UsarIdZxFzDnHBq6MH0ydfVGNFaw,13286
45
+ ivoryos/utils/utils.py,sha256=VP-4Wf-slK2pMe659K5TJLbZXVNvyCD1-3kdfp_COxI,14990
46
+ ivoryos-0.1.23.dist-info/LICENSE,sha256=p2c8S8i-8YqMpZCJnadLz1-ofxnRMILzz6NCMIypRag,1084
47
+ ivoryos-0.1.23.dist-info/METADATA,sha256=_QVtGZkZfoLRYpUTX4qsJwO-rJEU5P7y3H_QnDp7Xok,6846
48
+ ivoryos-0.1.23.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
49
+ ivoryos-0.1.23.dist-info/top_level.txt,sha256=FRIWWdiEvRKqw-XfF_UK3XV0CrnNb6EmVbEgjaVazRM,8
50
+ ivoryos-0.1.23.dist-info/RECORD,,