59 lines
1.8 KiB
Python
59 lines
1.8 KiB
Python
|
|
"""Versioned contracts shared by plans, trial artifacts, and analysis."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from collections.abc import Mapping, Sequence
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
|
||
|
|
SCHEMA_VERSION = "1.0.0"
|
||
|
|
SCHEMA_MAJOR = 1
|
||
|
|
|
||
|
|
STORAGE_FORMATS = {
|
||
|
|
"plan": "json",
|
||
|
|
"batch_manifest": "json",
|
||
|
|
"trial_manifest": "json",
|
||
|
|
"samples": "numpy-npz",
|
||
|
|
"events": "json-lines",
|
||
|
|
"trial_metrics": "json-lines",
|
||
|
|
"paper_source_data": "csv",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
class ContractError(ValueError):
|
||
|
|
"""Raised when an evidence artifact violates its declared contract."""
|
||
|
|
|
||
|
|
|
||
|
|
def require_schema(document: Mapping[str, Any], expected_kind: str) -> None:
|
||
|
|
if not isinstance(document, Mapping):
|
||
|
|
raise ContractError("document must be a mapping")
|
||
|
|
if document.get("kind") != expected_kind:
|
||
|
|
raise ContractError(
|
||
|
|
f"expected kind {expected_kind!r}, got {document.get('kind')!r}"
|
||
|
|
)
|
||
|
|
version = document.get("schema_version")
|
||
|
|
if not isinstance(version, str):
|
||
|
|
raise ContractError("schema_version must be a string")
|
||
|
|
try:
|
||
|
|
major = int(version.split(".", 1)[0])
|
||
|
|
except (TypeError, ValueError) as exc:
|
||
|
|
raise ContractError(f"invalid schema_version {version!r}") from exc
|
||
|
|
if major != SCHEMA_MAJOR:
|
||
|
|
raise ContractError(
|
||
|
|
f"unsupported schema major {major}; expected {SCHEMA_MAJOR}"
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def require_nonempty_string(value: Any, name: str) -> str:
|
||
|
|
if not isinstance(value, str) or not value.strip():
|
||
|
|
raise ContractError(f"{name} must be a non-empty string")
|
||
|
|
return value
|
||
|
|
|
||
|
|
|
||
|
|
def require_sequence(value: Any, name: str) -> Sequence[Any]:
|
||
|
|
if isinstance(value, (str, bytes)) or not isinstance(value, Sequence):
|
||
|
|
raise ContractError(f"{name} must be a sequence")
|
||
|
|
if not value:
|
||
|
|
raise ContractError(f"{name} must not be empty")
|
||
|
|
return value
|