rpx_benchmark.schemas

Strict, typed manifest schemas for the RPX benchmark.

This module defines Pydantic v2 models that mirror the JSON manifest format consumed by rpx_benchmark.loader.RPXDataset. It is an additive, opt-in validation layer: the loader's hand-rolled checks remain the default path and continue to work without Pydantic installed. Import this module when you want:

  • Field-level validation errors (e.g. "samples[3].boxes: Field required") instead of the loader's coarser KeyError surface.
  • JSON Schema export for third-party tooling or docs generation, via dump_json_schema().
  • Pre-upload validation in release automation that catches malformed shards before they reach the HuggingFace repo.

Stability

The model classes here are part of the public API at the rpx_benchmark.schemas surface. Field additions are non-breaking; renames or removals require a major version bump. TaskType and the enum values re-used here are defined in rpx_benchmark.api.

Design

One Manifest model holds the top-level fields (task, root, samples). Per-task sample schemas live in SAMPLE_MODELS and are dispatched on the manifest's task field by validate_manifest(). This keeps the model class count small while still producing precise errors per task.

  1"""Strict, typed manifest schemas for the RPX benchmark.
  2
  3This module defines Pydantic v2 models that mirror the JSON manifest
  4format consumed by :class:`rpx_benchmark.loader.RPXDataset`. It is an
  5**additive, opt-in** validation layer: the loader's hand-rolled checks
  6remain the default path and continue to work without Pydantic
  7installed. Import this module when you want:
  8
  9- Field-level validation errors (e.g. "samples[3].boxes: Field
 10  required") instead of the loader's coarser ``KeyError`` surface.
 11- JSON Schema export for third-party tooling or docs generation, via
 12  :func:`dump_json_schema`.
 13- Pre-upload validation in release automation that catches malformed
 14  shards before they reach the HuggingFace repo.
 15
 16Stability
 17---------
 18The model classes here are part of the public API at the
 19``rpx_benchmark.schemas`` surface. Field additions are non-breaking;
 20renames or removals require a major version bump. ``TaskType`` and
 21the enum values re-used here are defined in :mod:`rpx_benchmark.api`.
 22
 23Design
 24------
 25One :class:`Manifest` model holds the top-level fields (``task``,
 26``root``, ``samples``). Per-task sample schemas live in
 27:data:`SAMPLE_MODELS` and are dispatched on the manifest's
 28``task`` field by :func:`validate_manifest`. This keeps the model
 29class count small while still producing precise errors per task.
 30"""
 31
 32from __future__ import annotations
 33
 34from pathlib import Path
 35from typing import Any, Dict, List, Type, Union
 36
 37try:
 38    from pydantic import BaseModel, ConfigDict, Field, ValidationError
 39except ImportError as e:  # pragma: no cover — fail at import time with a clear hint
 40    raise ImportError(
 41        "rpx_benchmark.schemas requires pydantic>=2.5. "
 42        "Install with: pip install 'rpx-benchmark[schemas]'"
 43    ) from e
 44
 45from .api import Difficulty, Phase, TaskType
 46from .exceptions import ManifestError
 47
 48__all__ = [
 49    "BaseSampleEntry",
 50    "DepthSampleEntry",
 51    "DetectionSampleEntry",
 52    "SegmentationSampleEntry",
 53    "TrackingSampleEntry",
 54    "RelativePoseSampleEntry",
 55    "VisualGroundingSampleEntry",
 56    "SparseDepthSampleEntry",
 57    "KeypointMatchingSampleEntry",
 58    "Manifest",
 59    "SAMPLE_MODELS",
 60    "validate_manifest",
 61    "dump_json_schema",
 62]
 63
 64
 65# --------------------------------------------------------------------------- #
 66# Base configuration
 67# --------------------------------------------------------------------------- #
 68
 69
 70class _StrictBase(BaseModel):
 71    """Base for all manifest models.
 72
 73    - ``extra='allow'`` keeps forward-compatibility: unknown keys on a
 74      sample do not fail validation (they are preserved in metadata by
 75      the loader).
 76    - ``str_strip_whitespace`` normalises pasted paths.
 77    - ``frozen=False`` — we allow downstream tooling to mutate fields
 78      (e.g. rewriting relative paths to absolute).
 79    """
 80
 81    model_config = ConfigDict(
 82        extra="allow",
 83        str_strip_whitespace=True,
 84        populate_by_name=True,
 85    )
 86
 87
 88# --------------------------------------------------------------------------- #
 89# Sample schemas (one per task)
 90# --------------------------------------------------------------------------- #
 91
 92
 93class BaseSampleEntry(_StrictBase):
 94    """Fields common to every per-task sample entry."""
 95
 96    id: str = Field(..., description="Unique sample id, typically '{scene}_{phase}_{frame}'.")
 97    rgb: str = Field(..., description="Relative or absolute path to the RGB image.")
 98    scene: str | None = None
 99    phase: Phase | int | None = Field(
100        default=None,
101        description="Capture phase label ('clutter'/'interaction'/'clean') or 0/1/2.",
102    )
103    difficulty: Difficulty | None = None
104    pose: str | None = Field(
105        default=None,
106        description="Path to a T265 NPZ pose file (camera-to-world SE(3)).",
107    )
108    fisheye_left: str | None = None
109    fisheye_right: str | None = None
110    rgb_b: str | None = Field(
111        default=None,
112        description="Secondary RGB frame path (pair tasks: keypoint matching, pose).",
113    )
114    metadata: Dict[str, Any] | None = None
115
116
117class DepthSampleEntry(BaseSampleEntry):
118    depth: str = Field(..., description="Path to a 16-bit PNG depth map in millimetres.")
119
120
121class VideoDepthSampleEntry(BaseSampleEntry):
122    """Per-clip sample entry for the Video Depth task.
123
124    Differs from :class:`DepthSampleEntry` in that ``rgb`` and ``depth``
125    fields refer to entire frame sequences:
126
127    * ``rgb`` carries the *first* frame's path (kept for compatibility
128      with :class:`BaseSampleEntry`'s required field); the full clip
129      is in ``rgb_seq``.
130    * ``rgb_seq``, ``depth_seq`` are lists of relative-or-absolute
131      paths in temporal order.
132    * ``frame_indices`` records which original phase-relative indices
133      these paths correspond to (for adapters that downsample, this
134      lets the metric runner reconcile predictions to GT).
135    """
136
137    rgb_seq: List[str] = Field(
138        ...,
139        description="Ordered list of RGB frame paths for one (scene, phase) clip.",
140    )
141    depth_seq: List[str] = Field(
142        ...,
143        description="Ordered list of 16-bit PNG depth maps in millimetres.",
144    )
145    frame_indices: List[int] = Field(
146        ...,
147        description="Phase-relative integer indices for each frame in the clip.",
148    )
149
150
151class DetectionSampleEntry(BaseSampleEntry):
152    boxes: str = Field(..., description="Path to a JSON list of {bbox: [x1,y1,x2,y2], label: str}.")
153
154
155class SegmentationSampleEntry(BaseSampleEntry):
156    mask: str = Field(..., description="Path to a single-channel PNG with per-pixel instance IDs.")
157
158
159class TrackingSampleEntry(BaseSampleEntry):
160    tracks: str = Field(..., description="Path to a JSON list of tracklets.")
161
162
163class RelativePoseSampleEntry(BaseSampleEntry):
164    pose_a: str
165    pose_b: str
166    rgb_b: str = Field(..., description="Secondary RGB frame (required for pose pairs).")
167
168
169class VisualGroundingSampleEntry(BaseSampleEntry):
170    text: str
171    boxes: str | List[List[float]] | None = None
172    labels: List[str] | None = None
173
174
175class SparseDepthSampleEntry(BaseSampleEntry):
176    coordinates: str | List[List[float]]
177    depths: str | List[float]
178
179
180
181
182class KeypointMatchingSampleEntry(BaseSampleEntry):
183    points0: str | List[List[float]]
184    points1: str | List[List[float]]
185    visibility: str | List[bool] | None = None
186    rgb_b: str = Field(..., description="Secondary RGB frame (required for keypoint matching).")
187
188
189#: Dispatch table: ``TaskType → SampleEntry`` model class.
190SAMPLE_MODELS: Dict[TaskType, Type[BaseSampleEntry]] = {
191    TaskType.MONOCULAR_DEPTH: DepthSampleEntry,
192    TaskType.VIDEO_DEPTH: VideoDepthSampleEntry,
193    TaskType.OBJECT_DETECTION: DetectionSampleEntry,
194    TaskType.OPEN_VOCAB_DETECTION: DetectionSampleEntry,
195    TaskType.OBJECT_SEGMENTATION: SegmentationSampleEntry,
196    TaskType.OBJECT_TRACKING: TrackingSampleEntry,
197    TaskType.RELATIVE_CAMERA_POSE: RelativePoseSampleEntry,
198    TaskType.VISUAL_GROUNDING: VisualGroundingSampleEntry,
199    TaskType.SPARSE_DEPTH: SparseDepthSampleEntry,
200    TaskType.KEYPOINT_MATCHING: KeypointMatchingSampleEntry,
201}
202
203
204# --------------------------------------------------------------------------- #
205# Top-level manifest
206# --------------------------------------------------------------------------- #
207
208AnySampleEntry = Union[
209    DepthSampleEntry,
210    DetectionSampleEntry,
211    SegmentationSampleEntry,
212    TrackingSampleEntry,
213    RelativePoseSampleEntry,
214    VisualGroundingSampleEntry,
215    SparseDepthSampleEntry,
216    KeypointMatchingSampleEntry,
217]
218
219
220class Manifest(_StrictBase):
221    """Typed manifest describing a slice of the RPX dataset.
222
223    Attributes
224    ----------
225    task : TaskType
226        Task this manifest targets. Determines which
227        :class:`BaseSampleEntry` subclass each sample is validated
228        against.
229    root : str, optional
230        Root the sample paths resolve against. Absent manifests must
231        carry absolute paths in every sample.
232    samples : list of SampleEntry
233        Per-sample records. Parsed against the task-specific model in
234        :data:`SAMPLE_MODELS`.
235    scenes : list, optional
236        Alternate representation enumerating ``(scene, phase)`` pairs.
237        Preserved verbatim so hub download logic can read it without
238        round-tripping through per-sample records.
239    """
240
241    task: TaskType
242    root: str | None = None
243    samples: List[Dict[str, Any]] = Field(default_factory=list)
244    scenes: List[Dict[str, Any]] | None = None
245
246
247# --------------------------------------------------------------------------- #
248# Public helpers
249# --------------------------------------------------------------------------- #
250
251
252def validate_manifest(source: str | Path | Dict[str, Any]) -> Manifest:
253    """Validate ``source`` against the :class:`Manifest` schema.
254
255    Parameters
256    ----------
257    source : str, Path, or dict
258        Path to a manifest JSON, or an already-parsed dict.
259
260    Returns
261    -------
262    Manifest
263        The validated model.
264
265    Raises
266    ------
267    ManifestError
268        If the JSON is malformed or fails any schema validation. The
269        wrapped ``ValidationError`` message is surfaced in the
270        exception's ``details`` dict for programmatic access.
271    """
272    import json
273
274    if isinstance(source, (str, Path)):
275        path = Path(source)
276        if not path.is_file():
277            raise ManifestError(f"Manifest file not found: {path}")
278        try:
279            with path.open("r", encoding="utf-8") as f:
280                data = json.load(f)
281        except json.JSONDecodeError as e:
282            raise ManifestError(f"Manifest at {path} is not valid JSON: {e}") from e
283    else:
284        data = source
285
286    try:
287        manifest = Manifest.model_validate(data)
288    except ValidationError as e:
289        raise ManifestError(
290            "Manifest failed schema validation.",
291            hint="Run `rpx validate-manifest <path>` to see field-level errors.",
292            details={"pydantic_errors": e.errors()},
293        ) from e
294
295    # Per-sample validation against the task-specific schema. Kept
296    # outside the model_validator so per-sample errors surface as a
297    # single ManifestError with the full pydantic error list rather
298    # than being collapsed into one ValueError.
299    sample_model = SAMPLE_MODELS[manifest.task]
300    sample_errors: list[Dict[str, Any]] = []
301    rebuilt: List[Dict[str, Any]] = []
302    for idx, raw in enumerate(manifest.samples):
303        try:
304            rebuilt.append(sample_model.model_validate(raw).model_dump(exclude_none=True))
305        except ValidationError as e:
306            for err in e.errors():
307                # Prefix each error's location with the sample index so
308                # downstream tooling can point at the offending entry.
309                err = dict(err)
310                err["loc"] = ("samples", idx, *err.get("loc", ()))
311                sample_errors.append(err)
312    if sample_errors:
313        raise ManifestError(
314            f"{len(sample_errors)} sample(s) failed {sample_model.__name__} validation.",
315            hint="Each 'loc' entry points at the offending sample index and field.",
316            details={"pydantic_errors": sample_errors},
317        )
318    manifest.samples = rebuilt
319    return manifest
320
321
322def dump_json_schema(task: TaskType | None = None) -> Dict[str, Any]:
323    """Return the JSON Schema for a manifest (optionally task-scoped).
324
325    When ``task`` is ``None``, returns the generic
326    :class:`Manifest` schema. When a task is passed, returns the
327    per-task :class:`BaseSampleEntry` schema — useful for emitting
328    per-task documentation tables.
329
330    Examples
331    --------
332    >>> from rpx_benchmark.schemas import dump_json_schema
333    >>> from rpx_benchmark.api import TaskType
334    >>> schema = dump_json_schema(TaskType.MONOCULAR_DEPTH)
335    >>> "depth" in schema["properties"]
336    True
337    """
338    if task is None:
339        return Manifest.model_json_schema()
340    return SAMPLE_MODELS[task].model_json_schema()
class BaseSampleEntry(_StrictBase):
 94class BaseSampleEntry(_StrictBase):
 95    """Fields common to every per-task sample entry."""
 96
 97    id: str = Field(..., description="Unique sample id, typically '{scene}_{phase}_{frame}'.")
 98    rgb: str = Field(..., description="Relative or absolute path to the RGB image.")
 99    scene: str | None = None
