fix(viewer): label historical subscription runs by provider

read_run_summary backfills subscription_provider from the recorded provider/model slug (reusing subscription.provider_label) when the field is absent, so runs recorded before it existed still label correctly without a rescan. The viewer no longer defaults to "ChatGPT" when the provider is unknown. Rebuilds the committed viewer bundle.
This commit is contained in:
yoni
2026-07-29 17:04:38 +00:00
parent 218470f14d
commit 5c94872186
6 changed files with 88 additions and 5 deletions
+3
View File
@@ -225,6 +225,9 @@ ignore = [
# Stdlib HTTP handler overrides (do_GET/do_POST) and lazy imports that avoid a
# circular dependency with strix.telemetry / strix.interface.viewer.report_pdf.
"strix/interface/viewer/server.py" = ["N802", "PLC0415"]
# Lazy import of the TUI live-view projection so importing the viewer does not
# eagerly pull in the Textual TUI.
"strix/interface/viewer/transcript.py" = ["PLC0415"]
# Lazy telemetry import to avoid importing PostHog before the viewer starts.
"strix/interface/viewer/cli.py" = ["PLC0415"]
# Lazy imports inside functions to avoid circular dependency with
@@ -101,7 +101,7 @@ export function RunDetails({
const totalTokens = num(usage.total_tokens);
const cost = num(usage.cost);
const subscription = str(raw.auth_mode) === "subscription";
const subscriptionProvider = str(raw.subscription_provider) || "ChatGPT";
const subscriptionProvider = str(raw.subscription_provider);
const sub = (n: number, word: string) => (
<span className="text-[#666]"> ({formatNumber(n)} {word})</span>
@@ -181,7 +181,7 @@ export function RunDetails({
<Field label="Provider">
<span className="inline-flex items-center gap-1.5">
<span className="rounded-full border border-[#22c55e]/40 bg-[#22c55e]/10 px-2 py-0.5 text-[11px] text-[#22c55e]">
{subscriptionProvider} subscription
{subscriptionProvider ? `${subscriptionProvider} subscription` : "Subscription"}
</span>
</span>
</Field>
+1 -1
View File
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="dark" />
<title>Strix Results</title>
<script type="module" crossorigin src="./assets/index-BAP4-Z16.js"></script>
<script type="module" crossorigin src="./assets/index-CbkcEEWn.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-C3kQ5kk8.css">
</head>
<body>
+34 -1
View File
@@ -6,6 +6,7 @@ import json
import logging
from typing import TYPE_CHECKING, Any
from strix.config import subscription
from strix.core.paths import run_record_path
@@ -59,7 +60,39 @@ def read_run_summary(run_dir: Path) -> dict[str, Any]:
record = {}
status = record.get("status")
finished = status in _TERMINAL_STATUSES and bool(record.get("end_time"))
return {**record, "finished": finished}
summary = {**record, "finished": finished}
_backfill_subscription_provider(summary)
return summary
def _first_recorded_model(record: dict[str, Any]) -> str | None:
"""The first non-empty per-agent model slug in a run record, or None."""
usage = record.get("llm_usage")
if not isinstance(usage, dict):
return None
agents = usage.get("agents")
if not isinstance(agents, list):
return None
for agent in agents:
if isinstance(agent, dict):
model = agent.get("model")
if isinstance(model, str) and model:
return model
return None
def _backfill_subscription_provider(record: dict[str, Any]) -> None:
"""Name the subscription provider for runs recorded before that field
existed, deriving it from the recorded ``provider/model`` slug so the viewer
labels them correctly without a rescan. Newer runs already carry the field.
"""
if record.get("subscription_provider"):
return
if record.get("auth_mode") != "subscription":
return
label = subscription.provider_label(_first_recorded_model(record))
if label:
record["subscription_provider"] = label
def primary_target(record: dict[str, Any]) -> str | None:
+47
View File
@@ -70,6 +70,53 @@ def test_read_run_summary_finished_flag(tmp_path: Path) -> None:
assert read_run_summary(partial)["finished"] is False
def _write_record(base: Path, name: str, record: dict[str, object]) -> Path:
run_dir = base / "strix_runs" / name
run_dir.mkdir(parents=True)
(run_dir / "run.json").write_text(json.dumps(record), encoding="utf-8")
return run_dir
def test_read_run_summary_backfills_subscription_provider(tmp_path: Path) -> None:
# An older subscription run recorded no provider name; it is derived from
# the recorded provider/model slug so the viewer can label it.
run_dir = _write_record(
tmp_path,
"grok-run",
{
"auth_mode": "subscription",
"llm_usage": {"agents": [{"agent_id": "root", "model": "grok/grok-4"}]},
},
)
assert read_run_summary(run_dir)["subscription_provider"] == "Grok"
def test_read_run_summary_keeps_explicit_provider(tmp_path: Path) -> None:
run_dir = _write_record(
tmp_path,
"chatgpt-run",
{
"auth_mode": "subscription",
"subscription_provider": "ChatGPT",
"llm_usage": {"agents": [{"agent_id": "root", "model": "grok/grok-4"}]},
},
)
# An explicit field is authoritative and never overwritten by the slug.
assert read_run_summary(run_dir)["subscription_provider"] == "ChatGPT"
def test_read_run_summary_ignores_api_key_runs(tmp_path: Path) -> None:
run_dir = _write_record(
tmp_path,
"api-key-run",
{
"auth_mode": "api_key",
"llm_usage": {"agents": [{"agent_id": "root", "model": "openai/gpt-5.4"}]},
},
)
assert "subscription_provider" not in read_run_summary(run_dir)
def test_read_missing_artifacts_return_defaults(tmp_path: Path) -> None:
run_dir = _make_run(tmp_path, "empty", status="running", end_time=None)
assert read_vulnerabilities(run_dir) == []