code-loader 1.0.190.dev0__tar.gz → 1.0.191.dev0__tar.gz

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.
Files changed (37) hide show
  1. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/PKG-INFO +3 -3
  2. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/contract/datasetclasses.py +2 -2
  3. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/inner_leap_binder/leapbinder.py +21 -5
  4. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/inner_leap_binder/leapbinder_decorators.py +10 -27
  5. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/leaploader.py +43 -13
  6. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/leaploaderbase.py +4 -0
  7. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/pyproject.toml +1 -1
  8. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/LICENSE +0 -0
  9. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/README.md +0 -0
  10. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/__init__.py +0 -0
  11. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/contract/__init__.py +0 -0
  12. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/contract/enums.py +0 -0
  13. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/contract/exceptions.py +0 -0
  14. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/contract/mapping.py +0 -0
  15. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/contract/responsedataclasses.py +0 -0
  16. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/contract/sim_config.py +0 -0
  17. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/contract/visualizer_classes.py +0 -0
  18. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/default_losses.py +0 -0
  19. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/default_metrics.py +0 -0
  20. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/__init__.py +0 -0
  21. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/api.py +0 -0
  22. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/cli_config_utils.py +0 -0
  23. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/client.py +0 -0
  24. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/epoch.py +0 -0
  25. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/experiment.py +0 -0
  26. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/experiment_context.py +0 -0
  27. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/types.py +0 -0
  28. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/utils.py +0 -0
  29. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  30. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/inner_leap_binder/__init__.py +0 -0
  31. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/mixpanel_tracker.py +0 -0
  32. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/plot_functions/__init__.py +0 -0
  33. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/plot_functions/plot_functions.py +0 -0
  34. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/plot_functions/visualize.py +0 -0
  35. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/utils.py +0 -0
  36. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/visualizers/__init__.py +0 -0
  37. {code_loader-1.0.190.dev0 → code_loader-1.0.191.dev0}/code_loader/visualizers/default_visualizers.py +0 -0
@@ -1,7 +1,8 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.190.dev0
3
+ Version: 1.0.191.dev0
4
4
  Summary:
5
+ Home-page: https://github.com/tensorleap/code-loader
5
6
  License: MIT
6
7
  Author: dorhar
7
8
  Author-email: doron.harnoy@tensorleap.ai
@@ -19,7 +20,6 @@ Requires-Dist: numpy (>=2.3.2,<3.0.0) ; python_version >= "3.11" and python_vers
19
20
  Requires-Dist: psutil (>=5.9.5,<6.0.0)
20
21
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
21
22
  Requires-Dist: requests (>=2.32.3,<3.0.0)
22
- Project-URL: Homepage, https://github.com/tensorleap/code-loader
23
23
  Project-URL: Repository, https://github.com/tensorleap/code-loader
24
24
  Description-Content-Type: text/markdown
25
25
 
@@ -277,7 +277,7 @@ class DatasetIntegrationSetup:
277
277
  metrics: List[MetricHandler] = field(default_factory=list)
278
278
  instance_metrics: List[InstanceMetricHandler] = field(default_factory=list)
279
279
  custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
280
- custom_latent_space: Optional[CustomLatentSpaceHandler] = None
280
+ custom_latent_spaces: Dict[str, CustomLatentSpaceHandler] = field(default_factory=dict)
281
281
  simulations: List[SimulationHandler] = field(default_factory=list)
282
282
 
283
283
 
@@ -289,6 +289,6 @@ class DatasetSample:
289
289
  metadata_is_none: Dict[str, bool]
290
290
  index: Union[int, str]
291
291
  state: DataStateEnum
292
- custom_latent_space: Optional[npt.NDArray[np.float32]] = None
292
+ custom_latent_spaces: Optional[Dict[str, npt.NDArray[np.float32]]] = None
293
293
  instance_masks: Optional[Dict[str, ElementInstance]] = None
294
294
 
@@ -511,18 +511,34 @@ class LeapBinder:
511
511
  """
512
512
  self.setup_container.metadata.append(MetadataHandler(name, function, metadata_type))
513
513
 
514
- def set_custom_latent_space(self, function: SectionCallableInterface) -> None:
514
+ def set_custom_latent_space(self, function: SectionCallableInterface,
515
+ name: Optional[str] = None) -> None:
515
516
  """
