code-loader 1.0.188.dev0__py3-none-any.whl → 1.0.189.dev0__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.
@@ -92,6 +92,16 @@ SectionCallableInterface = Callable[[Union[int, str], PreprocessResponse], npt.N
92
92
  InstanceCallableInterface = Callable[[Union[int, str], PreprocessResponse, int], Optional[ElementInstance]]
93
93
  InstanceLengthCallableInterface = Callable[[Union[int, str], PreprocessResponse], int]
94
94
 
95
+ # (sample_id, prev_inputs, prev_outputs, preprocess) -> next model inputs, or None to end the chain.
96
+ # First call per chain receives prev_inputs=None, prev_outputs=None and returns the initial inputs.
97
+ # Keys starting with '_' are passthrough state: never fed to the model, handed back verbatim in
98
+ # prev_inputs on the next call. The hook must be stateless and deterministically seeded (the engine
99
+ # may replay a step after crash recovery and relies on identical results).
100
+ AutoregressiveStepCallableInterface = Callable[
101
+ [Union[int, str], Optional[Dict[str, npt.NDArray[np.float32]]], Optional[Dict[str, npt.NDArray[np.float32]]],
102
+ PreprocessResponse],
103
+ Optional[Dict[str, npt.NDArray[np.float32]]]]
104
+
95
105
  MetadataSectionCallableInterface = Union[
96
106
  Callable[[Union[int, str], PreprocessResponse], int],
97
107
  Callable[[Union[int, str], PreprocessResponse], Dict[str, int]],
@@ -239,6 +249,15 @@ class CustomLatentSpaceHandler:
239
249
  function: SectionCallableInterface
240
250
  name: str = 'custom_latent_space'
241
251
 
252
+
253
+ @dataclass
254
+ class AutoregressiveStepHandler:
255
+ function: AutoregressiveStepCallableInterface
256
+ name: str = 'autoregressive_step'
257
+ # Per-model-input shapes (unbatched, underscore passthrough keys excluded), discovered by running
258
+ # the hook's first call at parse time. Fills the role InputHandler.shape plays for input encoders.
259
+ input_shapes: Optional[Dict[str, List[int]]] = None
260
+
242
261
  @dataclass
243
262
  class PredictionTypeHandler:
244
263
  name: str
@@ -279,6 +298,7 @@ class DatasetIntegrationSetup:
279
298
  custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
280
299
  custom_latent_space: Optional[CustomLatentSpaceHandler] = None
281
300
  simulations: List[SimulationHandler] = field(default_factory=list)
301
+ autoregressive_step: Optional[AutoregressiveStepHandler] = None
282
302
 
283
303
 
284
304
  @dataclass
@@ -1,6 +1,6 @@
1
1
 
2
2
  import inspect
3
- from typing import Callable, List, Optional, Dict, Any, Type, Union
3
+ from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args
4
4
 
5
5
  import numpy as np
6
6
  import numpy.typing as npt
@@ -13,7 +13,7 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
13
13
  CustomMultipleReturnCallableInterfaceMultiArgs, DatasetBaseHandler, custom_latent_space_attribute, \
14
14
  RawInputsForHeatmap, VisualizerHandlerData, MetricHandlerData, CustomLossHandlerData, SamplePreprocessResponse, \
15
15
  ElementInstanceMasksHandler, InstanceCallableInterface, CustomLatentSpaceHandler, InstanceMetricHandler, \
16
- SimulationHandler, _simulation_context
16
+ SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface
17
17
  from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType
18
18
  from code_loader.contract.mapping import NodeConnection, NodeMapping, NodeMappingType
19
19
  from code_loader.contract.responsedataclasses import DatasetTestResultPayload, LeapAnalysisConfiguration
@@ -31,6 +31,27 @@ from code_loader.visualizers.default_visualizers import DefaultVisualizer, \
31
31
  mapping_runtime_mode_env_var_mame = '__MAPPING_RUNTIME_MODE__'
32
32
 
33
33
 
34
+ def _stringized_annotation_type_name(annotation: Any) -> Optional[str]:
35
+ """Bare type name if ``annotation`` is a stringized annotation (from ``from __future__
36
+ import annotations`` or a quoted hint), else ``None``. code_loader inspects raw
37
+ annotations expecting real classes, so stringization silently breaks type detection."""
38
+ if isinstance(annotation, str):
39
+ return annotation.split("[")[0].strip().rsplit(".", 1)[-1]
40
+ return None
41
+
42
+
43
+ def _reject_stringized_sample_preprocess_response(function: Callable[..., Any], arg_name: str, annotation: Any) -> None:
44
+ """Fail fast on a stringized SamplePreprocessResponse arg (otherwise it silently
45
+ mis-wires as a regular input, surfacing only on the platform)."""
46
+ if _stringized_annotation_type_name(annotation) == SamplePreprocessResponse.__name__:
47
+ raise Exception(
48
+ f"Argument '{arg_name}' of function '{function.__name__}' is annotated with a string "
49
+ f"('{annotation}') instead of the SamplePreprocessResponse type. This usually means the "
50
+ f"file uses 'from __future__ import annotations' (or a quoted hint), which stringizes "
51
+ f"annotations and breaks Tensorleap type detection. Remove that import (or the quotes) so "
52
+ f"SamplePreprocessResponse is referenced as a real type.")
53
+
54
+
34
55
 
35
56
 
36
57
  class LeapBinder:
@@ -123,6 +144,7 @@ class LeapBinder:
123
144
  regular_arg_names = inspect.getfullargspec(function)[0]
124
145
  preprocess_response_arg_name = None
125
146
  for arg_name, arg_type in inspect.getfullargspec(function).annotations.items():
147
+ _reject_stringized_sample_preprocess_response(function, arg_name, arg_type)
126
148
  if arg_type == SamplePreprocessResponse:
127
149
  if preprocess_response_arg_name is not None:
128
150
  raise Exception("only one argument can be of type SamplePreprocessResponse")
@@ -144,8 +166,8 @@ class LeapBinder:
144
166
 
145
167
  if visualizer_type.value not in map_leap_data_type_to_visualizer_class:
146
168
  raise Exception(
147
- f'The visualizer_type is invalid. current visualizer_type: {visualizer_type}, ' # type: ignore[attr-defined]
148
- f'should be one of : {", ".join([arg.__name__ for arg in LeapData.__args__])}')
169
+ f'The visualizer_type is invalid. current visualizer_type: {visualizer_type}, '
170
+ f'should be one of : {", ".join([arg.__name__ for arg in get_args(LeapData)])}')
149
171
 
150
172
  func_annotations = function.__annotations__
151
173
  if "return" not in func_annotations:
@@ -155,10 +177,17 @@ class LeapBinder:
155
177
  f"https://docs.python.org/3/library/typing.html")
