"""Small, dependency-free scoring surface for the Hugging Face Space.""" from __future__ import annotations import hashlib import json import re from collections.abc import Mapping, Sequence from typing import Any REPORT_SCHEMA_VERSION = "flavourbench-lab-report-v1" PRIMARY_FAMILIES = ("substitution", "pairing", "constraint") _MARKER = re.compile(r"FINAL_SELECTION\s*:\s*", flags=re.IGNORECASE) _LABEL_TRIPLE = re.compile( r"^\s*([A-H])\s*,\s*([A-H])\s*,\s*([A-H])" r"\s*(?:[\x60*_]+\s*)?(?:<\|close\|>response\s*)?$", flags=re.IGNORECASE, ) class SpaceLabError(ValueError): """The uploaded lab artifact is invalid.""" def _canonical(value: object) -> bytes: return json.dumps( value, ensure_ascii=False, separators=(",", ":"), sort_keys=True, allow_nan=False, ).encode() def _normal_name(value: str) -> str: return " ".join(value.replace("_", " ").casefold().split()) def _parse(task: Mapping[str, Any], completion: str) -> str | None: if len(completion.encode()) > 1024 * 1024: raise SpaceLabError("completion exceeds the 1 MiB endpoint limit") choices = task.get("choices") or {} if set(choices) != set("ABCDEFGH"): return None names = {_normal_name(str(name)): str(label) for label, name in choices.items()} if len(names) != len(choices): return None matches = tuple(_MARKER.finditer(completion)) candidates: set[str] = set() for index, match in enumerate(matches): stop = matches[index + 1].start() if index + 1 < len(matches) else len(completion) segment = completion[match.end() : stop].splitlines()[0].strip() label_match = _LABEL_TRIPLE.fullmatch(segment) if label_match: labels = tuple(label.upper() for label in label_match.groups()) if len(set(labels)) == 3: candidates.add("".join(sorted(labels))) continue rendered = segment.strip().strip("*_").strip(chr(96)).strip() ingredients = tuple(_normal_name(value) for value in rendered.split(",")) if len(ingredients) == 3 and all(value in names for value in ingredients): labels = tuple(names[value] for value in ingredients) if len(set(labels)) == 3: candidates.add("".join(sorted(labels))) return next(iter(candidates)) if len(candidates) == 1 else None def _extract(record: Mapping[str, Any]) -> str | None: if str(record.get("status") or "completed") not in {"completed", "success", "ok"}: return None for key in ("response", "completion", "answer", "answer_markdown"): value = record.get(key) if isinstance(value, str): return value return None def _records(payload: str) -> list[dict[str, Any]]: if len(payload.encode()) > 16 * 1024 * 1024: raise SpaceLabError("artifact exceeds the 16 MiB Space limit") try: value = json.loads(payload) except json.JSONDecodeError: output = [] for number, line in enumerate(payload.splitlines(), start=1): if not line.strip(): continue try: row = json.loads(line) except json.JSONDecodeError as error: raise SpaceLabError(f"invalid JSON on line {number}") from error if not isinstance(row, dict): raise SpaceLabError(f"line {number} is not an object") from None output.append(dict(row)) return output if isinstance(value, list) and all(isinstance(row, dict) for row in value): return [dict(row) for row in value] if isinstance(value, dict) and isinstance(value.get("responses"), list): rows = value["responses"] if all(isinstance(row, dict) for row in rows): return [dict(row) for row in rows] if isinstance(value, dict): return [dict(value)] raise SpaceLabError("artifact must be JSON Lines, an array, or an object with responses") def score_completion( tasks_by_id: Mapping[str, Mapping[str, Any]], task_id: str, completion: str ) -> dict[str, Any]: task = tasks_by_id.get(task_id) if task is None: raise SpaceLabError(f"unknown task_id: {task_id}") selection = _parse(task, completion) score_bps = int(task["selection_scores_bps"].get(selection, 0)) if selection else 0 return { "task_id": task_id, "observed_selection": selection, "parseable": selection is not None, "score_bps": score_bps, "score": score_bps / 100, "reward": score_bps / 10_000, "optimal": score_bps == 10_000, "optimal_selection": task["optimal_selection"], } def score_payload( tasks: Sequence[Mapping[str, Any]], payload: str ) -> tuple[dict[str, Any], list[dict[str, Any]]]: responses = _records(payload) task_by_id = {str(task["task_id"]): task for task in tasks} response_by_id: dict[str, dict[str, Any]] = {} for number, row in enumerate(responses, start=1): task_id = str(row.get("task_id") or "") if not task_id: raise SpaceLabError(f"response {number} has no task_id") if task_id not in task_by_id: raise SpaceLabError(f"unknown task_id: {task_id}") if task_id in response_by_id: raise SpaceLabError(f"duplicate task_id: {task_id}") response_by_id[task_id] = row per_task = [] for task in tasks: task_id = str(task["task_id"]) response = response_by_id.get(task_id) completion = _extract(response) if response is not None else None scoring = ( score_completion(task_by_id, task_id, completion) if completion is not None else { "task_id": task_id, "observed_selection": None, "parseable": False, "score_bps": 0, "score": 0.0, "reward": 0.0, "optimal": False, "optimal_selection": task["optimal_selection"], } ) per_task.append( { "family": task["family"], "anchor_ingredient": task.get("anchor_ingredient"), "status": ( "valid" if scoring["parseable"] else "missing" if response is None else "invalid" ), **scoring, } ) valid = sum(row["parseable"] is True for row in per_task) complete = valid == len(tasks) family_rows = [] for family in PRIMARY_FAMILIES: rows = [row for row in per_task if row["family"] == family] accepted = [row for row in rows if row["parseable"]] family_rows.append( { "family": family, "tasks": len(rows), "valid": len(accepted), "coverage": len(accepted) / len(rows), "score": ( sum(float(row["score"]) for row in accepted) / len(accepted) if rows and len(accepted) == len(rows) else None ), "diagnostic_valid_score": ( sum(float(row["score"]) for row in accepted) / len(accepted) if accepted else None ), } ) comparable_score = ( sum(float(row["score"]) for row in family_rows) / len(PRIMARY_FAMILIES) if complete else None ) diagnostic_families = [ float(row["diagnostic_valid_score"]) for row in family_rows if row["diagnostic_valid_score"] is not None ] report = { "schema_version": REPORT_SCHEMA_VERSION, "comparable": complete, "flavourbench_score": comparable_score, "diagnostic_valid_score": ( sum(diagnostic_families) / len(diagnostic_families) if diagnostic_families else None ), "coverage": { "tasks": len(tasks), "submitted": len(response_by_id), "valid": valid, "missing": sum(row["status"] == "missing" for row in per_task), "invalid": sum(row["status"] == "invalid" for row in per_task), "fraction_valid": valid / len(tasks), }, "families": family_rows, "task_set_semantic_sha256": hashlib.sha256( _canonical(sorted((dict(task) for task in tasks), key=lambda row: str(row["task_id"]))) ).hexdigest(), "response_set_semantic_sha256": hashlib.sha256( _canonical(sorted(responses, key=lambda row: str(row.get("task_id")))) ).hexdigest(), "parser": "flavourbench-selection-set-parser-v3", "scoring": "exact-selection-lookup-bps-v1; equal-family macro mean", "inference": None, "per_task": per_task, } report["artifact_sha256"] = hashlib.sha256(_canonical(report)).hexdigest() return report, per_task def score_payload_json(tasks: Sequence[Mapping[str, Any]], payload: str) -> dict[str, Any]: """Convenience wrapper used by the named Gradio batch endpoint.""" report, _ = score_payload(tasks, payload) return report