516
- Set a custom latent space function.
517
+ Register a custom latent space function.
518
+
519
+ Multiple custom latent spaces may be registered as long as each has a
520
+ unique name. They are stored name-keyed (insertion order preserved), so a
521
+ later registration no longer overrides an earlier one.
517
522
 
518
523
  Args:
519
- function (SectionCallableInterface): The metadata handler function.
524
+ function (SectionCallableInterface): The latent-space handler function.
520
525
  This function receives:
521
526
  subset (PreprocessResponse): The subset of the data.
522
527
  index (int): The index of the sample within the subset.
523
- This function should numpy float32 array contains the latent space vec of the sample.
528
+ This function should return a numpy float32 array containing the latent
529
+ space vec of the sample.
530
+ name (Optional[str]): Unique name for this custom latent space. Defaults to
531
+ the reserved single-LS name for backward compatibility.
524
532
  """
525
- self.setup_container.custom_latent_space = CustomLatentSpaceHandler(function)
533
+ if name is None:
534
+ name = custom_latent_space_attribute
535
+ if name in self.setup_container.custom_latent_spaces:
536
+ raise Exception(
537
+ f"A custom latent space named '{name}' is already registered. Each "
538
+ f"@tensorleap_custom_latent_space must have a unique name "
539
+ f"(pass name='...' to distinguish them)."
540
+ )
541
+ self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(function, name)
526
542
 
527
543
  def set_custom_layer(self, custom_layer: Type[Any], name: str, inspect_layer: bool = False,
528
544
  kernel_index: Optional[int] = None, use_custom_latent_space: bool = False) -> None:
@@ -197,11 +197,11 @@ def validate_output_structure(result, func_name: str, expected_type_name="np.nda
197
197
  def batch_warning(result, func_name):
198
198
  if len(result.shape) > 0 and result.shape[0] == 1:
199
199
  warnings.warn(
200
- f"{func_name} warning: Tensorleap will add a batch dimension at axis 0 of "
201
- f"{func_name}'s output, but axis 0 is already size 1 — this can produce a "
202
- f"doubly-batched shape (e.g. (1, 1, ...)). If axis 0 is a real dimension "
203
- f"(e.g. a single channel) and not an accidental batch, you can ignore this; "
204
- f"only fix it if you pre-batched the output."
200
+ f"{func_name} warning: Tensorleap will add a batch dimension at axis 0 to the output of {func_name}, "
201
+ f"although the detected size of axis 0 is already 1. "
202
+ f"This may lead to an extra batch dimension (e.g., shape (1, 1, ...)). "
203
+ f"Please ensure that the output of '{func_name}' is not already batched "
204
+ f"to avoid computation errors."
205
205
  )
206
206
 
207
207
 
@@ -349,14 +349,6 @@ def _safe_get_item(key):
349
349
 
350
350
 
351
351
  def tensorleap_load_model(prediction_types: Optional[List[PredictionTypeHandler]] = _UNSET):
352
- """Register the model-loading function.
353
-
354
- ``prediction_types``: declare one ``PredictionTypeHandler`` per model output, in
355
- output order. Multi-output models must declare an entry for EVERY output — the
356
- count is validated against the model's outputs — so for an output you don't consume
357
- as a prediction, pass a throwaway handler to satisfy the count. Omit the argument
358
- only when the model has a single prediction output.
359
- """
360
352
  prediction_types_was_provided = prediction_types is not _UNSET
361
353
 
362
354
  if not prediction_types_was_provided:
@@ -575,9 +567,6 @@ def tensorleap_load_model(prediction_types: Optional[List[PredictionTypeHandler]
575
567
  def get_inputs(self):
576
568
  return self.model.get_inputs()
577
569
 
578
- def get_outputs(self):
579
- return self.model.get_outputs()
580
-
581
570
  model_placeholder = ModelPlaceholder(prediction_types)
582
571
  if not _call_from_tl_platform:
583
572
  update_env_params_func("tensorleap_load_model", "v")
@@ -642,13 +631,6 @@ def tensorleap_load_model(prediction_types: Optional[List[PredictionTypeHandler]
642
631
 
643
632
  return FollowInputIndex()
644
633
 
645
- def get_outputs(self):
646
- # Mapping-mode counterpart of the ONNX-run get_outputs(): in mapping
647
- # mode outputs are reached by index, and get_outputs()[i] resolves to
648
- # Prediction{i} — exactly like run()/__call__, which also return a
649
- # ModelOutputPlaceholder. Reusing it keeps the mapping unchanged.
650
- return ModelOutputPlaceholder()
651
-
652
634
  return ModelPlaceholder()
653
635
 
654
636
  def final_inner(*args, **kwargs):
@@ -1586,8 +1568,9 @@ def tensorleap_metadata(
1586
1568
  return decorating_function
1587
1569
 
1588
1570
 
1589
- def tensorleap_custom_latent_space():
1571
+ def tensorleap_custom_latent_space(name: Optional[str] = None):
1590
1572
  def decorating_function(user_function: SectionCallableInterface):
1573
+ ls_name = name if name is not None else user_function.__name__
1591
1574
  def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
1592
1575
  assert isinstance(sample_id, (int, str)), \
1593
1576
  (f'tensorleap_custom_latent_space validation failed: '
@@ -1607,9 +1590,9 @@ def tensorleap_custom_latent_space():
1607
1590
  if result.ndim > 1:
1608
1591
  flat_dim = int(np.prod(result.shape))
1609
1592
  store_general_warning(
1610
- key=("tensorleap_custom_latent_space_flatten", tuple(result.shape)),
1593
+ key=("tensorleap_custom_latent_space_flatten", ls_name, tuple(result.shape)),
1611
1594
  message=(
1612
- f"tensorleap_custom_latent_space returned per-sample shape {tuple(result.shape)} "
1595
+ f"tensorleap_custom_latent_space '{ls_name}' returned per-sample shape {tuple(result.shape)} "
1613
1596
  f"(ndim={result.ndim}). Tensorleap assumes per-sample shape (d, ...) and will "
1614
1597
  f"flatten to ({flat_dim},) before downstream visualization and clustering. "
1615
1598
  f"If you want a different aggregation (e.g. global average pooling), do it "
@@ -1628,7 +1611,7 @@ def tensorleap_custom_latent_space():
1628
1611
 
1629
1612
  return result
1630
1613
 
1631
- leap_binder.set_custom_latent_space(inner_without_validate)
1614
+ leap_binder.set_custom_latent_space(inner_without_validate, ls_name)
1632
1615
 
1633
1616
  def inner(sample_id, preprocess_response):
1634
1617
  if os.environ.get(mapping_runtime_mode_env_var_mame):
@@ -251,11 +251,7 @@ class LeapLoader(LeapLoaderBase):
251
251
 
252
252
  metadata, metadata_is_none = self.get_metadata(state, sample_id)
253
253
 
254
- custom_latent_space = None
255
- if global_leap_binder.setup_container.custom_latent_space is not None:
256
- custom_latent_space = global_leap_binder.setup_container.custom_latent_space.function(sample_id,
257
- preprocess_result[
258
- state])
254
+ custom_latent_spaces = self._get_custom_latent_spaces(sample_id, preprocess_result[state])
259
255
  instance_mask = self._get_instances_masks(state, sample_id, instance_id)
260
256
  sample = DatasetSample(inputs=self._get_inputs(state, sample_id),
261
257
  gt=None if state == DataStateEnum.unlabeled else self._get_gt(state, sample_id),
@@ -263,7 +259,7 @@ class LeapLoader(LeapLoaderBase):
263
259
  metadata_is_none=metadata_is_none,
264
260
  index=sample_id,
265
261
  state=state,
266
- custom_latent_space=custom_latent_space,
262
+ custom_latent_spaces=custom_latent_spaces,
267
263
  instance_masks=instance_mask)
268
264
  return sample
269
265
 
@@ -294,11 +290,7 @@ class LeapLoader(LeapLoaderBase):
294
290
  handler.name, handler_result
295
291
  )
296
292
 
297
- custom_latent_space = None
298
- if global_leap_binder.setup_container.custom_latent_space is not None:
299
- custom_latent_space = global_leap_binder.setup_container.custom_latent_space.function(
300
- original_sample_id, preprocess
301
- )
293
+ custom_latent_spaces = self._get_custom_latent_spaces(original_sample_id, preprocess)
302
294
 
303
295
  return DatasetSample(
304
296
  inputs=inputs,
@@ -307,7 +299,7 @@ class LeapLoader(LeapLoaderBase):
307
299
  metadata_is_none=metadata_is_none,
308
300
  index=synthetic_index,
309
301
  state=DataStateEnum.additional,
310
- custom_latent_space=custom_latent_space,
302
+ custom_latent_spaces=custom_latent_spaces,
311
303
  instance_masks=None,
312
304
  )
313
305
 
@@ -320,6 +312,8 @@ class LeapLoader(LeapLoaderBase):
320
312
  try:
321
313
  self.exec_script()
322
314
 
315
+ integration_test_test_payload = self._check_integration_test_exists()
316
+ test_payloads.append(integration_test_test_payload)
323
317
  preprocess_test_payload = self._check_preprocess()
324
318
  test_payloads.append(preprocess_test_payload)
325
319
  handlers_test_payloads = self._check_handlers()
@@ -356,6 +350,23 @@ class LeapLoader(LeapLoaderBase):
356
350
  engine_file_contract=EngineFileContract(global_leap_binder.mapping_connections,
357
351
  global_leap_binder.leap_analysis_configuration))
358
352
 
353
+ def _check_integration_test_exists(self) -> DatasetTestResultPayload:
354
+ test_result = DatasetTestResultPayload('integration_test')
355
+ # Only enforced on the Tensorleap platform (i.e. during a push / inspection,
356
+ # where IS_TENSORLEAP_PLATFORM=true). Running code-loader locally or in unit
357
+ # tests must not require the decorator, so pre-decorator integrations and
358
+ # local check runs keep working unchanged.
359
+ is_on_platform = os.environ.get('IS_TENSORLEAP_PLATFORM') == 'true'
360
+ if is_on_platform and global_leap_binder.integration_test_func is None:
361
+ test_result.is_passed = False
362
+ test_result.display[TestingSectionEnum.Errors.name] = (
363
+ "No integration test was found in your integration file. A valid Tensorleap "
364
+ "integration file must define an integration test function decorated with "
365
+ "@tensorleap_integration_test. Without it the integration cannot be validated "
366
+ "and the push is aborted. See the mnist leap_integration.py for a valid example."
367
+ )
368
+ return test_result
369
+
359
370
  def _check_preprocess(self) -> DatasetTestResultPayload:
360
371
  test_result = DatasetTestResultPayload('preprocess')
361
372
  try:
@@ -855,10 +866,29 @@ class LeapLoader(LeapLoaderBase):
855
866
 
856
867
  return id_type
857
868
 
869
+ @staticmethod
870
+ def _get_custom_latent_spaces(
871
+ sample_id: Union[int, str],
872
+ preprocess: "PreprocessResponse") -> Optional[Dict[str, npt.NDArray[np.float32]]]:
873
+ handlers = global_leap_binder.setup_container.custom_latent_spaces
874
+ if not handlers:
875
+ return None
876
+ return {name: handler.function(sample_id, preprocess) for name, handler in handlers.items()}
877
+
858
878
  @lru_cache()
859
879
  def has_custom_latent_space_decorator(self) -> bool:
860
880
  self.exec_script()
861
- return global_leap_binder.setup_container.custom_latent_space is not None
881
+ return len(global_leap_binder.setup_container.custom_latent_spaces) > 0
882
+
883
+ @lru_cache()
884
+ def get_custom_latent_space_names(self) -> Tuple[str, ...]:
885
+ """Ordered names of all registered custom latent spaces.
886
+
887
+ Registration order is the canonical index mapping consumed by the engine
888
+ (name i -> user_custom_i). Returns a tuple so the lru_cache value is hashable.
889
+ """
890
+ self.exec_script()
891
+ return tuple(global_leap_binder.setup_container.custom_latent_spaces.keys())
862
892
 
863
893
  def get_instances_data(self, state: DataStateEnum) -> Tuple[Dict[str, List[str]], Dict[str, str]]:
864
894
  preprocess_result = self._preprocess_result()
@@ -149,6 +149,10 @@ class LeapLoaderBase:
149
149
  def has_custom_latent_space_decorator(self) -> bool:
150
150
  pass
151
151
 
152
+ @abstractmethod
153
+ def get_custom_latent_space_names(self) -> Tuple[str, ...]:
154
+ pass
155
+
152
156
  @abstractmethod
153
157
  def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
154
158
  pass
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "code-loader"
3
3
 
4
- version = "1.0.190.dev0"
4
+ version = "1.0.191.dev0"
5
5
  description = ""
6
6
  authors = ["dorhar <doron.harnoy@tensorleap.ai>"]
7
7
  license = "MIT"