Single consolidated commit for four completed, validated phases (77, 78, 79, 80). The pipeline run left all work uncommitted because the harness commits only with PHASE_COMMIT=1 while child executors are forbidden from committing; the phases themselves all passed validation and moved to .agents/phases/complete/. Phase 77 — navbar view refresh - router.js dispatches bor:view-refresh on re-show / active re-click / popstate (gated on wasMounted; first show and boot exempt) - History / RAG / Sources / Tuning re-fetch on refresh (admin branch); Chat deliberately excluded (stream survival) - History "Refresh" button (admin-only, in-flight disable + status line) - New story suite tests/e2e/test_navbar_refresh.py (7 tests) Phase 78 — static background - Removed the animated glow layers; static 44px grid over the flat --bg canvas; default and reduced-motion renders byte-identical - Updated background/theme E2E suites; removed bg-glow test pins Phase 79 — API tokens - api_tokens model + migration 0012; hash-only token service - Admin tokens API + Tokens admin view; POST /api/token-auth; live-revoking require_user on chat / suggestions / document content - Frontend token gate with localStorage cache; anonymous E2E suites migrated to token login - New story suite tests/e2e/test_api_tokens.py (9 tests) Phase 80 — history suggestion chips - last_questions() endpoint with SEED fallback; startNewChat() refetch - Seed-semantics docs (config.py, .env.example, README) - Integration state matrix + E2E suite rewritten to the 4 chip states Also included: phase-76 report artifacts and the repo restore-test-db skill (previously untracked), scripts/* ruff fixes from phase 77. Final gate state (phase 80 final pass, covers everything above): - uv run pytest --cov=app → 1637 passed, 0 failed, app/ coverage 99% - uv run ruff check . && uv run pyright → clean, 0 errors - Per-phase story E2E suites green in isolation
132 lines
4.1 KiB
Python
132 lines
4.1 KiB
Python
"""Shared CSV benchmark recorder for model testing scripts.
|
|
|
|
All model-test scripts (chat, summary, embedding) write their results
|
|
to ``benchmarks/model_benchmarks.csv`` so results accumulate across
|
|
runs and can be compared with a spreadsheet or a simple query.
|
|
|
|
CSV columns (one row per *run*, not per turn):
|
|
|
|
date,script,model,mode,gate_status,turns,answered,caps,
|
|
tool_turns,emitted,executed,contract,wall_s,
|
|
extra_col1,extra_val1,extra_col2,extra_val2
|
|
|
|
* ``script`` — ``chat`` | ``summary`` | ``embed``
|
|
* ``model`` — the model name (e.g. ``lite``, ``turbo``)
|
|
* ``mode`` — ``fixture`` | ``derived`` | ``quality`` | ``dimension`` |
|
|
``cosine`` | ``speed``
|
|
* ``gate_status`` — ``PASS`` | ``FAIL``
|
|
* ``turns`` — number of turns/questions
|
|
* ``answered`` — turns that produced an answer (no LLMError)
|
|
* ``caps`` — turns that hit the round cap
|
|
* ``tool_turns`` — turns that emitted at least one tool call (chat only)
|
|
* ``emitted`` / ``executed`` — tool-call counts (chat only)
|
|
* ``contract`` — well-formed / total calls (chat) or quality score 0-100
|
|
(summary) or cosine accuracy 0-100 (embed)
|
|
* ``wall_s`` — total wall seconds for the run
|
|
* ``extra_*`` — script-specific secondary metrics (e.g. summary
|
|
coherence, embed dimension, embed speed per vector)
|
|
|
|
Usage from any test script::
|
|
|
|
from scripts.model_benchmark import bench_write
|
|
|
|
bench_write(
|
|
script="chat", # or "summary" / "embed"
|
|
model="lite",
|
|
mode="fixture",
|
|
gate_status="PASS",
|
|
turns=10,
|
|
answered=10,
|
|
caps=0,
|
|
tool_turns=9,
|
|
emitted=9,
|
|
executed=9,
|
|
contract=11, # numerator (denominator = emitted)
|
|
wall_s=40.5,
|
|
# optional extras:
|
|
contract_denom=12,
|
|
executed_denom=12,
|
|
)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import csv
|
|
from datetime import date
|
|
from pathlib import Path
|
|
|
|
CSV_PATH = Path(__file__).resolve().parent.parent / "benchmarks" / "model_benchmarks.csv"
|
|
_HEADER = [
|
|
"date", "script", "model", "mode", "gate_status",
|
|
"turns", "answered", "caps",
|
|
"tool_turns", "emitted", "executed",
|
|
"contract", "wall_s",
|
|
"contract_denom", "executed_denom",
|
|
"tool_turns_denom", "extra1_col", "extra1_val",
|
|
"extra2_col", "extra2_val",
|
|
]
|
|
|
|
|
|
def bench_write(
|
|
*,
|
|
script: str,
|
|
model: str,
|
|
mode: str,
|
|
gate_status: str,
|
|
turns: int,
|
|
answered: int,
|
|
caps: int,
|
|
wall_s: float,
|
|
# chat-specific
|
|
tool_turns: int = 0,
|
|
emitted: int = 0,
|
|
executed: int = 0,
|
|
contract: int = 0,
|
|
# denominators (for percentages)
|
|
contract_denom: int | None = None,
|
|
executed_denom: int | None = None,
|
|
tool_turns_denom: int | None = None,
|
|
# extras (any script)
|
|
extra1_col: str = "",
|
|
extra1_val: str = "",
|
|
extra2_col: str = "",
|
|
extra2_val: str = "",
|
|
) -> None:
|
|
"""Append one row to the benchmark CSV."""
|
|
CSV_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Defaults: if denom not given, use the numerator
|
|
c_denom = contract_denom if contract_denom is not None else (contract if contract > 0 else 0)
|
|
e_denom = executed_denom if executed_denom is not None else (emitted if emitted > 0 else 0)
|
|
t_denom = tool_turns_denom if tool_turns_denom is not None else turns
|
|
|
|
row = {
|
|
"date": str(date.today()),
|
|
"script": script,
|
|
"model": model,
|
|
"mode": mode,
|
|
"gate_status": gate_status,
|
|
"turns": turns,
|
|
"answered": answered,
|
|
"caps": caps,
|
|
"tool_turns": tool_turns,
|
|
"tool_turns_denom": t_denom,
|
|
"emitted": emitted,
|
|
"executed": executed,
|
|
"executed_denom": e_denom,
|
|
"contract": contract,
|
|
"contract_denom": c_denom,
|
|
"wall_s": f"{wall_s:.1f}",
|
|
"extra1_col": extra1_col,
|
|
"extra1_val": extra1_val,
|
|
"extra2_col": extra2_col,
|
|
"extra2_val": extra2_val,
|
|
}
|
|
|
|
file_exists = CSV_PATH.exists() and CSV_PATH.stat().st_size > 0
|
|
|
|
with open(CSV_PATH, "a", newline="") as f:
|
|
writer = csv.DictWriter(f, fieldnames=_HEADER)
|
|
if not file_exists:
|
|
writer.writeheader()
|
|
writer.writerow(row)
|