code-loader 1.0.203.dev0__tar.gz → 1.0.204.dev1__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.203.dev0 → code_loader-1.0.204.dev1}/PKG-INFO +1 -1
  2. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/contract/datasetclasses.py +3 -0
  3. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/inner_leap_binder/leapbinder.py +18 -28
  4. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/inner_leap_binder/leapbinder_decorators.py +74 -2
  5. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/leaploader.py +67 -6
  6. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/pyproject.toml +1 -1
  7. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/LICENSE +0 -0
  8. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/README.md +0 -0
  9. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/__init__.py +0 -0
  10. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/contract/__init__.py +0 -0
  11. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/contract/enums.py +0 -0
  12. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/contract/exceptions.py +0 -0
  13. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/contract/mapping.py +0 -0
  14. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/contract/responsedataclasses.py +0 -0
  15. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/contract/sim_config.py +0 -0
  16. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/contract/visualizer_classes.py +0 -0
  17. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/default_losses.py +0 -0
  18. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/default_metrics.py +0 -0
  19. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/__init__.py +0 -0
  20. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/api.py +0 -0
  21. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/cli_config_utils.py +0 -0
  22. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/client.py +0 -0
  23. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/epoch.py +0 -0
  24. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/experiment.py +0 -0
  25. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/experiment_context.py +0 -0
  26. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/types.py +0 -0
  27. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/utils.py +0 -0
  28. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  29. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/inner_leap_binder/__init__.py +0 -0
  30. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/leaploaderbase.py +0 -0
  31. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/mixpanel_tracker.py +0 -0
  32. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/plot_functions/__init__.py +0 -0
  33. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/plot_functions/plot_functions.py +0 -0
  34. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/plot_functions/visualize.py +0 -0
  35. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/utils.py +0 -0
  36. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/visualizers/__init__.py +0 -0
  37. {code_loader-1.0.203.dev0 → code_loader-1.0.204.dev1}/code_loader/visualizers/default_visualizers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.203.dev0
3
+ Version: 1.0.204.dev1
4
4
  Summary:
5
5
  Home-page: https://github.com/tensorleap/code-loader
6
6
  License: MIT
@@ -147,6 +147,8 @@ class ElementInstance:
147
147
  SectionCallableInterface = Callable[[Union[int, str], PreprocessResponse], npt.NDArray[np.float32]]
148
148
  InstanceCallableInterface = Callable[[Union[int, str], PreprocessResponse, int], Optional[ElementInstance]]
149
149
  InstanceLengthCallableInterface = Callable[[Union[int, str], PreprocessResponse], int]
150
+ InstanceSectionCallableInterface = Callable[
151
+ [Union[int, str], PreprocessResponse, Optional[int]], npt.NDArray[np.float32]]
150
152
 
151
153
  # (sample_id, prev_inputs, prev_outputs, state, preprocess) -> (next model inputs | None, state).
152
154
  # First call per chain receives prev_inputs=None, prev_outputs=None, state=None and returns the
@@ -310,6 +312,7 @@ class CustomLatentSpaceHandler:
310
312
  function: SectionCallableInterface
311
313
  name: str = 'custom_latent_space'
312
314
  use_ls_for_analysis: bool = False
315
+ instance_aware: bool = False
313
316
 
314
317
 
315
318
  # How a chain's latent-space vectors are derived from its per-step forward passes.
@@ -15,7 +15,8 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
15
15
  CustomCallableInterfaceMultiArgs, ConfusionMatrixCallableInterfaceMultiArgs, LeapData, \
16
16
  CustomMultipleReturnCallableInterfaceMultiArgs, DatasetBaseHandler, custom_latent_space_attribute, \
17
17
  RawInputsForHeatmap, VisualizerHandlerData, MetricHandlerData, CustomLossHandlerData, SamplePreprocessResponse, \
18
- ElementInstanceMasksHandler, InstanceCallableInterface, CustomLatentSpaceHandler, InstanceMetricHandler, \
18
+ ElementInstanceMasksHandler, InstanceCallableInterface, InstanceSectionCallableInterface, \
19
+ CustomLatentSpaceHandler, InstanceMetricHandler, \
19
20
  SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface, \
20
21
  AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS, AUTOREGRESSIVE_IMPLICIT_ARG_NAMES, \
21
22
  AutoregressiveMetricHandler, AutoregressiveLossHandler, AutoregressiveVisualizerHandler
@@ -541,9 +542,10 @@ class LeapBinder:
541
542
  f"@tensorleap_metadata('my_metadata', {{'key': DatasetMetadataType.int}}).")