100    phase: Phase | int | None = Field(
101        default=None,
102        description="Capture phase label ('clutter'/'interaction'/'clean') or 0/1/2.",
103    )
104    difficulty: Difficulty | None = None
105    pose: str | None = Field(
106        default=None,
107        description="Path to a T265 NPZ pose file (camera-to-world SE(3)).",
108    )
109    fisheye_left: str | None = None
110    fisheye_right: str | None = None
111    rgb_b: str | None = Field(
112        default=None,
113        description="Secondary RGB frame path (pair tasks: keypoint matching, pose).",
114    )
115    metadata: Dict[str, Any] | None = None

Fields common to every per-task sample entry.

id: str
rgb: str
scene: str | None
phase: rpx_benchmark.api.Phase | int | None
difficulty: rpx_benchmark.api.Difficulty | None
pose: str | None
fisheye_left: str | None
fisheye_right: str | None
rgb_b: str | None
metadata: Optional[Dict[str, Any]]
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class DepthSampleEntry(BaseSampleEntry):
118class DepthSampleEntry(BaseSampleEntry):
119    depth: str = Field(..., description="Path to a 16-bit PNG depth map in millimetres.")

Fields common to every per-task sample entry.

depth: str
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class DetectionSampleEntry(BaseSampleEntry):
152class DetectionSampleEntry(BaseSampleEntry):
153    boxes: str = Field(..., description="Path to a JSON list of {bbox: [x1,y1,x2,y2], label: str}.")

