Source code for embodichain.data_pipeline.datasets.schema

# ----------------------------------------------------------------------------
# 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.
# ----------------------------------------------------------------------------

"""Shared semantic validation and identity for numeric training-data schemas."""

from __future__ import annotations

import hashlib
import json
import math
from collections.abc import Mapping
from typing import Any

import torch

__all__ = ["validate_data_schema", "data_schema_fingerprint"]


[docs] def validate_data_schema(schema: Mapping[str, Any]) -> None: """Validate declarations common to live and resident causal sources. Args: schema: Observation/action declarations. Actions require explicit semantics or representation and finite positive ``step_dt``. Optional names are ordered and unique; dtype names use Torch's spelling without its prefix. Optional observation ``timing`` must be ``pre_action``. Root and feature ``version`` values identify schemas and feature transformations when supplied. This validates declarations, not their truth or compatibility with a live robot. Source adapters also validate stored features and action contracts. Units, frames, rotations and normalization remain caller declarations; no conversion or missing semantic information is inferred from vector widths. Raises: ValueError: A required semantic declaration is absent or malformed. """ if not isinstance(schema, Mapping): raise ValueError("Schema must be a mapping") definitions = [schema] for section in ("observation", "action"): definition = schema.get(section, {}) if not isinstance(definition, Mapping): raise ValueError(f"Schema {section} must be a mapping") definitions.append(definition) names = definition.get("names") if names is not None and ( not isinstance(names, list) or not names or not all(isinstance(name, str) and name for name in names) or len(set(names)) != len(names) ): raise ValueError(f"Schema {section}.names must be ordered unique names") dtype = definition.get("dtype") if dtype is not None and ( not isinstance(dtype, str) or not isinstance(getattr(torch, dtype, None), torch.dtype) ): raise ValueError(f"Schema {section} dtype must be a Torch dtype name") for definition in definitions: if "version" in definition: value = definition["version"] if not ( isinstance(value, str) and value.strip() or type(value) is int and value > 0 ): raise ValueError( "Schema version must be a nonempty string or positive integer" ) action = schema.get("action", {}) semantics = action.get("semantics", action.get("representation")) if not isinstance(semantics, str) or not semantics.strip(): raise ValueError("Explicit action semantics or representation is required") step_dt = action.get("step_dt") if ( isinstance(step_dt, bool) or not isinstance(step_dt, (float, int)) or not math.isfinite(step_dt) or step_dt <= 0 ): raise ValueError("Explicit finite positive action step_dt is required") observation = schema.get("observation", {}) if observation.get("timing", "pre_action") != "pre_action": raise ValueError("Causal source observation timing must be pre_action")
[docs] def data_schema_fingerprint(schema: Mapping[str, Any]) -> str: """Hash a validated finite JSON schema without changing feature order. Args: schema: Persistable declaration, including any feature-transform, normalization, coordinate-frame and action-contract versions. Returns: SHA-256 of canonical UTF-8 JSON. Mapping key order is ignored; ordered lists and caller-declared versions remain significant. This is a schema identity, not a hash of trajectory content or callable implementation. Raises: ValueError: The schema is invalid or not finite JSON data. """ validate_data_schema(schema) try: payload = json.dumps( dict(schema), sort_keys=True, separators=(",", ":"), allow_nan=False ).encode() except (TypeError, ValueError) as error: raise ValueError("Schema fingerprint requires finite JSON metadata") from error return hashlib.sha256(payload).hexdigest()