542
543
  self.setup_container.metadata.append(MetadataHandler(name, function, metadata_type))
543
544
 
544
- def set_custom_latent_space(self, function: SectionCallableInterface,
545
+ def set_custom_latent_space(self, function: Union[SectionCallableInterface, InstanceSectionCallableInterface],
545
546
  name: Optional[str] = None,
546
- use_ls_for_analysis: bool = False) -> None:
547
+ use_ls_for_analysis: bool = False,
548
+ instance_aware: bool = False) -> None:
547
549
  """
548
550
  Register a custom latent space function.
549
551
 
@@ -564,6 +566,8 @@ class LeapBinder:
564
566
  space for the Out-Of-Distribution and Domain-Gap insights instead of the
565
567
  built-in defaults. At most one registered custom latent space may set this;
566
568
  registering a second one with the flag raises.
569
+ instance_aware (bool): When True, `function` takes a third `instance_id` argument
570
+ and is called once per element-instance row instead of once per sample.
567
571
  """
568
572
  if name is None:
569
573
  name = custom_latent_space_attribute
@@ -588,7 +592,7 @@ class LeapBinder:
588
592
  f"or '{already_flagged[0]}', not both."
589
593
  )
590
594
  self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(
591
- function, name, use_ls_for_analysis)
595
+ function=function, name=name, use_ls_for_analysis=use_ls_for_analysis, instance_aware=instance_aware)
592
596
 
593
597
  def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
594
598
  latent_space_aggregation: str = 'last_step') -> None:
@@ -1017,18 +1021,17 @@ class LeapBinder:
1017
1021
 
1018
1022
  Each entry must be one of:
1019
1023
  - "foreground" (the friendly name for the max-pooled default LS)
1020
- - "balanced" (the friendly name for the universal fallback default LS),
1021
- allowed only when a custom latent space is registered with
1022
- use_ls_for_analysis=True — that flagged LS is what replaces it
1023
1024
  - An input name registered via set_input(name=...)
1024
1025
  - A prediction name registered via add_prediction(name=...)
1025
1026
 
1026
1027
  Forbidden tokens (raise with a clear message):
1028
+ - "balanced" — the friendly name for the universal fallback default
1029
+ LS; cannot be disabled.
1027
1030
  - "user_custom" — populated by set_custom_latent_space; if the user
1028
1031
  registered a custom LS they should use it, not ignore it.
1029
1032
 
