94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
541 lines
17 KiB
Python
541 lines
17 KiB
Python
from dataclasses import dataclass
|
|
from types import SimpleNamespace
|
|
from typing import List, Optional, Tuple
|
|
|
|
from sglang.srt.utils import kill_process_tree
|
|
from sglang.test.run_eval import run_eval
|
|
from sglang.test.test_utils import (
|
|
DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
|
|
DEFAULT_URL_FOR_TEST,
|
|
ModelLaunchSettings,
|
|
dump_metric,
|
|
popen_launch_server,
|
|
write_github_step_summary,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class AccuracyTestParams:
|
|
"""Parameters for accuracy testing."""
|
|
|
|
dataset: str # e.g., "mgsm_en", "gsm8k", "mmmu", "gpqa"
|
|
baseline_accuracy: float # Required: minimum accuracy threshold
|
|
num_examples: Optional[int] = None
|
|
num_threads: Optional[int] = None
|
|
max_tokens: Optional[int] = None
|
|
return_latency: bool = False
|
|
# Extended parameters for special evaluations (e.g., GPQA with thinking mode)
|
|
thinking_mode: Optional[str] = None # e.g., "deepseek-v3"
|
|
temperature: Optional[float] = None
|
|
top_p: Optional[float] = None
|
|
top_k: Optional[int] = None
|
|
repeat: Optional[int] = None
|
|
api: Optional[str] = None # "chat" or "completion"; defaults to "chat" in run_eval
|
|
|
|
|
|
@dataclass
|
|
class AccuracyTestResult:
|
|
"""Result of an accuracy test."""
|
|
|
|
model: str
|
|
dataset: str
|
|
passed: bool
|
|
score: Optional[float]
|
|
baseline_accuracy: float
|
|
error: Optional[str]
|
|
latency: Optional[float] = None
|
|
variant: Optional[str] = None
|
|
|
|
|
|
def write_accuracy_github_summary(
|
|
test_name: str,
|
|
dataset: str,
|
|
results: List[AccuracyTestResult],
|
|
) -> None:
|
|
"""Write accuracy test results to GitHub step summary.
|
|
|
|
Args:
|
|
test_name: Name of the test
|
|
dataset: Dataset name used for evaluation
|
|
results: List of AccuracyTestResult objects
|
|
"""
|
|
summary = f"#### {test_name} - Accuracy ({dataset})\n"
|
|
summary += "| config | status | score | baseline | error |\n"
|
|
summary += "| ------ | ------ | ----- | -------- | ----- |\n"
|
|
|
|
for result in results:
|
|
status_emoji = "✅" if result.passed else "❌"
|
|
score_str = f"{result.score:.4f}" if result.score is not None else "N/A"
|
|
baseline_str = f"{result.baseline_accuracy:.4f}"
|
|
error_str = result.error if result.error else "-"
|
|
# Use variant name if available, otherwise use model path
|
|
config_name = result.variant if result.variant else result.model
|
|
summary += f"| {config_name} | {status_emoji} | {score_str} | {baseline_str} | {error_str} |\n"
|
|
|
|
write_github_step_summary(summary)
|
|
|
|
|
|
def _run_simple_eval(
|
|
model: ModelLaunchSettings,
|
|
base_url: str,
|
|
dataset: str,
|
|
num_examples: Optional[int] = None,
|
|
num_threads: Optional[int] = None,
|
|
max_tokens: Optional[int] = None,
|
|
return_latency: bool = False,
|
|
thinking_mode: Optional[str] = None,
|
|
temperature: Optional[float] = None,
|
|
top_p: Optional[float] = None,
|
|
top_k: Optional[int] = None,
|
|
repeat: Optional[int] = None,
|
|
api: Optional[str] = None,
|
|
) -> Tuple[bool, Optional[str], Optional[dict]]:
|
|
"""Run evaluation using simple_eval backend (run_eval.py).
|
|
|
|
Returns:
|
|
Tuple of (success, error_message, metrics_dict)
|
|
"""
|
|
process = None
|
|
try:
|
|
process = popen_launch_server(
|
|
model.model_path,
|
|
base_url,
|
|
other_args=model.extra_args,
|
|
timeout=model.launch_timeout or DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
|
|
env=model.env,
|
|
)
|
|
|
|
args = SimpleNamespace(
|
|
base_url=base_url,
|
|
model=model.model_path,
|
|
eval_name=dataset,
|
|
num_examples=num_examples,
|
|
num_threads=num_threads or 1024,
|
|
)
|
|
|
|
if api is not None:
|
|
args.api = api
|
|
|
|
if max_tokens is not None:
|
|
args.max_tokens = max_tokens
|
|
|
|
if return_latency:
|
|
args.return_latency = True
|
|
|
|
if thinking_mode is not None:
|
|
args.thinking_mode = thinking_mode
|
|
|
|
if temperature is not None:
|
|
args.temperature = temperature
|
|
|
|
if top_p is not None:
|
|
args.top_p = top_p
|
|
|
|
if top_k is not None:
|
|
args.top_k = top_k
|
|
|
|
if repeat is not None:
|
|
args.repeat = repeat
|
|
|
|
result = run_eval(args)
|
|
|
|
# Handle result format (run_eval can return metrics or (metrics, latency))
|
|
if return_latency and isinstance(result, tuple):
|
|
metrics, latency = result
|
|
metrics["latency"] = round(latency, 4)
|
|
else:
|
|
metrics = result
|
|
|
|
return True, None, metrics
|
|
|
|
except Exception as e:
|
|
return False, f"Accuracy test exception: {str(e)}", None
|
|
|
|
finally:
|
|
if process:
|
|
kill_process_tree(process.pid)
|
|
|
|
|
|
# Cached uv venv for NeMo Skills (persists across variants within a process).
|
|
_nemo_venv_dir: Optional[str] = None
|
|
_nemo_data_prepared: set = set()
|
|
|
|
|
|
def _get_nemo_venv() -> Tuple[str, dict]:
|
|
"""Get or create a uv venv with nemo_skills installed.
|
|
|
|
Returns (venv_python_path, env_dict) reusable across calls.
|
|
"""
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
|
|
global _nemo_venv_dir
|
|
|
|
if _nemo_venv_dir is not None:
|
|
venv_python = f"{_nemo_venv_dir}/venv/bin/python"
|
|
env = {
|
|
**dict(os.environ),
|
|
"NEMO_SKILLS_DISABLE_UNCOMMITTED_CHANGES_CHECK": "1",
|
|
"OPENAI_API_KEY": "dummy",
|
|
"VIRTUAL_ENV": f"{_nemo_venv_dir}/venv",
|
|
"PATH": f"{_nemo_venv_dir}/venv/bin:" + os.environ.get("PATH", ""),
|
|
}
|
|
return venv_python, env
|
|
|
|
_nemo_venv_dir = tempfile.mkdtemp(prefix="nemo_skills_")
|
|
print(f"Creating NeMo Skills venv in {_nemo_venv_dir}...")
|
|
|
|
# Create venv
|
|
result = subprocess.run(
|
|
["uv", "venv", f"{_nemo_venv_dir}/venv", "--python", "3.12"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if result.returncode != 0:
|
|
subprocess.run(
|
|
["uv", "venv", f"{_nemo_venv_dir}/venv"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
|
|
# Install nemo_skills.
|
|
# Pinned: NeMo-Skills main after PR #1433 pins litellm==1.83.14 (httpx==0.28.1),
|
|
# which is unsatisfiable against nemo-run's transitive leptonai dep.
|
|
nemo_skills_ref = "589294c"
|
|
print(f"Installing nemo_skills (pinned to {nemo_skills_ref})...")
|
|
pip_result = subprocess.run(
|
|
[
|
|
"uv",
|
|
"pip",
|
|
"install",
|
|
"--python",
|
|
f"{_nemo_venv_dir}/venv/bin/python",
|
|
f"git+https://github.com/NVIDIA/NeMo-Skills.git@{nemo_skills_ref}",
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=300,
|
|
)
|
|
if pip_result.returncode != 0:
|
|
raise RuntimeError(f"Failed to install nemo_skills: {pip_result.stderr[-500:]}")
|
|
|
|
print("NeMo Skills installed successfully")
|
|
return _get_nemo_venv()
|
|
|
|
|
|
def _ensure_nemo_data_prepared(
|
|
venv_python: str, env: dict, dataset: str
|
|
) -> Tuple[bool, Optional[str]]:
|
|
"""Prepare NeMo Skills dataset data if not already done.
|
|
|
|
Uses the venv python so data lands inside the venv's nemo_skills package.
|
|
"""
|
|
import subprocess
|
|
|
|
if dataset in _nemo_data_prepared:
|
|
return True, None
|
|
|
|
print(f"Preparing {dataset} data (this may take a few minutes for VLM datasets)...")
|
|
result = subprocess.run(
|
|
[venv_python, "-m", "nemo_skills.dataset.prepare", dataset],
|
|
text=True,
|
|
timeout=600,
|
|
env=env,
|
|
)
|
|
if result.returncode != 0:
|
|
return False, f"Failed to prepare {dataset} data (exit {result.returncode})"
|
|
|
|
_nemo_data_prepared.add(dataset)
|
|
return True, None
|
|
|
|
|
|
def _run_nemo_skills_eval(
|
|
model: ModelLaunchSettings,
|
|
base_url: str,
|
|
dataset: str,
|
|
max_tokens: Optional[int] = None,
|
|
repeat: Optional[int] = None,
|
|
temperature: Optional[float] = None,
|
|
top_p: Optional[float] = None,
|
|
) -> Tuple[bool, Optional[str], Optional[dict]]:
|
|
"""Run evaluation using NeMo Skills (ns eval) for benchmarks like mmmu-pro.
|
|
|
|
Uses an isolated uv venv (shared across variants) so nemo_skills dependencies
|
|
don't interfere with the system python / sglang server.
|
|
|
|
Returns:
|
|
Tuple of (success, error_message, metrics_dict)
|
|
"""
|
|
import subprocess
|
|
import tempfile
|
|
|
|
process = None
|
|
try:
|
|
# Get or create the shared venv (once per process)
|
|
venv_python, env = _get_nemo_venv()
|
|
|
|
# Prepare dataset (once per process, cached)
|
|
ok, err = _ensure_nemo_data_prepared(venv_python, env, dataset)
|
|
if not ok:
|
|
return False, err, None
|
|
|
|
process = popen_launch_server(
|
|
model.model_path,
|
|
base_url,
|
|
other_args=model.extra_args,
|
|
timeout=model.launch_timeout or DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
|
|
env=model.env,
|
|
)
|
|
|
|
port = int(base_url.split(":")[-1])
|
|
server_address = f"http://127.0.0.1:{port}/v1"
|
|
repeat_val = repeat or 1
|
|
max_tokens_val = max_tokens or 32768
|
|
benchmark_spec = f"{dataset}:{repeat_val}"
|
|
|
|
# Build ns eval command using venv python
|
|
# Note: nemo_skills.pipeline.eval requires the "eval" subcommand
|
|
output_dir = tempfile.mkdtemp(prefix="ns_eval_output_")
|
|
cmd = [
|
|
venv_python,
|
|
"-m",
|
|
"nemo_skills.pipeline.eval",
|
|
"eval",
|
|
f"--benchmarks={benchmark_spec}",
|
|
"--server_type=sglang",
|
|
f"--model={model.model_path}",
|
|
f"--server_address={server_address}",
|
|
f"--output_dir={output_dir}",
|
|
f"++inference.tokens_to_generate={max_tokens_val}",
|
|
]
|
|
|
|
if temperature is not None:
|
|
cmd.append(f"++inference.temperature={temperature}")
|
|
if top_p is not None:
|
|
cmd.append(f"++inference.top_p={top_p}")
|
|
|
|
# Add VLM-specific config
|
|
if dataset in ("mmmu-pro", "mmmu_pro"):
|
|
cmd.append("++prompt_config=vlm/mmmu-pro")
|
|
cmd.append("++max_concurrent_requests=512")
|
|
cmd.append("++max_samples=500")
|
|
|
|
print(f"Running: {' '.join(cmd)}")
|
|
eval_result = subprocess.run(
|
|
cmd,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=7200,
|
|
env=env,
|
|
)
|
|
|
|
print(eval_result.stdout[-2000:] if eval_result.stdout else "(no stdout)")
|
|
if eval_result.stderr:
|
|
print(eval_result.stderr[-1000:])
|
|
|
|
if eval_result.returncode != 0:
|
|
return (
|
|
False,
|
|
f"ns eval failed (exit {eval_result.returncode}): {eval_result.stderr[-500:]}",
|
|
None,
|
|
)
|
|
|
|
# Parse results
|
|
summarize_result = subprocess.run(
|
|
[
|
|
venv_python,
|
|
"-m",
|
|
"nemo_skills.pipeline.summarize_results",
|
|
f"{output_dir}/eval-results",
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=60,
|
|
env=env,
|
|
)
|
|
|
|
output = summarize_result.stdout + "\n" + eval_result.stdout
|
|
print(f"Summary: {summarize_result.stdout[:1000]}")
|
|
|
|
# Parse accuracy from output (format varies, look for common patterns)
|
|
import re
|
|
|
|
score = None
|
|
for line in output.split("\n"):
|
|
match = re.search(r"(?:accuracy|score)[:\s]+([0-9.]+)", line, re.IGNORECASE)
|
|
if match:
|
|
score = float(match.group(1))
|
|
|
|
if score is None:
|
|
# Try to find it in eval-results directory
|
|
import glob
|
|
import json
|
|
|
|
for result_file in glob.glob(
|
|
f"{output_dir}/eval-results/**/*.json", recursive=True
|
|
):
|
|
try:
|
|
with open(result_file) as f:
|
|
data = json.load(f)
|
|
if isinstance(data, dict):
|
|
score = (
|
|
data.get("accuracy")
|
|
or data.get("score")
|
|
or data.get("mean_score")
|
|
)
|
|
if score is not None:
|
|
break
|
|
except (json.JSONDecodeError, KeyError):
|
|
continue
|
|
|
|
if score is None:
|
|
# Last resort: compute accuracy directly from JSONL output
|
|
import glob
|
|
import json
|
|
|
|
for jsonl_file in sorted(
|
|
glob.glob(f"{output_dir}/eval-results/**/*.jsonl*", recursive=True)
|
|
):
|
|
correct = 0
|
|
total = 0
|
|
try:
|
|
with open(jsonl_file) as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
entry = json.loads(line)
|
|
expected = entry.get("expected_answer", "")
|
|
generation = entry.get("generation", "")
|
|
# Extract "Answer: X" from the end of generation
|
|
answer_match = re.search(
|
|
r"Answer:\s*([A-J])", generation, re.IGNORECASE
|
|
)
|
|
if answer_match:
|
|
predicted = answer_match.group(1).upper()
|
|
if predicted == expected.upper():
|
|
correct += 1
|
|
total += 1
|
|
except (json.JSONDecodeError, KeyError, OSError):
|
|
continue
|
|
if total > 0:
|
|
score = correct / total
|
|
print(
|
|
f"Computed accuracy from {jsonl_file}: "
|
|
f"{correct}/{total} = {score:.4f}"
|
|
)
|
|
break
|
|
|
|
if score is None:
|
|
return False, "Could not parse accuracy from ns eval output", None
|
|
|
|
dump_metric(
|
|
f"{dataset}_score",
|
|
score,
|
|
labels={"model": model.model_path, "eval": dataset, "api": "nemo-skills"},
|
|
)
|
|
|
|
return True, None, {"score": score}
|
|
|
|
except subprocess.TimeoutExpired:
|
|
return False, "NeMo Skills eval timed out", None
|
|
except Exception as e:
|
|
return False, f"NeMo Skills eval exception: {str(e)}", None
|
|
finally:
|
|
if process:
|
|
kill_process_tree(process.pid)
|
|
|
|
|
|
def run_accuracy_test(
|
|
model: ModelLaunchSettings,
|
|
params: AccuracyTestParams,
|
|
base_url: Optional[str] = None,
|
|
) -> AccuracyTestResult:
|
|
"""Run accuracy test for a single model.
|
|
|
|
Args:
|
|
model: ModelLaunchSettings with model config
|
|
params: AccuracyTestParams with dataset, baseline, and optional settings
|
|
base_url: Server base URL (default: DEFAULT_URL_FOR_TEST)
|
|
|
|
Returns:
|
|
AccuracyTestResult with test outcome
|
|
"""
|
|
base_url = base_url or DEFAULT_URL_FOR_TEST
|
|
|
|
print(f"\n{'='*60}")
|
|
print(f"Running ACCURACY test for {model.model_path}")
|
|
print(f" Dataset: {params.dataset}")
|
|
print(f" Baseline: {params.baseline_accuracy}")
|
|
print(f"{'='*60}\n")
|
|
|
|
# Run evaluation based on dataset type
|
|
# - NeMo Skills: mmmu-pro (and other VLM evals needing ns eval)
|
|
# - simple_eval: everything else (gsm8k, gpqa, mmlu, mmmu, etc.)
|
|
if params.dataset in ("mmmu-pro", "mmmu_pro"):
|
|
success, error, metrics = _run_nemo_skills_eval(
|
|
model=model,
|
|
base_url=base_url,
|
|
dataset="mmmu-pro",
|
|
max_tokens=params.max_tokens,
|
|
repeat=params.repeat or 1,
|
|
temperature=params.temperature,
|
|
top_p=params.top_p,
|
|
)
|
|
else:
|
|
success, error, metrics = _run_simple_eval(
|
|
model=model,
|
|
base_url=base_url,
|
|
dataset=params.dataset,
|
|
num_examples=params.num_examples,
|
|
num_threads=params.num_threads,
|
|
max_tokens=params.max_tokens,
|
|
return_latency=params.return_latency,
|
|
thinking_mode=params.thinking_mode,
|
|
temperature=params.temperature,
|
|
top_p=params.top_p,
|
|
top_k=params.top_k,
|
|
repeat=params.repeat,
|
|
api=params.api,
|
|
)
|
|
|
|
if not success:
|
|
print(f"✗ Accuracy test failed for {model.model_path}: {error}")
|
|
return AccuracyTestResult(
|
|
model=model.model_path,
|
|
dataset=params.dataset,
|
|
passed=False,
|
|
score=None,
|
|
baseline_accuracy=params.baseline_accuracy,
|
|
error=error,
|
|
variant=model.variant,
|
|
)
|
|
|
|
# Validate against baseline
|
|
# Handle different metric key names: "score", "mean_score" (for GPQA with repeat), "accuracy"
|
|
score = (
|
|
metrics.get("score")
|
|
or metrics.get("mean_score")
|
|
or metrics.get("accuracy", 0.0)
|
|
)
|
|
passed = score >= params.baseline_accuracy
|
|
latency = metrics.get("latency")
|
|
|
|
if passed:
|
|
print(f"✓ Accuracy {score:.3f} >= baseline {params.baseline_accuracy:.3f}")
|
|
else:
|
|
error = f"Accuracy {score:.3f} below baseline {params.baseline_accuracy:.3f}"
|
|
print(f"✗ {error}")
|
|
|
|
return AccuracyTestResult(
|
|
model=model.model_path,
|
|
dataset=params.dataset,
|
|
passed=passed,
|
|
score=score,
|
|
baseline_accuracy=params.baseline_accuracy,
|
|
error=error if not passed else None,
|
|
latency=latency,
|
|
variant=model.variant,
|
|
)
|