pjc43's picture
Sync from GitHub via hub-sync
0b79533 verified
Raw
History Blame Contribute Delete
2.53 kB
from __future__ import annotations
import csv
import json
from pathlib import Path
from typing import Any
def read_eval_records(path: Path) -> list[dict[str, Any]]:
suffix = path.suffix.lower()
if suffix == ".csv":
with path.open("r", encoding="utf-8", newline="") as input_file:
return [normalize_record(row, index) for index, row in enumerate(csv.DictReader(input_file), start=1)]
if suffix in {".jsonl", ".ndjson"}:
return read_jsonl(path)
raise ValueError(f"Unsupported eval input format: {path.suffix}. Use CSV or JSONL.")
def read_jsonl(path: Path) -> list[dict[str, Any]]:
rows = []
with path.open("r", encoding="utf-8") as input_file:
for line_number, line in enumerate(input_file, start=1):
text = line.strip()
if not text:
continue
try:
record = json.loads(text)
except json.JSONDecodeError as exc:
raise ValueError(f"{path}:{line_number} is not valid JSON") from exc
if not isinstance(record, dict):
raise ValueError(f"{path}:{line_number} must be a JSON object")
rows.append(normalize_record(record, line_number))
return rows
def normalize_record(record: dict[str, Any], index: int) -> dict[str, Any]:
transcript = str(record.get("transcript") or record.get("text") or "").strip()
normalized: dict[str, Any] = {
"id": str(record.get("id") or record.get("speech_id") or f"record-{index}"),
"transcript": transcript,
"stats": build_stats(record, transcript),
}
for key in ("category", "speech_type", "scenario_notes", "has_garble", "gold_feedback"):
value = record.get(key)
if value not in (None, ""):
normalized[key] = value
return normalized
def build_stats(record: dict[str, Any], transcript: str) -> dict[str, Any]:
from speech_stats import build_transcript_stats
duration_seconds = optional_float(record.get("duration_sec") or record.get("duration_seconds"))
duration_mmss = str(record.get("duration_mmss") or "").strip() or None
return build_transcript_stats(
transcript,
duration_seconds=duration_seconds,
duration_mmss=duration_mmss,
)
def optional_int(value: Any) -> int | None:
if value in (None, ""):
return None
return int(float(value))
def optional_float(value: Any) -> float | None:
if value in (None, ""):
return None
return float(value)