156
178
  else:
157
179
  return_type = func_annotations["return"]
158
- if return_type not in LeapData.__args__: # type: ignore[attr-defined]
180
+ if isinstance(return_type, str):
181
+ raise Exception(
182
+ f"The return type hint of function '{function.__name__}' is a string "
183
+ f"('{return_type}') instead of a type. This usually means the file uses "
184
+ f"'from __future__ import annotations' (or a quoted return hint), which stringizes "
185
+ f"annotations and prevents Tensorleap from detecting the visualizer type. Remove "
186
+ f"that import (or the quotes) so the return type is a real class.")
187
+ if return_type not in get_args(LeapData):
159
188
  raise Exception(
160
- f'The return type of function {function.__name__} is invalid. current return type: {return_type}, ' # type: ignore[attr-defined]
161
- f'should be one of : {", ".join([arg.__name__ for arg in LeapData.__args__])}')
189
+ f'The return type of function {function.__name__} is invalid. current return type: {return_type}, '
190
+ f'should be one of : {", ".join([arg.__name__ for arg in get_args(LeapData)])}')
162
191
 
163
192
  expected_return_type = map_leap_data_type_to_visualizer_class[visualizer_type.value]
164
193
  if not issubclass(return_type, expected_return_type):
@@ -296,6 +325,7 @@ class LeapBinder:
296
325
  regular_arg_names = inspect.getfullargspec(function)[0]
297
326
  preprocess_response_arg_name = None
298
327
  for arg_name, arg_type in inspect.getfullargspec(function).annotations.items():
328
+ _reject_stringized_sample_preprocess_response(function, arg_name, arg_type)
299
329
  if arg_type == SamplePreprocessResponse:
300
330
  if preprocess_response_arg_name is not None:
301
331
  raise Exception("only one argument can be of type SamplePreprocessResponse")
@@ -339,6 +369,7 @@ class LeapBinder:
339
369
  regular_arg_names = inspect.getfullargspec(function)[0]
340
370
  preprocess_response_arg_name = None
341
371
  for arg_name, arg_type in inspect.getfullargspec(function).annotations.items():
372
+ _reject_stringized_sample_preprocess_response(function, arg_name, arg_type)
342
373
  if arg_type == SamplePreprocessResponse:
343
374
  if preprocess_response_arg_name is not None:
344
375
  raise Exception("only one argument can be of type SamplePreprocessResponse")
@@ -380,6 +411,7 @@ class LeapBinder:
380
411
  regular_arg_names = inspect.getfullargspec(function)[0]
381
412
  preprocess_response_arg_name = None
382
413
  for arg_name, arg_type in inspect.getfullargspec(function).annotations.items():
414
+ _reject_stringized_sample_preprocess_response(function, arg_name, arg_type)
383
415
  if arg_type == SamplePreprocessResponse:
384
416
  if preprocess_response_arg_name is not None:
385
417
  raise Exception("only one argument can be of type SamplePreprocessResponse")
@@ -492,6 +524,26 @@ class LeapBinder:
492
524
  """
493
525
  self.setup_container.custom_latent_space = CustomLatentSpaceHandler(function)
494
526
 
527
+ def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface) -> None:
528
+ """
529
+ Set the autoregressive step hook — the feedback function that drives a chain:
530
+ it supplies the model's initial inputs on its first call (prev_inputs=None, prev_outputs=None)
531
+ and turns each step's inputs/outputs into the next step's inputs until it returns None.
532
+ An autoregressive integration has no input encoders; the hook is the sole input source.
533
+ """
534
+ if self.setup_container.autoregressive_step is not None:
535
+ raise Exception('tensorleap_autoregressive_step is already defined. '
536
+ 'Only one autoregressive step hook is allowed per integration.')
537
+ self.setup_container.autoregressive_step = AutoregressiveStepHandler(function)
538
+
539
+ # Builtin chain metadata, declared at parse time so it survives the reporter's
540
+ # metadata type mapping; the placeholder values are overwritten by the engine when a
541
+ # chain finalizes (realized length, truncated-by-safety-cap flag).
542
+ def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Dict[str, Any]:
543
+ return {'length': -1, 'truncated': False}
544
+
545
+ self.set_metadata(builtin_chain_metadata, 'builtin_chain')
546
+
495
547
  def set_custom_layer(self, custom_layer: Type[Any], name: str, inspect_layer: bool = False,
496
548
  kernel_index: Optional[int] = None, use_custom_latent_space: bool = False) -> None:
497
549
  """
@@ -702,6 +754,7 @@ class LeapBinder:
702
754
  )
703
755
 
704
756
  def check(self) -> None:
757
+ self.validate_autoregressive_setup()
705
758
  preprocess_result = self.get_preprocess_result()
706
759
  self.check_preprocess(preprocess_result)
707
760
  self.check_handlers(preprocess_result)
@@ -709,6 +762,32 @@ class LeapBinder:
709
762
  self.validate_ignore_latent_spaces()
710
763
  print("Successful!")
711
764
 
765
+ def validate_autoregressive_setup(self) -> None:
766
+ """Structural rules for autoregressive integrations (no-op when the hook is absent).
767
+
768
+ Order-independent: runs after the whole script registered, because decorators may
769
+ appear in any order in the integration file.
770
+ """
771
+ if self.setup_container.autoregressive_step is None:
772
+ return
773
+ if self.setup_container.inputs:
774
+ raise Exception(
775
+ "An autoregressive integration must not declare input encoders — "
776
+ "tensorleap_autoregressive_step is the sole source of model inputs "
777
+ "(its first call returns the initial inputs). Found input encoders: "
778
+ f"{[handler.name for handler in self.setup_container.inputs]}.")
779
+ if self.setup_container.instance_masks:
780
+ raise Exception(
781
+ "Element instances are not supported together with tensorleap_autoregressive_step: "
782
+ "instance generation masks the sample's encoded inputs, which do not exist in an "
783
+ "autoregressive integration. Remove the instance encoders or the autoregressive hook.")
784
+ if self.setup_container.custom_latent_space is not None:
785
+ raise Exception(
786
+ "tensorleap_custom_latent_space is not supported together with "
787
+ "tensorleap_autoregressive_step: the custom latent space function only sees "
788
+ "(sample_id, preprocess) — it runs before generation and cannot observe the chain. "
789
+ "The chain's latent is the final step's model latent.")
790
+
712
791
  def validate_ignore_latent_spaces(self) -> None:
713
792
  """Validate leap_analysis_configuration.ignore_latent_spaces against this binder.
714
793
 
@@ -21,7 +21,7 @@ from code_loader.contract.datasetclasses import CustomCallableInterfaceMultiArgs
21
21
  CustomMultipleReturnCallableInterfaceMultiArgs, ConfusionMatrixCallableInterfaceMultiArgs, CustomCallableInterface, \
22
22
  VisualizerCallableInterface, MetadataSectionCallableInterface, PreprocessResponse, SectionCallableInterface, \
23
23
  ConfusionMatrixElement, SamplePreprocessResponse, PredictionTypeHandler, InstanceCallableInterface, ElementInstance, \
24
- InstanceLengthCallableInterface
24
+ InstanceLengthCallableInterface, AutoregressiveStepCallableInterface
25
25
  from code_loader.contract.enums import MetricDirection, LeapDataType, DatasetMetadataType, DataStateType
26
26
  from code_loader import leap_binder, LeapLoader
27
27
  from code_loader.contract.mapping import NodeMapping, NodeMappingType, NodeConnection
@@ -223,6 +223,59 @@ def _add_mapping_connections(connects_to, arg_names, node_mapping_type, name):
223
223
  _add_mapping_connection(user_unique_name, connection_destinations, arg_names, name, node_mapping_type)
224
224
 
225
225
 
226
+ def _require_sample_preprocess_response_supplied(user_function: Callable, args: tuple, kwargs: dict) -> None:
227
+ """A SamplePreprocessResponse argument is auto-injected by the platform / check_dataset
228
+ but NOT inside integration_test, where the author calls the function directly. Fail fast
229
+ with an actionable message instead of a raw 'missing argument' TypeError."""
230
+ spr_arg_name = None
231
+ for arg_name, arg_type in inspect.getfullargspec(user_function).annotations.items():
232
+ if arg_type == SamplePreprocessResponse:
233
+ spr_arg_name = arg_name
234
+ break
235
+ if spr_arg_name is None:
236
+ return
237
+ signature = inspect.signature(user_function)
238
+ param = signature.parameters.get(spr_arg_name)
239
+ # Only enforce a REQUIRED SamplePreprocessResponse arg. If it has a default it is
240
+ # optional and the function may legitimately be called with or without it.
241
+ if param is None or param.default is not inspect.Parameter.empty:
242
+ return
243
+ try:
244
+ supplied = signature.bind_partial(*args, **kwargs).arguments
245
+ except TypeError:
246
+ return
247
+ if spr_arg_name not in supplied:
248
+ raise Exception(
249
+ f"'{user_function.__name__}' takes a SamplePreprocessResponse argument "
250
+ f"'{spr_arg_name}', but it was not provided. Inside integration_test pass it "
251
+ f"explicitly, e.g. {user_function.__name__}(..., "
252
+ f"SamplePreprocessResponse(sample_id, preprocess)).")
253
+
254
+
255
+ def _warn_loss_inputs_not_wired_to_model() -> None:
256
+ """Warn when a custom loss consumes an input encoder the model is not given.
257
+
258
+ Such an input stays a plain NodeMappingType.Input (model-fed inputs get re-tagged to
259
+ Input0/Input1/...) and crashes the platform model build. Losses only — the same pattern
260
+ in a metric or visualizer builds fine."""
261
+ loss_types = {NodeMappingType.Loss, NodeMappingType.CustomLoss}
262
+ for connection in leap_binder.mapping_connections:
263
+ if connection.node is None or connection.node.type not in loss_types:
264
+ continue
265
+ for arg_name, source in (connection.node_inputs or {}).items():
266
+ if source is not None and source.type == NodeMappingType.Input:
267
+ input_name = source.name or arg_name
268
+ store_general_warning(
269
+ key=("loss_input_not_wired_to_model", connection.node.name, input_name),
270
+ message=(
271
+ f"Custom loss '{connection.node.name}' consumes input '{input_name}', "
272
+ f"which is not fed to the model. This is not supported: the platform builds "
273
+ f"losses into the model graph, so an input the model does not take has no "
274
+ f"source there and the model build fails. Instead, carry this data in the "
275
+ f"ground truth and derive it inside the loss (not as a separate input "
276
+ f"encoder)."))
277
+
278
+
226
279
  def tensorleap_integration_test():
227
280
  def decorating_function(integration_test_function: Callable):
228
281
  leap_binder.integration_test_func = integration_test_function
@@ -279,6 +332,8 @@ def tensorleap_integration_test():
279
332
  finally:
280
333
  _called_from_inside_tl_integration_test_decorator = False
281
334
 
335
+ _warn_loss_inputs_not_wired_to_model()
336
+
282
337
  leap_binder.check()
283
338
 
284
339
  return inner
@@ -822,6 +877,7 @@ def tensorleap_custom_metric(name: str,
822
877
  _called_from_inside_tl_decorator += 1
823
878
 
824
879
  try:
880
+ _require_sample_preprocess_response_supplied(user_function, args, kwargs)
825
881
  result = user_function(*args, **kwargs)
826
882
  finally:
827
883
  _called_from_inside_tl_decorator -= 1
@@ -1031,6 +1087,7 @@ def tensorleap_custom_instances_metric(name: str,
1031
1087
  _called_from_inside_tl_decorator += 1
1032
1088
 
1033
1089
  try:
1090
+ _require_sample_preprocess_response_supplied(user_function, args, kwargs)
1034
1091
  result = user_function(*args, **kwargs)
1035
1092
  finally:
1036
1093
  _called_from_inside_tl_decorator -= 1
@@ -1383,6 +1440,7 @@ def tensorleap_custom_visualizer(name: str, visualizer_type: LeapDataType,
1383
1440
  _called_from_inside_tl_decorator += 1
1384
1441
 
1385
1442
  try:
1443
+ _require_sample_preprocess_response_supplied(user_function, args, kwargs)
1386
1444
  result = user_function(*args, **kwargs)
1387
1445
  finally:
1388
1446
  _called_from_inside_tl_decorator -= 1
@@ -1570,6 +1628,232 @@ def tensorleap_custom_latent_space():
1570
1628
  return decorating_function
1571
1629
 
1572
1630
 
1631
+ def tensorleap_autoregressive_step():
1632
+ """The feedback hook that drives an autoregressive chain.
1633
+
1634
+ Signature of the decorated function:
1635
+ (sample_id, prev_inputs, prev_outputs, preprocess) -> Optional[Dict[str, np.ndarray]]
1636
+
1637
+ First call per chain: prev_inputs=None, prev_outputs=None — returns the initial model inputs.
1638
+ Later calls receive the previous step's model inputs and outputs (unbatched, per-sample) and
1639
+ return the next step's full input dict; returning None ends the chain. Keys starting with '_'
1640
+ are passthrough state: never fed to the model, handed back verbatim in prev_inputs next call.
1641
+ The hook must be stateless and deterministically seeded (seed stochasticity from sample_id) —
1642
+ the engine replays steps after crash recovery and relies on identical results.
1643
+ """
1644
+ def decorating_function(user_function: AutoregressiveStepCallableInterface):
1645
+ # Contract: shapes and key set are fixed across all calls AND all chains (compiled graphs
1646
+ # need static shapes), so one record shared across samples is the correct scope.
1647
+ first_call_record: Dict[str, Any] = {'keys': None, 'shapes': None}
1648
+
1649
+ def _normalize_args(args, kwargs):
1650
+ expected_names = ["sample_id", "prev_inputs", "prev_outputs", "preprocess"]
1651
+ validate_args_structure(
1652
+ *args,
1653
+ types_order=[Union[int, str], Union[dict, type(None)], Union[dict, type(None)],
1654
+ PreprocessResponse],
1655
+ func_name=user_function.__name__, expected_names=expected_names, **kwargs)
1656
+ normalized = []
1657
+ for i, name in enumerate(expected_names):
1658
+ normalized.append(args[i] if i < len(args) else kwargs[name])
1659
+ return normalized
1660
+
1661
+ def _validate_input_args(sample_id, prev_inputs, prev_outputs, preprocess_response):
1662
+ assert (prev_inputs is None) == (prev_outputs is None), \
1663
+ (f'{user_function.__name__}() validation failed: '
1664
+ f'prev_inputs and prev_outputs must both be None (first call) or both be dicts '
1665
+ f'(subsequent calls). Got prev_inputs={type(prev_inputs).__name__}, '
1666
+ f'prev_outputs={type(prev_outputs).__name__}.')
1667
+ assert type(sample_id) == preprocess_response.sample_id_type, \
1668
+ (f'{user_function.__name__}() validation failed: '
1669
+ f'Argument sample_id should be as the same type as defined in the preprocess response '
1670
+ f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
1671
+
1672
+ def _validate_result(result, is_first_call):
1673
+ if result is None:
1674
+ assert not is_first_call, \
1675
+ (f'{user_function.__name__}() validation failed: '
1676
+ f'the first call (prev_inputs=None, prev_outputs=None) must return the initial '
1677
+ f'model inputs dict — returning None on the first call would produce an empty chain.')
1678
+ return
1679
+ assert isinstance(result, dict), \
1680
+ (f'{user_function.__name__}() validation failed: '
1681
+ f'the return value must be a dict of named model input tensors, or None to end the '
1682
+ f'chain. Got {type(result)}.')
1683
+ model_input_keys = [key for key in result if not key.startswith('_')]
1684
+ assert model_input_keys, \
1685
+ (f'{user_function.__name__}() validation failed: '
1686
+ f'the returned dict contains no model inputs — every key starts with "_" '
1687
+ f'(underscore keys are passthrough state, never fed to the model).')
1688
+ for key, value in result.items():
1689
+ assert isinstance(key, str), \
1690
+ (f'{user_function.__name__}() validation failed: '
1691
+ f'all keys in the returned dict must be strings. Got {type(key)}.')
1692
+ if key.startswith('_'):
1693
+ continue
1694
+ assert isinstance(value, np.ndarray), \
1695
+ (f'{user_function.__name__}() validation failed: '
1696
+ f'value for model input "{key}" must be a numpy array. Got {type(value)}.')
1697
+
1698
+ def _validate_consistency(result):
1699
+ # Rules 2-3 of the autoregressive contract: identical key set and identical
1700
+ # (non-passthrough) shapes on every call — compiled graphs need static shapes.
1701
+ if result is None:
1702
+ return
1703
+ keys = set(result.keys())
1704
+ shapes = {key: tuple(value.shape) for key, value in result.items()
1705
+ if not key.startswith('_') and isinstance(value, np.ndarray)}
1706
+ if first_call_record['keys'] is None:
1707
+ first_call_record['keys'] = keys
1708
+ first_call_record['shapes'] = shapes
1709
+ return
1710
+ assert keys == first_call_record['keys'], \
1711
+ (f'{user_function.__name__}() validation failed: '
1712
+ f'every call must return the same key set as the first call. '
1713
+ f'First call keys: {sorted(first_call_record["keys"])}, this call: {sorted(keys)}.')
1714
+ for key, shape in shapes.items():
1715
+ assert shape == first_call_record['shapes'][key], \
1716
+ (f'{user_function.__name__}() validation failed: '
1717
+ f'tensor shapes must be identical on every call (fixed-length padding + mask for '
1718
+ f'growing sequences). Input "{key}" was {first_call_record["shapes"][key]} on the '
1719
+ f'first call and {shape} now.')
1720
+
1721
+ def _assert_deterministic(first_result, second_result):
1722
+ error_message = (
1723
+ f'{user_function.__name__}() validation failed: '
1724
+ f'the hook is not deterministic — two calls with identical arguments returned different '
1725
+ f'outputs. Seed any stochasticity (e.g. initial noise) from sample_id: the engine '
1726
+ f'replays steps after crash recovery and relies on identical results.')
1727
+ if first_result is None or second_result is None:
1728
+ assert first_result is None and second_result is None, error_message
1729
+ return
1730
+ assert set(first_result.keys()) == set(second_result.keys()), error_message
1731
+ for key, value in first_result.items():
1732
+ if key.startswith('_') or not isinstance(value, np.ndarray):
1733
+ continue
1734
+ assert np.array_equal(value, second_result[key]), error_message
1735
+
1736
+ def _squeeze_test_batch_dim(tensors_dict):
1737
+ # Inside the integration test the model works on batch-1 tensors (this wrapper adds the
1738
+ # batch axis to returned inputs, and model outputs come back batched). The user's hook
1739
+ # body must see the same unbatched per-sample tensors it will see at engine runtime, so
1740
+ # strip the leading batch-1 axis before calling it.
1741
+ if tensors_dict is None:
1742
+ return None
1743
+ squeezed = {}
1744
+ for key, value in tensors_dict.items():
1745
+ if not key.startswith('_') and isinstance(value, np.ndarray) \
1746
+ and value.ndim > 0 and value.shape[0] == 1:
1747
+ squeezed[key] = value[0]
1748
+ else:
1749
+ squeezed[key] = value
1750
+ return squeezed
1751
+
1752
+ def inner_without_validate(sample_id, prev_inputs, prev_outputs, preprocess_response):
1753
+ global _called_from_inside_tl_decorator
1754
+ _called_from_inside_tl_decorator += 1
1755
+ try:
1756
+ result = user_function(sample_id, prev_inputs, prev_outputs, preprocess_response)
1757
+ finally:
1758
+ _called_from_inside_tl_decorator -= 1
1759
+ return result
1760
+
1761
+ leap_binder.set_autoregressive_step(inner_without_validate)
1762
+
1763
+ def inner(*args, **kwargs):
1764
+ if not _call_from_tl_platform:
1765
+ set_current('tensorleap_autoregressive_step')
1766
+ sample_id, prev_inputs, prev_outputs, preprocess_response = _normalize_args(args, kwargs)
1767
+
1768
+ is_top_level_in_test = (_called_from_inside_tl_decorator == 0
1769
+ and _called_from_inside_tl_integration_test_decorator)
1770
+ if is_top_level_in_test:
1771
+ prev_inputs = _squeeze_test_batch_dim(prev_inputs)
1772
+ prev_outputs = _squeeze_test_batch_dim(prev_outputs)
1773
+
1774
+ _validate_input_args(sample_id, prev_inputs, prev_outputs, preprocess_response)
1775
+
1776
+ result = inner_without_validate(sample_id, prev_inputs, prev_outputs, preprocess_response)
1777
+
1778
+ if is_top_level_in_test:
1779
+ # Rule 7: determinism double-call. An unseeded sampler fails here, on the user's
1780
+ # machine, instead of silently forking a chain on engine crash-replay.
1781
+ second_result = inner_without_validate(sample_id, prev_inputs, prev_outputs,
1782
+ preprocess_response)
1783
+ _assert_deterministic(result, second_result)
1784
+
1785
+ _validate_result(result, is_first_call=prev_inputs is None)
1786
+ _validate_consistency(result)
1787
+
1788
+ if is_top_level_in_test and result is not None:
1789
+ result = {key: (np.expand_dims(value, axis=0)
1790
+ if not key.startswith('_') and isinstance(value, np.ndarray) else value)
1791
+ for key, value in result.items()}
1792
+
1793
+ if not _call_from_tl_platform:
1794
+ update_env_params_func("tensorleap_autoregressive_step", "v")
1795
+ return result
1796
+
1797
+ class _MappingInputsPlaceholder:
1798
+ """Mapping-mode stand-in for the hook's returned dict.
1799
+
1800
+ Indexing it by a model-input name yields a placeholder whose node_mapping the model
1801
+ placeholder tags Input0/Input1/... — exactly how input encoders wire the graph. One
1802
+ NodeConnection is recorded per non-passthrough key, once.
1803
+ """
1804
+ def __init__(self):
1805
+ self._items: Dict[str, Any] = {}
1806
+
1807
+ def __getitem__(self, key):
1808
+ assert isinstance(key, str), \
1809
+ f'Expected a string model input name, got {type(key)} instead.'
1810
+ if key not in self._items:
1811
+ class TempMapping:
1812
+ pass
1813
+ ret = TempMapping()
1814
+ ret.node_mapping = NodeMapping(key, NodeMappingType.Input)
1815
+ if not key.startswith('_'):
1816
+ leap_binder.mapping_connections.append(NodeConnection(ret.node_mapping, None))
1817
+ self._items[key] = ret
1818
+ return self._items[key]
1819
+
1820
+ def get(self, key, default=None):
1821
+ return self[key]
1822
+
1823
+ def _unsupported_iteration(self):
1824
+ raise Exception(
1825
+ 'Iterating over the autoregressive hook result is not supported inside the '
1826
+ 'integration test. Pass the model its inputs with explicit keys, e.g. '
1827
+ "model.run(None, {'x': inputs['x'], 't': inputs['t']}).")
1828
+
1829
+ def items(self):
1830
+ self._unsupported_iteration()
1831
+
1832
+ def keys(self):
1833
+ self._unsupported_iteration()
1834
+
1835
+ def values(self):
1836
+ self._unsupported_iteration()
1837
+
1838
+ mapping_placeholder = _MappingInputsPlaceholder()
1839
+
1840
+ def mapping_inner(*args, **kwargs):
1841
+ return mapping_placeholder
1842
+
1843
+ def final_inner(*args, **kwargs):
1844
+ if os.environ.get(mapping_runtime_mode_env_var_mame):
1845
+ return mapping_inner(*args, **kwargs)
1846
+ else:
1847
+ return inner(*args, **kwargs)
1848
+
1849
+ if not _call_from_tl_platform:
1850
+ register_decorator_row("tensorleap_autoregressive_step")
1851
+
1852
+ return final_inner
1853
+
1854
+ return decorating_function
1855
+
1856
+
1573
1857
  def tensorleap_preprocess():
1574
1858
  def decorating_function(user_function: Callable[[], List[PreprocessResponse]]):
1575
1859
  leap_binder.set_preprocess(user_function)
@@ -1929,8 +2213,13 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
1929
2213
 
1930
2214
  )
1931
2215
 
2216
+ # channel_dim is the channel axis in the BATCHED tensor (batch = axis 0):
2217
+ # 1 for channel-first (NCHW), -1 for channel-last (NHWC); 0 is invalid.
1932
2218
  if channel_dim <= 0 and channel_dim != -1:
1933
- raise Exception(f"Channel dim for input {name} is expected to be either -1 or positive")
2219
+ raise Exception(
2220
+ f"Invalid channel_dim ({channel_dim}) for input '{name}'. channel_dim is the "
2221
+ f"channel axis in the batched tensor (batch = axis 0), so 0 is invalid. Use 1 "
2222
+ f"for channel-first (NCHW) and -1 for channel-last (NHWC).")
1934
2223
 
1935
2224
  def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
1936
2225
  assert type(sample_id) == preprocess_response.sample_id_type, \
@@ -2149,6 +2438,7 @@ def tensorleap_custom_loss(name: str, connects_to=None):
2149
2438
  _called_from_inside_tl_decorator += 1
2150
2439
 
2151
2440
  try:
2441
+ _require_sample_preprocess_response_supplied(user_function, args, kwargs)
2152
2442
  result = user_function(*args, **kwargs)
2153
2443
  finally:
2154
2444
  _called_from_inside_tl_decorator -= 1
@@ -2394,6 +2684,12 @@ def tensorleap_status_table():
2394
2684
  table.append({"name": "tensorleap simulation (optional)", "Added to integration": UNKNOWN})
2395
2685
  _sim_tracking[sim_name] = "registered"
2396
2686
 
2687
+ def register_decorator_row(name: str):
2688
+ # Rows for decorators that only apply to some integration kinds (e.g. the autoregressive
2689
+ # step hook) are appended on registration, so unrelated integrations never show them.
2690
+ if _find_row(_remove_suffix(name, " (optional)")) is None:
2691
+ table.append({"name": name, "Added to integration": UNKNOWN})
2692
+
2397
2693
  def mark_sim_result(sim_name: str, passed: bool):
2398
2694
  _sim_tracking[sim_name] = "passed" if passed else "failed"
2399
2695
 
@@ -2440,11 +2736,12 @@ def tensorleap_status_table():
2440
2736
  atexit.register(run_on_exit)
2441
2737
  sys.excepthook = handle_exception
2442
2738
 
2443
- return set_current, update_env_params, register_sim, mark_sim_result
2739
+ return set_current, update_env_params, register_sim, mark_sim_result, register_decorator_row
2444
2740
 
2445
2741
 
2446
2742
  if not _call_from_tl_platform:
2447
- set_current, update_env_params_func, register_sim, mark_sim_result = tensorleap_status_table()
2743
+ set_current, update_env_params_func, register_sim, mark_sim_result, register_decorator_row = \
2744
+ tensorleap_status_table()
2448
2745
 
2449
2746
 
2450
2747
 
code_loader/leaploader.py CHANGED
@@ -64,9 +64,6 @@ class LeapLoader(LeapLoaderBase):
64
64
  self.evaluate_module()
65
65
  if global_leap_binder.integration_test_func is not None:
66
66
  global_leap_binder.integration_test_func(None, PreprocessResponse(state=DataStateType.training, length=0))
67
- except DatasetScriptException:
68
- global_leap_binder.setup_container = DatasetIntegrationSetup()
69
- raise
70
67
  except TypeError as e:
71
68
  import traceback
72
69
  global_leap_binder.setup_container = DatasetIntegrationSetup()
@@ -92,11 +89,6 @@ class LeapLoader(LeapLoaderBase):
92
89
  sys.path.append(parent_path)
93
90
 
94
91
  file_path = Path(self.code_path, self.code_entry_name)
95
- if not file_path.is_file():
96
- raise DatasetScriptException(
97
- f"Entry file not found: '{self.code_entry_name}' does not exist under '{self.code_path}'. "
98
- f"Check the 'entryFile' setting in your leap.yaml."
99
- )
100
92
  append_path_recursively(str(file_path))
101
93
 
102
94
  importlib.invalidate_caches()
@@ -111,13 +103,6 @@ class LeapLoader(LeapLoaderBase):
111
103
 
112
104
  spec.loader.exec_module(file)
113
105
 
114
- if global_leap_binder.setup_container.preprocess is None:
115
- raise DatasetScriptException(
116
- f"No Tensorleap integration found in '{self.code_entry_name}': the script never called "
117
- f"leap_binder.set_preprocess(...). Check the 'entryFile' setting in your leap.yaml — "
118
- f"it may be pointing to the wrong file."
119
- )
120
-
121
106
  @lru_cache()
122
107
  def metric_by_name(self) -> Dict[str, MetricHandlerData]:
123
108
  self.exec_script()
@@ -335,12 +320,16 @@ class LeapLoader(LeapLoaderBase):
335
320
  try:
336
321
  self.exec_script()
337
322
 
323
+ global_leap_binder.validate_autoregressive_setup()
338
324
  preprocess_test_payload = self._check_preprocess()
339
325
  test_payloads.append(preprocess_test_payload)
340
326
  handlers_test_payloads = self._check_handlers()
341
327
  test_payloads.extend(handlers_test_payloads)
342
328
  simulation_test_payloads = self._check_simulations()
343
329
  test_payloads.extend(simulation_test_payloads)
330
+ autoregressive_test_payload = self._check_autoregressive_step()
331
+ if autoregressive_test_payload is not None:
332
+ test_payloads.append(autoregressive_test_payload)
344
333
  is_valid = all([payload.is_passed for payload in test_payloads])
345
334
  setup_response = self.get_dataset_setup_response(handlers_test_payloads)
346
335
 
@@ -460,6 +449,69 @@ class LeapLoader(LeapLoaderBase):
460
449
  result_payloads.append(test_result)
461
450
  return result_payloads
462
451
 
452
+ def _check_autoregressive_step(self) -> Optional[DatasetTestResultPayload]:
453
+ handler = global_leap_binder.setup_container.autoregressive_step
454
+ if handler is None:
455
+ return None
456
+ test_result = DatasetTestResultPayload(handler.name)
457
+ try:
458
+ preprocess_result = self._preprocess_result()
459
+ input_shapes: Dict[str, List[int]] = {}
460
+ for state, preprocess_response in preprocess_result.items():
461
+ if preprocess_response.sample_ids_to_instance_mappings:
462
+ raise Exception('Element instances are not supported together with '
463
+ 'tensorleap_autoregressive_step.')
464
+ sample_id = preprocess_response.sample_ids[0]
465
+ first_result = handler.function(sample_id, None, None, preprocess_response)
466
+ second_result = handler.function(sample_id, None, None, preprocess_response)
467
+ if not isinstance(first_result, dict):
468
+ raise Exception('The autoregressive step hook must return a dict of initial model '
469
+ f'inputs on its first call, got {type(first_result)} '
470
+ f'(state: {state.name}).')
471
+ model_input_items = {key: value for key, value in first_result.items()
472
+ if not key.startswith('_')}
473
+ if not model_input_items:
474
+ raise Exception('The autoregressive step hook returned no model inputs on its first '
475
+ 'call — every key starts with "_" (underscore keys are passthrough '
476
+ f'state, never fed to the model). State: {state.name}.')
477
+ for key, value in model_input_items.items():
478
+ if not isinstance(value, np.ndarray):
479
+ raise Exception(f'The autoregressive step hook returned a non-numpy value for '
480
+ f'model input "{key}" ({type(value)}). State: {state.name}.')
481
+ if key in input_shapes and input_shapes[key] != list(value.shape):
482
+ raise Exception(f'The autoregressive step hook returned different shapes for '
483
+ f'model input "{key}" across states: {input_shapes[key]} vs '
484
+ f'{list(value.shape)}. Shapes must be fixed.')
485
+ input_shapes[key] = list(value.shape)
486
+ if not np.array_equal(value, second_result[key]):
487
+ raise Exception('The autoregressive step hook is not deterministic — two calls '
488
+ 'with identical arguments returned different outputs for model '
489
+ f'input "{key}". Seed any stochasticity from sample_id: the '
490
+ 'engine replays steps after crash recovery and relies on '
491
+ 'identical results.')
492
+ handler.input_shapes = input_shapes
493
+ except Exception as e:
494
+ line_number, file_name, stacktrace = get_root_exception_file_and_line_number()
495
+ test_result.display[TestingSectionEnum.Errors.name] = \
496
+ f"{repr(e)} in file {file_name}, line_number: {line_number}\nStacktrace:\n{stacktrace}"
497
+ test_result.is_passed = False
498
+ return test_result
499
+
500
+ def _ensure_autoregressive_input_shapes(self) -> Dict[str, List[int]]:
501
+ handler = global_leap_binder.setup_container.autoregressive_step
502
+ assert handler is not None
503
+ if handler.input_shapes is None:
504
+ preprocess_result = self._preprocess_result()
505
+ first_state_response = next(iter(preprocess_result.values()))
506
+ first_result = handler.function(first_state_response.sample_ids[0], None, None,
507
+ first_state_response)
508
+ if not isinstance(first_result, dict):
509
+ raise Exception('The autoregressive step hook must return a dict of initial model '
510
+ f'inputs on its first call, got {type(first_result)}.')
511
+ handler.input_shapes = {key: list(value.shape) for key, value in first_result.items()
512
+ if not key.startswith('_') and isinstance(value, np.ndarray)}
513
+ return handler.input_shapes
514
+
463
515
  def run_simulation(self, sim_name, params=None, n_samples=1, seed=0,
464
516
  sample_ids=None, extend_preprocess=True):
465
517
  # type: (str, Optional[Dict[str, Any]], int, int, Optional[List[str]], bool) -> Dict[str, Any]
@@ -650,6 +702,12 @@ class LeapLoader(LeapLoaderBase):
650
702
  raise Exception(f"cant calculate shape for input, input name:{inp.name}")
651
703
  inputs.append(DatasetInputInstance(name=inp.name, shape=inp.shape, channel_dim=inp.channel_dim))
652
704
 
705
+ if setup.autoregressive_step is not None:
706
+ # The hook is the sole input source; report its step-0 inputs as the dataset inputs so
707
+ # graph building, input-coverage validation and type dictionaries see real specs.
708
+ for input_name, input_shape in self._ensure_autoregressive_input_shapes().items():
709
+ inputs.append(DatasetInputInstance(name=input_name, shape=input_shape, channel_dim=-1))
710
+
653
711
  ground_truths = []
654
712
  for gt in setup.ground_truths:
655
713
  if gt.shape is None:
@@ -757,7 +815,21 @@ class LeapLoader(LeapLoaderBase):
757
815
  return result_agg
758
816
 
759
817
  def _get_inputs(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
760
- return self._get_dataset_handlers(global_leap_binder.setup_container.inputs, state, sample_id)
818
+ inputs = self._get_dataset_handlers(global_leap_binder.setup_container.inputs, state, sample_id)
819
+ autoregressive_handler = global_leap_binder.setup_container.autoregressive_step
820
+ if autoregressive_handler is not None:
821
+ # Autoregressive integrations have no input encoders — the hook's first call supplies
822
+ # the sample's (step-0) model inputs, so every sample-fetching flow (graph validation,
823
+ # analysis, import checks) sees a fully-populated inputs dict.
824
+ preprocess_response = self._preprocess_result()[state]
825
+ step_zero_inputs = autoregressive_handler.function(sample_id, None, None, preprocess_response)
826
+ if not isinstance(step_zero_inputs, dict):
827
+ raise Exception(
828
+ 'The autoregressive step hook must return a dict of initial model inputs on its '
829
+ f'first call, got {type(step_zero_inputs)} for sample {sample_id}.')
830
+ inputs.update({key: value for key, value in step_zero_inputs.items()
831
+ if not key.startswith('_')})
832
+ return inputs
761
833
 
762
834
  def _get_instances_masks(self, state: DataStateEnum, sample_id: Union[int, str], instance_id: int) -> Optional[Dict[str, ElementInstance]]:
763
835
  if instance_id is None:
@@ -780,6 +852,12 @@ class LeapLoader(LeapLoaderBase):
780
852
  def _get_gt(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
781
853
  return self._get_dataset_handlers(global_leap_binder.setup_container.ground_truths, state, sample_id)
782
854
 
855
+ def get_gt(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
856
+ """Ground-truth tensors only ({} when no GT encoders are declared) — lets the engine's
857
+ rollout fetch a chain's GT without re-running the autoregressive hook via get_sample."""
858
+ self.exec_script()
859
+ return self._get_gt(state, sample_id)
860
+
783
861
  @lru_cache()
