Files
aituner/scripts/oracle_gap/analyze.py

367 lines
13 KiB
Python

#!/usr/bin/env python3
"""Score fixed-rate request logs and summarize static-vs-oracle frontiers."""
from __future__ import annotations
import argparse
import json
import math
import os
from collections import Counter, defaultdict
from pathlib import Path
from typing import Any, Iterable
TARGET_PASS_RATE = 0.95
TPOT_LIMIT_MS = 50.0
PHASES = ("P01", "P06")
CONFIGS = ("C00", "C10", "C01", "C11")
def atomic_json(path: Path, value: Any) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
temporary = path.with_name(f"{path.name}.tmp.{os.getpid()}")
temporary.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n")
os.replace(temporary, path)
def numeric(values: Iterable[float | int | None]) -> dict[str, Any]:
materialized = list(values)
finite = [
float(value)
for value in materialized
if value is not None and math.isfinite(float(value))
]
return {
"n": len(materialized),
"finite_n": len(finite),
"missing_n": len(materialized) - len(finite),
"min": min(finite) if finite else None,
"max": max(finite) if finite else None,
"distinct_n": len(set(finite)),
}
def percentile(values: Iterable[float], quantile: float) -> float | None:
ordered = sorted(float(value) for value in values)
if not ordered:
return None
index = max(0, min(len(ordered) - 1, math.ceil(quantile * len(ordered)) - 1))
return ordered[index]
def ttft_limit_ms(input_tokens: int) -> float:
if input_tokens <= 4096:
return 2000.0
if input_tokens <= 32768:
return 4000.0
return 6000.0
def score_trial(
request_path: Path,
result_path: Path,
*,
phase: str,
config: str,
target_rate: float,
repetition: int,
role: str,
) -> dict[str, Any]:
result = json.loads(result_path.read_text())
rows = [json.loads(line) for line in request_path.read_text().splitlines() if line]
clean_start = float(result["warmup_seconds"])
clean_seconds = float(result["clean_segment_seconds"]) * int(
result["num_clean_segments"]
)
clean_end = clean_start + clean_seconds
cohort = [row for row in rows if clean_start <= float(row["admitted_s"]) < clean_end]
ttft_values: list[float] = []
tpot_values: list[float] = []
lag_values: list[float] = []
reasons: Counter[str] = Counter()
passes = 0
exact_outputs = 0
for row in cohort:
lag_ms = (float(row["admitted_s"]) - float(row["scheduled_s"])) * 1000.0
lag_values.append(lag_ms)
request_reasons: list[str] = []
if not bool(row["success"]):
request_reasons.append(str(row.get("error_kind") or "request_failed"))
first = row.get("first_token_s")
if first is None:
request_reasons.append("ttft_missing")
ttft = None
else:
ttft = (float(first) - float(row["admitted_s"])) * 1000.0
ttft_values.append(ttft)
if ttft > ttft_limit_ms(int(row["input_tokens"])):
request_reasons.append("ttft_slo")
actual = row.get("actual_output_tokens")
requested = int(row["requested_output_tokens"])
if actual == requested:
exact_outputs += 1
if first is None or actual is None or int(actual) <= 1:
request_reasons.append("tpot_missing")
tpot = None
else:
tpot = (
(float(row["completed_s"]) - float(first))
* 1000.0
/ (int(actual) - 1)
)
tpot_values.append(tpot)
if tpot > TPOT_LIMIT_MS:
request_reasons.append("tpot_slo")
if request_reasons:
reasons.update(set(request_reasons))
else:
passes += 1
achieved_rate = len(cohort) / clean_seconds if clean_seconds else 0.0
pass_rate = passes / len(cohort) if cohort else 0.0
max_lag_ms = max(lag_values, default=math.inf)
offered_rate_valid = (
target_rate > 0 and abs(achieved_rate / target_rate - 1.0) <= 0.05
)
schedule_valid = bool(cohort) and max_lag_ms <= 1000.0
raw_feasible = pass_rate >= TARGET_PASS_RATE
feasible = raw_feasible and offered_rate_valid and schedule_valid
invariants = {
"cohort_nonempty": bool(cohort),
"clean_duration_positive": clean_seconds > 0,
"timestamps_nondecreasing": all(
float(row["scheduled_s"]) <= float(row["admitted_s"])
<= float(row["completed_s"])
for row in cohort
),
"exact_output_or_failed": all(
(not bool(row["success"]))
or row.get("actual_output_tokens") == row.get("requested_output_tokens")
for row in cohort
),
"latencies_nonnegative": all(value >= 0 for value in ttft_values + tpot_values),
"pass_rate_in_0_1": 0.0 <= pass_rate <= 1.0,
"goodput_nonnegative": passes >= 0,
}
if not all(invariants.values()):
raise RuntimeError(f"trial data invariant failed: {invariants}")
return {
"schema": 1,
"phase": phase,
"config": config,
"target_rate_rps": target_rate,
"repetition": repetition,
"role": role,
"clean_start_s": clean_start,
"clean_end_s": clean_end,
"clean_seconds": clean_seconds,
"cohort_n": len(cohort),
"pass_n": passes,
"pass_rate": pass_rate,
"slo_goodput_rps": passes / clean_seconds,
"achieved_offered_rps": achieved_rate,
"offered_rate_valid": offered_rate_valid,
"schedule_valid": schedule_valid,
"raw_slo_feasible": raw_feasible,
"feasible": feasible,
"exact_output_n": exact_outputs,
"failure_reasons": dict(sorted(reasons.items())),
"ttft_ms": {
**numeric(ttft_values),
"p50": percentile(ttft_values, 0.50),
"p95": percentile(ttft_values, 0.95),
"p99": percentile(ttft_values, 0.99),
},
"tpot_ms": {
**numeric(tpot_values),
"p50": percentile(tpot_values, 0.50),
"p95": percentile(tpot_values, 0.95),
"p99": percentile(tpot_values, 0.99),
},
"schedule_lag_ms": {
**numeric(lag_values),
"p95": percentile(lag_values, 0.95),
"p99": percentile(lag_values, 0.99),
},
"invariants": invariants,
}
def accepted_rate(rows: list[dict[str, Any]]) -> dict[str, Any]:
verdicts = [bool(row["feasible"]) for row in rows]
pass_n = sum(int(row["pass_n"]) for row in rows)
cohort_n = sum(int(row["cohort_n"]) for row in rows)
return {
"trials": len(rows),
"trial_feasible": verdicts,
"accepted_feasible": sum(verdicts) > len(verdicts) / 2,
"pooled_pass_n": pass_n,
"pooled_cohort_n": cohort_n,
"pooled_pass_rate": pass_n / cohort_n if cohort_n else 0.0,
"median_goodput_rps": sorted(float(row["slo_goodput_rps"]) for row in rows)[
len(rows) // 2
],
}
def frontier_for_cell(rows: list[dict[str, Any]]) -> dict[str, Any]:
by_rate: dict[float, list[dict[str, Any]]] = defaultdict(list)
for row in rows:
by_rate[float(row["target_rate_rps"])].append(row)
rates = []
for rate in sorted(by_rate):
rates.append({"rate_rps": rate, **accepted_rate(by_rate[rate])})
verdicts = [bool(row["accepted_feasible"]) for row in rates]
# Once a failure appears, no higher anchor may pass.
monotone = not any(
(not verdicts[i]) and any(verdicts[i + 1 :]) for i in range(len(verdicts))
)
feasible_rates = [row["rate_rps"] for row in rates if row["accepted_feasible"]]
infeasible_rates = [row["rate_rps"] for row in rates if not row["accepted_feasible"]]
lower = max(feasible_rates, default=None)
upper_candidates = [rate for rate in infeasible_rates if lower is None or rate > lower]
upper = min(upper_candidates, default=None)
boundary_repeated = bool(
lower is not None
and upper is not None
and next(row for row in rates if row["rate_rps"] == lower)["trials"] >= 3
and next(row for row in rates if row["rate_rps"] == upper)["trials"] >= 3
)
bracketed = lower is not None and upper is not None and monotone and boundary_repeated
return {
"rates": rates,
"lower_feasible_rps": lower,
"upper_infeasible_rps": upper,
"bracketed": bracketed,
"boundary_repeated": boundary_repeated,
"monotone": monotone,
}
def gap_at_weight(
lower: dict[str, dict[str, float]],
upper: dict[str, dict[str, float]],
p01_weight: float,
) -> dict[str, Any]:
weights = {"P01": p01_weight, "P06": 1.0 - p01_weight}
oracle = sum(weights[p] * max(upper[p].values()) for p in PHASES)
static_values = {
config: sum(weights[p] * lower[p][config] for p in PHASES)
for config in CONFIGS
}
best_config = max(static_values, key=static_values.get)
static = static_values[best_config]
return {
"p01_weight": p01_weight,
"oracle_upper_rps": oracle,
"static_lower_rps": static,
"best_static_config": best_config,
"gap": oracle / static - 1.0,
}
def summarize_trials(rows: list[dict[str, Any]]) -> dict[str, Any]:
grouped: dict[tuple[str, str], list[dict[str, Any]]] = defaultdict(list)
for row in rows:
grouped[(str(row["phase"]), str(row["config"]))].append(row)
expected = {(phase, config) for phase in PHASES for config in CONFIGS}
if set(grouped) != expected:
raise RuntimeError(f"cell coverage mismatch: {sorted(set(grouped) ^ expected)}")
frontiers = {
phase: {
config: frontier_for_cell(grouped[(phase, config)])
for config in CONFIGS
}
for phase in PHASES
}
all_bracketed = all(
frontiers[p][c]["bracketed"] for p in PHASES for c in CONFIGS
)
all_monotone = all(
frontiers[p][c]["monotone"] for p in PHASES for c in CONFIGS
)
lower = {
p: {c: float(frontiers[p][c]["lower_feasible_rps"]) for c in CONFIGS}
for p in PHASES
} if all_bracketed else {}
upper = {
p: {c: float(frontiers[p][c]["upper_infeasible_rps"]) for c in CONFIGS}
for p in PHASES
} if all_bracketed else {}
scan = [gap_at_weight(lower, upper, step / 10000) for step in range(10001)] if all_bracketed else []
worst = max(scan, key=lambda row: row["gap"]) if scan else None
equal = gap_at_weight(lower, upper, 0.5) if all_bracketed else None
distinct_by_cell = {
f"{p}-{c}": len(
{round(float(row["slo_goodput_rps"]), 12) for row in grouped[(p, c)]}
)
for p in PHASES for c in CONFIGS
}
sanity = {
"trial_count": numeric([row["cohort_n"] for row in rows]),
"target_rates": numeric([row["target_rate_rps"] for row in rows]),
"pass_rates": numeric([row["pass_rate"] for row in rows]),
"goodput_rps": numeric([row["slo_goodput_rps"] for row in rows]),
"distinct_goodput_by_cell": distinct_by_cell,
"invariants": {
"all_counters_nonnegative": all(
int(row["cohort_n"]) >= 0 and int(row["pass_n"]) >= 0 for row in rows
),
"all_ratios_in_0_1": all(0 <= float(row["pass_rate"]) <= 1 for row in rows),
"all_trial_invariants": all(all(row["invariants"].values()) for row in rows),
"all_cells_bracketed": all_bracketed,
"all_frontiers_monotone": all_monotone,
"per_cell_results_not_all_identical": all(value > 1 for value in distinct_by_cell.values()),
"weight_scan_continuous": len(scan) in (0, 10001),
},
}
verdict = "INCONCLUSIVE"
if all(sanity["invariants"].values()) and worst is not None:
verdict = "REFUTED" if float(worst["gap"]) < 0.10 else "NOT_ESTABLISHED"
return {
"schema": 1,
"verdict": verdict,
"threshold": 0.10,
"frontiers": frontiers,
"equal_time_conservative": equal,
"worst_mixture_conservative": worst,
"sanity": sanity,
}
def main() -> None:
parser = argparse.ArgumentParser()
sub = parser.add_subparsers(dest="command", required=True)
score = sub.add_parser("score")
score.add_argument("--requests", required=True)
score.add_argument("--result", required=True)
score.add_argument("--phase", choices=PHASES, required=True)
score.add_argument("--config", choices=CONFIGS, required=True)
score.add_argument("--target-rate", type=float, required=True)
score.add_argument("--repetition", type=int, required=True)
score.add_argument("--role", required=True)
score.add_argument("--out", required=True)
summary = sub.add_parser("summarize")
summary.add_argument("--trial-glob", required=True)
summary.add_argument("--out", required=True)
args = parser.parse_args()
if args.command == "score":
value = score_trial(
Path(args.requests), Path(args.result), phase=args.phase,
config=args.config, target_rate=args.target_rate,
repetition=args.repetition, role=args.role,
)
else:
import glob
paths = [Path(path) for path in sorted(glob.glob(args.trial_glob, recursive=True))]
value = summarize_trials([json.loads(path.read_text()) for path in paths])
atomic_json(Path(args.out), value)
print(json.dumps(value, sort_keys=True))
if __name__ == "__main__":
main()