Fields common to every per-task sample entry.

boxes: str
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class SegmentationSampleEntry(BaseSampleEntry):
156class SegmentationSampleEntry(BaseSampleEntry):
157    mask: str = Field(..., description="Path to a single-channel PNG with per-pixel instance IDs.")

Fields common to every per-task sample entry.

mask: str
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class TrackingSampleEntry(BaseSampleEntry):
160class TrackingSampleEntry(BaseSampleEntry):
161    tracks: str = Field(..., description="Path to a JSON list of tracklets.")

Fields common to every per-task sample entry.

tracks: str
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class RelativePoseSampleEntry(BaseSampleEntry):
164class RelativePoseSampleEntry(BaseSampleEntry):
165    pose_a: str
166    pose_b: str
167    rgb_b: str = Field(..., description="Secondary RGB frame (required for pose pairs).")

Fields common to every per-task sample entry.

pose_a: str
pose_b: str
rgb_b: str
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class VisualGroundingSampleEntry(BaseSampleEntry):
170class VisualGroundingSampleEntry(BaseSampleEntry):
171    text: str
172    boxes: str | List[List[float]] | None = None
173    labels: List[str] | None = None

Fields common to every per-task sample entry.

text: str
boxes: Union[str, List[List[float]], NoneType]
labels: Optional[List[str]]
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class SparseDepthSampleEntry(BaseSampleEntry):
176class SparseDepthSampleEntry(BaseSampleEntry):
177    coordinates: str | List[List[float]]
178    depths: str | List[float]