784
862
  def _metadata_name_to_type(self) -> Dict[str, DatasetMetadataType]:
785
863
  global_leap_binder.check_preprocess(self._preprocess_result())
@@ -875,6 +953,36 @@ class LeapLoader(LeapLoaderBase):
875
953
  self.exec_script()
876
954
  return global_leap_binder.setup_container.custom_latent_space is not None
877
955
 
956
+ @lru_cache()
957
+ def has_autoregressive_step(self) -> bool:
958
+ self.exec_script()
959
+ return global_leap_binder.setup_container.autoregressive_step is not None
960
+
961
+ def run_autoregressive_step(self, sample_id: Union[int, str],
962
+ prev_inputs: Optional[Dict[str, npt.NDArray[np.float32]]],
963
+ prev_outputs: Optional[Dict[str, npt.NDArray[np.float32]]],
964
+ state: DataStateEnum) -> Optional[Dict[str, npt.NDArray[np.float32]]]:
965
+ """Execute the user's autoregressive step hook against the in-process preprocess result.
966
+
967
+ This is the engine rollout's per-step entry point (invoked on the generic pod). The first
968
+ call per chain passes prev_inputs=None, prev_outputs=None and returns the initial model
969
+ inputs; a None return ends the chain.
970
+ """
971
+ self.exec_script()
972
+ handler = global_leap_binder.setup_container.autoregressive_step
973
+ if handler is None:
974
+ raise Exception('run_autoregressive_step was called but the integration has no '
975
+ 'tensorleap_autoregressive_step hook.')
976
+ preprocess_response = self._preprocess_result()[state]
977
+ return handler.function(sample_id, prev_inputs, prev_outputs, preprocess_response)
978
+
979
+ def get_prediction_names_in_order(self) -> List[str]:
980
+ """Model output names by declared prediction-type order (validated against the model's
981
+ output count at integration-test time), used to key prev_outputs for the hook."""
982
+ self.exec_script()
983
+ return [prediction_type.name for prediction_type in
984
+ global_leap_binder.setup_container.prediction_types]
985
+
878
986
  def get_instances_data(self, state: DataStateEnum) -> Tuple[Dict[str, List[str]], Dict[str, str]]:
