Video-ORA-4B / code /eval /task /canonical_data.py
lyhisme's picture
Add 4B model card, benchmark assets, and release code
0185029 verified
Raw
History Blame Contribute Delete
14.5 kB
"""Dependency-light compatibility adapter for canonical OraRL evaluation rows."""
from __future__ import annotations
import copy
import json
import os
import re
from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any
_CANONICAL_FIELDS = {
"schema_version",
"eval_task",
"sample_id",
"benchmark",
"split",
"problem",
"answer",
"images",
"videos",
"problem_type",
"source",
}
_MEDIA_PATH_KEYS = (
"path",
"video",
"image",
"video_path",
"image_path",
"file_name",
)
_PATH_KEY_RE = re.compile(
r"(?:^|_)(?:artifact|artifacts|cfg|checkpoint|ckpt|dir|directory|file|"
r"files|image|images|media|path|paths|prefix|root|subtitle|"
r"subtitles|tensor|tensors|video|videos)$"
)
_GROUND_TRUTH_KEYS = (
"bbox",
"bboxes",
"boxes",
"coordinates",
"duration",
"end",
"frame",
"frame_id",
"frame_ids",
"gt_bbox",
"mask",
"masks",
"negative_points",
"normalized_solution",
"object",
"positive_points",
"query",
"segment",
"segmentation",
"span",
"spans",
"start",
"target",
"time",
"timestamp",
"timestamps",
"trajectory",
)
_ASSET_DIRECTORIES = frozenset({"artifacts", "media"})
class CanonicalDataError(ValueError):
"""Raised when a canonical row cannot be safely adapted."""
def is_canonical_row(record: Any) -> bool:
"""Return whether a mapping uses the canonical evaluation-row contract."""
return (
isinstance(record, Mapping)
and record.get("schema_version") == 1
and _CANONICAL_FIELDS.issubset(record)
)
def _configured_root() -> Path | None:
value = os.environ.get("ORARL_EVAL_DATA_ROOT", "").strip()
if value:
return Path(value).expanduser().resolve()
manifest = os.environ.get("ORARL_EVAL_DATASETS_JSONL", "").strip()
if manifest:
return Path(manifest).expanduser().resolve().parent
return None
def _configured_asset_root() -> Path | None:
value = os.environ.get("ORARL_EVAL_ASSET_ROOT", "").strip()
return Path(value).expanduser().resolve() if value else None
def _infer_root(input_path: Path) -> Path | None:
configured = _configured_root()
if configured is not None:
return configured
for candidate in (input_path.parent, *input_path.parents):
if (candidate / "datasets.jsonl").is_file():
return candidate.resolve()
return None
def canonical_dataset_record(
eval_task: str,
split: str,
root: str | os.PathLike[str] | None = None,
) -> dict[str, Any] | None:
"""Return one canonical ``datasets.jsonl`` record for a task split."""
repository_root = (
Path(root).expanduser().resolve() if root is not None else _configured_root()
)
if repository_root is None:
return None
manifest = repository_root / "datasets.jsonl"
if not manifest.is_file():
return None
normalized_split = str(split).strip().casefold().replace("-", "_")
matches: list[dict[str, Any]] = []
try:
with manifest.open("r", encoding="utf-8") as handle:
for line_number, line in enumerate(handle, start=1):
if not line.strip():
continue
try:
record = json.loads(line)
except json.JSONDecodeError as error:
raise CanonicalDataError(
f"{manifest}:{line_number}: invalid JSON: {error}"
) from error
if not isinstance(record, Mapping):
raise CanonicalDataError(
f"{manifest}:{line_number}: dataset record must be an object"
)
record_task = str(record.get("task") or "").casefold()
record_split = str(record.get("split") or "").casefold()
if record_task == str(eval_task).casefold() and record_split == normalized_split:
matches.append(copy.deepcopy(dict(record)))
except OSError as error:
raise CanonicalDataError(f"cannot read canonical dataset manifest: {error}") from error
if len(matches) > 1:
raise CanonicalDataError(
f"duplicate canonical dataset profile for {eval_task}/{normalized_split}"
)
return matches[0] if matches else None
def resolve_repository_path(
value: str,
root: str | os.PathLike[str],
asset_root: str | os.PathLike[str] | None = None,
) -> str:
"""Resolve one repository-relative canonical path without allowing escape."""
raw = str(value).strip()
if not raw:
raise CanonicalDataError("canonical repository path must be nonempty")
repository_root = Path(root).expanduser().resolve()
configured_asset_root = (
Path(asset_root).expanduser().resolve()
if asset_root is not None
else _configured_asset_root()
)
candidate = Path(raw).expanduser()
selected_root = repository_root
if (
not candidate.is_absolute()
and candidate.parts
and candidate.parts[0].casefold() in _ASSET_DIRECTORIES
and configured_asset_root is not None
):
selected_root = configured_asset_root
resolved = (
candidate.resolve()
if candidate.is_absolute()
else (selected_root / candidate).resolve()
)
allowed_roots = [repository_root]
if configured_asset_root is not None:
allowed_roots.append(configured_asset_root)
try:
next(root for root in allowed_roots if resolved.is_relative_to(root))
except StopIteration as error:
raise CanonicalDataError(
f"canonical path resolves outside configured evaluation roots: {raw}"
) from error
return os.path.normpath(str(resolved))
def repository_relative_output_path(
value: str,
root: str | os.PathLike[str],
) -> str:
"""Return a ``./``-prefixed path when an asset is contained by ``root``."""
raw = str(value).strip()
if not raw or not str(root).strip():
return raw
candidate = Path(raw).expanduser()
if not candidate.is_absolute():
return raw
repository_root = Path(root).expanduser().resolve()
resolved = candidate.resolve()
try:
relative = resolved.relative_to(repository_root)
except ValueError:
return raw
return f"./{relative.as_posix()}"
def _is_path_key(value: object) -> bool:
return isinstance(value, str) and bool(_PATH_KEY_RE.search(value.casefold()))
def _resolve_media_entry(value: Any, root: Path, asset_root: Path | None) -> Any:
if isinstance(value, str):
return resolve_repository_path(value, root, asset_root)
if not isinstance(value, Mapping):
return copy.deepcopy(value)
resolved = copy.deepcopy(dict(value))
for key in _MEDIA_PATH_KEYS:
path = resolved.get(key)
if isinstance(path, str) and path.strip():
resolved[key] = resolve_repository_path(path, root, asset_root)
return resolved
def _resolve_declared_paths(
value: Any,
root: Path,
asset_root: Path | None,
path_context: bool = False,
) -> Any:
if isinstance(value, str):
return (
resolve_repository_path(value, root, asset_root)
if path_context
else value
)
if isinstance(value, Mapping):
return {
key: _resolve_declared_paths(
item,
root,
asset_root,
path_context=path_context or _is_path_key(key),
)
for key, item in value.items()
}
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
return [
_resolve_declared_paths(
item,
root,
asset_root,
path_context=path_context,
)
for item in value
]
return copy.deepcopy(value)
def _media_path(value: Any) -> str:
if isinstance(value, str):
return value
if isinstance(value, Mapping):
for key in _MEDIA_PATH_KEYS:
candidate = value.get(key)
if isinstance(candidate, str) and candidate:
return candidate
return ""
def _declared_path_values(value: Any, path_context: bool = False) -> list[str]:
if isinstance(value, str):
return [value] if path_context else []
if isinstance(value, Mapping):
paths: list[str] = []
for key, item in value.items():
paths.extend(
_declared_path_values(
item,
path_context=path_context or _is_path_key(key),
)
)
return paths
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
paths = []
for item in value:
paths.extend(_declared_path_values(item, path_context=path_context))
return paths
return []
def adapt_canonical_row(
record: Mapping[str, Any],
root: str | os.PathLike[str] | None = None,
asset_root: str | os.PathLike[str] | None = None,
) -> dict[str, Any]:
"""Resolve canonical paths and add non-destructive legacy aliases."""
if not is_canonical_row(record):
return copy.deepcopy(dict(record))
repository_root = (
Path(root).expanduser().resolve() if root is not None else _configured_root()
)
if repository_root is None:
raise CanonicalDataError(
"canonical rows require ORARL_EVAL_DATA_ROOT or an explicit root"
)
resolved_asset_root = (
Path(asset_root).expanduser().resolve()
if asset_root is not None
else _configured_asset_root()
)
adapted = copy.deepcopy(dict(record))
for field in ("images", "videos", "subtitles"):
values = adapted.get(field)
if isinstance(values, list):
adapted[field] = [
_resolve_media_entry(value, repository_root, resolved_asset_root)
for value in values
]
for field in ("preprocessed", "task_payload", "metadata", "evaluation"):
value = adapted.get(field)
if isinstance(value, Mapping):
adapted[field] = _resolve_declared_paths(
value,
repository_root,
resolved_asset_root,
)
image_paths = [
path for value in adapted.get("images", []) if (path := _media_path(value))
]
video_paths = [
path for value in adapted.get("videos", []) if (path := _media_path(value))
]
subtitle_paths = [
path
for value in adapted.get("subtitles", [])
if (path := _media_path(value))
]
adapted.setdefault("id", adapted["sample_id"])
adapted.setdefault("problem_id", adapted["sample_id"])
adapted.setdefault("question_id", adapted["sample_id"])
adapted.setdefault("question", adapted["problem"])
adapted.setdefault("prompt", adapted["problem"])
adapted.setdefault("ground_truth", adapted["answer"])
adapted.setdefault("solution", adapted["answer"])
adapted.setdefault("dataset", adapted["benchmark"])
adapted.setdefault("options", copy.deepcopy(adapted.get("choices", [])))
adapted.setdefault("image_list", image_paths)
if image_paths:
adapted.setdefault("image", image_paths[0])
adapted.setdefault("image_path", image_paths[0])
if video_paths:
adapted.setdefault("video", video_paths[0])
adapted.setdefault("video_path", video_paths[0])
if video_paths or image_paths:
adapted.setdefault("path", (video_paths or image_paths)[0])
if subtitle_paths:
adapted.setdefault("subtitle", subtitle_paths[0])
adapted.setdefault("subtitle_path", subtitle_paths[0])
metadata = adapted.get("metadata")
if isinstance(metadata, Mapping):
for key, value in metadata.items():
adapted.setdefault(str(key), copy.deepcopy(value))
payload = adapted.get("task_payload")
if isinstance(payload, Mapping):
for key, value in payload.items():
adapted.setdefault(str(key), copy.deepcopy(value))
if isinstance(adapted.get("answer"), Mapping):
for key in _GROUND_TRUTH_KEYS:
if key in adapted["answer"]:
adapted.setdefault(key, copy.deepcopy(adapted["answer"][key]))
preprocessed = adapted.get("preprocessed")
if isinstance(preprocessed, Mapping):
for key, value in preprocessed.items():
adapted.setdefault(str(key), copy.deepcopy(value))
paths = _declared_path_values(preprocessed)
if paths:
adapted.setdefault("preprocessed_video", paths[0])
adapted.setdefault("preprocessed_video_path", paths[0])
if video_paths:
adapted.setdefault("data_type", "video")
elif image_paths:
adapted.setdefault("data_type", "image")
adapted.setdefault("question_type", adapted["problem_type"])
adapted.setdefault("task_type", adapted["problem_type"])
return adapted
def load_json_records(
path: str | os.PathLike[str],
*,
root: str | os.PathLike[str] | None = None,
asset_root: str | os.PathLike[str] | None = None,
) -> list[dict[str, Any]]:
"""Read JSON/JSONL and adapt only rows that use the canonical contract."""
input_path = Path(path)
text = input_path.read_text(encoding="utf-8")
if not text.strip():
records: list[Any] = []
else:
try:
payload = json.loads(text)
except json.JSONDecodeError:
records = [json.loads(line) for line in text.splitlines() if line.strip()]
else:
records = payload if isinstance(payload, list) else [payload]
if not all(isinstance(record, Mapping) for record in records):
raise CanonicalDataError(f"{input_path}: every record must be a JSON object")
repository_root = (
Path(root).expanduser().resolve()
if root is not None
else _infer_root(input_path)
)
return [
adapt_canonical_row(record, repository_root, asset_root)
if is_canonical_row(record)
else copy.deepcopy(dict(record))
for record in records
]
__all__ = [
"CanonicalDataError",
"adapt_canonical_row",
"canonical_dataset_record",
"is_canonical_row",
"load_json_records",
"resolve_repository_path",
]