code-loader 1.0.201.dev0__tar.gz → 1.0.202.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.
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/PKG-INFO +4 -3
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/datasetclasses.py +8 -2
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/visualizer_classes.py +2 -2
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/inner_leap_binder/leapbinder.py +86 -8
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/inner_leap_binder/leapbinder_decorators.py +102 -19
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/leaploader.py +69 -28
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/leaploaderbase.py +13 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/visualizers/default_visualizers.py +4 -4
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/pyproject.toml +1 -1
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/LICENSE +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/README.md +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/__init__.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/__init__.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/enums.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/exceptions.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/mapping.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/responsedataclasses.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/sim_config.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/default_losses.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/default_metrics.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/__init__.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/api.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/cli_config_utils.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/client.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/epoch.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/experiment.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/experiment_context.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/types.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/utils.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/inner_leap_binder/__init__.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/mixpanel_tracker.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/plot_functions/__init__.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/plot_functions/plot_functions.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/plot_functions/visualize.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/utils.py +0 -0
- {code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/visualizers/__init__.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: code-loader
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.202.dev1
|
|
4
4
|
Summary:
|
|
5
|
-
Home-page: https://github.com/tensorleap/code-loader
|
|
6
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
7
|
Author: dorhar
|
|
8
8
|
Author-email: doron.harnoy@tensorleap.ai
|
|
9
9
|
Requires-Python: >=3.8,<3.13
|
|
@@ -20,6 +20,7 @@ Requires-Dist: numpy (>=2.3.2,<3.0.0) ; python_version >= "3.11" and python_vers
|
|
|
20
20
|
Requires-Dist: psutil (>=5.9.5,<6.0.0)
|
|
21
21
|
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
22
22
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
23
|
+
Project-URL: Homepage, https://github.com/tensorleap/code-loader
|
|
23
24
|
Project-URL: Repository, https://github.com/tensorleap/code-loader
|
|
24
25
|
Description-Content-Type: text/markdown
|
|
25
26
|
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/datasetclasses.py
RENAMED
|
@@ -309,6 +309,7 @@ class MetadataHandler:
|
|
|
309
309
|
class CustomLatentSpaceHandler:
|
|
310
310
|
function: SectionCallableInterface
|
|
311
311
|
name: str = 'custom_latent_space'
|
|
312
|
+
use_ls_for_analysis: bool = False
|
|
312
313
|
|
|
313
314
|
|
|
314
315
|
# How a chain's latent-space vectors are derived from its per-step forward passes.
|
|
@@ -331,6 +332,11 @@ class AutoregressiveStepHandler:
|
|
|
331
332
|
# time. Fills the role InputHandler.shape plays for input encoders.
|
|
332
333
|
input_shapes: Optional[Dict[str, List[int]]] = None
|
|
333
334
|
latent_space_aggregation: str = 'last_step'
|
|
335
|
+
# {model_input_name: model_output_name} for a KV-cache (or any threaded state): inputs whose
|
|
336
|
+
# value is the named output from the previous step. The platform threads these itself — they
|
|
337
|
+
# are never passed to the hook and never reported — so the hook must NOT return them. Empty
|
|
338
|
+
# when the model has no cache, in which case the chain re-feeds its full prompt each step.
|
|
339
|
+
cache_tensors: Optional[Dict[str, str]] = None
|
|
334
340
|
|
|
335
341
|
|
|
336
342
|
# Per-chain, unbatched callables: called once per finished chain with the final step's tensors —
|
|
@@ -391,7 +397,7 @@ class DatasetIntegrationSetup:
|
|
|
391
397
|
metrics: List[MetricHandler] = field(default_factory=list)
|
|
392
398
|
instance_metrics: List[InstanceMetricHandler] = field(default_factory=list)
|
|
393
399
|
custom_layers: Dict[str, CustomLayerHandler] = field(default_factory=dict)
|
|
394
|
-
|
|
400
|
+
custom_latent_spaces: Dict[str, CustomLatentSpaceHandler] = field(default_factory=dict)
|
|
395
401
|
simulations: List[SimulationHandler] = field(default_factory=list)
|
|
396
402
|
autoregressive_step: Optional[AutoregressiveStepHandler] = None
|
|
397
403
|
autoregressive_metrics: List[AutoregressiveMetricHandler] = field(default_factory=list)
|
|
@@ -407,6 +413,6 @@ class DatasetSample:
|
|
|
407
413
|
metadata_is_none: Dict[str, bool]
|
|
408
414
|
index: Union[int, str]
|
|
409
415
|
state: DataStateEnum
|
|
410
|
-
|
|
416
|
+
custom_latent_spaces: Optional[Dict[str, npt.NDArray[np.float32]]] = None
|
|
411
417
|
instance_masks: Optional[Dict[str, ElementInstance]] = None
|
|
412
418
|
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/visualizer_classes.py
RENAMED
|
@@ -353,7 +353,7 @@ class LeapAudio:
|
|
|
353
353
|
waveform (a LeapGraph). Build one with `default_audio_visualizer`.
|
|
354
354
|
|
|
355
355
|
Attributes:
|
|
356
|
-
audio (npt.NDArray): Mono waveform samples, shaped [N]. Multi-channel input
|
|
356
|
+
audio (npt.NDArray[np.float32]): Mono waveform samples, shaped [N]. Multi-channel input
|
|
357
357
|
should be mixed down before constructing LeapAudio.
|
|
358
358
|
sample_rate (int): Samples per second — needed to encode the clip and to map
|
|
359
359
|
the playhead position to time.
|
|
@@ -362,7 +362,7 @@ class LeapAudio:
|
|
|
362
362
|
driven by the audio element's currentTime, so no time range is needed here.
|
|
363
363
|
type (LeapDataType): The data type, default is LeapDataType.Audio.
|
|
364
364
|
"""
|
|
365
|
-
audio: npt.NDArray
|
|
365
|
+
audio: npt.NDArray[np.float32]
|
|
366
366
|
sample_rate: int
|
|
367
367
|
visual: Union[LeapImage, LeapGraph]
|
|
368
368
|
type: LeapDataType = LeapDataType.Audio
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/inner_leap_binder/leapbinder.py
RENAMED
|
@@ -534,23 +534,97 @@ class LeapBinder:
|
|
|
534
534
|
leap_binder.set_metadata(metadata_handler_index, name='metadata_index')
|
|
535
535
|
leap_binder.set_metadata(metadata_handler_image_mean, name='metadata_image_mean')
|
|
536
536
|
"""
|
|
537
|
+
if not isinstance(name, str):
|
|
538
|
+
raise TypeError(f"set_metadata validation failed.\n`name` must be a string, got type "
|
|
539
|
+
f"{type(name).__name__}. When using @tensorleap_metadata, the metadata "
|
|
540
|
+
f"types go in the second argument: "
|
|
541
|
+
f"@tensorleap_metadata('my_metadata', {{'key': DatasetMetadataType.int}}).")
|
|
537
542
|
self.setup_container.metadata.append(MetadataHandler(name, function, metadata_type))
|
|
538
543
|
|
|
539
|
-
def set_custom_latent_space(self, function: SectionCallableInterface
|
|
544
|
+
def set_custom_latent_space(self, function: SectionCallableInterface,
|
|
545
|
+
name: Optional[str] = None,
|
|
546
|
+
use_ls_for_analysis: bool = False) -> None:
|
|
540
547
|
"""
|
|
541
|
-
|
|
548
|
+
Register a custom latent space function.
|
|
549
|
+
|
|
550
|
+
Multiple custom latent spaces may be registered as long as each has a
|
|
551
|
+
unique name. They are stored name-keyed (insertion order preserved), so a
|
|
552
|
+
later registration no longer overrides an earlier one.
|
|
542
553
|
|
|
543
554
|
Args:
|
|
544
|
-
function (SectionCallableInterface): The
|
|
555
|
+
function (SectionCallableInterface): The latent-space handler function.
|
|
545
556
|
This function receives:
|
|
546
557
|
subset (PreprocessResponse): The subset of the data.
|
|
547
558
|
index (int): The index of the sample within the subset.
|
|
548
|
-
This function should numpy float32 array
|
|
559
|
+
This function should return a numpy float32 array containing the latent
|
|
560
|
+
space vec of the sample.
|
|
561
|
+
name (Optional[str]): Unique name for this custom latent space. Defaults to
|
|
562
|
+
the reserved single-LS name for backward compatibility.
|
|
563
|
+
use_ls_for_analysis (bool): When True, the engine uses this custom latent
|
|
564
|
+
space for the Out-Of-Distribution and Domain-Gap insights instead of the
|
|
565
|
+
built-in defaults. At most one registered custom latent space may set this;
|
|
566
|
+
registering a second one with the flag raises.
|
|
549
567
|
"""
|
|
550
|
-
|
|
568
|
+
if name is None:
|
|
569
|
+
name = custom_latent_space_attribute
|
|
570
|
+
if name in self.setup_container.custom_latent_spaces:
|
|
571
|
+
raise Exception(
|
|
572
|
+
f"A custom latent space named '{name}' is already registered. Each "
|
|
573
|
+
f"@tensorleap_custom_latent_space must have a unique name "
|
|
574
|
+
f"(pass name='...' to distinguish them)."
|
|
575
|
+
)
|
|
576
|
+
if use_ls_for_analysis:
|
|
577
|
+
already_flagged = [
|
|
578
|
+
existing_name
|
|
579
|
+
for existing_name, handler in self.setup_container.custom_latent_spaces.items()
|
|
580
|
+
if handler.use_ls_for_analysis
|
|
581
|
+
]
|
|
582
|
+
if already_flagged:
|
|
583
|
+
raise Exception(
|
|
584
|
+
f"use_ls_for_analysis=True is already set on custom latent space "
|
|
585
|
+
f"'{already_flagged[0]}'. Only one custom latent space may set "
|
|
586
|
+
f"use_ls_for_analysis=True (it selects the latent space used for the "
|
|
587
|
+
f"Out-Of-Distribution and Domain-Gap insights). Set it on '{name}' "
|
|
588
|
+
f"or '{already_flagged[0]}', not both."
|
|
589
|
+
)
|
|
590
|
+
self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(
|
|
591
|
+
function, name, use_ls_for_analysis)
|
|
592
|
+
|
|
593
|
+
@staticmethod
|
|
594
|
+
def _validated_cache_tensors(cache_tensors: Optional[Dict[str, str]]
|
|
595
|
+
) -> Optional[Dict[str, str]]:
|
|
596
|
+
"""Structural checks only — shape/dtype/existence are validated by the platform, which
|
|
597
|
+
is the side that has the model graph. This catches the typos that are cheap to catch
|
|
598
|
+
here, at integration-test time, instead of at push."""
|
|
599
|
+
if not cache_tensors:
|
|
600
|
+
return None
|
|
601
|
+
if not isinstance(cache_tensors, dict):
|
|
602
|
+
raise Exception(f'tensorleap_autoregressive_step: cache_tensors must be a dict of '
|
|
603
|
+
f'{{model_input_name: model_output_name}}, got '
|
|
604
|
+
f'{type(cache_tensors).__name__}.')
|
|
605
|
+
seen_outputs: Dict[str, str] = {}
|
|
606
|
+
for input_name, output_name in cache_tensors.items():
|
|
607
|
+
for role, name in (('key', input_name), ('value', output_name)):
|
|
608
|
+
if not isinstance(name, str) or not name:
|
|
609
|
+
raise Exception(
|
|
610
|
+
f'tensorleap_autoregressive_step: cache_tensors {role}s must be '
|
|
611
|
+
f'non-empty model tensor names, got {name!r}.')
|
|
612
|
+
if input_name == output_name:
|
|
613
|
+
raise Exception(
|
|
614
|
+
f'tensorleap_autoregressive_step: cache_tensors maps "{input_name}" to '
|
|
615
|
+
f'itself. A model input and output cannot share a name — the mapping is '
|
|
616
|
+
f'{{input_name: output_name}}.')
|
|
617
|
+
if output_name in seen_outputs:
|
|
618
|
+
raise Exception(
|
|
619
|
+
f'tensorleap_autoregressive_step: model output "{output_name}" is declared '
|
|
620
|
+
f'as the cache source for both "{seen_outputs[output_name]}" and '
|
|
621
|
+
f'"{input_name}". Each cache output feeds exactly one input.')
|
|
622
|
+
seen_outputs[output_name] = input_name
|
|
623
|
+
return dict(cache_tensors)
|
|
551
624
|
|
|
552
625
|
def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
|
|
553
|
-
latent_space_aggregation: str = 'last_step'
|
|
626
|
+
latent_space_aggregation: str = 'last_step',
|
|
627
|
+
cache_tensors: Optional[Dict[str, str]] = None) -> None:
|
|
554
628
|
"""
|
|
555
629
|
Set the autoregressive step hook — the feedback function that drives a chain:
|
|
556
630
|
it supplies the model's initial inputs on its first call (prev_inputs=None, prev_outputs=None)
|
|
@@ -558,6 +632,8 @@ class LeapBinder:
|
|
|
558
632
|
An autoregressive integration has no input encoders; the hook is the sole input source.
|
|
559
633
|
latent_space_aggregation declares how the chain's latent-space vectors are derived from
|
|
560
634
|
its steps (see AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS).
|
|
635
|
+
cache_tensors declares a KV-cache as {model_input_name: model_output_name}; the platform
|
|
636
|
+
threads those tensors itself and the hook neither receives nor returns them.
|
|
561
637
|
"""
|
|
562
638
|
if self.setup_container.autoregressive_step is not None:
|
|
563
639
|
raise Exception('tensorleap_autoregressive_step is already defined. '
|
|
@@ -566,8 +642,10 @@ class LeapBinder:
|
|
|
566
642
|
raise Exception(f'tensorleap_autoregressive_step: unknown latent_space_aggregation '
|
|
567
643
|
f'{latent_space_aggregation!r}. Supported values: '
|
|
568
644
|
f'{", ".join(AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS)}.')
|
|
645
|
+
cache_tensors = self._validated_cache_tensors(cache_tensors)
|
|
569
646
|
self.setup_container.autoregressive_step = AutoregressiveStepHandler(
|
|
570
|
-
function, latent_space_aggregation=latent_space_aggregation
|
|
647
|
+
function, latent_space_aggregation=latent_space_aggregation,
|
|
648
|
+
cache_tensors=cache_tensors)
|
|
571
649
|
|
|
572
650
|
# Builtin chain metadata, declared at parse time so it survives the reporter's
|
|
573
651
|
# metadata type mapping; the placeholder values are overwritten by the engine when a
|
|
@@ -958,7 +1036,7 @@ class LeapBinder:
|
|
|
958
1036
|
"Element instances are not supported together with tensorleap_autoregressive_step: "
|
|
959
1037
|
"instance generation masks the sample's encoded inputs, which do not exist in an "
|
|
960
1038
|
"autoregressive integration. Remove the instance encoders or the autoregressive hook.")
|
|
961
|
-
if self.setup_container.
|
|
1039
|
+
if self.setup_container.custom_latent_spaces:
|
|
962
1040
|
raise Exception(
|
|
963
1041
|
"tensorleap_custom_latent_space is not supported together with "
|
|
964
1042
|
"tensorleap_autoregressive_step: the custom latent space function only sees "
|
|
@@ -560,12 +560,22 @@ def tensorleap_load_model(prediction_types: Optional[List[PredictionTypeHandler]
|
|
|
560
560
|
return result
|
|
561
561
|
|
|
562
562
|
def validate_declared_prediction_types(self, ret):
|
|
563
|
-
|
|
564
|
-
|
|
563
|
+
n_outputs = len(ret) if isinstance(ret, list) else 1
|
|
564
|
+
# A declared KV-cache is plumbing: those outputs are fed straight back into
|
|
565
|
+
# the model and never reported, so they carry no prediction type. Count only
|
|
566
|
+
# the rest, or every cached model would demand 60 meaningless declarations.
|
|
567
|
+
step = leap_binder.setup_container.autoregressive_step
|
|
568
|
+
n_cache = len((step.cache_tensors if step else None) or {})
|
|
569
|
+
if len(self.prediction_types) != 0 and \
|
|
570
|
+
n_outputs - n_cache != len(self.prediction_types):
|
|
565
571
|
if not _call_from_tl_platform:
|
|
566
572
|
update_env_params_func("tensorleap_load_model", "x")
|
|
573
|
+
cache_note = (f' ({n_cache} of them are the declared KV-cache, which '
|
|
574
|
+
f'needs none)' if n_cache else '')
|
|
567
575
|
raise Exception(
|
|
568
|
-
f"tensorleap_load_model validation failed: number of declared
|
|
576
|
+
f"tensorleap_load_model validation failed: number of declared "
|
|
577
|
+
f"prediction types({len(prediction_types)}) != number of model "
|
|
578
|
+
f"outputs({n_outputs}){cache_note}")
|
|
569
579
|
|
|
570
580
|
def _convert_onnx_inputs_to_correct_type(
|
|
571
581
|
self, float_arrays_inputs: Dict[str, np.ndarray]
|
|
@@ -723,12 +733,22 @@ def tensorleap_load_model(prediction_types: Optional[List[PredictionTypeHandler]
|
|
|
723
733
|
assert isinstance(input_dict, dict), \
|
|
724
734
|
f'Expected input_dict to be a dict, got {type(input_dict)} instead.'
|
|
725
735
|
seen: set = set()
|
|
726
|
-
|
|
736
|
+
# A declared KV-cache is threaded by the platform, not wired from an input
|
|
737
|
+
# encoder, so those slots have no mapping node to assign — the loop feeds
|
|
738
|
+
# them real arrays even in mapping mode. Skip them, and count only the wired
|
|
739
|
+
# inputs so the remaining slot indices stay contiguous.
|
|
740
|
+
step_handler = leap_binder.setup_container.autoregressive_step
|
|
741
|
+
cache_tensors = (step_handler.cache_tensors if step_handler else None) or {}
|
|
742
|
+
slot = 0
|
|
743
|
+
for input_key, elem in input_dict.items():
|
|
744
|
+
if input_key in cache_tensors:
|
|
745
|
+
continue
|
|
727
746
|
self._reject_reused_input_source(elem, seen)
|
|
728
747
|
if isinstance(input_key, NodeMappingType):
|
|
729
748
|
elem.node_mapping.type = input_key
|
|
730
749
|
else:
|
|
731
|
-
elem.node_mapping.type = _safe_get_item(
|
|
750
|
+
elem.node_mapping.type = _safe_get_item(slot)
|
|
751
|
+
slot += 1
|
|
732
752
|
|
|
733
753
|
return ModelOutputPlaceholder()
|
|
734
754
|
|
|
@@ -1703,8 +1723,14 @@ def tensorleap_metadata(
|
|
|
1703
1723
|
return decorating_function
|
|
1704
1724
|
|
|
1705
1725
|
|
|
1706
|
-
def tensorleap_custom_latent_space():
|
|
1726
|
+
def tensorleap_custom_latent_space(name: Optional[str] = None, use_ls_for_analysis: bool = False):
|
|
1727
|
+
assert isinstance(use_ls_for_analysis, bool), \
|
|
1728
|
+
("tensorleap_custom_latent_space validation failed: use_ls_for_analysis must be a bool. "
|
|
1729
|
+
f"Got {type(use_ls_for_analysis)}.")
|
|
1730
|
+
|
|
1707
1731
|
def decorating_function(user_function: SectionCallableInterface):
|
|
1732
|
+
ls_name = name if name is not None else user_function.__name__
|
|
1733
|
+
|
|
1708
1734
|
def _validate_input_args(sample_id: Union[int, str, list], preprocess_response: PreprocessResponse):
|
|
1709
1735
|
_validate_id_or_group(sample_id, preprocess_response, 'tensorleap_custom_latent_space')
|
|
1710
1736
|
|
|
@@ -1715,9 +1741,9 @@ def tensorleap_custom_latent_space():
|
|
|
1715
1741
|
if single_result.ndim > 1:
|
|
1716
1742
|
flat_dim = int(np.prod(single_result.shape))
|
|
1717
1743
|
store_general_warning(
|
|
1718
|
-
key=("tensorleap_custom_latent_space_flatten", tuple(single_result.shape)),
|
|
1744
|
+
key=("tensorleap_custom_latent_space_flatten", ls_name, tuple(single_result.shape)),
|
|
1719
1745
|
message=(
|
|
1720
|
-
f"tensorleap_custom_latent_space returned per-sample shape {tuple(single_result.shape)} "
|
|
1746
|
+
f"tensorleap_custom_latent_space '{ls_name}' returned per-sample shape {tuple(single_result.shape)} "
|
|
1721
1747
|
f"(ndim={single_result.ndim}). Tensorleap assumes per-sample shape (d, ...) and will "
|
|
1722
1748
|
f"flatten to ({flat_dim},) before downstream visualization and clustering. "
|
|
1723
1749
|
f"If you want a different aggregation (e.g. global average pooling), do it "
|
|
@@ -1760,7 +1786,8 @@ def tensorleap_custom_latent_space():
|
|
|
1760
1786
|
|
|
1761
1787
|
return result
|
|
1762
1788
|
|
|
1763
|
-
leap_binder.set_custom_latent_space(inner_without_validate
|
|
1789
|
+
leap_binder.set_custom_latent_space(inner_without_validate, ls_name,
|
|
1790
|
+
use_ls_for_analysis=use_ls_for_analysis)
|
|
1764
1791
|
|
|
1765
1792
|
def inner(sample_id, preprocess_response):
|
|
1766
1793
|
if os.environ.get(mapping_runtime_mode_env_var_mame):
|
|
@@ -1877,11 +1904,16 @@ class _MappingStatePlaceholder:
|
|
|
1877
1904
|
|
|
1878
1905
|
|
|
1879
1906
|
class _ModelLoopContext:
|
|
1880
|
-
def __init__(self, sample_id, preprocess_response, prediction_names, is_mapping
|
|
1907
|
+
def __init__(self, sample_id, preprocess_response, prediction_names, is_mapping,
|
|
1908
|
+
cache_tensors=None):
|
|
1881
1909
|
self.sample_id = sample_id
|
|
1882
1910
|
self.preprocess_response = preprocess_response
|
|
1883
1911
|
self.prediction_names = prediction_names
|
|
1884
1912
|
self.is_mapping = is_mapping
|
|
1913
|
+
# Declared KV-cache, {model_input_name: model_output_name}. These tensors are supplied
|
|
1914
|
+
# by the loop (and by the platform at runtime), NOT by the hook, so both the fed-inputs
|
|
1915
|
+
# check and the output-count check have to account for them.
|
|
1916
|
+
self.cache_tensors = dict(cache_tensors or {})
|
|
1885
1917
|
self.phase = 'awaiting_first_hook'
|
|
1886
1918
|
self.steps = 0
|
|
1887
1919
|
self.fed_inputs = None
|
|
@@ -1890,6 +1922,9 @@ class _ModelLoopContext:
|
|
|
1890
1922
|
self.last_outputs_fingerprint = None
|
|
1891
1923
|
self.last_state = None
|
|
1892
1924
|
self.last_state_fingerprint = None
|
|
1925
|
+
# Declared-cache outputs of the last model call, in graph order (predictions first, so
|
|
1926
|
+
# this is the tail). Read by the loop through the model proxy.
|
|
1927
|
+
self.last_cache_outputs = []
|
|
1893
1928
|
|
|
1894
1929
|
def _fail(self, message):
|
|
1895
1930
|
raise LeapValidationError(f'tensorleap_model_loop validation failed: {message}')
|
|
@@ -1974,12 +2009,19 @@ class _ModelLoopContext:
|
|
|
1974
2009
|
'next_inputs the loop must exit without further hook or model calls.')
|
|
1975
2010
|
if self.is_mapping:
|
|
1976
2011
|
return
|
|
2012
|
+
# A declared KV-cache is threaded by the platform, so the hook neither receives nor
|
|
2013
|
+
# returns it and the loop is expected to supply it. Exclude those keys from the
|
|
2014
|
+
# identity check rather than demanding the hook produce tensors it cannot see.
|
|
2015
|
+
if isinstance(fed, dict) and self.cache_tensors:
|
|
2016
|
+
fed = {key: value for key, value in fed.items() if key not in self.cache_tensors}
|
|
1977
2017
|
fed_ids = {id(leaf) for leaf in _ndarray_leaves(fed)}
|
|
1978
2018
|
hook_ids = {id(value) for value in self.fed_inputs.values()}
|
|
1979
2019
|
if fed_ids != hook_ids:
|
|
2020
|
+
extra = (' (the declared KV-cache inputs are exempt — those are threaded for you)'
|
|
2021
|
+
if self.cache_tensors else '')
|
|
1980
2022
|
self._fail('the model must be fed exactly the tensors the last hook call returned — '
|
|
1981
2023
|
'any computation between the hook and the model is invisible to the '
|
|
1982
|
-
'platform. Move it into the hook.')
|
|
2024
|
+
f'platform. Move it into the hook.{extra}')
|
|
1983
2025
|
if _nest_fingerprint(self.fed_inputs) != self.fed_inputs_fingerprint:
|
|
1984
2026
|
self._fail('the model inputs were mutated in place after the hook returned them — '
|
|
1985
2027
|
'the platform feeds the model the tensors exactly as the hook returned '
|
|
@@ -1991,12 +2033,24 @@ class _ModelLoopContext:
|
|
|
1991
2033
|
self.last_outputs = raw_outputs
|
|
1992
2034
|
return raw_outputs
|
|
1993
2035
|
outputs_list = raw_outputs if isinstance(raw_outputs, list) else [raw_outputs]
|
|
1994
|
-
|
|
2036
|
+
# Cache outputs are plumbing, not predictions: they are fed straight back into the model
|
|
2037
|
+
# and never reported, so they need no prediction type. Only the remainder is keyed.
|
|
2038
|
+
n_cache_outputs = len(self.cache_tensors)
|
|
2039
|
+
if len(outputs_list) - n_cache_outputs != len(self.prediction_names):
|
|
2040
|
+
cache_note = (f' ({n_cache_outputs} of them are the declared KV-cache, which needs '
|
|
2041
|
+
f'no prediction types)' if n_cache_outputs else '')
|
|
1995
2042
|
self._fail(f'the model returned {len(outputs_list)} outputs but '
|
|
1996
2043
|
f'{len(self.prediction_names)} prediction types are declared on '
|
|
1997
|
-
f'tensorleap_load_model — declare one prediction type per
|
|
1998
|
-
|
|
1999
|
-
|
|
2044
|
+
f'tensorleap_load_model{cache_note} — declare one prediction type per '
|
|
2045
|
+
f'non-cache model output.')
|
|
2046
|
+
# Predictions come first in graph order, as the export declares them; the cache follows.
|
|
2047
|
+
named_outputs = {name: np.asarray(output) for name, output
|
|
2048
|
+
in zip(self.prediction_names, outputs_list)}
|
|
2049
|
+
# The hook must receive predictions only, exactly as on the platform, and must receive
|
|
2050
|
+
# this very dict (on_hook_call checks identity). The loop still needs the cache, because
|
|
2051
|
+
# locally it stands in for the platform and threads it by hand — so that goes out on the
|
|
2052
|
+
# proxy rather than being mixed into the hook's view.
|
|
2053
|
+
self.last_cache_outputs = list(outputs_list[len(self.prediction_names):])
|
|
2000
2054
|
self.last_outputs = named_outputs
|
|
2001
2055
|
self.last_outputs_fingerprint = _nest_fingerprint(named_outputs)
|
|
2002
2056
|
return named_outputs
|
|
@@ -2018,8 +2072,19 @@ class _ModelLoopModelProxy:
|
|
|
2018
2072
|
def get_inputs(self):
|
|
2019
2073
|
return self._model.get_inputs()
|
|
2020
2074
|
|
|
2075
|
+
@property
|
|
2076
|
+
def cache_outputs(self):
|
|
2077
|
+
"""Declared-cache outputs of the last model call, in graph order.
|
|
2021
2078
|
|
|
2022
|
-
|
|
2079
|
+
Only the local loop needs these: it stands in for the platform, which threads the cache
|
|
2080
|
+
for you at runtime. They are kept off the hook's `prev_outputs` deliberately — the hook
|
|
2081
|
+
must not see the cache in either place, or the local run would diverge from the real one.
|
|
2082
|
+
"""
|
|
2083
|
+
return self._context.last_cache_outputs
|
|
2084
|
+
|
|
2085
|
+
|
|
2086
|
+
def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step',
|
|
2087
|
+
cache_tensors: Optional[Dict[str, str]] = None):
|
|
2023
2088
|
"""The feedback hook that drives an autoregressive chain.
|
|
2024
2089
|
|
|
2025
2090
|
Signature of the decorated function:
|
|
@@ -2043,6 +2108,22 @@ def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
|
|
|
2043
2108
|
space comes from the final step's forward pass, except input-kind latent spaces which come
|
|
2044
2109
|
from the first step — the original sample, before generated content dominates the model
|
|
2045
2110
|
inputs. 'mean': every latent space is the elementwise mean over all steps of the chain.
|
|
2111
|
+
|
|
2112
|
+
cache_tensors declares a KV-cache (or any threaded state) as
|
|
2113
|
+
{model_input_name: model_output_name} — model inputs whose value is the named output from
|
|
2114
|
+
the previous step. The platform threads those tensors itself and holds them on the GPU, so
|
|
2115
|
+
the hook neither receives them in prev_inputs/prev_outputs nor returns them in next_inputs;
|
|
2116
|
+
return only the inputs you actually compute (e.g. input_ids, position_ids, attention_mask).
|
|
2117
|
+
The cache must be fixed-capacity: the declared input and output must have the same, fully
|
|
2118
|
+
concrete shape, since chains at different steps are batched together and the cache is
|
|
2119
|
+
preallocated. Exports with a growing cache (past length P in, P+1 out) are rejected at push.
|
|
2120
|
+
|
|
2121
|
+
@tensorleap_autoregressive_step(cache_tensors={
|
|
2122
|
+
f'past_key_values.{i}.{kv}': f'present.{i}.{kv}'
|
|
2123
|
+
for i in range(30) for kv in ('key', 'value')
|
|
2124
|
+
})
|
|
2125
|
+
|
|
2126
|
+
Omit it for a model with no cache — the chain then re-feeds its whole prompt every step.
|
|
2046
2127
|
"""
|
|
2047
2128
|
assert isinstance(latent_space_aggregation, str), \
|
|
2048
2129
|
('tensorleap_autoregressive_step must be called with parentheses: '
|
|
@@ -2190,7 +2271,8 @@ def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
|
|
|
2190
2271
|
return result
|
|
2191
2272
|
|
|
2192
2273
|
leap_binder.set_autoregressive_step(inner_without_validate,
|
|
2193
|
-
latent_space_aggregation=latent_space_aggregation
|
|
2274
|
+
latent_space_aggregation=latent_space_aggregation,
|
|
2275
|
+
cache_tensors=cache_tensors)
|
|
2194
2276
|
|
|
2195
2277
|
def inner(*args, **kwargs):
|
|
2196
2278
|
if not _call_from_tl_platform:
|
|
@@ -2390,8 +2472,9 @@ def tensorleap_model_loop():
|
|
|
2390
2472
|
if not prediction_names:
|
|
2391
2473
|
_fail('tensorleap_model_loop requires prediction types declared on '
|
|
2392
2474
|
'tensorleap_load_model — the loop keys the model outputs by their names.')
|
|
2393
|
-
context = _ModelLoopContext(
|
|
2394
|
-
|
|
2475
|
+
context = _ModelLoopContext(
|
|
2476
|
+
sample_id, preprocess_response, prediction_names, is_mapping,
|
|
2477
|
+
cache_tensors=leap_binder.setup_container.autoregressive_step.cache_tensors)
|
|
2395
2478
|
_active_model_loop = context
|
|
2396
2479
|
try:
|
|
2397
2480
|
result = user_function(_ModelLoopModelProxy(model, context), sample_id,
|
|
@@ -283,17 +283,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
283
283
|
|
|
284
284
|
metadata, metadata_is_none = self.get_metadata(state, sample_id)
|
|
285
285
|
|
|
286
|
-
|
|
287
|
-
if global_leap_binder.setup_container.custom_latent_space is not None:
|
|
288
|
-
latent_fn = global_leap_binder.setup_container.custom_latent_space.function
|
|
289
|
-
preprocess_state = preprocess_result[state]
|
|
290
|
-
if preprocess_state.is_grouped:
|
|
291
|
-
# Single-sample fetch: encode this sample only, not the whole group (same
|
|
292
|
-
# memory rationale as _get_dataset_handlers; see grouped-fetch-oom-bug.md).
|
|
293
|
-
self._locate_group(preprocess_state, sample_id) # validates group membership
|
|
294
|
-
custom_latent_space = self._to_grouped_list(latent_fn([sample_id], preprocess_state))[0]
|
|
295
|
-
else:
|
|
296
|
-
custom_latent_space = latent_fn(sample_id, preprocess_state)
|
|
286
|
+
custom_latent_spaces = self._get_custom_latent_spaces(sample_id, preprocess_result[state])
|
|
297
287
|
instance_mask = self._get_instances_masks(state, sample_id, instance_id)
|
|
298
288
|
sample = DatasetSample(inputs=self._get_inputs(state, sample_id),
|
|
299
289
|
gt=None if state == DataStateEnum.unlabeled else self._get_gt(state, sample_id),
|
|
@@ -301,7 +291,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
301
291
|
metadata_is_none=metadata_is_none,
|
|
302
292
|
index=sample_id,
|
|
303
293
|
state=state,
|
|
304
|
-
|
|
294
|
+
custom_latent_spaces=custom_latent_spaces,
|
|
305
295
|
instance_masks=instance_mask)
|
|
306
296
|
return sample
|
|
307
297
|
|
|
@@ -332,11 +322,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
332
322
|
handler.name, handler_result
|
|
333
323
|
)
|
|
334
324
|
|
|
335
|
-
|
|
336
|
-
if global_leap_binder.setup_container.custom_latent_space is not None:
|
|
337
|
-
custom_latent_space = global_leap_binder.setup_container.custom_latent_space.function(
|
|
338
|
-
original_sample_id, preprocess
|
|
339
|
-
)
|
|
325
|
+
custom_latent_spaces = self._get_custom_latent_spaces(original_sample_id, preprocess)
|
|
340
326
|
|
|
341
327
|
return DatasetSample(
|
|
342
328
|
inputs=inputs,
|
|
@@ -345,7 +331,7 @@ class LeapLoader(LeapLoaderBase):
|
|
|
345
331
|
metadata_is_none=metadata_is_none,
|
|
346
332
|
index=synthetic_index,
|
|
347
333
|
state=DataStateEnum.additional,
|
|
348
|
-
|
|
334
|
+
custom_latent_spaces=custom_latent_spaces,
|
|
349
335
|
instance_masks=None,
|
|
350
336
|
)
|
|
351
337
|
|
|
@@ -1114,17 +1100,20 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1114
1100
|
metadata[handler.name] = [v for v, _ in converted]
|
|
1115
1101
|
metadata_is_none[handler.name] = [is_none for _, is_none in converted]
|
|
1116
1102
|
|
|
1117
|
-
#
|
|
1118
|
-
# single call so a file-backed latent fn loads the file once, and normalize to
|
|
1119
|
-
# keeps the mandatory group path non-lossy. instance_masks stay None here: they are
|
|
1120
|
-
# per-(sample, instance) concern that needs an instance_id the group fetch does not carry.
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1103
|
+
# custom_latent_spaces are group-aware like the input/GT encoders: hand each the whole group
|
|
1104
|
+
# in a single call so a file-backed latent fn loads the file once, and normalize to per-sample
|
|
1105
|
+
# lists. This keeps the mandatory group path non-lossy. instance_masks stay None here: they are
|
|
1106
|
+
# a per-(sample, instance) concern that needs an instance_id the group fetch does not carry.
|
|
1107
|
+
latent_handlers = global_leap_binder.setup_container.custom_latent_spaces
|
|
1108
|
+
custom_latent_spaces = None
|
|
1109
|
+
if latent_handlers:
|
|
1110
|
+
custom_latent_spaces = {
|
|
1111
|
+
name: self._to_grouped_list(handler.function(group_ids, preprocess_state))
|
|
1112
|
+
for name, handler in latent_handlers.items()
|
|
1113
|
+
}
|
|
1125
1114
|
|
|
1126
1115
|
return DatasetSample(inputs=inputs, gt=gt, metadata=metadata, metadata_is_none=metadata_is_none,
|
|
1127
|
-
index=list(group_ids), state=state,
|
|
1116
|
+
index=list(group_ids), state=state, custom_latent_spaces=custom_latent_spaces,
|
|
1128
1117
|
instance_masks=None)
|
|
1129
1118
|
|
|
1130
1119
|
def _get_inputs(self, state: DataStateEnum, sample_id: Union[int, str]) -> Dict[str, npt.NDArray[np.float32]]:
|
|
@@ -1337,10 +1326,48 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1337
1326
|
|
|
1338
1327
|
return id_type
|
|
1339
1328
|
|
|
1329
|
+
def _get_custom_latent_spaces(
|
|
1330
|
+
self,
|
|
1331
|
+
sample_id: Union[int, str],
|
|
1332
|
+
preprocess: "PreprocessResponse") -> Optional[Dict[str, npt.NDArray[np.float32]]]:
|
|
1333
|
+
handlers = global_leap_binder.setup_container.custom_latent_spaces
|
|
1334
|
+
if not handlers:
|
|
1335
|
+
return None
|
|
1336
|
+
if preprocess.is_grouped:
|
|
1337
|
+
# Single-sample fetch: encode this sample only, not the whole group (same
|
|
1338
|
+
# memory rationale as _get_dataset_handlers; see grouped-fetch-oom-bug.md).
|
|
1339
|
+
self._locate_group(preprocess, sample_id) # validates group membership
|
|
1340
|
+
return {name: self._to_grouped_list(handler.function([sample_id], preprocess))[0]
|
|
1341
|
+
for name, handler in handlers.items()}
|
|
1342
|
+
return {name: handler.function(sample_id, preprocess) for name, handler in handlers.items()}
|
|
1343
|
+
|
|
1340
1344
|
@lru_cache()
|
|
1341
1345
|
def has_custom_latent_space_decorator(self) -> bool:
|
|
1342
1346
|
self.exec_script()
|
|
1343
|
-
return global_leap_binder.setup_container.
|
|
1347
|
+
return len(global_leap_binder.setup_container.custom_latent_spaces) > 0
|
|
1348
|
+
|
|
1349
|
+
@lru_cache()
|
|
1350
|
+
def get_custom_latent_space_names(self) -> Tuple[str, ...]:
|
|
1351
|
+
"""Ordered names of all registered custom latent spaces.
|
|
1352
|
+
|
|
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.
|
|
1355
|
+
"""
|
|
1356
|
+
self.exec_script()
|
|
1357
|
+
return tuple(global_leap_binder.setup_container.custom_latent_spaces.keys())
|
|
1358
|
+
|
|
1359
|
+
@lru_cache()
|
|
1360
|
+
def get_custom_latent_space_for_analysis(self) -> Optional[str]:
|
|
1361
|
+
"""Name of the custom latent space flagged with use_ls_for_analysis=True, if any.
|
|
1362
|
+
|
|
1363
|
+
The engine uses this latent space for the Out-Of-Distribution and Domain-Gap
|
|
1364
|
+
insights instead of the built-in defaults. Returns None when no custom latent
|
|
1365
|
+
space set the flag. At most one is flagged (enforced at registration time)."""
|
|
1366
|
+
self.exec_script()
|
|
1367
|
+
for name, handler in global_leap_binder.setup_container.custom_latent_spaces.items():
|
|
1368
|
+
if handler.use_ls_for_analysis:
|
|
1369
|
+
return name
|
|
1370
|
+
return None
|
|
1344
1371
|
|
|
1345
1372
|
@lru_cache()
|
|
1346
1373
|
def has_autoregressive_step(self) -> bool:
|
|
@@ -1355,6 +1382,20 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1355
1382
|
return 'last_step'
|
|
1356
1383
|
return handler.latent_space_aggregation
|
|
1357
1384
|
|
|
1385
|
+
@lru_cache()
|
|
1386
|
+
def get_autoregressive_cache_tensors(self) -> Dict[str, str]:
|
|
1387
|
+
"""Declared KV-cache pairing, {model_input_name: model_output_name}.
|
|
1388
|
+
|
|
1389
|
+
Empty when nothing is declared — the platform then runs the chain uncached. The platform
|
|
1390
|
+
validates these names against the real model graph at push time; only structural checks
|
|
1391
|
+
happen here.
|
|
1392
|
+
"""
|
|
1393
|
+
self.exec_script()
|
|
1394
|
+
handler = global_leap_binder.setup_container.autoregressive_step
|
|
1395
|
+
if handler is None or not handler.cache_tensors:
|
|
1396
|
+
return {}
|
|
1397
|
+
return dict(handler.cache_tensors)
|
|
1398
|
+
|
|
1358
1399
|
def run_autoregressive_step(self, sample_id: Union[int, str],
|
|
1359
1400
|
prev_inputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
1360
1401
|
prev_outputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
@@ -176,6 +176,11 @@ class LeapLoaderBase:
|
|
|
176
176
|
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
177
177
|
'get_autoregressive_latent_space_aggregation.')
|
|
178
178
|
|
|
179
|
+
@abstractmethod
|
|
180
|
+
def get_autoregressive_cache_tensors(self) -> Dict[str, str]:
|
|
181
|
+
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
182
|
+
'get_autoregressive_cache_tensors.')
|
|
183
|
+
|
|
179
184
|
@abstractmethod
|
|
180
185
|
def get_autoregressive_decorator_names(self) -> Dict[str, List[str]]:
|
|
181
186
|
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
@@ -218,6 +223,14 @@ class LeapLoaderBase:
|
|
|
218
223
|
raise NotImplementedError(f'{type(self).__name__} does not implement '
|
|
219
224
|
'autoregressive_visualizer_by_name.')
|
|
220
225
|
|
|
226
|
+
@abstractmethod
|
|
227
|
+
def get_custom_latent_space_names(self) -> Tuple[str, ...]:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
@abstractmethod
|
|
231
|
+
def get_custom_latent_space_for_analysis(self) -> Optional[str]:
|
|
232
|
+
pass
|
|
233
|
+
|
|
221
234
|
@abstractmethod
|
|
222
235
|
def get_heatmap_visualizer_raw_vis_input_arg_name(self, visualizer_name: str) -> Optional[str]:
|
|
223
236
|
pass
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/visualizers/default_visualizers.py
RENAMED
|
@@ -33,7 +33,7 @@ def default_video_visualizer(data: npt.NDArray[np.float32]) -> LeapVideo:
|
|
|
33
33
|
_MAX_WAVEFORM_POINTS = 2000
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def _to_mono(audio: npt.NDArray) -> npt.NDArray:
|
|
36
|
+
def _to_mono(audio: npt.NDArray[np.float32]) -> npt.NDArray[np.float32]:
|
|
37
37
|
a = np.squeeze(np.asarray(audio))
|
|
38
38
|
if a.ndim == 2: # mix down: channels are the shorter axis
|
|
39
39
|
a = a.mean(axis=int(np.argmin(a.shape)))
|
|
@@ -42,7 +42,7 @@ def _to_mono(audio: npt.NDArray) -> npt.NDArray:
|
|
|
42
42
|
return a.astype(np.float32)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def _waveform_graph(series: npt.NDArray, duration: float) -> LeapGraph:
|
|
45
|
+
def _waveform_graph(series: npt.NDArray[np.float32], duration: float) -> LeapGraph:
|
|
46
46
|
s = np.squeeze(np.asarray(series)).astype(np.float32)
|
|
47
47
|
# ponytail: recharts chokes on 100k+ points; stride down for the default view. x_range keeps full time span.
|
|
48
48
|
step = max(1, s.shape[0] // _MAX_WAVEFORM_POINTS)
|
|
@@ -50,9 +50,9 @@ def _waveform_graph(series: npt.NDArray, duration: float) -> LeapGraph:
|
|
|
50
50
|
return LeapGraph(s, x_label='Time [s]', y_label='Amplitude', x_range=(0.0, duration))
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def default_audio_visualizer(audio: npt.NDArray,
|
|
53
|
+
def default_audio_visualizer(audio: npt.NDArray[np.float32],
|
|
54
54
|
sample_rate: int,
|
|
55
|
-
visual: Optional[npt.NDArray] = None,
|
|
55
|
+
visual: Optional[npt.NDArray[np.float32]] = None,
|
|
56
56
|
time_axis_index: int = -1) -> LeapAudio:
|
|
57
57
|
"""Package a playable audio sample plus the visual a playhead runs through.
|
|
58
58
|
|
|
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.201.dev0 → code_loader-1.0.202.dev1}/code_loader/contract/responsedataclasses.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/cli_config_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/experiment_api/experiment.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/inner_leap_binder/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/plot_functions/__init__.py
RENAMED
|
File without changes
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/plot_functions/plot_functions.py
RENAMED
|
File without changes
|
{code_loader-1.0.201.dev0 → code_loader-1.0.202.dev1}/code_loader/plot_functions/visualize.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|