Fields common to every per-task sample entry.

coordinates: Union[str, List[List[float]]]
depths: Union[str, List[float]]
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class KeypointMatchingSampleEntry(BaseSampleEntry):
183class KeypointMatchingSampleEntry(BaseSampleEntry):
184    points0: str | List[List[float]]
185    points1: str | List[List[float]]
186    visibility: str | List[bool] | None = None
187    rgb_b: str = Field(..., description="Secondary RGB frame (required for keypoint matching).")

Fields common to every per-task sample entry.

points0: Union[str, List[List[float]]]
points1: Union[str, List[List[float]]]
visibility: Union[str, List[bool], NoneType]
rgb_b: str
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class Manifest(_StrictBase):
221class Manifest(_StrictBase):
222    """Typed manifest describing a slice of the RPX dataset.
223
224    Attributes
225    ----------
226    task : TaskType
227        Task this manifest targets. Determines which
228        :class:`BaseSampleEntry` subclass each sample is validated
229        against.
230    root : str, optional
231        Root the sample paths resolve against. Absent manifests must
232        carry absolute paths in every sample.
233    samples : list of SampleEntry
234        Per-sample records. Parsed against the task-specific model in
235        :data:`SAMPLE_MODELS`.
236    scenes : list, optional
237        Alternate representation enumerating ``(scene, phase)`` pairs.
238        Preserved verbatim so hub download logic can read it without
239        round-tripping through per-sample records.
240    """
241
242    task: TaskType
243    root: str | None = None
244    samples: List[Dict[str, Any]] = Field(default_factory=list)
245    scenes: List[Dict[str, Any]] | None = None

