code-loader 1.0.201__tar.gz → 1.0.202.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/PKG-INFO +4 -3
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/contract/datasetclasses.py +5 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/contract/visualizer_classes.py +2 -2
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/inner_leap_binder/leapbinder.py +44 -2
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/inner_leap_binder/leapbinder_decorators.py +113 -15
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/leaploader.py +14 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/leaploaderbase.py +5 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/visualizers/default_visualizers.py +4 -4
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/pyproject.toml +1 -1
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/LICENSE +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/README.md +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/__init__.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/contract/__init__.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/contract/enums.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/contract/exceptions.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/contract/mapping.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/contract/responsedataclasses.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/contract/sim_config.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/default_losses.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/default_metrics.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/__init__.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/api.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/cli_config_utils.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/client.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/epoch.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/experiment.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/experiment_context.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/types.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/utils.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/inner_leap_binder/__init__.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/mixpanel_tracker.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/plot_functions/__init__.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/plot_functions/plot_functions.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/plot_functions/visualize.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/utils.py +0 -0
- {code_loader-1.0.201 → code_loader-1.0.202.dev0}/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.dev0
|
|
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
|
|
|
@@ -332,6 +332,11 @@ class AutoregressiveStepHandler:
|
|
|
332
332
|
# time. Fills the role InputHandler.shape plays for input encoders.
|
|
333
333
|
input_shapes: Optional[Dict[str, List[int]]] = None
|
|
334
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
|
|
335
340
|
|
|
336
341
|
|
|
337
342
|
# Per-chain, unbatched callables: called once per finished chain with the final step's tensors —
|
|
@@ -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 → code_loader-1.0.202.dev0}/code_loader/inner_leap_binder/leapbinder.py
RENAMED
|
@@ -534,6 +534,11 @@ 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
544
|
def set_custom_latent_space(self, function: SectionCallableInterface,
|
|
@@ -585,8 +590,41 @@ class LeapBinder:
|
|
|
585
590
|
self.setup_container.custom_latent_spaces[name] = CustomLatentSpaceHandler(
|
|
586
591
|
function, name, use_ls_for_analysis)
|
|
587
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)
|
|
624
|
+
|
|
588
625
|
def set_autoregressive_step(self, function: AutoregressiveStepCallableInterface,
|
|
589
|
-
latent_space_aggregation: str = 'last_step'
|
|
626
|
+
latent_space_aggregation: str = 'last_step',
|
|
627
|
+
cache_tensors: Optional[Dict[str, str]] = None) -> None:
|
|
590
628
|
"""
|
|
591
629
|
Set the autoregressive step hook — the feedback function that drives a chain:
|
|
592
630
|
it supplies the model's initial inputs on its first call (prev_inputs=None, prev_outputs=None)
|
|
@@ -594,6 +632,8 @@ class LeapBinder:
|
|
|
594
632
|
An autoregressive integration has no input encoders; the hook is the sole input source.
|
|
595
633
|
latent_space_aggregation declares how the chain's latent-space vectors are derived from
|
|
596
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.
|
|
597
637
|
"""
|
|
598
638
|
if self.setup_container.autoregressive_step is not None:
|
|
599
639
|
raise Exception('tensorleap_autoregressive_step is already defined. '
|
|
@@ -602,8 +642,10 @@ class LeapBinder:
|
|
|
602
642
|
raise Exception(f'tensorleap_autoregressive_step: unknown latent_space_aggregation '
|
|
603
643
|
f'{latent_space_aggregation!r}. Supported values: '
|
|
604
644
|
f'{", ".join(AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS)}.')
|
|
645
|
+
cache_tensors = self._validated_cache_tensors(cache_tensors)
|
|
605
646
|
self.setup_container.autoregressive_step = AutoregressiveStepHandler(
|
|
606
|
-
function, latent_space_aggregation=latent_space_aggregation
|
|
647
|
+
function, latent_space_aggregation=latent_space_aggregation,
|
|
648
|
+
cache_tensors=cache_tensors)
|
|
607
649
|
|
|
608
650
|
# Builtin chain metadata, declared at parse time so it survives the reporter's
|
|
609
651
|
# metadata type mapping; the placeholder values are overwritten by the engine when a
|
|
@@ -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]
|
|
@@ -1883,12 +1893,29 @@ class _MappingStatePlaceholder:
|
|
|
1883
1893
|
self._unsupported()
|
|
1884
1894
|
|
|
1885
1895
|
|
|
1896
|
+
def _shares_base(leaf, original):
|
|
1897
|
+
"""True when `leaf` is `original` or a view onto it (e.g. original[None])."""
|
|
1898
|
+
if leaf is original:
|
|
1899
|
+
return True
|
|
1900
|
+
base = getattr(leaf, 'base', None)
|
|
1901
|
+
while base is not None:
|
|
1902
|
+
if base is original:
|
|
1903
|
+
return True
|
|
1904
|
+
base = getattr(base, 'base', None)
|
|
1905
|
+
return False
|
|
1906
|
+
|
|
1907
|
+
|
|
1886
1908
|
class _ModelLoopContext:
|
|
1887
|
-
def __init__(self, sample_id, preprocess_response, prediction_names, is_mapping
|
|
1909
|
+
def __init__(self, sample_id, preprocess_response, prediction_names, is_mapping,
|
|
1910
|
+
cache_tensors=None):
|
|
1888
1911
|
self.sample_id = sample_id
|
|
1889
1912
|
self.preprocess_response = preprocess_response
|
|
1890
1913
|
self.prediction_names = prediction_names
|
|
1891
1914
|
self.is_mapping = is_mapping
|
|
1915
|
+
# Declared KV-cache, {model_input_name: model_output_name}. These tensors are supplied
|
|
1916
|
+
# by the loop (and by the platform at runtime), NOT by the hook, so both the fed-inputs
|
|
1917
|
+
# check and the output-count check have to account for them.
|
|
1918
|
+
self.cache_tensors = dict(cache_tensors or {})
|
|
1892
1919
|
self.phase = 'awaiting_first_hook'
|
|
1893
1920
|
self.steps = 0
|
|
1894
1921
|
self.fed_inputs = None
|
|
@@ -1897,6 +1924,9 @@ class _ModelLoopContext:
|
|
|
1897
1924
|
self.last_outputs_fingerprint = None
|
|
1898
1925
|
self.last_state = None
|
|
1899
1926
|
self.last_state_fingerprint = None
|
|
1927
|
+
# Declared-cache outputs of the last model call, in graph order (predictions first, so
|
|
1928
|
+
# this is the tail). Read by the loop through the model proxy.
|
|
1929
|
+
self.last_cache_outputs = []
|
|
1900
1930
|
|
|
1901
1931
|
def _fail(self, message):
|
|
1902
1932
|
raise LeapValidationError(f'tensorleap_model_loop validation failed: {message}')
|
|
@@ -1981,12 +2011,39 @@ class _ModelLoopContext:
|
|
|
1981
2011
|
'next_inputs the loop must exit without further hook or model calls.')
|
|
1982
2012
|
if self.is_mapping:
|
|
1983
2013
|
return
|
|
1984
|
-
|
|
2014
|
+
# A declared KV-cache is threaded by the platform, so the hook neither receives nor
|
|
2015
|
+
# returns it and the loop is expected to supply it. Exclude those keys from the
|
|
2016
|
+
# identity check rather than demanding the hook produce tensors it cannot see.
|
|
2017
|
+
if isinstance(fed, dict) and self.cache_tensors:
|
|
2018
|
+
fed = {key: value for key, value in fed.items() if key not in self.cache_tensors}
|
|
1985
2019
|
hook_ids = {id(value) for value in self.fed_inputs.values()}
|
|
1986
|
-
|
|
2020
|
+
# A leaf may be the hook's array itself, or a VIEW of it: `arr[None]` adds the batch
|
|
2021
|
+
# axis an ONNX model needs and shares its memory, so `.base` is the original. Allowing
|
|
2022
|
+
# views keeps the guarantee that matters — no tensor may be COMPUTED between the hook
|
|
2023
|
+
# and the model, since the platform would not see that work — while not forcing every
|
|
2024
|
+
# integration to choose between the identity check and a model that expects a batch
|
|
2025
|
+
# dimension. A genuinely new array has no base among the hook's tensors.
|
|
2026
|
+
def _from_hook(leaf):
|
|
2027
|
+
if id(leaf) in hook_ids:
|
|
2028
|
+
return True
|
|
2029
|
+
base = getattr(leaf, 'base', None)
|
|
2030
|
+
while base is not None:
|
|
2031
|
+
if id(base) in hook_ids:
|
|
2032
|
+
return True
|
|
2033
|
+
base = getattr(base, 'base', None)
|
|
2034
|
+
return False
|
|
2035
|
+
|
|
2036
|
+
fed_leaves = list(_ndarray_leaves(fed))
|
|
2037
|
+
matched = {id(value) for value in self.fed_inputs.values()
|
|
2038
|
+
if any(_from_hook(leaf) and (id(leaf) == id(value)
|
|
2039
|
+
or _shares_base(leaf, value))
|
|
2040
|
+
for leaf in fed_leaves)}
|
|
2041
|
+
if not all(_from_hook(leaf) for leaf in fed_leaves) or matched != hook_ids:
|
|
2042
|
+
extra = (' (the declared KV-cache inputs are exempt — those are threaded for you)'
|
|
2043
|
+
if self.cache_tensors else '')
|
|
1987
2044
|
self._fail('the model must be fed exactly the tensors the last hook call returned — '
|
|
1988
2045
|
'any computation between the hook and the model is invisible to the '
|
|
1989
|
-
'platform. Move it into the hook.')
|
|
2046
|
+
f'platform. Move it into the hook.{extra}')
|
|
1990
2047
|
if _nest_fingerprint(self.fed_inputs) != self.fed_inputs_fingerprint:
|
|
1991
2048
|
self._fail('the model inputs were mutated in place after the hook returned them — '
|
|
1992
2049
|
'the platform feeds the model the tensors exactly as the hook returned '
|
|
@@ -1998,12 +2055,24 @@ class _ModelLoopContext:
|
|
|
1998
2055
|
self.last_outputs = raw_outputs
|
|
1999
2056
|
return raw_outputs
|
|
2000
2057
|
outputs_list = raw_outputs if isinstance(raw_outputs, list) else [raw_outputs]
|
|
2001
|
-
|
|
2058
|
+
# Cache outputs are plumbing, not predictions: they are fed straight back into the model
|
|
2059
|
+
# and never reported, so they need no prediction type. Only the remainder is keyed.
|
|
2060
|
+
n_cache_outputs = len(self.cache_tensors)
|
|
2061
|
+
if len(outputs_list) - n_cache_outputs != len(self.prediction_names):
|
|
2062
|
+
cache_note = (f' ({n_cache_outputs} of them are the declared KV-cache, which needs '
|
|
2063
|
+
f'no prediction types)' if n_cache_outputs else '')
|
|
2002
2064
|
self._fail(f'the model returned {len(outputs_list)} outputs but '
|
|
2003
2065
|
f'{len(self.prediction_names)} prediction types are declared on '
|
|
2004
|
-
f'tensorleap_load_model — declare one prediction type per
|
|
2005
|
-
|
|
2006
|
-
|
|
2066
|
+
f'tensorleap_load_model{cache_note} — declare one prediction type per '
|
|
2067
|
+
f'non-cache model output.')
|
|
2068
|
+
# Predictions come first in graph order, as the export declares them; the cache follows.
|
|
2069
|
+
named_outputs = {name: np.asarray(output) for name, output
|
|
2070
|
+
in zip(self.prediction_names, outputs_list)}
|
|
2071
|
+
# The hook must receive predictions only, exactly as on the platform, and must receive
|
|
2072
|
+
# this very dict (on_hook_call checks identity). The loop still needs the cache, because
|
|
2073
|
+
# locally it stands in for the platform and threads it by hand — so that goes out on the
|
|
2074
|
+
# proxy rather than being mixed into the hook's view.
|
|
2075
|
+
self.last_cache_outputs = list(outputs_list[len(self.prediction_names):])
|
|
2007
2076
|
self.last_outputs = named_outputs
|
|
2008
2077
|
self.last_outputs_fingerprint = _nest_fingerprint(named_outputs)
|
|
2009
2078
|
return named_outputs
|
|
@@ -2025,8 +2094,19 @@ class _ModelLoopModelProxy:
|
|
|
2025
2094
|
def get_inputs(self):
|
|
2026
2095
|
return self._model.get_inputs()
|
|
2027
2096
|
|
|
2097
|
+
@property
|
|
2098
|
+
def cache_outputs(self):
|
|
2099
|
+
"""Declared-cache outputs of the last model call, in graph order.
|
|
2100
|
+
|
|
2101
|
+
Only the local loop needs these: it stands in for the platform, which threads the cache
|
|
2102
|
+
for you at runtime. They are kept off the hook's `prev_outputs` deliberately — the hook
|
|
2103
|
+
must not see the cache in either place, or the local run would diverge from the real one.
|
|
2104
|
+
"""
|
|
2105
|
+
return self._context.last_cache_outputs
|
|
2028
2106
|
|
|
2029
|
-
|
|
2107
|
+
|
|
2108
|
+
def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step',
|
|
2109
|
+
cache_tensors: Optional[Dict[str, str]] = None):
|
|
2030
2110
|
"""The feedback hook that drives an autoregressive chain.
|
|
2031
2111
|
|
|
2032
2112
|
Signature of the decorated function:
|
|
@@ -2050,6 +2130,22 @@ def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
|
|
|
2050
2130
|
space comes from the final step's forward pass, except input-kind latent spaces which come
|
|
2051
2131
|
from the first step — the original sample, before generated content dominates the model
|
|
2052
2132
|
inputs. 'mean': every latent space is the elementwise mean over all steps of the chain.
|
|
2133
|
+
|
|
2134
|
+
cache_tensors declares a KV-cache (or any threaded state) as
|
|
2135
|
+
{model_input_name: model_output_name} — model inputs whose value is the named output from
|
|
2136
|
+
the previous step. The platform threads those tensors itself and holds them on the GPU, so
|
|
2137
|
+
the hook neither receives them in prev_inputs/prev_outputs nor returns them in next_inputs;
|
|
2138
|
+
return only the inputs you actually compute (e.g. input_ids, position_ids, attention_mask).
|
|
2139
|
+
The cache must be fixed-capacity: the declared input and output must have the same, fully
|
|
2140
|
+
concrete shape, since chains at different steps are batched together and the cache is
|
|
2141
|
+
preallocated. Exports with a growing cache (past length P in, P+1 out) are rejected at push.
|
|
2142
|
+
|
|
2143
|
+
@tensorleap_autoregressive_step(cache_tensors={
|
|
2144
|
+
f'past_key_values.{i}.{kv}': f'present.{i}.{kv}'
|
|
2145
|
+
for i in range(30) for kv in ('key', 'value')
|
|
2146
|
+
})
|
|
2147
|
+
|
|
2148
|
+
Omit it for a model with no cache — the chain then re-feeds its whole prompt every step.
|
|
2053
2149
|
"""
|
|
2054
2150
|
assert isinstance(latent_space_aggregation, str), \
|
|
2055
2151
|
('tensorleap_autoregressive_step must be called with parentheses: '
|
|
@@ -2197,7 +2293,8 @@ def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
|
|
|
2197
2293
|
return result
|
|
2198
2294
|
|
|
2199
2295
|
leap_binder.set_autoregressive_step(inner_without_validate,
|
|
2200
|
-
latent_space_aggregation=latent_space_aggregation
|
|
2296
|
+
latent_space_aggregation=latent_space_aggregation,
|
|
2297
|
+
cache_tensors=cache_tensors)
|
|
2201
2298
|
|
|
2202
2299
|
def inner(*args, **kwargs):
|
|
2203
2300
|
if not _call_from_tl_platform:
|
|
@@ -2397,8 +2494,9 @@ def tensorleap_model_loop():
|
|
|
2397
2494
|
if not prediction_names:
|
|
2398
2495
|
_fail('tensorleap_model_loop requires prediction types declared on '
|
|
2399
2496
|
'tensorleap_load_model — the loop keys the model outputs by their names.')
|
|
2400
|
-
context = _ModelLoopContext(
|
|
2401
|
-
|
|
2497
|
+
context = _ModelLoopContext(
|
|
2498
|
+
sample_id, preprocess_response, prediction_names, is_mapping,
|
|
2499
|
+
cache_tensors=leap_binder.setup_container.autoregressive_step.cache_tensors)
|
|
2402
2500
|
_active_model_loop = context
|
|
2403
2501
|
try:
|
|
2404
2502
|
result = user_function(_ModelLoopModelProxy(model, context), sample_id,
|
|
@@ -1382,6 +1382,20 @@ class LeapLoader(LeapLoaderBase):
|
|
|
1382
1382
|
return 'last_step'
|
|
1383
1383
|
return handler.latent_space_aggregation
|
|
1384
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
|
+
|
|
1385
1399
|
def run_autoregressive_step(self, sample_id: Union[int, str],
|
|
1386
1400
|
prev_inputs: Optional[Dict[str, npt.NDArray[np.float32]]],
|
|
1387
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 '
|
{code_loader-1.0.201 → code_loader-1.0.202.dev0}/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 → code_loader-1.0.202.dev0}/code_loader/contract/responsedataclasses.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/cli_config_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{code_loader-1.0.201 → code_loader-1.0.202.dev0}/code_loader/experiment_api/experiment_context.py
RENAMED
|
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 → code_loader-1.0.202.dev0}/code_loader/plot_functions/plot_functions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|