879
987
  preprocess_result = self._preprocess_result()
880
988
  preprocess_state = preprocess_result.get(state)
@@ -149,6 +149,11 @@ class LeapLoaderBase:
149
149
  def has_custom_latent_space_decorator(self) -> bool:
150
150
  pass
151
151
 
152
+ def has_autoregressive_step(self) -> bool:
153
+ # Default False so existing LeapLoaderBase implementations stay valid; the concrete
154
+ # loaders that can answer (LeapLoader in-process, LeapLoaderWithRedis via RPC) override.
155
+ return False
156
+
152
157
  @abstractmethod
153
158
  def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
154
159
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.188.dev0
3
+ Version: 1.0.189.dev0
4
4
  Summary:
5
5
  Home-page: https://github.com/tensorleap/code-loader
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
1
  LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
2
2
  code_loader/__init__.py,sha256=outxRQ0M-zMfV0QGVJmAed5qWfRmyD0TV6-goEGAzBw,406
3
3
  code_loader/contract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- code_loader/contract/datasetclasses.py,sha256=RYZcnX7vLMYhyQtCbX2PscVFNAYULxgAA9U4DRRfLqA,10456
4
+ code_loader/contract/datasetclasses.py,sha256=yib7LKHwii0tl0SSMUp1PWgskCs8eI2MQWvtoHs5qgs,11630
5
5
  code_loader/contract/enums.py,sha256=2q-IV_5g9lLE306DIbWA1c0tn5IhDtxsKxyV1x_Lreg,1671
