code-loader 1.0.196.dev1__py3-none-any.whl → 1.0.197.dev1__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.
@@ -1,6 +1,6 @@
1
1
  import warnings
2
2
  from dataclasses import dataclass, field
3
- from typing import Any, Callable, List, Optional, Dict, Union, Type, Literal, Tuple, cast
3
+ from typing import Any, Callable, List, Optional, Dict, Union, Type, Literal, Tuple
4
4
  import re
5
5
  import numpy as np
6
6
  import numpy.typing as npt
@@ -15,8 +15,6 @@ custom_latent_space_attribute = "custom_latent_space"
15
15
 
16
16
  _simulation_context: Dict[str, bool] = {"active": False}
17
17
 
18
- SampleId = Union[int, str]
19
-
20
18
 
21
19
  @dataclass
22
20
  class PreprocessResponse:
@@ -41,46 +39,27 @@ class PreprocessResponse:
41
39
  """
42
40
  length: Optional[int] = None # Deprecated. Please use sample_ids instead
43
41
  data: Any = None
44
- sample_ids: Optional[Union[List[SampleId], List[List[SampleId]]]] = None
42
+ sample_ids: Optional[Union[List[str], List[int]]] = None
45
43
  state: Optional[DataStateType] = None
46
44
  sample_id_type: Optional[Union[Type[str], Type[int]]] = None
47
45
  sample_ids_to_instance_mappings: Optional[Dict[str, List[str]]] = None # in use only for element instance
48
46
  instance_to_sample_ids_mappings: Optional[Dict[str, str]] = None # in use only for element instance
49
47
  tl_generated: bool = False
50
- _grouped: bool = field(default=False, init=False, repr=False, compare=False)
51
- _group_pos_cache: Optional[Dict[SampleId, Tuple[List[SampleId], int]]] = field(
52
- default=None, init=False, repr=False, compare=False)
53
48
 
54
49
  def __post_init__(self) -> None:
55
50
  assert self.sample_ids_to_instance_mappings is None, f"Keep sample_ids_to_instance_mappings None when initializing PreprocessResponse"
56
51
  assert self.instance_to_sample_ids_mappings is None, f"Keep instance_to_sample_ids_mappings None when initializing PreprocessResponse"
57
52
 
58
53
  if self.length is not None and self.sample_ids is None:
59
- self.sample_ids = cast(List[SampleId], [i for i in range(self.length)])
54
+ self.sample_ids = [i for i in range(self.length)]
60
55
  self.sample_id_type = int
61
- self._grouped = False
62
56
  elif self.length is None and self.sample_ids is not None:
63
- self._grouped = len(self.sample_ids) > 0 and isinstance(self.sample_ids[0], list)
64
- if self._grouped:
65
- groups = cast(List[List[SampleId]], self.sample_ids)
66
- assert len(groups) >= 1, "Grouped PreprocessResponse must have at least one group."
67
- for group in groups:
68
- assert isinstance(group, list) and len(group) > 0, \
69
- "Each group in a grouped PreprocessResponse must be a non-empty list."
70
- self.sample_id_type = type(groups[0][0])
71
- for group in groups:
72
- for sample_id in group:
73
- assert isinstance(sample_id, self.sample_id_type), \
74
- f"Sample id should be of type {self.sample_id_type.__name__}. Got: {type(sample_id)}"
75
- self.length = sum(len(group) for group in groups)
76
- else:
77
- flat_ids = cast(List[SampleId], self.sample_ids)
78
- self.length = len(flat_ids)
79
- if self.sample_id_type is None:
80
- self.sample_id_type = str
81
- if self.sample_id_type == str:
82
- for sample_id in flat_ids:
83
- assert isinstance(sample_id, str), f"Sample id should be of type str. Got: {type(sample_id)}"
57
+ self.length = len(self.sample_ids)
58
+ if self.sample_id_type is None:
59
+ self.sample_id_type = str
60
+ if self.sample_id_type == str:
61
+ for sample_id in self.sample_ids:
62
+ assert isinstance(sample_id, str), f"Sample id should be of type str. Got: {type(sample_id)}"
84
63
  else:
85
64
  raise Exception("length is deprecated, please use sample_ids instead.")
86
65
 
@@ -100,36 +79,9 @@ class PreprocessResponse:
100
79
  return id(self)
101
80
 
102
81
  def __len__(self) -> int:
103
- assert self.length is not None
104
- return self.length
105
-
106
- @property
107
- def is_grouped(self) -> bool:
108
- return self._grouped
109
-
110
- @property
111
- def num_groups(self) -> int:
112
- # Grouped: the number of groups. Flat: the (flat) sample count, since sample_ids is the
113
- # flat id list and there is one sample per "group".
114
82
  assert self.sample_ids is not None
115
83
  return len(self.sample_ids)
116
84
 
117
- @property
118
- def groups(self) -> Optional[List[List[SampleId]]]:
119
- # The nested list of groups when grouped, otherwise None. Callers should
120
- # branch on is_grouped before relying on this.
121
- return cast(List[List[SampleId]], self.sample_ids) if self._grouped else None
122
-
123
- @property
124
- def flat_sample_ids(self) -> List[SampleId]:
125
- # All sample ids in order: concatenation of the groups when grouped,
126
- # otherwise the flat sample_ids list as-is.
127
- assert self.sample_ids is not None
128
- if self._grouped:
129
- groups = cast(List[List[SampleId]], self.sample_ids)
130
- return [sample_id for group in groups for sample_id in group]
131
- return cast(List[SampleId], self.sample_ids)
132
-
133
85
  @dataclass
134
86
  class ElementInstance:
135
87
  name: str
@@ -300,6 +252,7 @@ class MetadataHandler:
300
252
  class CustomLatentSpaceHandler:
301
253
  function: SectionCallableInterface
302
254
  name: str = 'custom_latent_space'
255
+ use_ls_for_analysis: bool = False
303
256
 
304
257
 
305
258
  # How a chain's latent-space vectors are derived from its per-step forward passes.
@@ -382,7 +335,7 @@ class DatasetIntegrationSetup:
382
335
  metrics: List[MetricHandler] = field(default_factory=list)
383
336
  instance_metrics: List[InstanceMetricHandler] = field(default_factory=list)
384
337
  custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
385
- custom_latent_space: Optional[CustomLatentSpaceHandler] = None
338
+ custom_latent_spaces: Dict[str, CustomLatentSpaceHandler] = field(default_factory=dict)
386
339
  simulations: List[SimulationHandler] = field(default_factory=list)
387
340
  autoregressive_step: Optional[AutoregressiveStepHandler] = None
388
341
  autoregressive_metrics: List[AutoregressiveMetricHandler] = field(default_factory=list)
@@ -398,6 +351,6 @@ class DatasetSample:
398
351
  metadata_is_none: Dict[str, bool]
399
352
  index: Union[int, str]
400
353
  state: DataStateEnum
401
- custom_latent_space: Optional[npt.NDArray[np.float32]] = None
354
+ custom_latent_spaces: Optional[Dict[str, npt.NDArray[np.float32]]] = None
402
355
  instance_masks: Optional[Dict[str, ElementInstance]] = None
403
356
 
@@ -1,9 +1,6 @@
1
1
 
2
- import builtins
3
2
  import inspect
4
- import os
5
- from contextlib import contextmanager
6
- from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args, cast, Iterator, Set
3
+ from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args
7
4
 
8
5
  import numpy as np
9
6
  import numpy.typing as npt
@@ -19,8 +16,7 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
19
16
  SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface, \
20
17
  AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS, AUTOREGRESSIVE_IMPLICIT_ARG_NAMES, \
21
18
  AutoregressiveMetricHandler, AutoregressiveLossHandler, AutoregressiveVisualizerHandler
22
- from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType, \
23
- TestingSectionEnum
19
+ from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType
24
20
  from code_loader.contract.mapping import NodeConnection, NodeMapping, NodeMappingType
25
21
  from code_loader.contract.responsedataclasses import DatasetTestResultPayload, LeapAnalysisConfiguration
26
22
  from code_loader.contract.visualizer_classes import map_leap_data_type_to_visualizer_class
@@ -37,25 +33,6 @@ from code_loader.visualizers.default_visualizers import DefaultVisualizer, \
37
33
  mapping_runtime_mode_env_var_mame = '__MAPPING_RUNTIME_MODE__'
38
34
 
39
35
 
40
- @contextmanager
41
- def _track_opened_files() -> Iterator[Set[str]]:
42
- opened: Set[str] = set()
43
- original_open = builtins.open
44
-
45
- def tracking_open(file: Any, *args: Any, **kwargs: Any) -> Any:
46
- mode = args[0] if args else kwargs.get('mode', 'r')
47
- is_read_mode = isinstance(mode, str) and not any(c in mode for c in ('a', 'w', 'x'))
48
- if is_read_mode and isinstance(file, (str, os.PathLike)):
49
- opened.add(os.fspath(file))
50
- return original_open(file, *args, **kwargs)
51
-
52
- builtins.open = tracking_open
53
- try:
54
- yield opened
55
- finally:
56
- builtins.open = original_open
57
-
58
-
59
36
  def _stringized_annotation_type_name(annotation: Any) -> Optional[str]:
60
37
  """Bare type name if ``annotation`` is a stringized annotation (from ``from __future__
61
38
  import annotations`` or a quoted hint), else ``None``. code_loader inspects raw
