fix(viewer): show the actual subscription provider name

The Run details panel hardcoded "ChatGPT subscription" for any subscription run. Emit subscription_provider (ChatGPT/Grok) in the run record and render it in the viewer, so Grok runs read "Grok subscription". Rebuilds the committed viewer bundle.
This commit is contained in:
yoni
2026-07-29 16:40:06 +00:00
parent bfceb65a4c
commit 218470f14d
6 changed files with 59 additions and 25 deletions
+12
View File
@@ -19,6 +19,9 @@ if TYPE_CHECKING:
_PROVIDERS: tuple[ModuleType, ...] = (codex, grok)
# Human-facing provider names keyed by each module's ``PROVIDER`` constant.
_DISPLAY_NAMES: dict[str, str] = {codex.PROVIDER: "ChatGPT", grok.PROVIDER: "Grok"}
def provider_for_model(model_name: str | None) -> ModuleType | None:
"""Return the subscription provider module that owns ``model_name``'s prefix,
@@ -31,3 +34,12 @@ def provider_for_model(model_name: str | None) -> ModuleType | None:
def auth_mode(model_name: str | None) -> str:
return "subscription" if provider_for_model(model_name) is not None else "api_key"
def provider_label(model_name: str | None) -> str | None:
"""Human-facing name of the subscription provider for ``model_name`` (e.g.
"ChatGPT" or "Grok"), or None when the model isn't a subscription model."""
provider = provider_for_model(model_name)
if provider is None:
return None
return _DISPLAY_NAMES.get(provider.PROVIDER)
@@ -101,6 +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 sub = (n: number, word: string) => (
<span className="text-[#666]"> ({formatNumber(n)} {word})</span>
@@ -180,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]">
ChatGPT subscription
{subscriptionProvider} subscription
</span>
</span>
</Field>
File diff suppressed because one or more lines are too long
+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-DzvI_0HX.js"></script>
<script type="module" crossorigin src="./assets/index-BAP4-Z16.js"></script>
<link rel="stylesheet" crossorigin href="./assets/index-C3kQ5kk8.css">
</head>
<body>
+3 -1
View File
@@ -119,7 +119,8 @@ class ReportState:
self.scan_results: dict[str, Any] | None = None
self.scan_config: dict[str, Any] | None = None
self._llm_usage = LLMUsageLedger()
auth_mode = subscription.auth_mode(load_settings().llm.model)
model = load_settings().llm.model
auth_mode = subscription.auth_mode(model)
self._llm_usage.zero_cost = auth_mode == "subscription"
self.run_record: dict[str, Any] = {
"run_id": self.run_id,
@@ -128,6 +129,7 @@ class ReportState:
"end_time": None,
"status": "running",
"auth_mode": auth_mode,
"subscription_provider": subscription.provider_label(model),
"targets_info": [],
"llm_usage": self._build_llm_usage_record(),
}
+20 -1
View File
@@ -6,8 +6,9 @@ from unittest import mock
from agents.models.openai_chatcompletions import OpenAIChatCompletionsModel
from strix.config import grok
from strix.config import grok, subscription
from strix.config.models import StrixProvider
from strix.report import state as state_mod
def test_grok_prefix_routes_to_chat_completions(monkeypatch) -> None: # type: ignore[no-untyped-def]
@@ -32,3 +33,21 @@ def test_non_subscription_model_is_not_hijacked_by_grok(monkeypatch) -> None: #
# not the subscription route.
model = StrixProvider().get_model("xai/grok-4")
assert not (isinstance(model, OpenAIChatCompletionsModel) and model.model == "grok-4")
def test_provider_label_names_the_subscription() -> None:
assert subscription.provider_label("grok/grok-4") == "Grok"
assert subscription.provider_label("chatgpt/gpt-5.4") == "ChatGPT"
# Metered API-key models are not subscriptions.
assert subscription.provider_label("xai/grok-4") is None
assert subscription.provider_label("openai/gpt-5.4") is None
def test_run_record_reports_grok_provider(monkeypatch) -> None: # type: ignore[no-untyped-def]
settings = mock.MagicMock()
settings.llm.model = "grok/grok-4"
monkeypatch.setattr(state_mod, "load_settings", lambda: settings)
record = state_mod.ReportState(run_name="run-test").run_record
assert record["auth_mode"] == "subscription"
assert record["subscription_provider"] == "Grok"