← Files RAGOpsARCHIVED FILE
skills/evaluate-ai-release/scripts/vendor/ragops/datasets.py
14 KB · Oct 2, 2026 · 00:29 UTC
from __future__ import annotations
import hashlib
import json
import re
import unicodedata
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any, Iterable, Mapping
from ragops.loader import ContractError
@dataclass(frozen=True)
class DatasetCaseRecord:
id: str
digest: str
normalized_content_digest: str
category: str
severity: str
language: str
tags: tuple[str, ...]
source: str
reviewed: bool
@dataclass(frozen=True)
class DatasetManifest:
schema_version: str
dataset_id: str
version: str
source_classification: str
owners: tuple[str, ...]
review_state: str
cases: tuple[DatasetCaseRecord, ...]
splits: dict[str, tuple[str, ...]]
def to_dict(self) -> dict[str, Any]:
return {
"schema_version": self.schema_version,
"dataset_id": self.dataset_id,
"version": self.version,
"source_classification": self.source_classification,
"owners": list(self.owners),
"review_state": self.review_state,
"cases": [asdict(item) | {"tags": list(item.tags)} for item in self.cases],
"splits": {name: list(case_ids) for name, case_ids in sorted(self.splits.items())},
}
@dataclass(frozen=True)
class DatasetIssue:
code: str
message: str
case_ids: tuple[str, ...] = ()
observed: int | None = None
required: int | None = None
def to_dict(self) -> dict[str, Any]:
return asdict(self)
@dataclass(frozen=True)
class DatasetDiff:
dataset_id: str
from_version: str
to_version: str
added_case_ids: tuple[str, ...]
removed_case_ids: tuple[str, ...]
changed_case_ids: tuple[str, ...]
def to_dict(self) -> dict[str, Any]:
return asdict(self)
def create_dataset_manifest(
dataset_id: str,
version: str,
cases: Iterable[Mapping[str, Any]],
*,
splits: Mapping[str, tuple[str, ...]],
source_classification: str,
owners: tuple[str, ...],
review_state: str = "draft",
) -> DatasetManifest:
if not isinstance(dataset_id, str) or not dataset_id:
raise ContractError("Dataset ID must be a non-empty string")
if not isinstance(version, str) or not version:
raise ContractError("Dataset version must be a non-empty string")
raw_cases = tuple(cases)
if not raw_cases:
raise ContractError("Dataset manifest requires at least one case")
records = tuple(_case_record(item, index) for index, item in enumerate(raw_cases))
identifiers = [item.id for item in records]
if len(set(identifiers)) != len(identifiers):
raise ContractError("Dataset case IDs must be unique")
normalized_splits = _splits(splits, set(identifiers))
manifest = DatasetManifest(
schema_version="1.0",
dataset_id=dataset_id,
version=version,
source_classification=source_classification,
owners=tuple(owners),
review_state=review_state,
cases=records,
splits=normalized_splits,
)
_validate_manifest_contract(manifest)
return manifest
def validate_dataset_manifest(
manifest: DatasetManifest,
*,
minimum_slice_counts: Mapping[str, int] | None = None,
) -> tuple[DatasetIssue, ...]:
_validate_manifest_contract(manifest)
issues = []
split_by_case: dict[str, set[str]] = {}
for split_name, case_ids in manifest.splits.items():
for case_id in case_ids:
split_by_case.setdefault(case_id, set()).add(split_name)
for case_id, split_names in sorted(split_by_case.items()):
if len(split_names) > 1:
issues.append(
DatasetIssue(
"split_membership_leakage",
f"Case {case_id!r} appears in multiple splits: {sorted(split_names)}",
(case_id,),
)
)
grouped: dict[str, list[DatasetCaseRecord]] = {}
for case in manifest.cases:
grouped.setdefault(case.normalized_content_digest, []).append(case)
for matching in grouped.values():
if len(matching) < 2:
continue
case_ids = tuple(sorted(item.id for item in matching))
split_names = {
split
for case_id in case_ids
for split in split_by_case.get(case_id, set())
}
code = "normalized_content_leakage" if len(split_names) > 1 else "duplicate_content"
issues.append(
DatasetIssue(code, f"Normalized duplicate cases detected: {list(case_ids)}", case_ids)
)
missing_source = tuple(sorted(item.id for item in manifest.cases if not item.source))
if missing_source:
issues.append(
DatasetIssue("missing_provenance", "Dataset cases are missing source provenance", missing_source)
)
if manifest.source_classification == "production-derived":
unreviewed = tuple(sorted(item.id for item in manifest.cases if not item.reviewed))
if unreviewed:
issues.append(
DatasetIssue(
"review_required",
"Production-derived cases must remain quarantined until reviewed",
unreviewed,
)
)
for selector, required in sorted((minimum_slice_counts or {}).items()):
if isinstance(required, bool) or not isinstance(required, int) or required <= 0:
raise ContractError(f"Dataset slice {selector!r} minimum must be positive")
selected = tuple(item.id for item in manifest.cases if _slice_match(item, selector))
if len(selected) < required:
issues.append(
DatasetIssue(
"slice_coverage",
f"Dataset slice {selector!r} has {len(selected)} cases; requires {required}",
selected,
len(selected),
required,
)
)
return tuple(sorted(issues, key=lambda item: (item.code, item.case_ids)))
def diff_dataset_manifests(before: DatasetManifest, after: DatasetManifest) -> DatasetDiff:
if before.dataset_id != after.dataset_id:
raise ContractError("Dataset diff requires matching dataset IDs")
before_cases = {item.id: item.digest for item in before.cases}
after_cases = {item.id: item.digest for item in after.cases}
shared = set(before_cases) & set(after_cases)
return DatasetDiff(
dataset_id=before.dataset_id,
from_version=before.version,
to_version=after.version,
added_case_ids=tuple(sorted(set(after_cases) - set(before_cases))),
removed_case_ids=tuple(sorted(set(before_cases) - set(after_cases))),
changed_case_ids=tuple(
sorted(case_id for case_id in shared if before_cases[case_id] != after_cases[case_id])
),
)
def write_dataset_manifest(path: str | Path, manifest: DatasetManifest) -> None:
_validate_manifest_contract(manifest)
output = Path(path)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(
json.dumps(manifest.to_dict(), ensure_ascii=False, sort_keys=True, indent=2) + "\n",
encoding="utf-8",
)
def load_dataset_manifest(path: str | Path) -> DatasetManifest:
try:
data = json.loads(Path(path).read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise ContractError(f"Cannot load dataset manifest from {path}: {exc}") from exc
if not isinstance(data, dict) or set(data) != {
"schema_version",
"dataset_id",
"version",
"source_classification",
"owners",
"review_state",
"cases",
"splits",
}:
raise ContractError("Dataset manifest fields do not match schema 1.0")
if data["schema_version"] != "1.0":
raise ContractError(f"Unsupported dataset manifest schema: {data['schema_version']}")
raw_cases = data["cases"]
if not isinstance(raw_cases, list):
raise ContractError("Dataset manifest cases must be an array")
records = []
expected_case_fields = {
"id",
"digest",
"normalized_content_digest",
"category",
"severity",
"language",
"tags",
"source",
"reviewed",
}
for index, item in enumerate(raw_cases):
if not isinstance(item, dict) or set(item) != expected_case_fields:
raise ContractError(f"Dataset case record {index} fields are invalid")
records.append(
DatasetCaseRecord(
id=item["id"],
digest=item["digest"],
normalized_content_digest=item["normalized_content_digest"],
category=item["category"],
severity=item["severity"],
language=item["language"],
tags=tuple(item["tags"]),
source=item["source"],
reviewed=item["reviewed"],
)
)
raw_splits = data["splits"]
if not isinstance(raw_splits, dict):
raise ContractError("Dataset splits must be an object")
manifest = DatasetManifest(
"1.0",
data["dataset_id"],
data["version"],
data["source_classification"],
tuple(data["owners"]),
data["review_state"],
tuple(records),
{name: tuple(case_ids) for name, case_ids in raw_splits.items()},
)
_validate_manifest_contract(manifest)
return manifest
def _case_record(value: Mapping[str, Any], index: int) -> DatasetCaseRecord:
required = {"id", "question", "evidence", "required_citation_ids"}
missing = sorted(required - set(value))
if missing:
raise ContractError(f"Dataset case {index} missing fields: {missing}")
try:
canonical = json.dumps(dict(value), ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False)
except (TypeError, ValueError) as exc:
raise ContractError(f"Dataset case {index} must contain finite JSON values") from exc
normalized_payload = {
"question": _normalize_text(value["question"]),
"evidence": sorted(_normalize_text(item) for item in value["evidence"]),
}
normalized = json.dumps(normalized_payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
tags = value.get("tags", [])
if not isinstance(tags, (list, tuple)) or any(not isinstance(item, str) for item in tags):
raise ContractError(f"Dataset case {index} tags must be a string array")
reviewed = value.get("reviewed", False)
if not isinstance(reviewed, bool):
raise ContractError(f"Dataset case {index} reviewed must be boolean")
return DatasetCaseRecord(
id=value["id"],
digest=hashlib.sha256(canonical.encode("utf-8")).hexdigest(),
normalized_content_digest=hashlib.sha256(normalized.encode("utf-8")).hexdigest(),
category=value.get("category", "unspecified"),
severity=value.get("severity", "medium"),
language=value.get("language", "und"),
tags=tuple(tags),
source=value.get("source", ""),
reviewed=reviewed,
)
def _splits(value: Mapping[str, tuple[str, ...]], case_ids: set[str]) -> dict[str, tuple[str, ...]]:
if not isinstance(value, Mapping) or not value:
raise ContractError("Dataset splits must be a non-empty mapping")
result = {}
for name, identifiers in value.items():
if not isinstance(name, str) or not name:
raise ContractError("Dataset split names must be non-empty strings")
if not isinstance(identifiers, (list, tuple)) or not identifiers:
raise ContractError(f"Dataset split {name!r} must contain case IDs")
if any(not isinstance(item, str) or not item for item in identifiers):
raise ContractError(f"Dataset split {name!r} case IDs must be non-empty strings")
if len(set(identifiers)) != len(identifiers):
raise ContractError(f"Dataset split {name!r} case IDs must be unique")
unknown = sorted(set(identifiers) - case_ids)
if unknown:
raise ContractError(f"Dataset split {name!r} has unknown case IDs: {unknown}")
result[name] = tuple(identifiers)
return dict(sorted(result.items()))
def _validate_manifest_contract(manifest: DatasetManifest) -> None:
if manifest.schema_version != "1.0":
raise ContractError(f"Unsupported dataset manifest schema: {manifest.schema_version}")
if not manifest.dataset_id or not manifest.version:
raise ContractError("Dataset ID and version must be non-empty")
if manifest.source_classification not in {"synthetic", "public", "production-derived"}:
raise ContractError("Dataset source classification is invalid")
if not manifest.owners or any(not owner for owner in manifest.owners):
raise ContractError("Dataset owners must be non-empty strings")
if manifest.review_state not in {"draft", "reviewed", "accepted", "superseded"}:
raise ContractError("Dataset review state is invalid")
identifiers = [item.id for item in manifest.cases]
if not identifiers or len(set(identifiers)) != len(identifiers):
raise ContractError("Dataset case IDs must be unique")
digest_pattern = re.compile(r"^[0-9a-f]{64}$")
for item in manifest.cases:
if not item.id or digest_pattern.fullmatch(item.digest) is None or digest_pattern.fullmatch(item.normalized_content_digest) is None:
raise ContractError(f"Dataset case record {item.id!r} is invalid")
_splits(manifest.splits, set(identifiers))
def _slice_match(item: DatasetCaseRecord, selector: str) -> bool:
if "=" not in selector:
field, expected = "severity", selector
else:
field, expected = selector.split("=", 1)
if field == "tag":
return expected in item.tags
if field not in {"category", "severity", "language"} or not expected:
raise ContractError(f"Invalid dataset slice selector: {selector!r}")
return getattr(item, field) == expected
def _normalize_text(value: object) -> str:
if not isinstance(value, str):
raise ContractError("Dataset question and evidence must be strings")
return " ".join(unicodedata.normalize("NFKC", value).casefold().split())
SHA-256: 65db2c9bb4a19d85064081283bd1095cd5742ade2206c5a48d8a2e33e8eda061