6
6
  code_loader/contract/exceptions.py,sha256=jWqu5i7t-0IG0jGRsKF4DjJdrsdpJjIYpUkN1F4RiyQ,51
7
7
  code_loader/contract/mapping.py,sha256=sWJhpng-IkOzQnWQdMT5w2ZZ3X1Z_OOzSwCLXIS7oxE,1446
@@ -21,10 +21,10 @@ code_loader/experiment_api/types.py,sha256=MY8xFARHwdVA7p4dxyhD60ShmttgTvb4qdp1o
21
21
  code_loader/experiment_api/utils.py,sha256=XZHtxge12TS4H4-8PjV3sKuhp8Ud6ojAiIzTZJEqBqc,3304
22
22
  code_loader/experiment_api/workingspace_config_utils.py,sha256=DLzXQCg4dgTV_YgaSbeTVzq-2ja_SQw4zi7LXwKL9cY,990
23
23
  code_loader/inner_leap_binder/__init__.py,sha256=koOlJyMNYzGbEsoIbXathSmQ-L38N_pEXH_HvL7beXU,99
24
- code_loader/inner_leap_binder/leapbinder.py,sha256=XLYYcV50qjMvoC1S6WW0tLBch_0g5gl1UyHiVSWYbvg,40491
25
- code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=qfKa--0kG_K_Eb8wRd5tIXEJ6rbKva3360gU8EU9eKo,118375
26
- code_loader/leaploader.py,sha256=qtWn_6EsKuerCcURitCsFzEk2MSoRDUlHHBM0ohI9Io,45472
27
- code_loader/leaploaderbase.py,sha256=l36qDA00GhZEG5NLKpEtAXgWJA-UQQIhNFGxywK7mUA,6530
24
+ code_loader/inner_leap_binder/leapbinder.py,sha256=6wnjzTzMgWtypCNyIwubMSYRfY4eZOhSLyNEYZf2fvg,45680
25
+ code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=Z5TBE8HnljJCwY1KY43p1zRqd7zh_eF7nxh2oYfdPDE,134547
26
+ code_loader/leaploader.py,sha256=_Ylgu8vTJoy30_ZDymPZ9pZiSLPbIn85dgC8HVKhzbU,52983
27
+ code_loader/leaploaderbase.py,sha256=2UPunTcEZRs99cbFXkNDWdFrs5nQ7EG_T2qwZBepEXY,6788
28
28
  code_loader/mixpanel_tracker.py,sha256=rNwRmFifNbdUoqLQvvhhgpKczWpWiEmd8MfyJe27sxw,9131
