Source code for embodichain.data_pipeline.datasets.sequence

# ----------------------------------------------------------------------------
# Copyright (c) 2021-2026 DexForce Technology Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ----------------------------------------------------------------------------

"""Storage-independent contracts for causal training sequences."""

from __future__ import annotations

from collections.abc import Mapping
from typing import Any, Literal, Protocol, runtime_checkable

import torch
from tensordict import TensorDict

from embodichain.utils import configclass

__all__ = [
    "SequenceSpecCfg",
    "BatchSource",
    "DataBudgetExhaustedError",
    "DataUnavailableError",
]


[docs] class DataBudgetExhaustedError(RuntimeError): """A complete requested batch exceeds eligible source delivery allowances."""
[docs] class DataUnavailableError(RuntimeError): """No eligible unlocked window exists under the current source/filter state. This is distinct from quota depletion and a terminal producer/read failure. The caller decides whether new publication can help and whether to wait with a deadline; a fixed offline source may never satisfy the request. Legacy consumers catching RuntimeError remain compatible. """
[docs] @configclass class SequenceSpecCfg: """Specify causal history and current/future supervision. Args: history_length: Number of observation frames, including the current one. prediction_horizon: Number of target frames, including the current one. sampling_mode: Allow segment crossings within an episode, or restrict context to one accepted segment. Continuity changes always pad. generations: Optional source-generation filter. Missing generation metadata raises rather than silently ignoring this filter. The current index is ``history_length - 1`` in a sequence of length ``history_length + prediction_horizon - 1``. Numeric padding is zero, source/language padding is -1, and only real current/future frames enter ``loss_mask``. Boundary-crossing chunk sampling remains an engine chunk API. """ history_length: int = 2 prediction_horizon: int = 64 sampling_mode: Literal["episode", "segment"] = "episode" generations: tuple[int, ...] | None = None def __post_init__(self) -> None: self._check() def _check(self) -> None: if any( type(v) is not int or v < 1 for v in (self.history_length, self.prediction_horizon) ): raise ValueError("Context lengths must be positive integers") if self.sampling_mode not in {"episode", "segment"}: raise ValueError("Context sampling_mode must be episode or segment") if self.generations is not None and ( not isinstance(self.generations, tuple) or not self.generations or any(type(v) is not int or v < 1 for v in self.generations) ): raise ValueError( "generations must be a nonempty tuple of positive integers" )
[docs] @runtime_checkable class BatchSource(Protocol): """A source of owned training batches with explicit data semantics. ``schema`` declares ordered observations/actions, their timing and units. ``source_id`` identifies the source dataset/run independently of cache slots. Production/refill and loading lifecycles belong to concrete sources and the caller, not to this sampling protocol. Sampling never performs hidden waits. """ schema: Mapping[str, Any] source_id: str
[docs] def sample_context( self, batch_size: int, spec: SequenceSpecCfg, *, generator: torch.Generator | None = None, ) -> TensorDict: """Return copied ``obs/actions/valid/loss_mask`` and source metadata. Args: batch_size: Number of episode-uniform contexts, with replacement. spec: Window, boundary and optional generation requirements. generator: Sampling RNG; ``None`` uses the current Torch RNG. Returns: TensorDict with batch axes ``[B, H + F - 1]``. Returned storage remains valid after subsequent sampling or source replacement. Raises: DataBudgetExhaustedError: Eligible delivery credits cannot provide the entire batch. No partial debit is performed. DataUnavailableError: No eligible anchor exists in the current data. RuntimeError: A producer or source read failed. ValueError: Schema or requested capabilities are incompatible. """ ...