Typed manifest describing a slice of the RPX dataset.

Attributes

task : TaskType Task this manifest targets. Determines which BaseSampleEntry subclass each sample is validated against. root : str, optional Root the sample paths resolve against. Absent manifests must carry absolute paths in every sample. samples : list of SampleEntry Per-sample records. Parsed against the task-specific model in SAMPLE_MODELS. scenes : list, optional Alternate representation enumerating (scene, phase) pairs. Preserved verbatim so hub download logic can read it without round-tripping through per-sample records.

root: str | None
samples: List[Dict[str, Any]]
scenes: Optional[List[Dict[str, Any]]]
model_config = {'extra': 'allow', 'str_strip_whitespace': True, 'populate_by_name': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

SAMPLE_MODELS: Dict[rpx_benchmark.api.TaskType, Type[BaseSampleEntry]] = {<TaskType.MONOCULAR_DEPTH: 'monocular_depth'>: <class 'DepthSampleEntry'>, <TaskType.VIDEO_DEPTH: 'video_depth'>: <class 'rpx_benchmark.schemas.VideoDepthSampleEntry'>, <TaskType.OBJECT_DETECTION: 'object_detection'>: <class 'DetectionSampleEntry'>, <TaskType.OPEN_VOCAB_DETECTION: 'open_vocab_detection'>: <class 'DetectionSampleEntry'>, <TaskType.OBJECT_SEGMENTATION: 'object_segmentation'>: <class 'SegmentationSampleEntry'>, <TaskType.OBJECT_TRACKING: 'object_tracking'>: <class 'TrackingSampleEntry'>, <TaskType.RELATIVE_CAMERA_POSE: 'relative_camera_pose'>: <class 'RelativePoseSampleEntry'>, <TaskType.VISUAL_GROUNDING: 'visual_grounding'>: <class 'VisualGroundingSampleEntry'>, <TaskType.SPARSE_DEPTH: 'sparse_depth'>: <class 'SparseDepthSampleEntry'>, <TaskType.KEYPOINT_MATCHING: 'keypoint_matching'>: <class 'KeypointMatchingSampleEntry'>}
def validate_manifest( source: Union[str, pathlib.Path, Dict[str, Any]]) -> Manifest:
253def validate_manifest(source: str | Path | Dict[str, Any]) -> Manifest:
254    """Validate ``source`` against the :class:`Manifest` schema.
255
256    Parameters
257    ----------
258    source : str, Path, or dict
259        Path to a manifest JSON, or an already-parsed dict.
260
261    Returns
262    -------
263    Manifest
264        The validated model.
265
266    Raises
267    ------
268    ManifestError
269        If the JSON is malformed or fails any schema validation. The
270        wrapped ``ValidationError`` message is surfaced in the
271        exception's ``details`` dict for programmatic access.
272    """
273    import json
274
275    if isinstance(source, (str, Path)):
276        path = Path(source)
277        if not path.is_file():
278            raise ManifestError(f"Manifest file not found: {path}")
279        try:
280            with path.open("r", encoding="utf-8") as f:
281                data = json.load(f)
282        except json.JSONDecodeError as e:
283            raise ManifestError(f"Manifest at {path} is not valid JSON: {e}") from e
284    else:
285        data = source
286
287    try:
288        manifest = Manifest.model_validate(data)
289    except ValidationError as e:
290        raise ManifestError(
291            "Manifest failed schema validation.",
292            hint="Run `rpx validate-manifest <path>` to see field-level errors.",
293            details={"pydantic_errors": e.errors()},
294        ) from e
295
296    # Per-sample validation against the task-specific schema. Kept
297    # outside the model_validator so per-sample errors surface as a
298    # single ManifestError with the full pydantic error list rather
299    # than being collapsed into one ValueError.
300    sample_model = SAMPLE_MODELS[manifest.task]
301    sample_errors: list[Dict[str, Any]] = []
302    rebuilt: List[Dict[str, Any]] = []
303    for idx, raw in enumerate(manifest.samples):
304        try:
305            rebuilt.append(sample_model.model_validate(raw).model_dump(exclude_none=True))
306        except ValidationError as e:
307            for err in e.errors():
308                # Prefix each error's location with the sample index so
309                # downstream tooling can point at the offending entry.
310                err = dict(err)
311                err["loc"] = ("samples", idx, *err.get("loc", ()))
312                sample_errors.append(err)
313    if sample_errors:
314        raise ManifestError(
315            f"{len(sample_errors)} sample(s) failed {sample_model.__name__} validation.",
316            hint="Each 'loc' entry points at the offending sample index and field.",
317            details={"pydantic_errors": sample_errors},
318        )
319    manifest.samples = rebuilt
320    return manifest

Validate source against the Manifest schema.

Parameters

source : str, Path, or dict Path to a manifest JSON, or an already-parsed dict.

Returns

Manifest The validated model.

Raises

ManifestError If the JSON is malformed or fails any schema validation. The wrapped ValidationError message is surfaced in the exception's details dict for programmatic access.

def dump_json_schema(task: rpx_benchmark.api.TaskType | None = None) -> Dict[str, Any]:
323def dump_json_schema(task: TaskType | None = None) -> Dict[str, Any]:
324    """Return the JSON Schema for a manifest (optionally task-scoped).
325
326    When ``task`` is ``None``, returns the generic
327    :class:`Manifest` schema. When a task is passed, returns the
328    per-task :class:`BaseSampleEntry` schema — useful for emitting
329    per-task documentation tables.
330
331    Examples
332    --------
333    >>> from rpx_benchmark.schemas import dump_json_schema
334    >>> from rpx_benchmark.api import TaskType
335    >>> schema = dump_json_schema(TaskType.MONOCULAR_DEPTH)
336    >>> "depth" in schema["properties"]
337    True
338    """
339    if task is None:
340        return Manifest.model_json_schema()
341    return SAMPLE_MODELS[task].model_json_schema()

Return the JSON Schema for a manifest (optionally task-scoped).

When task is None, returns the generic Manifest schema. When a task is passed, returns the per-task BaseSampleEntry schema — useful for emitting per-task documentation tables.

Examples

>>> from rpx_benchmark.schemas import dump_json_schema
>>> from rpx_benchmark.api import TaskType
>>> schema = dump_json_schema(TaskType.MONOCULAR_DEPTH)
>>> "depth" in schema["properties"]
True