df8f1881d8
Fourth benchmark axis: teams of LLM agents deliberate in a room, implement code in an isolated VM, and are scored deterministically on correctness/speed. - Multi-language adapter (python/js/go/rust/bash) via MultiPL-E continuation mode - Append-only JSONL ledger with status tracking (ok/dnf/killed/error) so budget-exhausted or crashed runs still record a row (fixes selection bias) - Model-aware wall-clock scaling (U-shaped by param count; 3x for reasoning) - Self-owned SIGALRM/SIGTERM watchdog (RunTimeout: BaseException so broad except Exception handlers in the infer/completion path can't swallow it) - Seed forwarded to Ollama sampler + markdown-fence stripping in completion.py
128 lines
5.5 KiB
Python
128 lines
5.5 KiB
Python
"""Deterministic scoring for one (team, event).
|
|
|
|
M1 covers the verifiable-first layer of SPEC §9: correctness gates everything,
|
|
speed and cost are normalized deterministically, quality/collaboration are left
|
|
to the judge (M4) and reported as ``None`` here. The composite uses a weight
|
|
*profile* (same idea as ``bench/workflows.json``) so re-ranking never re-runs a
|
|
model.
|
|
|
|
Single-sample M1 note: with one implementation attempt, ``pass@1`` is 0/1 and
|
|
``pass^k`` (worst-case reliability) equals it; both fields are emitted so the
|
|
schema is stable when M2 adds repeated samples.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
|
|
# Default weight profile. Quality/collaboration weights are reserved for the
|
|
# judge layer (M4); with judge scores absent they contribute 0 and the profile
|
|
# is renormalized over the available axes so M1 scores stay in [0, 1].
|
|
PROFILES: dict[str, dict[str, float]] = {
|
|
"balanced": {"correctness": 0.6, "speed": 0.2, "quality": 0.1,
|
|
"collaboration": 0.1},
|
|
"correctness": {"correctness": 0.9, "speed": 0.1, "quality": 0.0,
|
|
"collaboration": 0.0},
|
|
"speed": {"correctness": 0.5, "speed": 0.5, "quality": 0.0,
|
|
"collaboration": 0.0},
|
|
}
|
|
|
|
|
|
@dataclass
|
|
class EventOutcome:
|
|
"""What the loop produces for one team at one event (deterministic facts)."""
|
|
team: str
|
|
challenge: str
|
|
submitted: bool
|
|
correct: bool # hidden tests all passed
|
|
rounds: int # deliberate/implement rounds consumed
|
|
wall_clock_s: float
|
|
tokens: dict = field(default_factory=lambda: {"in": 0, "out": 0})
|
|
public_passed: bool = False # public tests green before SUBMIT
|
|
rounds_to_green: int | None = None # round at which public went green
|
|
penalties: list = field(default_factory=list) # [{"kind","detail"}]
|
|
quality: float | None = None # filled by judge (M4)
|
|
collaboration: float | None = None # filled by judge (M4)
|
|
# bridge diagnostics: separate a broken product path from a model error, and
|
|
# record the model's true ability via an un-stripped control generation.
|
|
defect_class: str | None = None # e.g. "product-path-defect:IndentationError"
|
|
model_baseline_correct: bool | None = None
|
|
|
|
|
|
def _speed_score(o: EventOutcome, budget: dict) -> float:
|
|
"""1.0 for instant green, decaying toward 0 as rounds approach the cap.
|
|
Only meaningful if the team got correct code; uncorrect teams get 0."""
|
|
if not o.correct:
|
|
return 0.0
|
|
cap = max(1, int(budget.get("max_rounds", 6)))
|
|
used = o.rounds_to_green if o.rounds_to_green is not None else o.rounds
|
|
used = max(1, min(used, cap))
|
|
return round(1.0 - (used - 1) / cap, 4)
|
|
|
|
|
|
def _penalty_total(o: EventOutcome) -> float:
|
|
# each penalty shaves a flat slice; capped so a score never goes negative.
|
|
return min(0.5, 0.1 * len(o.penalties))
|
|
|
|
|
|
def score_event(o: EventOutcome, *, profile: str = "balanced",
|
|
budget: dict | None = None) -> dict:
|
|
budget = budget or {}
|
|
w = PROFILES.get(profile, PROFILES["balanced"])
|
|
axes = {
|
|
"correctness": 1.0 if o.correct else 0.0,
|
|
"speed": _speed_score(o, budget),
|
|
"quality": o.quality, # may be None (no judge yet)
|
|
"collaboration": o.collaboration,
|
|
}
|
|
# renormalize weights over axes that actually have a value this run
|
|
active = {k: w[k] for k, v in axes.items() if v is not None and w.get(k, 0)}
|
|
wsum = sum(active.values()) or 1.0
|
|
composite = sum(active[k] / wsum * axes[k] for k in active)
|
|
composite = round(max(0.0, composite - _penalty_total(o)), 4)
|
|
|
|
tok = o.tokens.get("in", 0) + o.tokens.get("out", 0)
|
|
return {
|
|
"team": o.team, "challenge": o.challenge,
|
|
"submitted": o.submitted, "correct": o.correct,
|
|
"composite": composite,
|
|
"axes": {k: (round(v, 4) if v is not None else None)
|
|
for k, v in axes.items()},
|
|
"pass@1": 1.0 if o.correct else 0.0,
|
|
"pass^1": 1.0 if o.correct else 0.0, # worst-case == pass@1 at n=1
|
|
"rounds": o.rounds, "rounds_to_green": o.rounds_to_green,
|
|
"wall_clock_s": round(o.wall_clock_s, 1),
|
|
"tokens": dict(o.tokens), "total_tokens": tok,
|
|
"cost_normalized": round(composite / tok, 8) if tok else None,
|
|
"penalties": list(o.penalties),
|
|
"defect_class": o.defect_class,
|
|
"model_baseline_correct": o.model_baseline_correct,
|
|
"profile": profile,
|
|
}
|
|
|
|
|
|
def print_score(s: dict) -> None:
|
|
print("=" * 72)
|
|
print(f"score · team={s['team']} · challenge={s['challenge']} "
|
|
f"· profile={s['profile']}")
|
|
print("-" * 72)
|
|
g = "✓" if s["correct"] else "✗"
|
|
green = f"(green@{s['rounds_to_green']})" if s["rounds_to_green"] else ""
|
|
print(f" correct={g} composite={s['composite']:.3f} "
|
|
f"pass@1={s['pass@1']:.0f} rounds={s['rounds']}{green}"
|
|
f" {s['wall_clock_s']:.1f}s")
|
|
print(" axes: " + " ".join(
|
|
f"{k}={'—' if v is None else f'{v:.2f}'}" for k, v in s["axes"].items()))
|
|
print(f" tokens={s['total_tokens']} "
|
|
f"cost_norm={s['cost_normalized']}")
|
|
if s.get("defect_class") or s.get("model_baseline_correct") is not None:
|
|
base = s.get("model_baseline_correct")
|
|
base_str = "—" if base is None else ("✓" if base else "✗")
|
|
print(f" defect={s.get('defect_class') or 'none'} "
|
|
f"model_baseline={base_str}")
|
|
if s["penalties"]:
|
|
print(" penalties:")
|
|
for p in s["penalties"]:
|
|
print(f" - {p}")
|
|
print("=" * 72)
|