@@ -536,18 +513,54 @@ class LeapBinder:
536
513
  """
537
514
  self.setup_container.metadata.append(MetadataHandler(name, function, metadata_type))
538
515
 
539
- def set_custom_latent_space(self, function: SectionCallableInterface) -> None:
516
+ def set_custom_latent_space(self, function: SectionCallableInterface,
517
+ name: Optional[str] = None,
518
+ use_ls_for_analysis: bool = False) -> None:
540
519
  """
541
- Set a custom latent space function.
520
+ Register a custom latent space function.
521
+
522
+ Multiple custom latent spaces may be registered as long as each has a
523
+ unique name. They are stored name-keyed (insertion order preserved), so a
524
+ later registration no longer overrides an earlier one.
542
525
 
543
526
  Args:
544
- function (SectionCallableInterface): The metadata handler function.
527
+ function (SectionCallableInterface): The latent-space handler function.
545
528
  This function receives:
546
529
  subset (PreprocessResponse): The subset of the data.
547
530
  index (int): The index of the sample within the subset.
548
- This function should numpy float32 array contains the latent space vec of the sample.
531
+ This function should return a numpy float32 array containing the latent
532
+ space vec of the sample.
533
+ name (Optional[str]): Unique name for this custom latent space. Defaults to
534
+ the reserved single-LS name for backward compatibility.
535
+ use_ls_for_analysis (bool): When True, the engine uses this custom latent
536
+ space for the Out-Of-Distribution and Domain-Gap insights instead of the
537
+ built-in defaults. At most one registered custom latent space may set this;
538
+ registering a second one with the flag raises.
549
539
  """
550
- self.setup_container.custom_latent_space = CustomLatentSpaceHandler(function)
540
+ if name is None:
541
+ name = custom_latent_space_attribute
542
+ if name in self.setup_container.custom_latent_spaces:
543
+ raise Exception(
544
+ f"A custom latent space named '{name}' is already registered. Each "
545
+ f"@tensorleap_custom_latent_space must have a unique name "
546
+ f"(pass name='...' to distinguish them)."
547
+ )
548
+ if use_ls_for_analysis:
549
+ already_flagged = [
550
+ existing_name
551
+ for existing_name, handler in self.setup_container.custom_latent_spaces.items()
552
+ if handler.use_ls_for_analysis
553
+ ]
554
+ if already_flagged:
555
+ raise Exception(
556
+ f"use_ls_for_analysis=True is already set on custom latent space "
557
+ f"'{already_flagged[0]}'. Only one custom latent space may set "
558
+ f"use_ls_for_analysis=True (it selects the latent space used for the "
559
+ f"Out-Of-Distribution and Domain-Gap insights). Set it on '{name}' "
560
+ f"or '{already_flagged[0]}', not both."
561
+ )
562
+ self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(
563
+ function, name, use_ls_for_analysis)
551
564
 
552
565
  def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
553
566
  latent_space_aggregation: str = 'last_step') -> None:
@@ -572,9 +585,7 @@ class LeapBinder:
572
585
  # Builtin chain metadata, declared at parse time so it survives the reporter's
573
586
  # metadata type mapping; the placeholder values are overwritten by the engine when a
574
587
  # chain finalizes (realized length, truncated-by-safety-cap flag).
575
- def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Any:
576
- if isinstance(idx, list):
577
- return [{'length': -1, 'truncated': False} for _ in idx]
588
+ def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Dict[str, Any]:
578
589
  return {'length': -1, 'truncated': False}
579
590
 
580
591
  self.set_metadata(builtin_chain_metadata, 'builtin_chain')
@@ -738,15 +749,6 @@ class LeapBinder:
738
749
  if state_enum in preprocess_result_dict:
739
750
  preprocess_result_dict_in_correct_order[state_enum] = preprocess_result_dict[state_enum]
740
751
 
741
- # All-or-none: every state must agree on grouped (nested sample_ids) vs flat.
742
- if len({r.is_grouped for r in preprocess_result_dict_in_correct_order.values()}) > 1:
743
- shapes = ', '.join(
744
- f"{state.name}={'grouped' if r.is_grouped else 'flat'}"
745
- for state, r in preprocess_result_dict_in_correct_order.items())
746
- raise Exception(
747
- "All preprocess states must be either all grouped (nested sample_ids) or all flat, "
748
- f"but got a mix: {shapes}.")
749
-
750
752
  return preprocess_result_dict_in_correct_order
751
753
 
752
754
  def get_preprocess_unlabeled_result(self) -> Optional[PreprocessResponse]:
@@ -767,34 +769,7 @@ class LeapBinder:
767
769
  preprocess_response: PreprocessResponse, test_result: List[DatasetTestResultPayload],
768
770
  dataset_base_handler: Union[DatasetBaseHandler, MetadataHandler], state: DataStateEnum) -> List[DatasetTestResultPayload]:
769
771
  assert preprocess_response.sample_ids is not None
770
- opened_files: Optional[Set[str]] = None
771
- if preprocess_response.is_grouped:
772
- # Grouped: probe with the first group, then reduce to a single sample's result so the
773
- # recorded shape/type is per-sample (matching the flat case), not the (B, *) batch.
774
- # (casts: sample_ids[0] is a group list here, and the grouped result is a per-sample list.)
775
- group = preprocess_response.sample_ids[0]
776
- with _track_opened_files() as opened_files:
777
- raw_result = cast(Any, dataset_base_handler.function(cast(Any, group), preprocess_response))[0]
778
- else:
779
- raw_result = dataset_base_handler.function(
780
- cast(Union[int, str], preprocess_response.sample_ids[0]), preprocess_response)
781
-
782
- def _warn_if_group_spans_multiple_files(payloads: List[DatasetTestResultPayload]) -> None:
783
- if opened_files is None or len(opened_files) <= 1:
784
- return
785
- shown = sorted(opened_files)[:5]
786
- suffix = f" (+{len(opened_files) - 5} more)" if len(opened_files) > 5 else ""
787
- warning = (
788
- f"Encoding declared group 0 (size {len(cast(List[Any], group))}) for '{dataset_base_handler.name}' opened "
789
- f"{len(opened_files)} distinct files: {shown}{suffix}. If this group is meant to be one "
790
- f"physical source (e.g. one Parquet file), it may be grouped incorrectly - check your "
791
- f"preprocess grouping logic. (Only files opened via Python's open() are tracked, so this "
792
- f"can miss other I/O paths or be a false positive if grouping isn't meant to reflect file "
793
- f"locality.)")
794
- for payload in payloads:
795
- existing = payload.display.get(TestingSectionEnum.Errors.name, '')
796
- payload.display[TestingSectionEnum.Errors.name] = (existing + '\n' if existing else '') + warning
797
-
772
+ raw_result = dataset_base_handler.function(preprocess_response.sample_ids[0], preprocess_response)
798
773
  handler_type = 'metadata' if isinstance(dataset_base_handler, MetadataHandler) else None
799
774
  if isinstance(dataset_base_handler, MetadataHandler):
800
775
  if isinstance(raw_result, dict):
@@ -834,7 +809,6 @@ class LeapBinder:
834
809
  else:
835
810
  if raw_result is None:
836
811
  if state != DataStateEnum.training:
837
- _warn_if_group_spans_multiple_files(test_result)
838
812
  return test_result
839
813
 
840
814
  if dataset_base_handler.metadata_type is None:
@@ -856,7 +830,6 @@ class LeapBinder:
856
830
  # setting shape in setup for all encoders
857
831
  if isinstance(dataset_base_handler, (InputHandler, GroundTruthHandler)):
858
832
  dataset_base_handler.shape = result_shape
859
- _warn_if_group_spans_multiple_files(test_result)
860
833
  return test_result
861
834
 
862
835
  def check_handlers(self, preprocess_result: Dict[DataStateEnum, PreprocessResponse]) -> None:
@@ -892,11 +865,10 @@ class LeapBinder:
892
865
  preprocess_response.tl_generated = True
893
866
  if not preprocess_response.length or preprocess_response.length < 1:
894
867
  raise Exception("Simulation '{}' returned PreprocessResponse with length < 1".format(sim.name))
895
- preprocess_response.sample_ids = cast(List[Union[int, str]], [0])
896
- sim_sample_ids = cast(List[Union[int, str]], preprocess_response.sample_ids)
868
+ preprocess_response.sample_ids = [0]
897
869
  for handler in self.setup_container.inputs:
898
- out1 = handler.function(sim_sample_ids[0], preprocess_response)
899
- out2 = handler.function(sim_sample_ids[0], preprocess_response)
870
+ out1 = handler.function(preprocess_response.sample_ids[0], preprocess_response)
871
+ out2 = handler.function(preprocess_response.sample_ids[0], preprocess_response)
900
872
  if not np.allclose(out1, out2):
901
873
  raise Exception(
902
874
  "Simulation '{}': encoder '{}' is non-deterministic — consecutive calls with seed=0 returned different outputs".format(
@@ -954,7 +926,7 @@ class LeapBinder:
954
926
  "Element instances are not supported together with tensorleap_autoregressive_step: "
955
927
  "instance generation masks the sample's encoded inputs, which do not exist in an "
956
928
  "autoregressive integration. Remove the instance encoders or the autoregressive hook.")
957
- if self.setup_container.custom_latent_space is not None:
929
+ if self.setup_container.custom_latent_spaces:
958
930
  raise Exception(
959
931
  "tensorleap_custom_latent_space is not supported together with "
960
932
  "tensorleap_autoregressive_step: the custom latent space function only sees "
@@ -40,7 +40,6 @@ _called_from_inside_tl_integration_test_decorator = False
40
40
  # to tell a raise in the test body (report as code-flow failure) from a module-level crash
41
41
  # before any test ran (report as a plain script crash).
42
42
  _integration_test_started = False
43
- _mapping_dataset_is_grouped = False
44
43
  _call_from_tl_platform = os.environ.get('IS_TENSORLEAP_PLATFORM') == 'true'
45
44
 
46
45
  # ---- warnings store (module-level) ----
@@ -214,53 +213,6 @@ def batch_warning(result, func_name):
214
213
  )
215
214
 
216
215
 
217
- def _validate_id_or_group(sample_id, preprocess_response, func_name):
218
- """Accept either a single sample id (flat dataset) or a group — a list of ids (grouped
219
- dataset). The argument shape must match the dataset: a group requires a grouped
220
- PreprocessResponse and a single id requires a flat one. Every id must match the declared
221
- sample_id_type."""
222
- is_group = isinstance(sample_id, list)
223
- if is_group and not preprocess_response.is_grouped:
224
- raise AssertionError(
225
- f'{func_name}() validation failed: got a group (list of sample ids) but the '
226
- f'PreprocessResponse is not grouped. Pass a single sample id for a flat dataset.')
227
- if not is_group and preprocess_response.is_grouped:
228
- raise AssertionError(
229
- f'{func_name}() validation failed: got a single sample id but the PreprocessResponse '
230
- f'is grouped. Pass a group (list of sample ids) for a grouped dataset.')
231
- ids = sample_id if is_group else [sample_id]
232
- for sid in ids:
233
- assert type(sid) == preprocess_response.sample_id_type, \
234
- (f'{func_name}() validation failed: '
235
- f'Argument sample_id should be as the same type as defined in the preprocess response '
236
- f'{preprocess_response.sample_id_type}. Got {type(sid)}.')
237
-
238
-
239
- def _validate_grouped_result(result, group_size, func_name, validate_single):
240
- """A grouped (group) encoder result must be either a single ndarray with leading dim
241
- == group_size, or a list of `group_size` per-sample results (each validated by
242
- validate_single). Metadata validates its grouped result separately (list only).
243
- An `(B, *)` array is validated per-sample slice (not as the whole batched array) so
244
- per-sample checks (e.g. channel_dim <= rank) use the sample shape, not the batch shape."""
245
- if isinstance(result, np.ndarray):
246
- assert len(result.shape) > 0 and result.shape[0] == group_size, \
247
- (f'{func_name}() validation failed: expected a grouped output for a group of '
248
- f'{group_size}: an array with leading dim {group_size}, got shape {result.shape}.')
249
- for single in result:
250
- validate_single(single)
251
- elif isinstance(result, list):
252
- assert len(result) == group_size, \
253
- (f'{func_name}() validation failed: expected a grouped output for a group of '
254
- f'{group_size}: a list of {group_size} arrays, got {len(result)}.')
255
- for r in result:
256
- validate_single(r)
257
- else:
258
- raise AssertionError(
259
- f'{func_name}() validation failed: expected a grouped output for a group of '
260
- f'{group_size}: either an array with leading dim {group_size} or a list of '
261
- f'{group_size} arrays, got {type(result)}.')
262
-
263
-
264
216
  def _add_mapping_connection(user_unique_name, connection_destinations, arg_names, name, node_mapping_type):
265
217
  connection_destinations = [connection_destination for connection_destination in connection_destinations
266
218
  if not isinstance(connection_destination, SamplePreprocessResponse)]
@@ -338,7 +290,11 @@ def tensorleap_integration_test():
338
290
 
339
291
  def _validate_input_args(*args, **kwargs):
340
292
  sample_id, preprocess_response = args
341
- _validate_id_or_group(sample_id, preprocess_response, 'tensorleap_integration_test')
293
+ assert type(sample_id) == preprocess_response.sample_id_type, (
294
+ f"tensorleap_integration_test validation failed: "
295
+ f"sample_id type ({type(sample_id).__name__}) does not match the expected "
296
+ f"type ({preprocess_response.sample_id_type}) from the PreprocessResponse."
297
+ )
342
298
 
343
299
  def inner(*args, **kwargs):
344
300
  if not _call_from_tl_platform:
@@ -349,7 +305,7 @@ def tensorleap_integration_test():
349
305
  expected_names = inspect.getfullargspec(integration_test_function)[0][:2]
350
306
  if len(expected_names) < 2:
351
307
  expected_names = ['idx', 'preprocess']
352
- validate_args_structure(*args, types_order=[Union[int, str, list], PreprocessResponse],
308
+ validate_args_structure(*args, types_order=[Union[int, str], PreprocessResponse],
353
309
  func_name='integration_test', expected_names=expected_names,
354
310
  **kwargs)
355
311
  sample_id, preprocess_response = (
@@ -373,8 +329,6 @@ def tensorleap_integration_test():
373
329
  _reset_model_loop_state()
374
330
  ret = integration_test_function(*args, **kwargs)
375
331
 
376
- global _mapping_dataset_is_grouped
377
- _mapping_dataset_is_grouped = args[1].is_grouped
378
332
  try:
379
333
  os.environ[mapping_runtime_mode_env_var_mame] = 'True'
380
334
  _reset_model_loop_state()
@@ -395,7 +349,6 @@ def tensorleap_integration_test():
395
349
  f'Integration test is only allowed to call Tensorleap decorators. '
396
350
  f'Ensure any arithmetics, external library use, Python logic is placed within Tensorleap decoders')
397
351
  finally:
398
- _mapping_dataset_is_grouped = False
399
352
  if mapping_runtime_mode_env_var_mame in os.environ:
400
353
  del os.environ[mapping_runtime_mode_env_var_mame]
401
354
  finally:
@@ -1608,10 +1561,13 @@ def tensorleap_metadata(
1608
1561
  raise Exception(f'Metadata with name {name} already exists. '
1609
1562
  f'Please choose another')
1610
1563
 
1611
- def _validate_input_args(sample_id: Union[int, str, list], preprocess_response: PreprocessResponse):
1612
- _validate_id_or_group(sample_id, preprocess_response, user_function.__name__)
1564
+ def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
1565
+ assert type(sample_id) == preprocess_response.sample_id_type, \
1566
+ (f'{user_function.__name__}() validation failed: '
1567
+ f'Argument sample_id should be as the same type as defined in the preprocess response '
1568
+ f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
1613
1569
 
1614
- def _validate_single(result):
1570
+ def _validate_result(result):
1615
1571
  supported_result_types = (type(None), int, str, bool, float, dict, np.floating,
1616
1572
  np.bool_, np.unsignedinteger, np.signedinteger, np.integer)
1617
1573
  if isinstance(result, tuple):
@@ -1629,20 +1585,6 @@ def tensorleap_metadata(
1629
1585
  (f'{user_function.__name__}() validation failed: '
1630
1586
  f'Values in the return dict should be of type {str(supported_result_types)}. Got {type(value)}.')
1631
1587
 
1632
- def _validate_result(result, grouped=False, group_size=None):
1633
- if not grouped:
1634
- _validate_single(result)
1635
- return
1636
- # Grouped metadata: a list of `group_size` per-sample scalars/dicts.
1637
- assert isinstance(result, list), \
1638
- (f'{user_function.__name__}() validation failed: expected a grouped output for a group '
1639
- f'of {group_size}: a list of {group_size} metadata values, got {type(result)}.')
1640
- assert len(result) == group_size, \
1641
- (f'{user_function.__name__}() validation failed: expected a grouped output for a group '
1642
- f'of {group_size}: a list of {group_size} metadata values, got {len(result)}.')
1643
- for value in result:
1644
- _validate_single(value)
1645
-
1646
1588
  def inner_without_validate(sample_id, preprocess_response):
1647
1589
 
1648
1590
  global _called_from_inside_tl_decorator
@@ -1662,16 +1604,14 @@ def tensorleap_metadata(
1662
1604
  set_current('tensorleap_metadata')
1663
1605
  if os.environ.get(mapping_runtime_mode_env_var_mame):
1664
1606
  return None
1665
- validate_args_structure(*args, types_order=[Union[int, str, list], PreprocessResponse],
1607
+ validate_args_structure(*args, types_order=[Union[int, str], PreprocessResponse],
1666
1608
  func_name=user_function.__name__, expected_names=["idx", "preprocess"], **kwargs)
1667
1609
  sample_id, preprocess_response = args if len(args) != 0 else kwargs.values()
1668
1610
  _validate_input_args(sample_id, preprocess_response)
1669
1611
 
1670
- grouped = isinstance(sample_id, list)
1671
- group_size = len(sample_id) if grouped else None
1672
1612
  result = inner_without_validate(sample_id, preprocess_response)
1673
1613
 
1674
- _validate_result(result, grouped, group_size)
1614
+ _validate_result(result)
1675
1615
  if not _call_from_tl_platform:
1676
1616
  update_env_params_func("tensorleap_metadata", "v")
1677
1617
  return result
@@ -1681,52 +1621,42 @@ def tensorleap_metadata(
1681
1621
  return decorating_function
1682
1622
 
1683
1623
 
1684
- def tensorleap_custom_latent_space():
1624
+ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analysis: bool = False):
1625
+ assert isinstance(use_ls_for_analysis, bool), \
1626
+ ("tensorleap_custom_latent_space validation failed: use_ls_for_analysis must be a bool. "
1627
+ f"Got {type(use_ls_for_analysis)}.")
1628
+
1685
1629
  def decorating_function(user_function: SectionCallableInterface):
1686
- def _validate_input_args(sample_id: Union[int, str, list], preprocess_response: PreprocessResponse):
1687
- _validate_id_or_group(sample_id, preprocess_response, 'tensorleap_custom_latent_space')
1630
+ ls_name = name if name is not None else user_function.__name__
1631
+ def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
1632
+ assert isinstance(sample_id, (int, str)), \
1633
+ (f'tensorleap_custom_latent_space validation failed: '
1634
+ f'Argument sample_id should be either int or str. Got {type(sample_id)}.')
1635
+ assert isinstance(preprocess_response, PreprocessResponse), \
1636
+ (f'tensorleap_custom_latent_space validation failed: '
1637
+ f'Argument preprocess_response should be a PreprocessResponse. Got {type(preprocess_response)}.')
1638
+ assert type(sample_id) == preprocess_response.sample_id_type, \
1639
+ (f'tensorleap_custom_latent_space validation failed: '
1640
+ f'Argument sample_id should be as the same type as defined in the preprocess response '
1641
+ f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
1688
1642
 
1689
- def _validate_single(single_result):
1690
- assert isinstance(single_result, np.ndarray), \
1643
+ def _validate_result(result):
1644
+ assert isinstance(result, np.ndarray), \
1691
1645
  (f'tensorleap_custom_latent_space validation failed: '
1692
- f'The return type should be a numpy array. Got {type(single_result)}.')
1693
- if single_result.ndim > 1:
1694
- flat_dim = int(np.prod(single_result.shape))
1646
+ f'The return type should be a numpy array. Got {type(result)}.')
1647
+ if result.ndim > 1:
1648
+ flat_dim = int(np.prod(result.shape))
1695
1649
  store_general_warning(
1696
- key=("tensorleap_custom_latent_space_flatten", tuple(single_result.shape)),
1650
+ key=("tensorleap_custom_latent_space_flatten", ls_name, tuple(result.shape)),
1697
1651
  message=(
1698
- f"tensorleap_custom_latent_space returned per-sample shape {tuple(single_result.shape)} "
1699
- f"(ndim={single_result.ndim}). Tensorleap assumes per-sample shape (d, ...) and will "
1652
+ f"tensorleap_custom_latent_space '{ls_name}' returned per-sample shape {tuple(result.shape)} "
1653
+ f"(ndim={result.ndim}). Tensorleap assumes per-sample shape (d, ...) and will "
1700
1654
  f"flatten to ({flat_dim},) before downstream visualization and clustering. "
1701
1655
  f"If you want a different aggregation (e.g. global average pooling), do it "
1702
1656
  f"inside your function."
1703
1657
  ),
1704
1658
  )
1705
1659
 
1706
- def _validate_result(result, grouped=False, group_size=None):
1707
- if not grouped:
1708
- _validate_single(result)
1709
- return
1710
- # Grouped: (B, *) array or list of B per-sample arrays. Validate each per-sample slice so
1711
- # the per-sample flatten check/warning uses the sample shape, not the (B, *) batch shape.
1712
- if isinstance(result, np.ndarray):
1713
- assert result.ndim >= 1 and result.shape[0] == group_size, \
1714
- (f'tensorleap_custom_latent_space validation failed: expected a grouped output for a '
1715
- f'group of {group_size}: an array with leading dim {group_size}, got shape {result.shape}.')
1716
- for single in result:
1717
- _validate_single(single)
1718
- elif isinstance(result, list):
1719
- assert len(result) == group_size, \
1720
- (f'tensorleap_custom_latent_space validation failed: expected a grouped output for a '
1721
- f'group of {group_size}: a list of {group_size} arrays, got {len(result)}.')
1722
- for single in result:
1723
- _validate_single(single)
1724
- else:
1725
- raise AssertionError(
1726
- f'tensorleap_custom_latent_space validation failed: expected a grouped output for a '
1727
- f'group of {group_size}: either an array with leading dim {group_size} or a list of '
1728
- f'{group_size} arrays, got {type(result)}.')
1729
-
1730
1660
  def inner_without_validate(sample_id, preprocess_response):
1731
1661
  global _called_from_inside_tl_decorator
1732
1662
  _called_from_inside_tl_decorator += 1
@@ -1738,7 +1668,8 @@ def tensorleap_custom_latent_space():
1738
1668
 
1739
1669
  return result
1740
1670
 
1741
- leap_binder.set_custom_latent_space(inner_without_validate)
1671
+ leap_binder.set_custom_latent_space(inner_without_validate, ls_name,
1672
+ use_ls_for_analysis=use_ls_for_analysis)
1742
1673
 
1743
1674
  def inner(sample_id, preprocess_response):
1744
1675
  if os.environ.get(mapping_runtime_mode_env_var_mame):
@@ -1746,11 +1677,9 @@ def tensorleap_custom_latent_space():
1746
1677
 
1747
1678
  _validate_input_args(sample_id, preprocess_response)
1748
1679
 
1749
- grouped = isinstance(sample_id, list)
1750
- group_size = len(sample_id) if grouped else None
1751
1680
  result = inner_without_validate(sample_id, preprocess_response)
1752
1681
 
1753
- _validate_result(result, grouped, group_size)
1682
+ _validate_result(result)
1754
1683
  return result
1755
1684
 
1756
1685
  return inner
@@ -2719,15 +2648,6 @@ def tensorleap_preprocess():
2719
2648
  assert len(set(result)) == len(result), \
2720
2649
  (f'{user_function.__name__}() validation failed: '
2721
2650
  f'The return list should not contain duplicate PreprocessResponse objects.')
2722
- # All-or-none: a dataset is either all grouped (nested sample_ids) or all flat.
2723
- if len({response.is_grouped for response in result}) != 1:
2724
- shapes = ', '.join(
2725
- f"#{i}={'grouped' if response.is_grouped else 'flat'}"
2726
- for i, response in enumerate(result))
2727
- raise AssertionError(
2728
- f'{user_function.__name__}() validation failed: '
2729
- f'All PreprocessResponses must be either all grouped (nested sample_ids) '
2730
- f'or all flat, but got a mix: {shapes}.')
2731
2651
 
2732
2652
  def inner(*args, **kwargs):
2733
2653
  if not _call_from_tl_platform:
@@ -3065,10 +2985,14 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
3065
2985
  f"channel axis in the batched tensor (batch = axis 0), so 0 is invalid. Use 1 "
3066
2986
  f"for channel-first (NCHW) and -1 for channel-last (NHWC).")
3067
2987
 
3068
- def _validate_input_args(sample_id: Union[int, str, list], preprocess_response: PreprocessResponse):
3069
- _validate_id_or_group(sample_id, preprocess_response, user_function.__name__)
2988
+ def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
2989
+ assert type(sample_id) == preprocess_response.sample_id_type, \
2990
+ (f'{user_function.__name__}() validation failed: '
2991
+ f'Argument sample_id should be as the same type as defined in the preprocess response '
2992
+ f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
3070
2993
 
3071
- def _validate_single(result):
2994
+ def _validate_result(result):
2995
+ validate_output_structure(result, func_name=user_function.__name__, expected_type_name="np.ndarray")
3072
2996
  assert isinstance(result, np.ndarray), \
3073
2997
  (f'{user_function.__name__}() validation failed: '
3074
2998
  f'Unsupported return type. Should be a numpy array. Got {type(result)}.')
@@ -3078,13 +3002,6 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
3078
3002
  assert channel_dim - 1 <= len(result.shape), (f'{user_function.__name__}() validation failed: '
3079
3003
  f'The channel_dim ({channel_dim}) should be <= to the rank of the resulting input rank ({len(result.shape)}).')
3080
3004
 
3081
- def _validate_result(result, grouped=False, group_size=None):
3082
- if not grouped:
3083
- validate_output_structure(result, func_name=user_function.__name__, expected_type_name="np.ndarray")
3084
- _validate_single(result)
3085
- return
3086
- _validate_grouped_result(result, group_size, user_function.__name__, _validate_single)
3087
-
3088
3005
  def inner_without_validate(sample_id, preprocess_response):
3089
3006
  global _called_from_inside_tl_decorator
3090
3007
  _called_from_inside_tl_decorator += 1
@@ -3101,29 +3018,18 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
3101
3018
  def inner(*args, **kwargs):
3102
3019
  if not _call_from_tl_platform:
3103
3020
  set_current("tensorleap_input_encoder")
3104
- validate_args_structure(*args, types_order=[Union[int, str, list], PreprocessResponse],
3021
+ validate_args_structure(*args, types_order=[Union[int, str], PreprocessResponse],
3105
3022
  func_name=user_function.__name__, expected_names=["idx", "preprocess"], **kwargs)
3106
3023
  sample_id, preprocess_response = args if len(args) != 0 else kwargs.values()
3107
3024
  _validate_input_args(sample_id, preprocess_response)
3108
3025
 
3109
- grouped = isinstance(sample_id, list)
3110
- group_size = len(sample_id) if grouped else None
3111
3026
  result = inner_without_validate(sample_id, preprocess_response)
3112
3027
 
3113
- _validate_result(result, grouped, group_size)
3028
+ _validate_result(result)
3114
3029
 
3115
3030
  if _called_from_inside_tl_decorator == 0 and _called_from_inside_tl_integration_test_decorator:
3116
- if grouped:
3117
- # A grouped result is a list of per-sample arrays (never stacked, so a
3118
- # B=1-only model can be fed one sample at a time). A user may also return a
3119
- # supported (B, *) array; split it back into per-sample rows first (mirrors
3120
- # LeapLoader._to_grouped_list) so both forms match the runtime path. Then add
3121
- # a batch dim to each sample, mirroring the flat path's single-sample expand_dims.
3122
- rows = result if isinstance(result, list) else [row for row in np.asarray(result)]
3123
- result = [np.expand_dims(r, axis=0) for r in rows]
3124
- else:
3125
- batch_warning(result, user_function.__name__)
3126
- result = np.expand_dims(result, axis=0)
3031
+ batch_warning(result, user_function.__name__)
3032
+ result = np.expand_dims(result, axis=0)
3127
3033
  # Emit integration test event once per test
3128
3034
  try:
3129
3035
  emit_integration_event_once(AnalyticsEvent.INPUT_ENCODER_INTEGRATION_TEST, {
@@ -3144,15 +3050,8 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
3144
3050
  inner.node_mapping = NodeMapping(name, node_mapping_type)
3145
3051
 
3146
3052
  def mapping_inner(*args, **kwargs):
3147
- if _mapping_dataset_is_grouped:
3148
- class TempMapping:
3149
- def __getitem__(self, key):
3150
- # Indexing a grouped input group (input_list[i]) selects a sample from the
3151
- # same input source; the model wiring is identical to the un-grouped input.
3152
- return self
3153
- else:
3154
- class TempMapping:
3155
- pass
3053
+ class TempMapping:
3054
+ pass
3156
3055
 
3157
3056
  ret = TempMapping()
3158
3057
  ret.node_mapping = mapping_inner.node_mapping
@@ -3182,10 +3081,15 @@ def tensorleap_gt_encoder(name: str):
3182
3081
  raise Exception(f'GT with name {name} already exists. '
3183
3082
  f'Please choose another')
3184
3083
 
3185
- def _validate_input_args(sample_id: Union[int, str, list], preprocess_response: PreprocessResponse):
3186
- _validate_id_or_group(sample_id, preprocess_response, user_function.__name__)
3084
+ def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
3085
+ assert type(sample_id) == preprocess_response.sample_id_type, \
3086
+ (f'{user_function.__name__}() validation failed: '
3087
+ f'Argument sample_id should be as the same type as defined in the preprocess response '
3088
+ f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
3187
3089
 
3188
- def _validate_single(result):
3090
+ def _validate_result(result):
3091
+ validate_output_structure(result, func_name=user_function.__name__, expected_type_name="np.ndarray",
3092
+ gt_flag=True)
3189
3093
  assert isinstance(result, np.ndarray), \
3190
3094
  (f'{user_function.__name__}() validation failed: '
3191
3095
  f'Unsupported return type. Should be a numpy array. Got {type(result)}.')
@@ -3193,14 +3097,6 @@ def tensorleap_gt_encoder(name: str):
3193
3097
  (f'{user_function.__name__}() validation failed: '
3194
3098
  f'The return type should be a numpy array of type float32. Got {result.dtype}.')
3195
3099
 
3196
- def _validate_result(result, grouped=False, group_size=None):
3197
- if not grouped:
3198
- validate_output_structure(result, func_name=user_function.__name__, expected_type_name="np.ndarray",
3199
- gt_flag=True)
3200
- _validate_single(result)
3201
- return
3202
- _validate_grouped_result(result, group_size, user_function.__name__, _validate_single)
3203
-
3204
3100
  def inner_without_validate(sample_id, preprocess_response):
3205
3101
  global _called_from_inside_tl_decorator
3206
3102
  _called_from_inside_tl_decorator += 1
@@ -3217,29 +3113,18 @@ def tensorleap_gt_encoder(name: str):
3217
3113
  def inner(*args, **kwargs):
3218
3114
  if not _call_from_tl_platform:
3219
3115
  set_current("tensorleap_gt_encoder")
3220
- validate_args_structure(*args, types_order=[Union[int, str, list], PreprocessResponse],
3116
+ validate_args_structure(*args, types_order=[Union[int, str], PreprocessResponse],
3221
3117
  func_name=user_function.__name__, expected_names=["idx", "preprocess"], **kwargs)
3222
3118
  sample_id, preprocess_response = args
3223
3119
  _validate_input_args(sample_id, preprocess_response)
3224
3120
 
3225
- grouped = isinstance(sample_id, list)
3226
- group_size = len(sample_id) if grouped else None
3227
3121
  result = inner_without_validate(sample_id, preprocess_response)
3228
3122
 
3229
- _validate_result(result, grouped, group_size)
3123
+ _validate_result(result)
3230
3124
 
3231
3125
  if _called_from_inside_tl_decorator == 0 and _called_from_inside_tl_integration_test_decorator:
3232
- if grouped:
3233
- # A grouped result is a list of per-sample arrays (never stacked, so a
3234
- # B=1-only model can be fed one sample at a time). A user may also return a
3235
- # supported (B, *) array; split it back into per-sample rows first (mirrors
3236
- # LeapLoader._to_grouped_list) so both forms match the runtime path. Then add
3237
- # a batch dim to each sample, mirroring the flat path's single-sample expand_dims.
3238
- rows = result if isinstance(result, list) else [row for row in np.asarray(result)]
3239
- result = [np.expand_dims(r, axis=0) for r in rows]
3240
- else:
3241
- batch_warning(result, user_function.__name__)
3242
- result = np.expand_dims(result, axis=0)
3126
+ batch_warning(result, user_function.__name__)
3127
+ result = np.expand_dims(result, axis=0)
3243
3128
  _register_chain_artifact(result, 'ground_truth')
3244
3129
  # Emit integration test event once per test
3245
3130
  try:
@@ -3255,15 +3140,8 @@ def tensorleap_gt_encoder(name: str):
3255
3140
  inner.node_mapping = NodeMapping(name, NodeMappingType.GroundTruth)
3256
3141
 
3257
3142
  def mapping_inner(*args, **kwargs):
3258
- if _mapping_dataset_is_grouped:
3259
- class TempMapping:
3260
- def __getitem__(self, key):
3261
- # Indexing a grouped GT group (gt_list[i]) selects a sample from the same
3262
- # ground-truth source; wiring is identical to the un-grouped GT node.
3263
- return self
3264
- else:
3265
- class TempMapping:
3266
- pass
3143
+ class TempMapping:
3144
+ pass
3267
3145
 
3268
3146
  ret = TempMapping()
3269
3147
  ret.node_mapping = mapping_inner.node_mapping
code_loader/leaploader.py CHANGED
@@ -60,36 +60,14 @@ class LeapLoader(LeapLoaderBase):
60
60
 
61
61
  @lru_cache()
62
62
  def exec_script(self) -> None:
63
- from code_loader.inner_leap_binder import leapbinder_decorators as _leap_dec
64
63
  try:
65
64
  os.environ[mapping_runtime_mode_env_var_mame] = 'TRUE'
66
65
  self.evaluate_module()
67
-
68
- # A grouped integration test may index a grouped input/gt (image_list[i]) to wire a
69
- # single sample of a group; that indexing is only valid when the mapping graph is
70
- # built in grouped mode. Detect grouped-ness so the placeholder pass below matches
71
- # the dataset. Preprocess is stubbed while mapping mode is on, so run it with mapping
72
- # mode momentarily disabled, and cache it so _preprocess_result() doesn't re-run it.
73
- is_grouped = False
74
- if global_leap_binder.integration_test_func is not None:
75
- del os.environ[mapping_runtime_mode_env_var_mame]
76
- try:
77
- preprocess_result = global_leap_binder.get_preprocess_result()
78
- self._preprocess_result_cached = preprocess_result
79
- is_grouped = any(r.is_grouped for r in preprocess_result.values())
80
- finally:
81
- os.environ[mapping_runtime_mode_env_var_mame] = 'TRUE'
82
-
83
- _leap_dec._mapping_dataset_is_grouped = is_grouped
84
66
  if global_leap_binder.integration_test_func is not None:
85
67
  from code_loader.inner_leap_binder.leapbinder_decorators import \
86
68
  _reset_model_loop_state
87
69
  _reset_model_loop_state()
88
- mapping_preprocess = (
89
- PreprocessResponse(sample_ids=[["__mapping_placeholder__"]], state=DataStateType.training)
90
- if is_grouped else
91
- PreprocessResponse(state=DataStateType.training, length=0))
92
- global_leap_binder.integration_test_func(None, mapping_preprocess)
70
+ global_leap_binder.integration_test_func(None, PreprocessResponse(state=DataStateType.training, length=0))
93
71
  except TypeError as e:
94
72
  import traceback
95
73
  global_leap_binder.setup_container = DatasetIntegrationSetup()
@@ -102,7 +80,6 @@ class LeapLoader(LeapLoaderBase):
102
80
  raise DatasetScriptException(getattr(e, 'message', repr(e))) from e
103
81
  finally:
104
82
  # ensure that the environment variable is removed after the script execution
105
- _leap_dec._mapping_dataset_is_grouped = False
106
83
  if mapping_runtime_mode_env_var_mame in os.environ:
107
84
  del os.environ[mapping_runtime_mode_env_var_mame]
108
85
 
@@ -232,7 +209,7 @@ class LeapLoader(LeapLoaderBase):
232
209
  additional = self._preprocess_result().get(DataStateEnum.additional)
233
210
  if additional is None:
234
211
  return set()
235
- return set(additional.flat_sample_ids)
212
+ return set(additional.sample_ids)
236
213
 
237
214
  def _resolve_synthetic(self, sample_id: Union[int, str],
238
215
  state: Optional[DataStateEnum] = None
@@ -273,20 +250,12 @@ class LeapLoader(LeapLoaderBase):
273
250
  )
274
251
 
275
252
  preprocess_result = self._preprocess_result()
276
- if state == DataStateEnum.unlabeled and sample_id not in preprocess_result[state].flat_sample_ids:
253
+ if state == DataStateEnum.unlabeled and sample_id not in preprocess_result[state].sample_ids:
277
254
  self._preprocess_result(update_unlabeled_preprocess=True)
278
255
 
279
256
  metadata, metadata_is_none = self.get_metadata(state, sample_id)
280
257
 
281
- custom_latent_space = None
282
- if global_leap_binder.setup_container.custom_latent_space is not None:
283
- latent_fn = global_leap_binder.setup_container.custom_latent_space.function
284
- preprocess_state = preprocess_result[state]
285
- if preprocess_state.is_grouped:
286
- group_ids, pos = self._locate_group(preprocess_state, sample_id)
287
- custom_latent_space = self._to_grouped_list(latent_fn(group_ids, preprocess_state))[pos]
288
- else:
289
- custom_latent_space = latent_fn(sample_id, preprocess_state)
258
+ custom_latent_spaces = self._get_custom_latent_spaces(sample_id, preprocess_result[state])
290
259
  instance_mask = self._get_instances_masks(state, sample_id, instance_id)
291
260
  sample = DatasetSample(inputs=self._get_inputs(state, sample_id),
292
261
  gt=None if state == DataStateEnum.unlabeled else self._get_gt(state, sample_id),
@@ -294,7 +263,7 @@ class LeapLoader(LeapLoaderBase):
294
263
  metadata_is_none=metadata_is_none,
295
264
  index=sample_id,
296
265
  state=state,
297
- custom_latent_space=custom_latent_space,
266
+ custom_latent_spaces=custom_latent_spaces,
298
267
  instance_masks=instance_mask)
299
268
  return sample
300
269
 
@@ -325,11 +294,7 @@ class LeapLoader(LeapLoaderBase):
325
294
  handler.name, handler_result
326
295
  )
327
296
 
328
- custom_latent_space = None
329
- if global_leap_binder.setup_container.custom_latent_space is not None:
330
- custom_latent_space = global_leap_binder.setup_container.custom_latent_space.function(
331
- original_sample_id, preprocess
332
- )
297
+ custom_latent_spaces = self._get_custom_latent_spaces(original_sample_id, preprocess)
333
298
 
334
299
  return DatasetSample(
335
300
  inputs=inputs,
@@ -338,7 +303,7 @@ class LeapLoader(LeapLoaderBase):
338
303
  metadata_is_none=metadata_is_none,
339
304
  index=synthetic_index,
340
305
  state=DataStateEnum.additional,
341
- custom_latent_space=custom_latent_space,
306
+ custom_latent_spaces=custom_latent_spaces,
342
307
  instance_masks=None,
343
308
  )
344
309
 
@@ -417,7 +382,7 @@ class LeapLoader(LeapLoaderBase):
417
382
  if self.get_sample_id_type() is str:
418
383
  max_allowed_item_size = np.dtype('<U256').itemsize
419
384
  for state, preprocess_response in preprocess_result.items():
420
- sample_ids_array = np.array(preprocess_response.flat_sample_ids)
385
+ sample_ids_array = np.array(preprocess_response.sample_ids)
421
386
  if sample_ids_array.dtype.itemsize > max_allowed_item_size:
422
387
  raise Exception(f"Sample id are too long. Max allowed length is 256 charecters.")
423
388
 
@@ -513,10 +478,7 @@ class LeapLoader(LeapLoaderBase):
513
478
  if preprocess_response.sample_ids_to_instance_mappings:
514
479
  raise Exception('Element instances are not supported together with '
515
480
  'tensorleap_autoregressive_step.')
516
- # Grouped preprocess is fine: grouping only amortizes the shared base reads
517
- # (GT/metadata/custom_latent) — the AR hook always drives a single scalar chain
518
- # and is never handed a group.
519
- sample_id = preprocess_response.flat_sample_ids[0]
481
+ sample_id = preprocess_response.sample_ids[0]
520
482
  first_result = handler.function(sample_id, None, None, None, preprocess_response)
521
483
  second_result = handler.function(sample_id, None, None, None, preprocess_response)
522
484
  if not isinstance(first_result, tuple) or len(first_result) != 2:
@@ -597,7 +559,7 @@ class LeapLoader(LeapLoaderBase):
597
559
  if handler.input_shapes is None:
598
560
  preprocess_result = self._preprocess_result()
599
561
  first_state_response = next(iter(preprocess_result.values()))
600
- first_sample_id = first_state_response.flat_sample_ids[0]
562
+ first_sample_id = first_state_response.sample_ids[0]
601
563
  first_result = handler.function(first_sample_id, None, None, None,
602
564
  first_state_response)
603
565
  if not isinstance(first_result, tuple) or len(first_result) != 2 or \
@@ -936,57 +898,6 @@ class LeapLoader(LeapLoaderBase):
936
898
 
937
899
  return sample_ids
938
900
 
939
- @staticmethod
940
- def _grouped_dict_keys(rows: List[Dict[str, Any]], handler_name: str) -> List[str]:
941
- """Keys for a group of dict-metadata rows, requiring every row to share the same keys.
942
- A group must expose a uniform metadata schema; otherwise the per-key lists would silently
943
- drop or KeyError on rows that differ from row 0."""
944
- keys = list(rows[0].keys())
945
- key_set = set(keys)
946
- for pos, row in enumerate(rows):
947
- if set(row.keys()) != key_set:
948
- raise ValueError(
949
- f"Metadata handler {handler_name!r} returned inconsistent dict keys across the "
950
- f"group: row 0 has {sorted(key_set)} but row {pos} has {sorted(row.keys())}. "
951
- f"All rows in a group must share the same metadata keys.")
952
- return keys
953
-
954
- @staticmethod
955
- def _to_grouped_list(raw: Any) -> List[npt.NDArray[np.float32]]:
956
- """Normalize a grouped encoder result to a list of per-sample arrays.
957
-
958
- Grouped results are NEVER stacked into a (B, *) batch. Grouping is only a storage
959
- read-unit (one file load per group); assembling the model batch (B) is the engine's
960
- single responsibility, so it stays the only place samples get batched. Keeping
961
- per-sample arrays lets the engine batch to any B — including a B=1-only model — without
962
- an intermediate stack/unstack. A user encoder that already returned a stacked (B, *)
963
- array is split back into its per-sample rows so the contract is a list either way."""
964
- if isinstance(raw, list):
965
- return raw
966
- return [row for row in np.asarray(raw)]
967
-
968
- def _locate_group(self, preprocess_state: PreprocessResponse,
969
- sample_id: Union[int, str]) -> Tuple[List[Union[int, str]], int]:
970
- """Map a sample id to (its group, its position in that group). Cached on the
971
- PreprocessResponse itself (not keyed by id() on this loader) so grouped single-sample
972
- fetches don't rescan the groups, and the cache can never go stale: when the response is
973
- replaced (e.g. unlabeled preprocess refresh), the old object and its cache are simply
974
- discarded together instead of lingering under a possibly-reused id()."""
975
- mapping = preprocess_state._group_pos_cache
976
- if mapping is None:
977
- mapping = {}
978
- for group in preprocess_state.groups:
979
- for pos, sid in enumerate(group):
980
- if sid in mapping:
981
- raise ValueError(
982
- f"Duplicate sample id {sid!r} across groups in the preprocess response; "
983
- f"sample ids must be unique within and across groups.")
984
- mapping[sid] = (group, pos)
985
- preprocess_state._group_pos_cache = mapping
986
- if sample_id not in mapping:
987
- raise KeyError(f"Sample id {sample_id!r} is not present in any group of this preprocess response.")
988
- return mapping[sample_id]
989
-
990
901
  def _get_dataset_handlers(self, handlers: Iterable[DatasetBaseHandler],
991
902
  state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
992
903
  result_agg = {}
@@ -998,100 +909,12 @@ class LeapLoader(LeapLoaderBase):
998
909
  sample_id = original_local_id
999
910
  else:
1000
911
  preprocess_state = preprocess_result[state]
1001
- if preprocess_state.is_grouped:
1002
- # Grouped dataset: call the encoder once with the whole group, index out the sample.
1003
- group_ids, pos = self._locate_group(preprocess_state, sample_id)
1004
- for handler in handlers:
1005
- grouped = self._to_grouped_list(handler.function(group_ids, preprocess_state))
1006
- result_agg[handler.name] = grouped[pos]
1007
- return result_agg
1008
912
  for handler in handlers:
1009
913
  handler_result = handler.function(sample_id, preprocess_state)
1010
914
  handler_name = handler.name
1011
915
  result_agg[handler_name] = handler_result
1012
916
  return result_agg
1013
917
 
1014
- def get_samples(self, state: DataStateEnum, group_ids: List[Union[int, str]]) -> DatasetSample:
1015
- """Group-aware fetch: hand the whole group to each encoder in a single call and return a
1016
- grouped DatasetSample (inputs/gt as per-sample lists of length B, metadata as per-key lists
1017
- of length B, index = the group). Grouped results are never stacked here — batching the
1018
- samples into the model's B is the engine's job. This is the path a group-aware engine calls
1019
- to read a file once per group. Row order matches group_ids exactly."""
1020
- self.exec_script()
1021
- preprocess_result = self._preprocess_result()
1022
- if state == DataStateEnum.unlabeled and any(
1023
- sid not in preprocess_result[state].flat_sample_ids for sid in group_ids):
1024
- # Mirrors get_sample's refresh: the unlabeled preprocess can grow between calls, so a
1025
- # group_id absent from the current snapshot may just not have been generated yet.
1026
- self._preprocess_result(update_unlabeled_preprocess=True)
1027
- preprocess_state = preprocess_result[state]
1028
- assert preprocess_state.is_grouped, (
1029
- "get_samples is the group-aware fetch path and requires a grouped preprocess "
1030
- "response; call get_sample for a flat (non-grouped) dataset.")
1031
- # All requested ids must belong to a single group (one file). A cross-group request
1032
- # would force the encoder to load multiple files, defeating the one-load-per-group
1033
- # contract; partitioning a scattered request by group is the engine's responsibility.
1034
- distinct_groups = {id(self._locate_group(preprocess_state, sid)[0]) for sid in group_ids}
1035
- assert len(distinct_groups) == 1, (
1036
- f"get_samples received sample ids spanning {len(distinct_groups)} groups; a request "
1037
- f"must be confined to a single group. The engine partitions cross-group requests and "
1038
- f"issues one get_samples call per group.")
1039
- inputs = {handler.name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
1040
- for handler in global_leap_binder.setup_container.inputs}
1041
- autoregressive_handler = global_leap_binder.setup_container.autoregressive_step
1042
- if autoregressive_handler is not None:
1043
- # AR integrations have no input encoders — the hook's first call supplies each
1044
- # chain's step-0 model inputs. The hook is strictly per-scalar-chain (never handed
1045
- # a group), so it is called once per flat id in the group and the per-sample results
1046
- # are collected into per-key lists of length B, matching the grouped (never-stacked)
1047
- # contract used for every other encoder here.
1048
- step_zero_per_sample = []
1049
- for sid in group_ids:
1050
- step_zero_result = autoregressive_handler.function(sid, None, None, None,
1051
- preprocess_state)
1052
- if not isinstance(step_zero_result, tuple) or len(step_zero_result) != 2 or \
1053
- not isinstance(step_zero_result[0], dict):
1054
- raise Exception(
1055
- 'The autoregressive step hook must return a (next_inputs, state) tuple '
1056
- f'with a dict of initial model inputs on its first call, got '
1057
- f'{type(step_zero_result)} for sample {sid}.')
1058
- self._validate_autoregressive_step_inputs(step_zero_result[0], sid)
1059
- step_zero_per_sample.append(step_zero_result[0])
1060
- for key in (step_zero_per_sample[0].keys() if step_zero_per_sample else []):
1061
- inputs[key] = [row[key] for row in step_zero_per_sample]
1062
- gt = None
1063
- if state != DataStateEnum.unlabeled:
1064
- gt = {handler.name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
1065
- for handler in global_leap_binder.setup_container.ground_truths}
1066
-
1067
- metadata: Dict[str, Any] = {}
1068
- metadata_is_none: Dict[str, Any] = {}
1069
- for handler in global_leap_binder.setup_container.metadata:
1070
- rows = handler.function(group_ids, preprocess_state) # list of B scalars/dicts
1071
- if rows and isinstance(rows[0], dict):
1072
- for key in self._grouped_dict_keys(rows, handler.name):
1073
- name = "{}_{}".format(handler.name, key)
1074
- converted = [self._convert_metadata_to_correct_type(name, row[key]) for row in rows]
1075
- metadata[name] = [v for v, _ in converted]
1076
- metadata_is_none[name] = [is_none for _, is_none in converted]
1077
- else:
1078
- converted = [self._convert_metadata_to_correct_type(handler.name, row) for row in rows]
1079
- metadata[handler.name] = [v for v, _ in converted]
1080
- metadata_is_none[handler.name] = [is_none for _, is_none in converted]
1081
-
1082
- # custom_latent_space is group-aware like the input/GT encoders: hand it the whole group in a
1083
- # single call so a file-backed latent fn loads the file once, and normalize to (B, d). This
1084
- # keeps the mandatory group path non-lossy. instance_masks stay None here: they are a
1085
- # per-(sample, instance) concern that needs an instance_id the group fetch does not carry.
1086
- custom_latent_space = None
1087
- if global_leap_binder.setup_container.custom_latent_space is not None:
1088
- latent_fn = global_leap_binder.setup_container.custom_latent_space.function
1089
- custom_latent_space = self._to_grouped_list(latent_fn(group_ids, preprocess_state))
1090
-
1091
- return DatasetSample(inputs=inputs, gt=gt, metadata=metadata, metadata_is_none=metadata_is_none,
1092
- index=list(group_ids), state=state, custom_latent_space=custom_latent_space,
1093
- instance_masks=None)
1094
-
1095
918
  def _get_inputs(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
1096
919
  inputs = self._get_dataset_handlers(global_leap_binder.setup_container.inputs, state, sample_id)
1097
920
  autoregressive_handler = global_leap_binder.setup_container.autoregressive_step
@@ -1195,18 +1018,12 @@ class LeapLoader(LeapLoaderBase):
1195
1018
  sample_id = original_local_id
1196
1019
  else:
1197
1020
  preprocess_state = preprocess_result[state]
1198
- group_ids, pos = (None, None)
1199
- if preprocess_state.is_grouped:
1200
- group_ids, pos = self._locate_group(preprocess_state, sample_id)
1201
1021
  for handler in global_leap_binder.setup_container.metadata:
1202
1022
  if requested_metadata_names:
1203
1023
  if not is_metadata_name_starts_with_handler_name(handler):
1204
1024
  continue
1205
1025
 
1206
- if preprocess_state.is_grouped:
1207
- handler_result = handler.function(group_ids, preprocess_state)[pos]
1208
- else:
1209
- handler_result = handler.function(sample_id, preprocess_state)
1026
+ handler_result = handler.function(sample_id, preprocess_state)
1210
1027
  if isinstance(handler_result, dict):
1211
1028
  for single_metadata_name, single_metadata_result in handler_result.items():
1212
1029
  handler_name = f'{handler.name}_{single_metadata_name}'
@@ -1225,66 +1042,6 @@ class LeapLoader(LeapLoaderBase):
1225
1042
 
1226
1043
  return result_agg, is_none
1227
1044
 
1228
- def _grouped_metadata_for_group(self, preprocess_state: PreprocessResponse,
1229
- group_ids: List[Union[int, str]],
1230
- requested_metadata_names: Optional[List[str]]
1231
- ) -> Tuple[Dict[str, List[Any]], Dict[str, List[bool]]]:
1232
- def is_wanted(_handler):
1233
- if not requested_metadata_names:
1234
- return True
1235
- for metadata_name in requested_metadata_names:
1236
- if metadata_name.startswith(_handler.name + '_') or metadata_name == _handler.name:
1237
- return True
1238
- return False
1239
-
1240
- results: Dict[str, List[Any]] = {}
1241
- is_none: Dict[str, List[bool]] = {}
1242
- for handler in global_leap_binder.setup_container.metadata:
1243
- if requested_metadata_names and not is_wanted(handler):
1244
- continue
1245
- rows = handler.function(group_ids, preprocess_state)
1246
- if rows and isinstance(rows[0], dict):
1247
- for key in self._grouped_dict_keys(rows, handler.name):
1248
- name = f'{handler.name}_{key}'
1249
- if requested_metadata_names and name not in requested_metadata_names:
1250
- continue
1251
- converted = [self._convert_metadata_to_correct_type(name, row[key]) for row in rows]
1252
- results[name] = [v for v, _ in converted]
1253
- is_none[name] = [n for _, n in converted]
1254
- else:
1255
- name = handler.name
1256
- if requested_metadata_names and name not in requested_metadata_names:
1257
- continue
1258
- converted = [self._convert_metadata_to_correct_type(name, row) for row in rows]
1259
- results[name] = [v for v, _ in converted]
1260
- is_none[name] = [n for _, n in converted]
1261
- return results, is_none
1262
-
1263
- def get_metadata_multiple_samples(self, state: DataStateEnum, sample_ids: Union[List[int], List[str]],
1264
- requested_metadata_names: Optional[List[str]] = None
1265
- ) -> Tuple[Dict[str, Union[List[str], List[int], List[bool], List[float]]],
1266
- Dict[str, List[bool]]]:
1267
- preprocess_state = self._preprocess_result().get(state)
1268
- if preprocess_state is None or not preprocess_state.is_grouped:
1269
- return super().get_metadata_multiple_samples(state, sample_ids, requested_metadata_names)
1270
-
1271
- sample_id_type = self.get_sample_id_type()
1272
- aggregated_results: Dict[str, List[Any]] = {}
1273
- aggregated_is_none: Dict[str, List[bool]] = {}
1274
- group_cache: Dict[int, Tuple[Dict[str, List[Any]], Dict[str, List[bool]]]] = {}
1275
- for sample_id in sample_ids:
1276
- sample_id = sample_id_type(sample_id)
1277
- group_ids, pos = self._locate_group(preprocess_state, sample_id)
1278
- key = id(group_ids)
1279
- if key not in group_cache:
1280
- group_cache[key] = self._grouped_metadata_for_group(
1281
- preprocess_state, group_ids, requested_metadata_names)
1282
- group_results, group_is_none = group_cache[key]
1283
- for name, values in group_results.items():
1284
- aggregated_results.setdefault(name, []).append(values[pos])
1285
- aggregated_is_none.setdefault(name, []).append(group_is_none[name][pos])
1286
- return aggregated_results, aggregated_is_none
1287
-
1288
1045
  @lru_cache()
1289
1046
  def get_sample_id_type(self) -> Type:
1290
1047
  preprocess_results = list(self._preprocess_result().values())
@@ -1295,10 +1052,42 @@ class LeapLoader(LeapLoaderBase):
1295
1052
 
1296
1053
  return id_type
1297
1054
 
1055
+ @staticmethod
1056
+ def _get_custom_latent_spaces(
1057
+ sample_id: Union[int, str],
1058
+ preprocess: "PreprocessResponse") -> Optional[Dict[str, npt.NDArray[np.float32]]]:
1059
+ handlers = global_leap_binder.setup_container.custom_latent_spaces
1060
+ if not handlers:
1061
+ return None
1062
+ return {name: handler.function(sample_id, preprocess) for name, handler in handlers.items()}
1063
+
1298
1064
  @lru_cache()
1299
1065
  def has_custom_latent_space_decorator(self) -> bool:
1300
1066
  self.exec_script()
1301
- return global_leap_binder.setup_container.custom_latent_space is not None
1067
+ return len(global_leap_binder.setup_container.custom_latent_spaces) > 0
1068
+
1069
+ @lru_cache()
1070
+ def get_custom_latent_space_names(self) -> Tuple[str, ...]:
1071
+ """Ordered names of all registered custom latent spaces.
1072
+
1073
+ Registration order is the canonical index mapping consumed by the engine
1074
+ (name i -> user_custom_i). Returns a tuple so the lru_cache value is hashable.
1075
+ """
1076
+ self.exec_script()
1077
+ return tuple(global_leap_binder.setup_container.custom_latent_spaces.keys())
1078
+
1079
+ @lru_cache()
1080
+ def get_custom_latent_space_for_analysis(self) -> Optional[str]:
1081
+ """Name of the custom latent space flagged with use_ls_for_analysis=True, if any.
1082
+
1083
+ The engine uses this latent space for the Out-Of-Distribution and Domain-Gap
1084
+ insights instead of the built-in defaults. Returns None when no custom latent
1085
+ space set the flag. At most one is flagged (enforced at registration time)."""
1086
+ self.exec_script()
1087
+ for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items():
1088
+ if handler.use_ls_for_analysis:
1089
+ return name
1090
+ return None
1302
1091
 
1303
1092
  @lru_cache()
1304
1093
  def has_autoregressive_step(self) -> bool:
@@ -218,6 +218,14 @@ class LeapLoaderBase:
218
218
  raise NotImplementedError(f'{type(self).__name__} does not implement '
219
219
  'autoregressive_visualizer_by_name.')
220
220
 
221
+ @abstractmethod
222
+ def get_custom_latent_space_names(self) -> Tuple[str, ...]:
223
+ pass
224
+
225
+ @abstractmethod
226
+ def get_custom_latent_space_for_analysis(self) -> Optional[str]:
227
+ pass
228
+
221
229
  @abstractmethod
222
230
  def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
223
231
  pass
code_loader/utils.py CHANGED
@@ -17,13 +17,6 @@ from code_loader.contract.enums import DatasetMetadataType
17
17
  def to_numpy_return_wrapper(encoder_function: SectionCallableInterface) -> SectionCallableInterface:
18
18
  def numpy_encoder_function(idx: Union[int, str], samples: PreprocessResponse) -> npt.NDArray[np.float32]:
19
19
  result = encoder_function(idx, samples)
20
- if isinstance(idx, list) and isinstance(result, list) and len(result) > 0 \
21
- and all(isinstance(sample, np.ndarray) for sample in result):
22
- # Grouped call (idx is a list of sample ids): keep the per-sample list, never stack
23
- # it into a (B, *) batch — grouping is only a storage read-unit; batching the group to
24
- # the model's B is the engine's single responsibility. Gate on the call shape (idx is a
25
- # list), not the result shape, so a flat encoder returning a list of arrays still stacks.
26
- return result
27
20
  numpy_result: npt.NDArray[np.float32] = np.array(result)
28
21
  return numpy_result
29
22
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.196.dev1
3
+ Version: 1.0.197.dev1
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=CX4HO3xiuBUiXaeSBEyjAFV7i1NGS7KZ3JnLGxofH28,16164
4
+ code_loader/contract/datasetclasses.py,sha256=LjYe-Ub_WzjXaloFKxwoL1sT1sWOrFSFU6XXxEoYENs,13826
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,18 +21,18 @@ 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=yW9EBA4E7YwWoWaiVELWoSP9uWTQjnPl5PfLbmX4cFk,56617
25
- code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=GyuyffnSmkELj63q_NBAxa6Z0szP6MsaNxDodwCo-pE,183746
26
- code_loader/leaploader.py,sha256=yWzSc-ynm_ykfup_tk8mwZX3TFq1ZW-fN18fEDXOrfc,83386
27
- code_loader/leaploaderbase.py,sha256=PvBU_2OQqBfJLDMsbK1s372954eYr4Y3O4r18y5S7uY,10739
24
+ code_loader/inner_leap_binder/leapbinder.py,sha256=MsXT_1DcbCp7dQavHHe2zTEYwGJkIPTO4xaYPMYgsZg,55223
25
+ code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=InZSgWLrt977fIMbw_3WQE0MWhsQhnafhO3uZuMYc0I,176589
26
+ code_loader/leaploader.py,sha256=2_t0IQPQNp9KcSKs4-_Ji8FK4Av8iouyK0eaRYNoUOM,69344
27
+ code_loader/leaploaderbase.py,sha256=Aa8wCcxojf8EBn_14W5I1gKI4OMBdpdEcNAZ_6E2eV4,10940
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
31
31
  code_loader/plot_functions/visualize.py,sha256=gsBAYYkwMh7jIpJeDMPS8G4CW-pxwx6LznoQIvi4vpo,657
32
- code_loader/utils.py,sha256=Grrp8OLagrVAOYjt83DDmE0CPfjE22DuOQftgqDASyU,7215
32
+ code_loader/utils.py,sha256=gEJCKpDWf6p9_KoEz-0EGiJqxD6QfPgZN6zdKdtSAz8,6628
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.196.dev1.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
36
- code_loader-1.0.196.dev1.dist-info/METADATA,sha256=qjJUAz8M-nUFhOQa1jAFKQXk2Qm-pdh4zns9jWNEglY,1095
37
- code_loader-1.0.196.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
38
- code_loader-1.0.196.dev1.dist-info/RECORD,,
35
+ code_loader-1.0.197.dev1.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
36
+ code_loader-1.0.197.dev1.dist-info/METADATA,sha256=USKyIvkEWJ6ak0J1mWQuFQ61R6BP1t3CUbfBlCDAVSo,1095
37
+ code_loader-1.0.197.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
38
+ code_loader-1.0.197.dev1.dist-info/RECORD,,