code-loader 1.0.191.dev1__tar.gz → 1.0.193__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.
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/PKG-INFO +1 -1
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/contract/datasetclasses.py +37 -55
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/inner_leap_binder/leapbinder.py +69 -17
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/inner_leap_binder/leapbinder_decorators.py +346 -188
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/leaploader.py +200 -195
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/leaploaderbase.py +12 -2
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/utils.py +0 -5
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/pyproject.toml +1 -2
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/LICENSE +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/README.md +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/__init__.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/contract/__init__.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/contract/enums.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/contract/exceptions.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/contract/mapping.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/contract/responsedataclasses.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/contract/sim_config.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/contract/visualizer_classes.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/default_losses.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/default_metrics.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/__init__.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/api.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/cli_config_utils.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/client.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/epoch.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/experiment.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/experiment_context.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/types.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/utils.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/inner_leap_binder/__init__.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/mixpanel_tracker.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/plot_functions/__init__.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/plot_functions/plot_functions.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/plot_functions/visualize.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/visualizers/__init__.py +0 -0
- {code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/visualizers/default_visualizers.py +0 -0
|
@@ -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
|
|
3
|
+
from typing import Any, Callable, List, Optional, Dict, Union, Type, Literal
|
|
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,44 +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[
|
|
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
48
|
|
|
52
49
|
def __post_init__(self) -> None:
|
|
53
50
|
assert self.sample_ids_to_instance_mappings is None, f"Keep sample_ids_to_instance_mappings None when initializing PreprocessResponse"
|
|
54
51
|
assert self.instance_to_sample_ids_mappings is None, f"Keep instance_to_sample_ids_mappings None when initializing PreprocessResponse"
|
|
55
52
|
|
|
56
53
|
if self.length is not None and self.sample_ids is None:
|
|
57
|
-
self.sample_ids =
|
|
54
|
+
self.sample_ids = [i for i in range(self.length)]
|
|
58
55
|
self.sample_id_type = int
|
|
59
|
-
self._grouped = False
|
|
60
56
|
elif self.length is None and self.sample_ids is not None:
|
|
61
|
-
self.
|
|
62
|
-
if self.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
for
|
|
66
|
-
assert isinstance(
|
|
67
|
-
"Each group in a grouped PreprocessResponse must be a non-empty list."
|
|
68
|
-
self.sample_id_type = type(groups[0][0])
|
|
69
|
-
for group in groups:
|
|
70
|
-
for sample_id in group:
|
|
71
|
-
assert isinstance(sample_id, self.sample_id_type), \
|
|
72
|
-
f"Sample id should be of type {self.sample_id_type.__name__}. Got: {type(sample_id)}"
|
|
73
|
-
self.length = sum(len(group) for group in groups)
|
|
74
|
-
else:
|
|
75
|
-
flat_ids = cast(List[SampleId], self.sample_ids)
|
|
76
|
-
self.length = len(flat_ids)
|
|
77
|
-
if self.sample_id_type is None:
|
|
78
|
-
self.sample_id_type = str
|
|
79
|
-
if self.sample_id_type == str:
|
|
80
|
-
for sample_id in flat_ids:
|
|
81
|
-
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)}"
|
|
82
63
|
else:
|
|
83
64
|
raise Exception("length is deprecated, please use sample_ids instead.")
|
|
84
65
|
|
|
@@ -98,36 +79,9 @@ class PreprocessResponse:
|
|
|
98
79
|
return id(self)
|
|
99
80
|
|
|
100
81
|
def __len__(self) -> int:
|
|
101
|
-
assert self.length is not None
|
|
102
|
-
return self.length
|
|
103
|
-
|
|
104
|
-
@property
|
|
105
|
-
def is_grouped(self) -> bool:
|
|
106
|
-
return self._grouped
|
|
107
|
-
|
|
108
|
-
@property
|
|
109
|
-
def num_groups(self) -> int:
|
|
110
|
-
# Grouped: the number of groups. Flat: the (flat) sample count, since sample_ids is the
|
|
111
|
-
# flat id list and there is one sample per "group".
|
|
112
82
|
assert self.sample_ids is not None
|
|
113
83
|
return len(self.sample_ids)
|
|
114
84
|
|
|
115
|
-
@property
|
|
116
|
-
def groups(self) -> Optional[List[List[SampleId]]]:
|
|
117
|
-
# The nested list of groups when grouped, otherwise None. Callers should
|
|
118
|
-
# branch on is_grouped before relying on this.
|
|
119
|
-
return cast(List[List[SampleId]], self.sample_ids) if self._grouped else None
|
|
120
|
-
|
|
121
|
-
@property
|
|
122
|
-
def flat_sample_ids(self) -> List[SampleId]:
|
|
123
|
-
# All sample ids in order: concatenation of the groups when grouped,
|
|
124
|
-
# otherwise the flat sample_ids list as-is.
|
|
125
|
-
assert self.sample_ids is not None
|
|
126
|
-
if self._grouped:
|
|
127
|
-
groups = cast(List[List[SampleId]], self.sample_ids)
|
|
128
|
-
return [sample_id for group in groups for sample_id in group]
|
|
129
|
-
return cast(List[SampleId], self.sample_ids)
|
|
130
|
-
|
|
131
85
|
@dataclass
|
|
132
86
|
class ElementInstance:
|
|
133
87
|
name: str
|
|
@@ -138,6 +92,16 @@ SectionCallableInterface = Callable[[Union[int, str], PreprocessResponse], npt.N
|
|
|
138
92
|
InstanceCallableInterface = Callable[[Union[int, str], PreprocessResponse, int], Optional[ElementInstance]]
|
|
139
93
|
InstanceLengthCallableInterface = Callable[[Union[int, str], PreprocessResponse], int]
|
|
140
94
|
|
|
95
|
+
# (sample_id, prev_inputs, prev_outputs, preprocess) -> next model inputs, or None to end the chain.
|
|
96
|
+
# First call per chain receives prev_inputs=None, prev_outputs=None and returns the initial inputs.
|
|
97
|
+
# Keys starting with '_' are passthrough state: never fed to the model, handed back verbatim in
|
|
98
|
+
# prev_inputs on the next call. The hook must be stateless and deterministically seeded (the engine
|
|
99
|
+
# may replay a step after crash recovery and relies on identical results).
|
|
100
|
+
AutoregressiveStepCallableInterface = Callable[
|
|
101
|
+
[Union[int, str], Optional[Dict[str, npt.NDArray[np.float32]]], Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
102
|
+
PreprocessResponse],
|
|
103
|
+
Optional[Dict[str, npt.NDArray[np.float32]]]]
|
|
104
|
+
|
|
141
105
|
MetadataSectionCallableInterface = Union[
|
|
142
106
|
Callable[[Union[int, str], PreprocessResponse], int],
|
|
143
107
|
Callable[[Union[int, str], PreprocessResponse], Dict[str, int]],
|
|
@@ -285,6 +249,23 @@ class CustomLatentSpaceHandler:
|
|
|
285
249
|
function: SectionCallableInterface
|
|
286
250
|
name: str = 'custom_latent_space'
|
|
287
251
|
|
|
252
|
+
|
|
253
|
+
# How a chain's latent-space vectors are derived from its per-step forward passes.
|
|
254
|
+
# 'last_step': every latent space comes from the final step's forward pass, except input-kind
|
|
255
|
+
# latent spaces which come from the first step (the original sample, before generated content
|
|
256
|
+
# dominates the model inputs). 'mean': every latent space is the elementwise mean over all steps.
|
|
257
|
+
AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS = ('last_step', 'mean')
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@dataclass
|
|
261
|
+
class AutoregressiveStepHandler:
|
|
262
|
+
function: AutoregressiveStepCallableInterface
|
|
263
|
+
name: str = 'autoregressive_step'
|
|
264
|
+
# Per-model-input shapes (unbatched, underscore passthrough keys excluded), discovered by running
|
|
265
|
+
# the hook's first call at parse time. Fills the role InputHandler.shape plays for input encoders.
|
|
266
|
+
input_shapes: Optional[Dict[str, List[int]]] = None
|
|
267
|
+
latent_space_aggregation: str = 'last_step'
|
|
268
|
+
|
|
288
269
|
@dataclass
|
|
289
270
|
class PredictionTypeHandler:
|
|
290
271
|
name: str
|
|
@@ -325,6 +306,7 @@ class DatasetIntegrationSetup:
|
|
|
325
306
|
custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
|
|
326
307
|
custom_latent_space: Optional[CustomLatentSpaceHandler] = None
|
|
327
308
|
simulations: List[SimulationHandler] = field(default_factory=list)
|
|
309
|
+
autoregressive_step: Optional[AutoregressiveStepHandler] = None
|
|
328
310
|
|
|
329
311
|
|
|
330
312
|
@dataclass
|
{code_loader-1.0.191.dev1 → code_loader-1.0.193}/code_loader/inner_leap_binder/leapbinder.py
RENAMED
|
@@ -13,7 +13,8 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
|
|
|
13
13
|
CustomMultipleReturnCallableInterfaceMultiArgs, DatasetBaseHandler, custom_latent_space_attribute, \
|
|
14
14
|
RawInputsForHeatmap, VisualizerHandlerData, MetricHandlerData, CustomLossHandlerData, SamplePreprocessResponse, \
|
|
15
15
|
ElementInstanceMasksHandler, InstanceCallableInterface, CustomLatentSpaceHandler, InstanceMetricHandler, \
|
|
16
|
-
SimulationHandler, _simulation_context
|
|
16
|
+
SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface, \
|
|
17
|
+
AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS
|
|
17
18
|
from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType
|
|
18
19
|
from code_loader.contract.mapping import NodeConnection, NodeMapping, NodeMappingType
|
|
19
20
|
from code_loader.contract.responsedataclasses import DatasetTestResultPayload, LeapAnalysisConfiguration
|
|
@@ -524,6 +525,34 @@ class LeapBinder:
|
|
|
524
525
|
"""
|
|
525
526
|
self.setup_container.custom_latent_space = CustomLatentSpaceHandler(function)
|
|
526
527
|
|
|
528
|
+
def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
|
|
529
|
+
latent_space_aggregation: str = 'last_step') -> None:
|
|
530
|
+
"""
|
|
531
|
+
Set the autoregressive step hook — the feedback function that drives a chain:
|
|
532
|
+
it supplies the model's initial inputs on its first call (prev_inputs=None, prev_outputs=None)
|
|
533
|
+
and turns each step's inputs/outputs into the next step's inputs until it returns None.
|
|
534
|
+
An autoregressive integration has no input encoders; the hook is the sole input source.
|
|
535
|
+
latent_space_aggregation declares how the chain's latent-space vectors are derived from
|
|
536
|
+
its steps (see AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS).
|
|
537
|
+
"""
|
|
538
|
+
if self.setup_container.autoregressive_step is not None:
|
|
539
|
+
raise Exception('tensorleap_autoregressive_step is already defined. '
|
|
540
|
+
'Only one autoregressive step hook is allowed per integration.')
|
|
541
|
+
if latent_space_aggregation not in AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS:
|
|
542
|
+
raise Exception(f'tensorleap_autoregressive_step: unknown latent_space_aggregation '
|
|
543
|
+
f'{latent_space_aggregation!r}. Supported values: '
|
|
544
|
+
f'{", ".join(AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS)}.')
|
|
545
|
+
self.setup_container.autoregressive_step = AutoregressiveStepHandler(
|
|
546
|
+
function, latent_space_aggregation=latent_space_aggregation)
|
|
547
|
+
|
|
548
|
+
# Builtin chain metadata, declared at parse time so it survives the reporter's
|
|
549
|
+
# metadata type mapping; the placeholder values are overwritten by the engine when a
|
|
550
|
+
# chain finalizes (realized length, truncated-by-safety-cap flag).
|
|
551
|
+
def builtin_chain_metadata(idx: Any, preprocess: PreprocessResponse) -> Dict[str, Any]:
|
|
552
|
+
return {'length': -1, 'truncated': False}
|
|
553
|
+
|
|
554
|
+
self.set_metadata(builtin_chain_metadata, 'builtin_chain')
|
|
555
|
+
|
|
527
556
|
def set_custom_layer(self, custom_layer: Type[Any], name: str, inspect_layer: bool = False,
|
|
528
557
|
kernel_index: Optional[int] = None, use_custom_latent_space: bool = False) -> None:
|
|
529
558
|
"""
|
|
@@ -606,15 +635,6 @@ class LeapBinder:
|
|
|
606
635
|
if state_enum in preprocess_result_dict:
|
|
607
636
|
preprocess_result_dict_in_correct_order[state_enum] = preprocess_result_dict[state_enum]
|
|
608
637
|
|
|
609
|
-
# All-or-none: every state must agree on grouped (nested sample_ids) vs flat.
|
|
610
|
-
if len({r.is_grouped for r in preprocess_result_dict_in_correct_order.values()}) > 1:
|
|
611
|
-
shapes = ', '.join(
|
|
612
|
-
f"{state.name}={'grouped' if r.is_grouped else 'flat'}"
|
|
613
|
-
for state, r in preprocess_result_dict_in_correct_order.items())
|
|
614
|
-
raise Exception(
|
|
615
|
-
"All preprocess states must be either all grouped (nested sample_ids) or all flat, "
|
|
616
|
-
f"but got a mix: {shapes}.")
|
|
617
|
-
|
|
618
638
|
return preprocess_result_dict_in_correct_order
|
|
619
639
|
|
|
620
640
|
def get_preprocess_unlabeled_result(self) -> Optional[PreprocessResponse]:
|
|
@@ -635,13 +655,7 @@ class LeapBinder:
|
|
|
635
655
|
preprocess_response: PreprocessResponse, test_result: List[DatasetTestResultPayload],
|
|
636
656
|
dataset_base_handler: Union[DatasetBaseHandler, MetadataHandler], state: DataStateEnum) -> List[DatasetTestResultPayload]:
|
|
637
657
|
assert preprocess_response.sample_ids is not None
|
|
638
|
-
|
|
639
|
-
# Grouped: probe with the first group, then reduce to a single sample's result so the
|
|
640
|
-
# recorded shape/type is per-sample (matching the flat case), not the (B, *) batch.
|
|
641
|
-
group = preprocess_response.sample_ids[0]
|
|
642
|
-
raw_result = dataset_base_handler.function(group, preprocess_response)[0]
|
|
643
|
-
else:
|
|
644
|
-
raw_result = dataset_base_handler.function(preprocess_response.sample_ids[0], preprocess_response)
|
|
658
|
+
raw_result = dataset_base_handler.function(preprocess_response.sample_ids[0], preprocess_response)
|
|
645
659
|
handler_type = 'metadata' if isinstance(dataset_base_handler, MetadataHandler) else None
|
|
646
660
|
if isinstance(dataset_base_handler, MetadataHandler):
|
|
647
661
|
if isinstance(raw_result, dict):
|
|
@@ -749,6 +763,7 @@ class LeapBinder:
|
|
|
749
763
|
)
|
|
750
764
|
|
|
751
765
|
def check(self) -> None:
|
|
766
|
+
self.validate_autoregressive_setup()
|
|
752
767
|
preprocess_result = self.get_preprocess_result()
|
|
753
768
|
self.check_preprocess(preprocess_result)
|
|
754
769
|
self.check_handlers(preprocess_result)
|
|
@@ -756,6 +771,43 @@ class LeapBinder:
|
|
|
756
771
|
self.validate_ignore_latent_spaces()
|
|
757
772
|
print("Successful!")
|
|
758
773
|
|
|
774
|
+
def validate_autoregressive_setup(self) -> None:
|
|
775
|
+
"""Structural rules for autoregressive integrations (no-op when the hook is absent).
|
|
776
|
+
|
|
777
|
+
Order-independent: runs after the whole script registered, because decorators may
|
|
778
|
+
appear in any order in the integration file.
|
|
779
|
+
"""
|
|
780
|
+
if self.setup_container.autoregressive_step is None:
|
|
781
|
+
return
|
|
782
|
+
if self.setup_container.inputs:
|
|
783
|
+
raise Exception(
|
|
784
|
+
"An autoregressive integration must not declare input encoders — "
|
|
785
|
+
"tensorleap_autoregressive_step is the sole source of model inputs "
|
|
786
|
+
"(its first call returns the initial inputs). Found input encoders: "
|
|
787
|
+
f"{[handler.name for handler in self.setup_container.inputs]}.")
|
|
788
|
+
if self.setup_container.simulations:
|
|
789
|
+
raise Exception(
|
|
790
|
+
"Simulations are not supported together with tensorleap_autoregressive_step yet: "
|
|
791
|
+
"the autoregressive entry points do not resolve synthetic sample ids. Remove the "
|
|
792
|
+
"simulation decorators or the autoregressive hook.")
|
|
793
|
+
if self.setup_container.instance_masks:
|
|
794
|
+
raise Exception(
|
|
795
|
+
"Element instances are not supported together with tensorleap_autoregressive_step: "
|
|
796
|
+
"instance generation masks the sample's encoded inputs, which do not exist in an "
|
|
797
|
+
"autoregressive integration. Remove the instance encoders or the autoregressive hook.")
|
|
798
|
+
if self.setup_container.custom_latent_space is not None:
|
|
799
|
+
raise Exception(
|
|
800
|
+
"tensorleap_custom_latent_space is not supported together with "
|
|
801
|
+
"tensorleap_autoregressive_step: the custom latent space function only sees "
|
|
802
|
+
"(sample_id, preprocess) — it runs before generation and cannot observe the chain. "
|
|
803
|
+
"The chain's latent is the final step's model latent.")
|
|
804
|
+
if not self.setup_container.prediction_types:
|
|
805
|
+
raise Exception(
|
|
806
|
+
"An autoregressive integration must declare prediction types "
|
|
807
|
+
"(tensorleap_load_model(prediction_types=[...])): the engine keys each step's "
|
|
808
|
+
"model outputs by the declared names to build the hook's prev_outputs dict. "
|
|
809
|
+
"Without them, prev_outputs would be empty at runtime.")
|
|
810
|
+
|
|
759
811
|
def validate_ignore_latent_spaces(self) -> None:
|
|
760
812
|
"""Validate leap_analysis_configuration.ignore_latent_spaces against this binder.
|
|
761
813
|
|