Files
2026-07-13 13:12:33 +08:00

252 lines
10 KiB
Python

#!/usr/bin/env python3
"""Assert LLM treatment delivery from per-instance llm_calls.jsonl records.
Experiment arms must verify that configured treatments actually reached the
provider before a run is scored; any delivery mismatch makes the decision
``invalid``. This scans every ``llm.request`` record in each instance's
llm_calls.jsonl and asserts, on all requests:
- ``metadata.request_proof.proof_budget`` equals --expected-proof-budget
- ``payload.reasoning.effort`` equals --expected-reasoning-effort
(OpenRouter/GLM adapter: effort string; payloads carry no numeric budget)
- ``payload.thinking_budget`` equals --expected-thinking-budget
(DashScope/Qwen adapter: numeric budget)
The engine has a designed one-shot recovery that retries a failed provider
call with thinking disabled; the request shape is adapter-specific
(provider/openai.py): OpenRouter/GLM emits ``payload.reasoning = {"enabled":
false}``; DashScope/Qwen emits ``payload.enable_thinking = false`` with no
reasoning key and no thinking_budget. Such requests are counted separately
and excluded from the reasoning-effort and thinking-budget assertions, but
any occurrence beyond --allow-reasoning-fallbacks (default 0) is an error.
The proof-budget assertion still applies to them. (Detection assumes a
thinking-enabled arm; a deliberately thinking-off DashScope arm would count
every request as a fallback.)
Separately, an httpx stream timeout with no stream event triggers a
non-stream retry of the same budget-coordinated payload
(``_complete_non_stream``); its record carries ``metadata.fallback_from ==
"stream_timeout"`` and no ``request_proof`` block at all. These records are
excluded from the proof-budget assertion (the payload assertions still
apply), reported per instance as ``stream_fallbacks``, and never gated —
the treatment itself was delivered unchanged.
One stdout line per instance reports the request count and distinct observed
values. Exit is non-zero on any mismatch, unreadable request record, or
instance with zero ``llm.request`` records.
Known limit: lines are prefiltered on the substring ``"llm.request"`` before
JSON parsing, so a request line truncated within its first ~200 bytes (before
the ``event`` key) is skipped silently rather than counted as unparsed. Tail
truncation from a killed run cuts inside the large ``payload`` field instead,
which still matches the prefilter and lands in the unparsed-error path — and a
killed run is already invalid under the rc!=0 rule.
"""
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
def _instance_dirs(run_dir: Path) -> list[Path]:
if (run_dir / "llm_calls.jsonl").is_file():
return [run_dir]
return sorted(child for child in run_dir.iterdir() if child.is_dir())
class _InstanceScan:
def __init__(self) -> None:
self.requests = 0
self.unparsed = 0
self.reasoning_fallbacks = 0
self.stream_fallbacks = 0
self.proof_budgets: set[object] = set()
self.efforts: set[object] = set()
self.thinking_budgets: set[object] = set()
def _scan_llm_requests(path: Path) -> _InstanceScan:
scan = _InstanceScan()
with path.open("r", encoding="utf-8", errors="replace") as handle:
for line in handle:
if '"llm.request"' not in line:
continue
try:
record = json.loads(line)
except json.JSONDecodeError:
scan.unparsed += 1
continue
if not isinstance(record, dict) or record.get("event") != "llm.request":
continue
scan.requests += 1
metadata = record.get("metadata")
metadata = metadata if isinstance(metadata, dict) else {}
payload = record.get("payload")
payload = payload if isinstance(payload, dict) else {}
if metadata.get("fallback_from") == "stream_timeout":
# Non-stream retry of a stream timeout re-sends the same
# budget-coordinated payload, but its record carries no
# request_proof metadata (provider/openai.py record_request).
scan.stream_fallbacks += 1
else:
request_proof = metadata.get("request_proof")
scan.proof_budgets.add(
request_proof.get("proof_budget") if isinstance(request_proof, dict) else None
)
reasoning = payload.get("reasoning")
if reasoning == {"enabled": False} or payload.get("enable_thinking") is False:
# Exact shapes the engine's one-shot thinking-disable recovery
# emits (provider/openai.py): OpenRouter reasoning={"enabled":
# false}; DashScope enable_thinking=false with no reasoning key
# and no thinking_budget. Anything else is a treatment value.
scan.reasoning_fallbacks += 1
else:
scan.efforts.add(reasoning.get("effort") if isinstance(reasoning, dict) else None)
scan.thinking_budgets.add(payload.get("thinking_budget"))
return scan
def _distinct(values: set[object]) -> str:
return ",".join(sorted("null" if value is None else str(value) for value in values))
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--run-dir",
action="append",
required=True,
type=Path,
help=(
"Run directory holding per-instance subdirectories with llm_calls.jsonl "
"(or a single instance directory). May be repeated."
),
)
parser.add_argument(
"--expected-proof-budget",
type=int,
default=None,
help="Expected metadata.request_proof.proof_budget on every llm.request.",
)
parser.add_argument(
"--expected-reasoning-effort",
default=None,
help="Expected payload.reasoning.effort string on every llm.request.",
)
parser.add_argument(
"--expected-thinking-budget",
type=int,
default=None,
help="Expected numeric payload.thinking_budget on every llm.request.",
)
parser.add_argument(
"--allow-reasoning-fallbacks",
type=int,
default=0,
help=(
"Max engine thinking-disable recovery requests tolerated per "
"instance (OpenRouter payload.reasoning.enabled == false, or "
"DashScope payload.enable_thinking == false)."
),
)
args = parser.parse_args(argv)
if (
args.expected_proof_budget is None
and args.expected_reasoning_effort is None
and args.expected_thinking_budget is None
):
parser.error("at least one --expected-* assertion is required")
checked = 0
errors = 0
for run_dir in args.run_dir:
if not run_dir.is_dir():
print(f"missing_run_dir: {run_dir}", file=sys.stderr)
errors += 1
continue
instance_dirs = _instance_dirs(run_dir)
if not instance_dirs:
print(f"no_instances: {run_dir}", file=sys.stderr)
errors += 1
continue
for instance_dir in instance_dirs:
checked += 1
instance_id = instance_dir.name
calls_path = instance_dir / "llm_calls.jsonl"
if not calls_path.is_file():
print(f"missing_llm_calls: {instance_id}", file=sys.stderr)
errors += 1
continue
scan = _scan_llm_requests(calls_path)
print(
f"instance={instance_id} requests={scan.requests} "
f"proof_budget={_distinct(scan.proof_budgets)} "
f"reasoning_effort={_distinct(scan.efforts)} "
f"reasoning_fallbacks={scan.reasoning_fallbacks} "
f"stream_fallbacks={scan.stream_fallbacks} "
f"thinking_budget={_distinct(scan.thinking_budgets)}"
)
if scan.unparsed:
print(
f"unparsed_request_lines: {instance_id} count={scan.unparsed}",
file=sys.stderr,
)
errors += 1
if scan.requests == 0:
print(f"no_llm_requests: {instance_id}", file=sys.stderr)
errors += 1
continue
if scan.reasoning_fallbacks > args.allow_reasoning_fallbacks:
print(
f"reasoning_fallback_exceeded: {instance_id} "
f"count={scan.reasoning_fallbacks} "
f"allowed={args.allow_reasoning_fallbacks}",
file=sys.stderr,
)
errors += 1
if args.expected_proof_budget is not None and scan.proof_budgets != {
args.expected_proof_budget
}:
print(
f"proof_budget_mismatch: {instance_id} "
f"expected={args.expected_proof_budget} "
f"actual={_distinct(scan.proof_budgets)}",
file=sys.stderr,
)
errors += 1
if args.expected_reasoning_effort is not None and scan.efforts != {
args.expected_reasoning_effort
}:
print(
f"reasoning_effort_mismatch: {instance_id} "
f"expected={args.expected_reasoning_effort} "
f"actual={_distinct(scan.efforts)}",
file=sys.stderr,
)
errors += 1
if args.expected_thinking_budget is not None and scan.thinking_budgets != {
args.expected_thinking_budget
}:
print(
f"thinking_budget_mismatch: {instance_id} "
f"expected={args.expected_thinking_budget} "
f"actual={_distinct(scan.thinking_budgets)}",
file=sys.stderr,
)
errors += 1
if errors:
print(f"checked={checked} errors={errors}", file=sys.stderr)
return 1
print(f"checked={checked} errors=0")
return 0
if __name__ == "__main__":
raise SystemExit(main())