1030
- Order-independent: this runs after all set_input / add_prediction /
1031
- set_custom_latent_space calls because it consults setup_container.
1033
+ Order-independent: this runs after all set_input / add_prediction calls
1034
+ because it consults setup_container.inputs and prediction_types.
1032
1035
  """
1033
1036
  if not self.leap_analysis_configuration:
1034
1037
  return
@@ -1036,37 +1039,24 @@ class LeapBinder:
1036
1039
  if not names:
1037
1040
  return
1038
1041
 
1039
- forbidden = {"user_custom"}
1042
+ forbidden = {"balanced", "user_custom"}
1040
1043
  input_names = {h.name for h in self.setup_container.inputs}
1041
1044
  prediction_names = {h.name for h in self.setup_container.prediction_types}
1042
- allowed_tokens = {"foreground", "balanced"} | input_names | prediction_names
1043
- analysis_ls_names = [
1044
- ls_name
1045
- for ls_name, handler in self.setup_container.custom_latent_spaces.items()
1046
- if handler.use_ls_for_analysis
1047
- ]
1045
+ allowed_tokens = {"foreground"} | input_names | prediction_names
1048
1046
 
1049
1047
  for name in names:
1050
1048
  if name in forbidden:
1051
1049
  raise Exception(
1052
- f"Latent space '{name}' cannot be ignored. 'user_custom' is reserved "
1053
- f"for set_custom_latent_space."
1054
- )
1055
- if name == "balanced" and not analysis_ls_names:
1056
- raise Exception(
1057
- "Latent space 'balanced' (the model-derived default latent space) can "
1058
- "only be ignored when a replacement is registered. Add "
1059
- "@tensorleap_custom_latent_space(..., use_ls_for_analysis=True) to the "
1060
- "latent space that should be analyzed instead, or drop 'balanced' from "
1061
- "ignore_latent_spaces — ignoring it with nothing in its place would "
1062
- "leave the analyses with no latent space."
1050
+ f"Latent space '{name}' cannot be ignored. "
1051
+ f"'balanced' is the universal fallback default LS and 'user_custom' is "
1052
+ f"reserved for set_custom_latent_space."
1063
1053
  )
1064
1054
  if name not in allowed_tokens:
1065
1055
  raise Exception(
1066
1056
  f"Latent space name '{name}' in ignore_latent_spaces did not match "
1067
1057
  f"any registered input or prediction. Available names: "
1068
1058
  f"inputs={sorted(input_names)}, predictions={sorted(prediction_names)}, "
1069
- f"tokens=['foreground', 'balanced']."
1059
+ f"tokens=['foreground']."
1070
1060
  )
1071
1061
 
1072
1062
  def set_batch_size_to_validate(self, batch_size: int) -> None:
@@ -24,7 +24,7 @@ from code_loader.contract.datasetclasses import CustomCallableInterfaceMultiArgs
24
24
  CustomMultipleReturnCallableInterfaceMultiArgs, ConfusionMatrixCallableInterfaceMultiArgs, CustomCallableInterface, \
25
25
  VisualizerCallableInterface, MetadataSectionCallableInterface, PreprocessResponse, SectionCallableInterface, \
26
26
  ConfusionMatrixElement, SamplePreprocessResponse, PredictionTypeHandler, InstanceCallableInterface, ElementInstance, \
27
- InstanceLengthCallableInterface, AutoregressiveStepCallableInterface
27
+ InstanceLengthCallableInterface, InstanceSectionCallableInterface, AutoregressiveStepCallableInterface
28
28
  from code_loader.contract.enums import MetricDirection, LeapDataType, DatasetMetadataType, DataStateType
29
29
  from code_loader import leap_binder, LeapLoader
30
30
  from code_loader.contract.mapping import NodeMapping, NodeMappingType, NodeConnection
@@ -1787,6 +1787,73 @@ def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analys
1787
1787
  return decorating_function
1788
1788
 
1789
1789
 
1790
+ def tensorleap_instance_custom_latent_space(name: Optional[str] = None, use_ls_for_analysis: bool = False):
1791
+ assert isinstance(use_ls_for_analysis, bool), \
1792
+ ("tensorleap_instance_custom_latent_space validation failed: use_ls_for_analysis must be a bool. "
1793
+ f"Got {type(use_ls_for_analysis)}.")
1794
+
1795
+ def decorating_function(user_function: InstanceSectionCallableInterface):
1796
+ ls_name = name if name is not None else user_function.__name__
1797
+
1798
+ argspec = inspect.getfullargspec(user_function)
1799
+ assert len(argspec.args) == 3, \
1800
+ (f'tensorleap_instance_custom_latent_space validation failed: '
1801
+ f'The function should take exactly 3 positional parameters '
1802
+ f'(sample_id, preprocess_response, instance_id). Got {len(argspec.args)}.')
1803
+
1804
+ def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse,
1805
+ instance_id: int):
1806
+ _validate_id_or_group(sample_id, preprocess_response, 'tensorleap_instance_custom_latent_space')
1807
+ assert isinstance(instance_id, (int, np.integer)), \
1808
+ (f'tensorleap_instance_custom_latent_space validation failed: '
1809
+ f'Argument instance_id should be int. Got {type(instance_id)}.')
1810
+
1811
+ def _validate_result(result):
1812
+ assert isinstance(result, np.ndarray), \
1813
+ (f'tensorleap_instance_custom_latent_space validation failed: '
1814
+ f'The return type should be a numpy array. Got {type(result)}.')
1815
+ if result.ndim > 1:
1816
+ flat_dim = int(np.prod(result.shape))
1817
+ store_general_warning(
1818
+ key=("tensorleap_instance_custom_latent_space_flatten", ls_name, tuple(result.shape)),
1819
+ message=(
1820
+ f"tensorleap_instance_custom_latent_space '{ls_name}' returned per-sample shape "
1821
+ f"{tuple(result.shape)} (ndim={result.ndim}). Tensorleap assumes per-sample shape "
1822
+ f"(d, ...) and will flatten to ({flat_dim},) before downstream visualization and "
1823
+ f"clustering. If you want a different aggregation, do it inside your function."
1824
+ ),
1825
+ )
1826
+
1827
+ def inner_without_validate(sample_id, preprocess_response, instance_id):
1828
+ global _called_from_inside_tl_decorator
1829
+ _called_from_inside_tl_decorator += 1
1830
+
1831
+ try:
1832
+ result = user_function(sample_id, preprocess_response, instance_id)
1833
+ finally:
1834
+ _called_from_inside_tl_decorator -= 1
1835
+
1836
+ return result
1837
+
1838
+ leap_binder.set_custom_latent_space(inner_without_validate, ls_name,
1839
+ use_ls_for_analysis=use_ls_for_analysis, instance_aware=True)
1840
+
1841
+ def inner(sample_id, preprocess_response, instance_id):
1842
+ if os.environ.get(mapping_runtime_mode_env_var_mame):
1843
+ return None
1844
+
1845
+ _validate_input_args(sample_id, preprocess_response, instance_id)
1846
+
1847
+ result = inner_without_validate(sample_id, preprocess_response, instance_id)
1848
+
1849
+ _validate_result(result)
1850
+ return result
1851
+
1852
+ return inner
1853
+
1854
+ return decorating_function
1855
+
1856
+
1790
1857
  _MODEL_LOOP_MAX_STEPS = 1000
1791
1858
 
1792
1859
  _active_model_loop = None
@@ -2888,7 +2955,12 @@ def tensorleap_element_instance_preprocess(
2888
2955
  instances_length = instance_length_encoder(sample_id, preprocess_response)
2889
2956
  instances_ids = [f'{sample_id}_{instance_id}' for instance_id in range(instances_length)]
2890
2957
  sample_ids_to_instance_mappings[sample_id] = instances_ids
2891
- instance_to_sample_ids_mappings[sample_id] = sample_id
2958
+ # Only instance ids belong in instance_to_sample_ids_mappings: the engine uses
2959
+ # membership in it as "is this row an element instance?" (custometl.py,
2960
+ # samplesgenerator.py). Mapping an original id to itself made that test true for
2961
+ # every image row, routing it into construct_instance_sample, where looking the
2962
+ # original up in its own instance list raises
2963
+ # "Index <id> with sample_id: <id> cannot be found!".
2892
2964
  for idx, instance_id in enumerate(instances_ids):
2893
2965
  if not found_instance_metadata:
2894
2966
  element_instance = instance_mask_encoder(sample_id, preprocess_response, idx)
@@ -283,7 +283,7 @@ class LeapLoader(LeapLoaderBase):
283
283
 
284
284
  metadata, metadata_is_none = self.get_metadata(state, sample_id)
285
285
 
286
- custom_latent_spaces = self._get_custom_latent_spaces(sample_id, preprocess_result[state])
286
+ custom_latent_spaces = self._get_custom_latent_spaces(sample_id, preprocess_result[state], instance_id)
287
287
  instance_mask = self._get_instances_masks(state, sample_id, instance_id)
288
288
  sample = DatasetSample(inputs=self._get_inputs(state, sample_id),
289
289
  gt=None if state == DataStateEnum.unlabeled else self._get_gt(state, sample_id),
@@ -349,6 +349,9 @@ class LeapLoader(LeapLoaderBase):
349
349
  global_leap_binder.validate_autoregressive_setup()
350
350
  preprocess_test_payload = self._check_preprocess()
351
351
  test_payloads.append(preprocess_test_payload)
352
+ instance_ls_test_payload = self._check_instance_custom_latent_spaces()
353
+ if instance_ls_test_payload is not None:
354
+ test_payloads.append(instance_ls_test_payload)
352
355
  handlers_test_payloads = self._check_handlers()
353
356
  test_payloads.extend(handlers_test_payloads)
354
357
  simulation_test_payloads = self._check_simulations()
@@ -422,6 +425,37 @@ class LeapLoader(LeapLoaderBase):
422
425
  test_result.is_passed = False
423
426
  return test_result
424
427
 
428
+ def _check_instance_custom_latent_spaces(self) -> Optional[DatasetTestResultPayload]:
429
+ instance_aware_names = [
430
+ name for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items()
431
+ if handler.instance_aware
432
+ ]
433
+ if not instance_aware_names:
434
+ return None
435
+
436
+ test_result = DatasetTestResultPayload('instance_custom_latent_space')
437
+ errors = []
438
+ if not global_leap_binder.setup_container.instance_masks:
439
+ errors.append(
440
+ f"Instance-aware custom latent space(s) {instance_aware_names} require "
441
+ f"@tensorleap_instances_masks_encoder to be registered."
442
+ )
443
+ preprocess_result = self._preprocess_result()
444
+ has_instance_mappings = any(
445
+ preprocess_response.instance_to_sample_ids_mappings
446
+ for preprocess_response in preprocess_result.values()
447
+ )
448
+ if not has_instance_mappings:
449
+ errors.append(
450
+ f"Instance-aware custom latent space(s) {instance_aware_names} require the preprocess "
451
+ f"function to be decorated with @tensorleap_element_instance_preprocess("
452
+ f"instance_length_encoder, instance_mask_encoder)."
453
+ )
454
+ if errors:
455
+ test_result.is_passed = False
456
+ test_result.display[TestingSectionEnum.Errors.name] = "\n".join(errors)
457
+ return test_result
458
+
425
459
  def _check_handlers(self) -> List[DatasetTestResultPayload]:
426
460
  preprocess_result = self._preprocess_result()
427
461
  result_payloads: List[DatasetTestResultPayload] = []
@@ -1107,6 +1141,9 @@ class LeapLoader(LeapLoaderBase):
1107
1141
  latent_handlers = global_leap_binder.setup_container.custom_latent_spaces
1108
1142
  custom_latent_spaces = None
1109
1143
  if latent_handlers:
1144
+ if any(handler.instance_aware for handler in latent_handlers.values()):
1145
+ raise Exception(
1146
+ "instance-aware custom latent spaces are not supported with grouped preprocess responses")
1110
1147
  custom_latent_spaces = {
1111
1148
  name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
1112
1149
  for name, handler in latent_handlers.items()
@@ -1329,17 +1366,29 @@ class LeapLoader(LeapLoaderBase):
1329
1366
  def _get_custom_latent_spaces(
1330
1367
  self,
1331
1368
  sample_id: Union[int, str],
1332
- preprocess: "PreprocessResponse") -> Optional[Dict[str, npt.NDArray[np.float32]]]:
1369
+ preprocess: "PreprocessResponse",
1370
+ instance_id: Optional[int] = None) -> Optional[Dict[str, npt.NDArray[np.float32]]]:
1333
1371
  handlers = global_leap_binder.setup_container.custom_latent_spaces
1334
1372
  if not handlers:
1335
1373
  return None
1336
1374
  if preprocess.is_grouped:
1337
1375
  # Single-sample fetch: encode this sample only, not the whole group (same
1338
1376
  # memory rationale as _get_dataset_handlers; see grouped-fetch-oom-bug.md).
1377
+ if any(handler.instance_aware for handler in handlers.values()):
1378
+ raise Exception(
1379
+ "instance-aware custom latent spaces are not supported with grouped preprocess responses")
1339
1380
  self._locate_group(preprocess, sample_id) # validates group membership
1340
1381
  return {name: self._to_grouped_list(handler.function([sample_id], preprocess))[0]
1341
1382
  for name, handler in handlers.items()}
1342
- return {name: handler.function(sample_id, preprocess) for name, handler in handlers.items()}
1383
+ result: Dict[str, npt.NDArray[np.float32]] = {}
1384
+ for name, handler in handlers.items():
1385
+ if handler.instance_aware:
1386
+ if instance_id is None:
1387
+ continue
1388
+ result[name] = handler.function(sample_id, preprocess, instance_id)
1389
+ else:
1390
+ result[name] = handler.function(sample_id, preprocess)
1391
+ return result
1343
1392
 
1344
1393
  @lru_cache()
1345
1394
  def has_custom_latent_space_decorator(self) -> bool:
@@ -1348,14 +1397,26 @@ class LeapLoader(LeapLoaderBase):
1348
1397
 
1349
1398
  @lru_cache()
1350
1399
  def get_custom_latent_space_names(self) -> Tuple[str, ...]:
1351
- """Ordered names of all registered custom latent spaces.
1400
+ """Names of all registered custom latent spaces, in registration order.
1352
1401
 
1353
- Registration order is the canonical index mapping consumed by the engine
1354
- (name i -> user_custom_i). Returns a tuple so the lru_cache value is hashable.
1402
+ The engine resolves each name to its `user_custom_i` index by sorted name, not
1403
+ registration order, so this order is not the index mapping. Returns a tuple so the
1404
+ lru_cache value is hashable.
1355
1405
  """
1356
1406
  self.exec_script()
1357
1407
  return tuple(global_leap_binder.setup_container.custom_latent_spaces.keys())
1358
1408
 
1409
+ @lru_cache()
1410
+ def get_instance_custom_latent_space_names(self) -> Tuple[str, ...]:
1411
+ """Names of registered custom latent spaces that are instance-aware.
1412
+
1413
+ Names, not indices — the engine resolves each name to its `user_custom_i` index
1414
+ by sorted name.
1415
+ """
1416
+ self.exec_script()
1417
+ return tuple(name for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items()
1418
+ if handler.instance_aware)
1419
+
1359
1420
  @lru_cache()
1360
1421
  def get_custom_latent_space_for_analysis(self) -> Optional[str]:
1361
1422
  """Name of the custom latent space flagged with use_ls_for_analysis=True, if any.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "code-loader"
3
- version = "1.0.203.dev0"
3
+ version = "1.0.204.dev1"
4
4
  description = ""
5
5
  authors = ["dorhar <doron.harnoy@tensorleap.ai>"]
6
6
  license = "MIT"