29
29
  code_loader/plot_functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  code_loader/plot_functions/plot_functions.py,sha256=2DC-zlVaN13P4VNx5d8csgs80C6SisaeP1-Kq2LW7iM,16075
@@ -32,7 +32,7 @@ code_loader/plot_functions/visualize.py,sha256=gsBAYYkwMh7jIpJeDMPS8G4CW-pxwx6Lz
32
32
  code_loader/utils.py,sha256=YecipkdTA-VcE9F0RQcY9cFnY8P3AksPnHM2Db7xUSk,3972
33
33
  code_loader/visualizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  code_loader/visualizers/default_visualizers.py,sha256=onRnLE_TXfgLN4o52hQIOOhUcFexGlqJ3xSpQDVLuZM,2604
35
- code_loader-1.0.188.dev0.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
36
- code_loader-1.0.188.dev0.dist-info/METADATA,sha256=mtdX0lb1AbMGSDjxqIesGwlp3GNQoYFBcSBhIdaB8Lk,1095
37
- code_loader-1.0.188.dev0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
38
- code_loader-1.0.188.dev0.dist-info/RECORD,,
35
+ code_loader-1.0.189.dev0.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
36
+ code_loader-1.0.189.dev0.dist-info/METADATA,sha256=9PckzREIEk-hdCCTiMj-Lw5fzub_mPz2oBxEbzDRSzo,1095
37
+ code_loader-1.0.189.dev0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
38
+ code_loader-1.0.189.dev0.dist-info/RECORD,,