code-loader 1.0.191__tar.gz → 1.0.192__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 → code_loader-1.0.192}/PKG-INFO +3 -3
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/contract/datasetclasses.py +28 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/inner_leap_binder/leapbinder.py +68 -1
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/inner_leap_binder/leapbinder_decorators.py +250 -3
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/leaploader.py +165 -1
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/leaploaderbase.py +5 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/pyproject.toml +1 -1
- {code_loader-1.0.191 → code_loader-1.0.192}/LICENSE +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/README.md +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/__init__.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/contract/__init__.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/contract/enums.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/contract/exceptions.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/contract/mapping.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/contract/responsedataclasses.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/contract/sim_config.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/contract/visualizer_classes.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/default_losses.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/default_metrics.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/__init__.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/api.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/cli_config_utils.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/client.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/epoch.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/experiment.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/experiment_context.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/types.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/utils.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/inner_leap_binder/__init__.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/mixpanel_tracker.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/plot_functions/__init__.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/plot_functions/plot_functions.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/plot_functions/visualize.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/utils.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/visualizers/__init__.py +0 -0
- {code_loader-1.0.191 → code_loader-1.0.192}/code_loader/visualizers/default_visualizers.py +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: code-loader
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.192
|
|
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
|
|
|
@@ -92,6 +92,16 @@ SectionCallableInterface = Callable[[Union[int, str], PreprocessResponse], npt.N
|
|
|
92
92
|
InstanceCallableInterface = Callable[[Union[int, str], PreprocessResponse, int], Optional[ElementInstance]]
|
|
93
93
|
InstanceLengthCallableInterface = Callable[[Union[int, str], PreprocessResponse], int]
|
|
94
94
|
|
|
95
|
+
# (sample_id, prev_inputs, prev_outputs, preprocess) -> next model inputs, or None to end the chain.
|
|
96
|
+
# First call per chain receives prev_inputs=None, prev_outputs=None and returns the initial inputs.
|
|
97
|
+
# Keys starting with '_' are passthrough state: never fed to the model, handed back verbatim in
|
|
98
|
+
# prev_inputs on the next call. The hook must be stateless and deterministically seeded (the engine
|
|
99
|
+
# may replay a step after crash recovery and relies on identical results).
|
|
100
|
+
AutoregressiveStepCallableInterface = Callable[
|
|
101
|
+
[Union[int, str], Optional[Dict[str, npt.NDArray[np.float32]]], Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
102
|
+
PreprocessResponse],
|
|
103
|
+
Optional[Dict[str, npt.NDArray[np.float32]]]]
|
|
104
|
+
|
|
95
105
|
MetadataSectionCallableInterface = Union[
|
|
96
106
|
Callable[[Union[int, str], PreprocessResponse], int],
|
|
97
107
|
Callable[[Union[int, str], PreprocessResponse], Dict[str, int]],
|
|
@@ -239,6 +249,23 @@ class CustomLatentSpaceHandler:
|
|
|
239
249
|
function: SectionCallableInterface
|
|
240
250
|
name: str = 'custom_latent_space'
|
|
241
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
|
+
|
|
242
269
|
@dataclass
|
|
243
270
|
class PredictionTypeHandler:
|
|
244
271
|
name: str
|
|
@@ -279,6 +306,7 @@ class DatasetIntegrationSetup:
|
|
|
279
306
|
custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
|
|
280
307
|
custom_latent_space: Optional[CustomLatentSpaceHandler] = None
|
|
281
308
|
simulations: List[SimulationHandler] = field(default_factory=list)
|
|
309
|
+
autoregressive_step: Optional[AutoregressiveStepHandler] = None
|
|
282
310
|
|
|
283
311
|
|
|
284
312
|
@dataclass
|
|
@@ -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
|
"""
|
|
@@ -734,6 +763,7 @@ class LeapBinder:
|
|
|
734
763
|
)
|
|
735
764
|
|
|
736
765
|
def check(self) -> None:
|
|
766
|
+
self.validate_autoregressive_setup()
|
|
737
767
|
preprocess_result = self.get_preprocess_result()
|
|
738
768
|
self.check_preprocess(preprocess_result)
|
|
739
769
|
self.check_handlers(preprocess_result)
|
|
@@ -741,6 +771,43 @@ class LeapBinder:
|
|
|
741
771
|
self.validate_ignore_latent_spaces()
|
|
742
772
|
print("Successful!")
|
|
743
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
|
+
|
|
744
811
|
def validate_ignore_latent_spaces(self) -> None:
|
|
745
812
|
"""Validate leap_analysis_configuration.ignore_latent_spaces against this binder.
|
|
746
813
|
|
{code_loader-1.0.191 → code_loader-1.0.192}/code_loader/inner_leap_binder/leapbinder_decorators.py
RENAMED
|
@@ -21,7 +21,7 @@ from code_loader.contract.datasetclasses import CustomCallableInterfaceMultiArgs
|
|
|
21
21
|
CustomMultipleReturnCallableInterfaceMultiArgs, ConfusionMatrixCallableInterfaceMultiArgs, CustomCallableInterface, \
|
|
22
22
|
VisualizerCallableInterface, MetadataSectionCallableInterface, PreprocessResponse, SectionCallableInterface, \
|
|
23
23
|
ConfusionMatrixElement, SamplePreprocessResponse, PredictionTypeHandler, InstanceCallableInterface, ElementInstance, \
|
|
24
|
-
InstanceLengthCallableInterface
|
|
24
|
+
InstanceLengthCallableInterface, AutoregressiveStepCallableInterface
|
|
25
25
|
from code_loader.contract.enums import MetricDirection, LeapDataType, DatasetMetadataType, DataStateType
|
|
26
26
|
from code_loader import leap_binder, LeapLoader
|
|
27
27
|
from code_loader.contract.mapping import NodeMapping, NodeMappingType, NodeConnection
|
|
@@ -1638,6 +1638,246 @@ def tensorleap_custom_latent_space():
|
|
|
1638
1638
|
return decorating_function
|
|
1639
1639
|
|
|
1640
1640
|
|
|
1641
|
+
def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
|
|
1642
|
+
"""The feedback hook that drives an autoregressive chain.
|
|
1643
|
+
|
|
1644
|
+
Signature of the decorated function:
|
|
1645
|
+
(sample_id, prev_inputs, prev_outputs, preprocess) -> Optional[Dict[str, np.ndarray]]
|
|
1646
|
+
|
|
1647
|
+
First call per chain: prev_inputs=None, prev_outputs=None — returns the initial model inputs.
|
|
1648
|
+
Later calls receive the previous step's model inputs and outputs (unbatched, per-sample) and
|
|
1649
|
+
return the next step's full input dict; returning None ends the chain. Keys starting with '_'
|
|
1650
|
+
are passthrough state: never fed to the model, handed back verbatim in prev_inputs next call.
|
|
1651
|
+
The hook must be stateless and deterministically seeded (seed stochasticity from sample_id) —
|
|
1652
|
+
the engine replays steps after crash recovery and relies on identical results.
|
|
1653
|
+
|
|
1654
|
+
latent_space_aggregation ('last_step' | 'mean') declares how the chain's latent-space
|
|
1655
|
+
vectors are derived from its per-step forward passes. 'last_step' (default): every latent
|
|
1656
|
+
space comes from the final step's forward pass, except input-kind latent spaces which come
|
|
1657
|
+
from the first step — the original sample, before generated content dominates the model
|
|
1658
|
+
inputs. 'mean': every latent space is the elementwise mean over all steps of the chain.
|
|
1659
|
+
"""
|
|
1660
|
+
assert isinstance(latent_space_aggregation, str), \
|
|
1661
|
+
('tensorleap_autoregressive_step must be called with parentheses: '
|
|
1662
|
+
'@tensorleap_autoregressive_step() or '
|
|
1663
|
+
"@tensorleap_autoregressive_step(latent_space_aggregation='mean').")
|
|
1664
|
+
|
|
1665
|
+
def decorating_function(user_function: AutoregressiveStepCallableInterface):
|
|
1666
|
+
# Contract: shapes and key set are fixed across all calls AND all chains (compiled graphs
|
|
1667
|
+
# need static shapes), so one record shared across samples is the correct scope.
|
|
1668
|
+
first_call_record: Dict[str, Any] = {'keys': None, 'shapes': None}
|
|
1669
|
+
|
|
1670
|
+
def _normalize_args(args, kwargs):
|
|
1671
|
+
expected_names = ["sample_id", "prev_inputs", "prev_outputs", "preprocess"]
|
|
1672
|
+
validate_args_structure(
|
|
1673
|
+
*args,
|
|
1674
|
+
types_order=[Union[int, str], Union[dict, type(None)], Union[dict, type(None)],
|
|
1675
|
+
PreprocessResponse],
|
|
1676
|
+
func_name=user_function.__name__, expected_names=expected_names, **kwargs)
|
|
1677
|
+
normalized = []
|
|
1678
|
+
for i, name in enumerate(expected_names):
|
|
1679
|
+
normalized.append(args[i] if i < len(args) else kwargs[name])
|
|
1680
|
+
return normalized
|
|
1681
|
+
|
|
1682
|
+
def _validate_input_args(sample_id, prev_inputs, prev_outputs, preprocess_response):
|
|
1683
|
+
assert (prev_inputs is None) == (prev_outputs is None), \
|
|
1684
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1685
|
+
f'prev_inputs and prev_outputs must both be None (first call) or both be dicts '
|
|
1686
|
+
f'(subsequent calls). Got prev_inputs={type(prev_inputs).__name__}, '
|
|
1687
|
+
f'prev_outputs={type(prev_outputs).__name__}.')
|
|
1688
|
+
assert type(sample_id) == preprocess_response.sample_id_type, \
|
|
1689
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1690
|
+
f'Argument sample_id should be as the same type as defined in the preprocess response '
|
|
1691
|
+
f'{preprocess_response.sample_id_type}. Got {type(sample_id)}.')
|
|
1692
|
+
|
|
1693
|
+
def _validate_result(result, is_first_call):
|
|
1694
|
+
if result is None:
|
|
1695
|
+
assert not is_first_call, \
|
|
1696
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1697
|
+
f'the first call (prev_inputs=None, prev_outputs=None) must return the initial '
|
|
1698
|
+
f'model inputs dict — returning None on the first call would produce an empty chain.')
|
|
1699
|
+
return
|
|
1700
|
+
assert isinstance(result, dict), \
|
|
1701
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1702
|
+
f'the return value must be a dict of named model input tensors, or None to end the '
|
|
1703
|
+
f'chain. Got {type(result)}.')
|
|
1704
|
+
model_input_keys = [key for key in result if not key.startswith('_')]
|
|
1705
|
+
assert model_input_keys, \
|
|
1706
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1707
|
+
f'the returned dict contains no model inputs — every key starts with "_" '
|
|
1708
|
+
f'(underscore keys are passthrough state, never fed to the model).')
|
|
1709
|
+
for key, value in result.items():
|
|
1710
|
+
assert isinstance(key, str), \
|
|
1711
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1712
|
+
f'all keys in the returned dict must be strings. Got {type(key)}.')
|
|
1713
|
+
if key.startswith('_'):
|
|
1714
|
+
continue
|
|
1715
|
+
assert isinstance(value, np.ndarray), \
|
|
1716
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1717
|
+
f'value for model input "{key}" must be a numpy array. Got {type(value)}.')
|
|
1718
|
+
|
|
1719
|
+
def _validate_consistency(result):
|
|
1720
|
+
# Rules 2-3 of the autoregressive contract: identical key set and identical
|
|
1721
|
+
# (non-passthrough) shapes on every call — compiled graphs need static shapes.
|
|
1722
|
+
if result is None:
|
|
1723
|
+
return
|
|
1724
|
+
keys = set(result.keys())
|
|
1725
|
+
shapes = {key: tuple(value.shape) for key, value in result.items()
|
|
1726
|
+
if not key.startswith('_') and isinstance(value, np.ndarray)}
|
|
1727
|
+
if first_call_record['keys'] is None:
|
|
1728
|
+
first_call_record['keys'] = keys
|
|
1729
|
+
first_call_record['shapes'] = shapes
|
|
1730
|
+
return
|
|
1731
|
+
assert keys == first_call_record['keys'], \
|
|
1732
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1733
|
+
f'every call must return the same key set as the first call. '
|
|
1734
|
+
f'First call keys: {sorted(first_call_record["keys"])}, this call: {sorted(keys)}.')
|
|
1735
|
+
for key, shape in shapes.items():
|
|
1736
|
+
assert shape == first_call_record['shapes'][key], \
|
|
1737
|
+
(f'{user_function.__name__}() validation failed: '
|
|
1738
|
+
f'tensor shapes must be identical on every call (fixed-length padding + mask for '
|
|
1739
|
+
f'growing sequences). Input "{key}" was {first_call_record["shapes"][key]} on the '
|
|
1740
|
+
f'first call and {shape} now.')
|
|
1741
|
+
|
|
1742
|
+
def _assert_deterministic(first_result, second_result):
|
|
1743
|
+
error_message = (
|
|
1744
|
+
f'{user_function.__name__}() validation failed: '
|
|
1745
|
+
f'the hook is not deterministic — two calls with identical arguments returned different '
|
|
1746
|
+
f'outputs. Seed any stochasticity (e.g. initial noise) from sample_id: the engine '
|
|
1747
|
+
f'replays steps after crash recovery and relies on identical results.')
|
|
1748
|
+
if first_result is None or second_result is None:
|
|
1749
|
+
assert first_result is None and second_result is None, error_message
|
|
1750
|
+
return
|
|
1751
|
+
assert set(first_result.keys()) == set(second_result.keys()), error_message
|
|
1752
|
+
for key, value in first_result.items():
|
|
1753
|
+
if key.startswith('_') or not isinstance(value, np.ndarray):
|
|
1754
|
+
continue
|
|
1755
|
+
assert np.array_equal(value, second_result[key]), error_message
|
|
1756
|
+
|
|
1757
|
+
def _squeeze_test_batch_dim(tensors_dict):
|
|
1758
|
+
# Inside the integration test the model works on batch-1 tensors (this wrapper adds the
|
|
1759
|
+
# batch axis to returned inputs, and model outputs come back batched). The user's hook
|
|
1760
|
+
# body must see the same unbatched per-sample tensors it will see at engine runtime, so
|
|
1761
|
+
# strip the leading batch-1 axis before calling it.
|
|
1762
|
+
if tensors_dict is None:
|
|
1763
|
+
return None
|
|
1764
|
+
squeezed = {}
|
|
1765
|
+
for key, value in tensors_dict.items():
|
|
1766
|
+
if not key.startswith('_') and isinstance(value, np.ndarray) \
|
|
1767
|
+
and value.ndim > 0 and value.shape[0] == 1:
|
|
1768
|
+
squeezed[key] = value[0]
|
|
1769
|
+
else:
|
|
1770
|
+
squeezed[key] = value
|
|
1771
|
+
return squeezed
|
|
1772
|
+
|
|
1773
|
+
def inner_without_validate(sample_id, prev_inputs, prev_outputs, preprocess_response):
|
|
1774
|
+
global _called_from_inside_tl_decorator
|
|
1775
|
+
_called_from_inside_tl_decorator += 1
|
|
1776
|
+
try:
|
|
1777
|
+
result = user_function(sample_id, prev_inputs, prev_outputs, preprocess_response)
|
|
1778
|
+
finally:
|
|
1779
|
+
_called_from_inside_tl_decorator -= 1
|
|
1780
|
+
return result
|
|
1781
|
+
|
|
1782
|
+
leap_binder.set_autoregressive_step(inner_without_validate,
|
|
1783
|
+
latent_space_aggregation=latent_space_aggregation)
|
|
1784
|
+
|
|
1785
|
+
def inner(*args, **kwargs):
|
|
1786
|
+
if not _call_from_tl_platform:
|
|
1787
|
+
set_current('tensorleap_autoregressive_step')
|
|
1788
|
+
sample_id, prev_inputs, prev_outputs, preprocess_response = _normalize_args(args, kwargs)
|
|
1789
|
+
|
|
1790
|
+
is_top_level_in_test = (_called_from_inside_tl_decorator == 0
|
|
1791
|
+
and _called_from_inside_tl_integration_test_decorator)
|
|
1792
|
+
if is_top_level_in_test:
|
|
1793
|
+
prev_inputs = _squeeze_test_batch_dim(prev_inputs)
|
|
1794
|
+
prev_outputs = _squeeze_test_batch_dim(prev_outputs)
|
|
1795
|
+
|
|
1796
|
+
_validate_input_args(sample_id, prev_inputs, prev_outputs, preprocess_response)
|
|
1797
|
+
|
|
1798
|
+
result = inner_without_validate(sample_id, prev_inputs, prev_outputs, preprocess_response)
|
|
1799
|
+
|
|
1800
|
+
if is_top_level_in_test:
|
|
1801
|
+
# Rule 7: determinism double-call. An unseeded sampler fails here, on the user's
|
|
1802
|
+
# machine, instead of silently forking a chain on engine crash-replay.
|
|
1803
|
+
second_result = inner_without_validate(sample_id, prev_inputs, prev_outputs,
|
|
1804
|
+
preprocess_response)
|
|
1805
|
+
_assert_deterministic(result, second_result)
|
|
1806
|
+
|
|
1807
|
+
_validate_result(result, is_first_call=prev_inputs is None)
|
|
1808
|
+
_validate_consistency(result)
|
|
1809
|
+
|
|
1810
|
+
if is_top_level_in_test and result is not None:
|
|
1811
|
+
result = {key: (np.expand_dims(value, axis=0)
|
|
1812
|
+
if not key.startswith('_') and isinstance(value, np.ndarray) else value)
|
|
1813
|
+
for key, value in result.items()}
|
|
1814
|
+
|
|
1815
|
+
if not _call_from_tl_platform:
|
|
1816
|
+
update_env_params_func("tensorleap_autoregressive_step", "v")
|
|
1817
|
+
return result
|
|
1818
|
+
|
|
1819
|
+
class _MappingInputsPlaceholder:
|
|
1820
|
+
"""Mapping-mode stand-in for the hook's returned dict.
|
|
1821
|
+
|
|
1822
|
+
Indexing it by a model-input name yields a placeholder whose node_mapping the model
|
|
1823
|
+
placeholder tags Input0/Input1/... — exactly how input encoders wire the graph. One
|
|
1824
|
+
NodeConnection is recorded per non-passthrough key, once.
|
|
1825
|
+
"""
|
|
1826
|
+
def __init__(self):
|
|
1827
|
+
self._items: Dict[str, Any] = {}
|
|
1828
|
+
|
|
1829
|
+
def __getitem__(self, key):
|
|
1830
|
+
assert isinstance(key, str), \
|
|
1831
|
+
f'Expected a string model input name, got {type(key)} instead.'
|
|
1832
|
+
if key not in self._items:
|
|
1833
|
+
class TempMapping:
|
|
1834
|
+
pass
|
|
1835
|
+
ret = TempMapping()
|
|
1836
|
+
ret.node_mapping = NodeMapping(key, NodeMappingType.Input)
|
|
1837
|
+
if not key.startswith('_'):
|
|
1838
|
+
leap_binder.mapping_connections.append(NodeConnection(ret.node_mapping, None))
|
|
1839
|
+
self._items[key] = ret
|
|
1840
|
+
return self._items[key]
|
|
1841
|
+
|
|
1842
|
+
def get(self, key, default=None):
|
|
1843
|
+
# Deliberate alias of [] — the fixed-key-set contract makes a defaulted lookup
|
|
1844
|
+
# meaningless in mapping mode, and every accessed key must wire into the graph.
|
|
1845
|
+
return self[key]
|
|
1846
|
+
|
|
1847
|
+
def _unsupported_iteration(self):
|
|
1848
|
+
raise Exception(
|
|
1849
|
+
'Iterating over the autoregressive hook result is not supported inside the '
|
|
1850
|
+
'integration test. Pass the model its inputs with explicit keys, e.g. '
|
|
1851
|
+
"model.run(None, {'x': inputs['x'], 't': inputs['t']}).")
|
|
1852
|
+
|
|
1853
|
+
def items(self):
|
|
1854
|
+
self._unsupported_iteration()
|
|
1855
|
+
|
|
1856
|
+
def keys(self):
|
|
1857
|
+
self._unsupported_iteration()
|
|
1858
|
+
|
|
1859
|
+
def values(self):
|
|
1860
|
+
self._unsupported_iteration()
|
|
1861
|
+
|
|
1862
|
+
mapping_placeholder = _MappingInputsPlaceholder()
|
|
1863
|
+
|
|
1864
|
+
def mapping_inner(*args, **kwargs):
|
|
1865
|
+
return mapping_placeholder
|
|
1866
|
+
|
|
1867
|
+
def final_inner(*args, **kwargs):
|
|
1868
|
+
if os.environ.get(mapping_runtime_mode_env_var_mame):
|
|
1869
|
+
return mapping_inner(*args, **kwargs)
|
|
1870
|
+
else:
|
|
1871
|
+
return inner(*args, **kwargs)
|
|
1872
|
+
|
|
1873
|
+
if not _call_from_tl_platform:
|
|
1874
|
+
register_decorator_row("tensorleap_autoregressive_step")
|
|
1875
|
+
|
|
1876
|
+
return final_inner
|
|
1877
|
+
|
|
1878
|
+
return decorating_function
|
|
1879
|
+
|
|
1880
|
+
|
|
1641
1881
|
def tensorleap_preprocess():
|
|
1642
1882
|
def decorating_function(user_function: Callable[[], List[PreprocessResponse]]):
|
|
1643
1883
|
leap_binder.set_preprocess(user_function)
|
|
@@ -2472,6 +2712,12 @@ def tensorleap_status_table():
|
|
|
2472
2712
|
table.append({"name": "tensorleap simulation (optional)", "Added to integration": UNKNOWN})
|
|
2473
2713
|
_sim_tracking[sim_name] = "registered"
|
|
2474
2714
|
|
|
2715
|
+
def register_decorator_row(name: str):
|
|
2716
|
+
# Rows for decorators that only apply to some integration kinds (e.g. the autoregressive
|
|
2717
|
+
# step hook) are appended on registration, so unrelated integrations never show them.
|
|
2718
|
+
if _find_row(_remove_suffix(name, " (optional)")) is None:
|
|
2719
|
+
table.append({"name": name, "Added to integration": UNKNOWN})
|
|
2720
|
+
|
|
2475
2721
|
def mark_sim_result(sim_name: str, passed: bool):
|
|
2476
2722
|
_sim_tracking[sim_name] = "passed" if passed else "failed"
|
|
2477
2723
|
|
|
@@ -2522,11 +2768,12 @@ def tensorleap_status_table():
|
|
|
2522
2768
|
atexit.register(run_on_exit)
|
|
2523
2769
|
sys.excepthook = handle_exception
|
|
2524
2770
|
|
|
2525
|
-
return set_current, update_env_params, register_sim, mark_sim_result
|
|
2771
|
+
return set_current, update_env_params, register_sim, mark_sim_result, register_decorator_row
|
|
2526
2772
|
|
|
2527
2773
|
|
|
2528
2774
|
if not _call_from_tl_platform:
|
|
2529
|
-
set_current, update_env_params_func, register_sim, mark_sim_result =
|
|
2775
|
+
set_current, update_env_params_func, register_sim, mark_sim_result, register_decorator_row = \
|
|
2776
|
+
tensorleap_status_table()
|
|
2530
2777
|
|
|
2531
2778
|
|
|
2532
2779
|
|
|
@@ -322,12 +322,16 @@ class LeapLoader(LeapLoaderBase):
|
|
|
322
322
|
|
|
323
323
|
integration_test_test_payload = self._check_integration_test_exists()
|
|
324
324
|
test_payloads.append(integration_test_test_payload)
|
|
325
|
+
global_leap_binder.validate_autoregressive_setup()
|
|
325
326
|
preprocess_test_payload = self._check_preprocess()
|
|
326
327
|
test_payloads.append(preprocess_test_payload)
|
|
327
328
|
handlers_test_payloads = self._check_handlers()
|
|
328
329
|
test_payloads.extend(handlers_test_payloads)
|
|
329
330
|
simulation_test_payloads = self._check_simulations()
|
|
330
331
|
test_payloads.extend(simulation_test_payloads)
|
|
332
|
+
autoregressive_test_payload = self._check_autoregressive_step()
|
|
333
|
+
if autoregressive_test_payload is not None:
|
|
334
|
+
test_payloads.append(autoregressive_test_payload)
|
|
331
335
|
is_valid = all([payload.is_passed for payload in test_payloads])
|
|
332
336
|
setup_response = self.get_dataset_setup_response(handlers_test_payloads)
|
|
333
337
|
|
|
@@ -464,6 +468,76 @@ class LeapLoader(LeapLoaderBase):
|
|
|
464
468
|
result_payloads.append(test_result)
|
|
465
469
|
return result_payloads
|
|
466
470
|
|
|
471
|
+
def _check_autoregressive_step(self) -> Optional[DatasetTestResultPayload]:
|
|
472
|
+
handler = global_leap_binder.setup_container.autoregressive_step
|
|
473
|
+
if handler is None:
|
|
474
|
+
return None
|
|
475
|
+
test_result = DatasetTestResultPayload(handler.name)
|
|
476
|
+
try:
|
|
477
|
+
preprocess_result = self._preprocess_result()
|
|
478
|
+
input_shapes: Dict[str, List[int]] = {}
|
|
479
|
+
for state, preprocess_response in preprocess_result.items():
|
|
480
|
+
if preprocess_response.sample_ids_to_instance_mappings:
|
|
481
|
+
raise Exception('Element instances are not supported together with '
|
|
482
|
+
'tensorleap_autoregressive_step.')
|
|
483
|
+
sample_id = preprocess_response.sample_ids[0]
|
|
484
|
+
first_result = handler.function(sample_id, None, None, preprocess_response)
|
|
485
|
+
second_result = handler.function(sample_id, None, None, preprocess_response)
|
|
486
|
+
if not isinstance(first_result, dict):
|
|
487
|
+
raise Exception('The autoregressive step hook must return a dict of initial model '
|
|
488
|
+
f'inputs on its first call, got {type(first_result)} '
|
|
489
|
+
f'(state: {state.name}).')
|
|
490
|
+
if not isinstance(second_result, dict) or \
|
|
491
|
+
set(second_result.keys()) != set(first_result.keys()):
|
|
492
|
+
raise Exception('The autoregressive step hook is not deterministic — two calls '
|
|
493
|
+
'with identical arguments returned different results '
|
|
494
|
+
f'(state: {state.name}). Seed any stochasticity from '
|
|
495
|
+
'sample_id: the engine replays steps after crash recovery '
|
|
496
|
+
'and relies on identical results.')
|
|
497
|
+
model_input_items = {key: value for key, value in first_result.items()
|
|
498
|
+
if not key.startswith('_')}
|
|
499
|
+
if not model_input_items:
|
|
500
|
+
raise Exception('The autoregressive step hook returned no model inputs on its first '
|
|
501
|
+
'call — every key starts with "_" (underscore keys are passthrough '
|
|
502
|
+
f'state, never fed to the model). State: {state.name}.')
|
|
503
|
+
for key, value in model_input_items.items():
|
|
504
|
+
if not isinstance(value, np.ndarray):
|
|
505
|
+
raise Exception(f'The autoregressive step hook returned a non-numpy value for '
|
|
506
|
+
f'model input "{key}" ({type(value)}). State: {state.name}.')
|
|
507
|
+
if key in input_shapes and input_shapes[key] != list(value.shape):
|
|
508
|
+
raise Exception(f'The autoregressive step hook returned different shapes for '
|
|
509
|
+
f'model input "{key}" across states: {input_shapes[key]} vs '
|
|
510
|
+
f'{list(value.shape)}. Shapes must be fixed.')
|
|
511
|
+
input_shapes[key] = list(value.shape)
|
|
512
|
+
if not np.array_equal(value, second_result[key]):
|
|
513
|
+
raise Exception('The autoregressive step hook is not deterministic — two calls '
|
|
514
|
+
'with identical arguments returned different outputs for model '
|
|
515
|
+
f'input "{key}". Seed any stochasticity from sample_id: the '
|
|
516
|
+
'engine replays steps after crash recovery and relies on '
|
|
517
|
+
'identical results.')
|
|
518
|
+
handler.input_shapes = input_shapes
|
|
519
|
+
except Exception as e:
|
|
520
|
+
line_number, file_name, stacktrace = get_root_exception_file_and_line_number()
|
|
521
|
+
test_result.display[TestingSectionEnum.Errors.name] = \
|
|
522
|
+
f"{repr(e)} in file {file_name}, line_number: {line_number}\nStacktrace:\n{stacktrace}"
|
|
523
|
+
test_result.is_passed = False
|
|
524
|
+
return test_result
|
|
525
|
+
|
|
526
|
+
def _ensure_autoregressive_input_shapes(self) -> Dict[str, List[int]]:
|
|
527
|
+
handler = global_leap_binder.setup_container.autoregressive_step
|
|
528
|
+
assert handler is not None
|
|
529
|
+
if handler.input_shapes is None:
|
|
530
|
+
preprocess_result = self._preprocess_result()
|
|
531
|
+
first_state_response = next(iter(preprocess_result.values()))
|
|
532
|
+
first_result = handler.function(first_state_response.sample_ids[0], None, None,
|
|
533
|
+
first_state_response)
|
|
534
|
+
if not isinstance(first_result, dict):
|
|
535
|
+
raise Exception('The autoregressive step hook must return a dict of initial model '
|
|
536
|
+
f'inputs on its first call, got {type(first_result)}.')
|
|
537
|
+
handler.input_shapes = {key: list(value.shape) for key, value in first_result.items()
|
|
538
|
+
if not key.startswith('_') and isinstance(value, np.ndarray)}
|
|
539
|
+
return handler.input_shapes
|
|
540
|
+
|
|
467
541
|
def run_simulation(self, sim_name, params=None, n_samples=1, seed=0,
|
|
468
542
|
sample_ids=None, extend_preprocess=True):
|
|
469
543
|
# type: (str, Optional[Dict[str, Any]], int, int, Optional[List[str]], bool) -> Dict[str, Any]
|
|
@@ -654,6 +728,14 @@ class LeapLoader(LeapLoaderBase):
|
|
|
654
728
|
raise Exception(f"cant calculate shape for input, input name:{inp.name}")
|
|
655
729
|
inputs.append(DatasetInputInstance(name=inp.name, shape=inp.shape, channel_dim=inp.channel_dim))
|
|
656
730
|
|
|
731
|
+
if setup.autoregressive_step is not None:
|
|
732
|
+
# The hook is the sole input source; report its step-0 inputs as the dataset inputs so
|
|
733
|
+
# graph building, input-coverage validation and type dictionaries see real specs.
|
|
734
|
+
# channel_dim=-1 is intentional for AR inputs — there is no InputHandler to carry a
|
|
735
|
+
# user-declared value; expose it on the decorator if a use case ever needs otherwise.
|
|
736
|
+
for input_name, input_shape in self._ensure_autoregressive_input_shapes().items():
|
|
737
|
+
inputs.append(DatasetInputInstance(name=input_name, shape=input_shape, channel_dim=-1))
|
|
738
|
+
|
|
657
739
|
ground_truths = []
|
|
658
740
|
for gt in setup.ground_truths:
|
|
659
741
|
if gt.shape is None:
|
|
@@ -761,7 +843,21 @@ class LeapLoader(LeapLoaderBase):
|
|
|
761
843
|
return result_agg
|
|
762
844
|
|
|
763
845
|
def _get_inputs(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
|
|
764
|
-
|
|
846
|
+
inputs = self._get_dataset_handlers(global_leap_binder.setup_container.inputs, state, sample_id)
|
|
847
|
+
autoregressive_handler = global_leap_binder.setup_container.autoregressive_step
|
|
848
|
+
if autoregressive_handler is not None:
|
|
849
|
+
# Autoregressive integrations have no input encoders — the hook's first call supplies
|
|
850
|
+
# the sample's (step-0) model inputs, so every sample-fetching flow (graph validation,
|
|
851
|
+
# analysis, import checks) sees a fully-populated inputs dict.
|
|
852
|
+
preprocess_response = self._preprocess_result()[state]
|
|
853
|
+
step_zero_inputs = autoregressive_handler.function(sample_id, None, None, preprocess_response)
|
|
854
|
+
if not isinstance(step_zero_inputs, dict):
|
|
855
|
+
raise Exception(
|
|
856
|
+
'The autoregressive step hook must return a dict of initial model inputs on its '
|
|
857
|
+
f'first call, got {type(step_zero_inputs)} for sample {sample_id}.')
|
|
858
|
+
inputs.update({key: value for key, value in step_zero_inputs.items()
|
|
859
|
+
if not key.startswith('_')})
|
|
860
|
+
return inputs
|
|
765
861
|
|
|
766
862
|
def _get_instances_masks(self, state: DataStateEnum, sample_id: Union[int, str], instance_id: int) -> Optional[Dict[str, ElementInstance]]:
|
|
767
863
|
if instance_id is None:
|
|
@@ -784,6 +880,12 @@ class LeapLoader(LeapLoaderBase):
|
|
|
784
880
|
def _get_gt(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
|
|
785
881
|
return self._get_dataset_handlers(global_leap_binder.setup_container.ground_truths, state, sample_id)
|
|
786
882
|
|
|
883
|
+
def get_gt(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
|
|
884
|
+
"""Ground-truth tensors only ({} when no GT encoders are declared) — lets the engine's
|
|
885
|
+
rollout fetch a chain's GT without re-running the autoregressive hook via get_sample."""
|
|
886
|
+
self.exec_script()
|
|
887
|
+
return self._get_gt(state, sample_id)
|
|
888
|
+
|
|
787
889
|
@lru_cache()
|
|
788
890
|
def _metadata_name_to_type(self) -> Dict[str, DatasetMetadataType]:
|
|
789
891
|
global_leap_binder.check_preprocess(self._preprocess_result())
|
|
@@ -879,6 +981,68 @@ class LeapLoader(LeapLoaderBase):
|
|
|
879
981
|
self.exec_script()
|
|
880
982
|
return global_leap_binder.setup_container.custom_latent_space is not None
|
|
881
983
|
|
|
984
|
+
@lru_cache()
|
|
985
|
+
def has_autoregressive_step(self) -> bool:
|
|
986
|
+
self.exec_script()
|
|
987
|
+
return global_leap_binder.setup_container.autoregressive_step is not None
|
|
988
|
+
|
|
989
|
+
@lru_cache()
|
|
990
|
+
def get_autoregressive_latent_space_aggregation(self) -> str:
|
|
991
|
+
self.exec_script()
|
|
992
|
+
handler = global_leap_binder.setup_container.autoregressive_step
|
|
993
|
+
if handler is None:
|
|
994
|
+
return 'last_step'
|
|
995
|
+
return handler.latent_space_aggregation
|
|
996
|
+
|
|
997
|
+
def run_autoregressive_step(self, sample_id: Union[int, str],
|
|
998
|
+
prev_inputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
999
|
+
prev_outputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
1000
|
+
state: DataStateEnum) -> Optional[Dict[str, npt.NDArray[np.float32]]]:
|
|
1001
|
+
"""Execute the user's autoregressive step hook against the in-process preprocess result.
|
|
1002
|
+
|
|
1003
|
+
This is the engine rollout's per-step entry point (invoked on the generic pod). The first
|
|
1004
|
+
call per chain passes prev_inputs=None, prev_outputs=None and returns the initial model
|
|
1005
|
+
inputs; a None return ends the chain.
|
|
1006
|
+
"""
|
|
1007
|
+
self.exec_script()
|
|
1008
|
+
handler = global_leap_binder.setup_container.autoregressive_step
|
|
1009
|
+
if handler is None:
|
|
1010
|
+
raise Exception('run_autoregressive_step was called but the integration has no '
|
|
1011
|
+
'tensorleap_autoregressive_step hook.')
|
|
1012
|
+
preprocess_response = self._preprocess_result()[state]
|
|
1013
|
+
result = handler.function(sample_id, prev_inputs, prev_outputs, preprocess_response)
|
|
1014
|
+
if result is None:
|
|
1015
|
+
return None
|
|
1016
|
+
if not isinstance(result, dict):
|
|
1017
|
+
raise Exception('The autoregressive step hook must return a dict of model inputs or '
|
|
1018
|
+
f'None to end the chain, got {type(result)} for sample {sample_id}.')
|
|
1019
|
+
# Parse-time validation only exercises step 0; enforce the fixed key-set/shape contract
|
|
1020
|
+
# on every step here so drift fails with an actionable message instead of a cryptic
|
|
1021
|
+
# engine error on a compiled graph.
|
|
1022
|
+
expected_shapes = self._ensure_autoregressive_input_shapes()
|
|
1023
|
+
model_inputs = {key: value for key, value in result.items() if not key.startswith('_')}
|
|
1024
|
+
if set(model_inputs) != set(expected_shapes):
|
|
1025
|
+
raise Exception('The autoregressive step hook must return the same model input keys '
|
|
1026
|
+
f'on every call. Expected {sorted(expected_shapes)}, got '
|
|
1027
|
+
f'{sorted(model_inputs)} for sample {sample_id}.')
|
|
1028
|
+
for key, value in model_inputs.items():
|
|
1029
|
+
if isinstance(value, np.ndarray) and list(value.shape) != expected_shapes[key]:
|
|
1030
|
+
raise Exception('The autoregressive step hook must return fixed tensor shapes on '
|
|
1031
|
+
f'every call (use fixed-length padding + mask for growing '
|
|
1032
|
+
f'sequences). Input "{key}" was {expected_shapes[key]} on the '
|
|
1033
|
+
f'first call and {list(value.shape)} now (sample {sample_id}).')
|
|
1034
|
+
return result
|
|
1035
|
+
|
|
1036
|
+
def get_prediction_names_in_order(self) -> List[str]:
|
|
1037
|
+
"""Model output names by declared prediction-type order, used to key prev_outputs for the
|
|
1038
|
+
hook. AR integrations must declare prediction types (validate_autoregressive_setup) and
|
|
1039
|
+
the count is checked against the model's outputs at integration-test time; the declared
|
|
1040
|
+
ORDER matching the model's output order is the user's responsibility — it is not
|
|
1041
|
+
validated."""
|
|
1042
|
+
self.exec_script()
|
|
1043
|
+
return [prediction_type.name for prediction_type in
|
|
1044
|
+
global_leap_binder.setup_container.prediction_types]
|
|
1045
|
+
|
|
882
1046
|
def get_instances_data(self, state: DataStateEnum) -> Tuple[Dict[str, List[str]], Dict[str, str]]:
|
|
883
1047
|
preprocess_result = self._preprocess_result()
|
|
884
1048
|
preprocess_state = preprocess_result.get(state)
|
|
@@ -149,6 +149,11 @@ class LeapLoaderBase:
|
|
|
149
149
|
def has_custom_latent_space_decorator(self) -> bool:
|
|
150
150
|
pass
|
|
151
151
|
|
|
152
|
+
def has_autoregressive_step(self) -> bool:
|
|
153
|
+
# Default False so existing LeapLoaderBase implementations stay valid; the concrete
|
|
154
|
+
# loaders that can answer (LeapLoader in-process, LeapLoaderWithRedis via RPC) override.
|
|
155
|
+
return False
|
|
156
|
+
|
|
152
157
|
@abstractmethod
|
|
153
158
|
def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
|
|
154
159
|
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/experiment_context.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.191 → code_loader-1.0.192}/code_loader/experiment_api/workingspace_config_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|