mirror of
https://github.com/usestrix/strix.git
synced 2026-08-25 20:32:38 +02:00
fix(tui): show the actual subscription provider in the Go TUI
The Go TUI rewrite hardcoded "ChatGPT subscription" in the stats view and its snapshot only carried a boolean, so a Grok run was labeled ChatGPT — the same bug previously fixed on the Python side. Carry the provider label through the snapshot protocol and render it, falling back to a generic "Subscription". utils._subscription_label becomes public subscription_label since the TUI backend now needs it too.
This commit is contained in:
@@ -24,7 +24,7 @@ from strix.interface.tui.backend.projection import (
|
|||||||
sanitize_terminal_text,
|
sanitize_terminal_text,
|
||||||
terminal_projection,
|
terminal_projection,
|
||||||
)
|
)
|
||||||
from strix.interface.utils import is_subscription_run
|
from strix.interface.utils import is_subscription_run, subscription_label
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -162,8 +162,11 @@ class TuiController:
|
|||||||
if self.report_state is not None:
|
if self.report_state is not None:
|
||||||
usage = dict(self.report_state.get_total_llm_usage())
|
usage = dict(self.report_state.get_total_llm_usage())
|
||||||
subscription = False
|
subscription = False
|
||||||
|
subscription_name = ""
|
||||||
with contextlib.suppress(Exception):
|
with contextlib.suppress(Exception):
|
||||||
subscription = is_subscription_run(self.report_state)
|
subscription = is_subscription_run(self.report_state)
|
||||||
|
if subscription:
|
||||||
|
subscription_name = subscription_label(self.report_state)
|
||||||
model_warning = ""
|
model_warning = ""
|
||||||
if model and not is_recommended_or_frontier_model(model):
|
if model and not is_recommended_or_frontier_model(model):
|
||||||
model_warning = (
|
model_warning = (
|
||||||
@@ -200,6 +203,7 @@ class TuiController:
|
|||||||
],
|
],
|
||||||
"usage": terminal_projection(usage, max_string=256, max_items=20),
|
"usage": terminal_projection(usage, max_string=256, max_items=20),
|
||||||
"subscription": subscription,
|
"subscription": subscription,
|
||||||
|
"subscription_label": terminal_projection(subscription_name, max_string=64),
|
||||||
"viewer_status": self.viewer_status,
|
"viewer_status": self.viewer_status,
|
||||||
"viewer_url": terminal_projection(self.viewer_url, max_string=1024),
|
"viewer_url": terminal_projection(self.viewer_url, max_string=1024),
|
||||||
"error": terminal_projection(self.error, max_string=2 * 1024),
|
"error": terminal_projection(self.error, max_string=2 * 1024),
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ def bounded_state_projection(state: dict[str, Any]) -> dict[str, Any]:
|
|||||||
"messages": [],
|
"messages": [],
|
||||||
"usage": {},
|
"usage": {},
|
||||||
"subscription": state["subscription"],
|
"subscription": state["subscription"],
|
||||||
|
"subscription_label": state["subscription_label"],
|
||||||
"viewer_status": state["viewer_status"],
|
"viewer_status": state["viewer_status"],
|
||||||
"viewer_url": None,
|
"viewer_url": None,
|
||||||
"error": terminal_projection(state["error"], max_string=256),
|
"error": terminal_projection(state["error"], max_string=256),
|
||||||
|
|||||||
@@ -1067,11 +1067,12 @@ func TestBudgetPauseShowsOneWarningToastUntilResumed(t *testing.T) {
|
|||||||
|
|
||||||
func TestStatsViewShowsSubscription(t *testing.T) {
|
func TestStatsViewShowsSubscription(t *testing.T) {
|
||||||
model := New(nil)
|
model := New(nil)
|
||||||
model.snapshot.Model = "gpt-5"
|
model.snapshot.Model = "grok/grok-4"
|
||||||
model.snapshot.Subscription = true
|
model.snapshot.Subscription = true
|
||||||
|
model.snapshot.SubscriptionLabel = "Grok subscription"
|
||||||
model.snapshot.Usage = map[string]any{"total_tokens": float64(1200), "cost": 3.5}
|
model.snapshot.Usage = map[string]any{"total_tokens": float64(1200), "cost": 3.5}
|
||||||
stats := ansi.Strip(model.statsView())
|
stats := ansi.Strip(model.statsView())
|
||||||
if !strings.Contains(stats, "ChatGPT subscription") {
|
if !strings.Contains(stats, "Grok subscription") {
|
||||||
t.Fatalf("stats missing subscription line: %q", stats)
|
t.Fatalf("stats missing subscription line: %q", stats)
|
||||||
}
|
}
|
||||||
if strings.Contains(stats, "$") {
|
if strings.Contains(stats, "$") {
|
||||||
@@ -1079,6 +1080,16 @@ func TestStatsViewShowsSubscription(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStatsViewSubscriptionFallsBackWithoutLabel(t *testing.T) {
|
||||||
|
model := New(nil)
|
||||||
|
model.snapshot.Model = "gpt-5"
|
||||||
|
model.snapshot.Subscription = true
|
||||||
|
stats := ansi.Strip(model.statsView())
|
||||||
|
if !strings.Contains(stats, "Subscription") {
|
||||||
|
t.Fatalf("stats missing generic subscription line: %q", stats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestVulnerabilityMarkdownReport(t *testing.T) {
|
func TestVulnerabilityMarkdownReport(t *testing.T) {
|
||||||
report := vulnerabilityMarkdownReport(map[string]any{
|
report := vulnerabilityMarkdownReport(map[string]any{
|
||||||
"title": "SQLi in login",
|
"title": "SQLi in login",
|
||||||
|
|||||||
@@ -596,7 +596,11 @@ func (m Model) statsView() string {
|
|||||||
if b.Len() > 0 {
|
if b.Len() > 0 {
|
||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
}
|
}
|
||||||
b.WriteString(lipgloss.NewStyle().Foreground(green).Render("ChatGPT subscription"))
|
label := m.snapshot.SubscriptionLabel
|
||||||
|
if label == "" {
|
||||||
|
label = "Subscription"
|
||||||
|
}
|
||||||
|
b.WriteString(lipgloss.NewStyle().Foreground(green).Render(label))
|
||||||
}
|
}
|
||||||
total := numberValue(m.snapshot.Usage["total_tokens"])
|
total := numberValue(m.snapshot.Usage["total_tokens"])
|
||||||
if total > 0 {
|
if total > 0 {
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ type Snapshot struct {
|
|||||||
Vulnerabilities []map[string]any `json:"-"`
|
Vulnerabilities []map[string]any `json:"-"`
|
||||||
Usage map[string]any `json:"usage"`
|
Usage map[string]any `json:"usage"`
|
||||||
Subscription bool `json:"subscription"`
|
Subscription bool `json:"subscription"`
|
||||||
|
SubscriptionLabel string `json:"subscription_label"`
|
||||||
ViewerStatus string `json:"viewer_status"`
|
ViewerStatus string `json:"viewer_status"`
|
||||||
ViewerURL *string `json:"viewer_url"`
|
ViewerURL *string `json:"viewer_url"`
|
||||||
Error *string `json:"error"`
|
Error *string `json:"error"`
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ def is_subscription_run(report_state: Any) -> bool:
|
|||||||
return subscription.auth_mode(load_settings().llm.model) == "subscription"
|
return subscription.auth_mode(load_settings().llm.model) == "subscription"
|
||||||
|
|
||||||
|
|
||||||
def _subscription_label(report_state: Any) -> str:
|
def subscription_label(report_state: Any) -> str:
|
||||||
"""Human label for the active model subscription (e.g. "Grok subscription").
|
"""Human label for the active model subscription (e.g. "Grok subscription").
|
||||||
|
|
||||||
Prefers the persisted run record so a resumed run keeps its original provider
|
Prefers the persisted run record so a resumed run keeps its original provider
|
||||||
@@ -386,7 +386,7 @@ def build_live_stats_text(report_state: Any) -> Text:
|
|||||||
stats_text.append(str(model), style="white")
|
stats_text.append(str(model), style="white")
|
||||||
if is_subscription_run(report_state):
|
if is_subscription_run(report_state):
|
||||||
stats_text.append(" · ", style="dim white")
|
stats_text.append(" · ", style="dim white")
|
||||||
stats_text.append(_subscription_label(report_state), style="#22c55e")
|
stats_text.append(subscription_label(report_state), style="#22c55e")
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
|
|
||||||
vuln_count = len(report_state.vulnerability_reports)
|
vuln_count = len(report_state.vulnerability_reports)
|
||||||
@@ -432,7 +432,7 @@ def build_tui_stats_text(report_state: Any) -> Text:
|
|||||||
subscription = is_subscription_run(report_state)
|
subscription = is_subscription_run(report_state)
|
||||||
if subscription:
|
if subscription:
|
||||||
stats_text.append("\n")
|
stats_text.append("\n")
|
||||||
stats_text.append(_subscription_label(report_state), style="#22c55e")
|
stats_text.append(subscription_label(report_state), style="#22c55e")
|
||||||
|
|
||||||
usage = _llm_usage(report_state)
|
usage = _llm_usage(report_state)
|
||||||
if usage and _int_stat(usage, "total_tokens") > 0:
|
if usage and _int_stat(usage, "total_tokens") > 0:
|
||||||
|
|||||||
@@ -66,12 +66,12 @@ def test_subscription_label_prefers_persisted_provider(monkeypatch) -> None: #
|
|||||||
resumed = mock.MagicMock(
|
resumed = mock.MagicMock(
|
||||||
run_record={"auth_mode": "subscription", "subscription_provider": "Grok"}
|
run_record={"auth_mode": "subscription", "subscription_provider": "Grok"}
|
||||||
)
|
)
|
||||||
assert utils._subscription_label(resumed) == "Grok subscription"
|
assert utils.subscription_label(resumed) == "Grok subscription"
|
||||||
|
|
||||||
# With no persisted provider, it derives the label from settings (not a
|
# With no persisted provider, it derives the label from settings (not a
|
||||||
# hardcoded default).
|
# hardcoded default).
|
||||||
fresh = mock.MagicMock(run_record={})
|
fresh = mock.MagicMock(run_record={})
|
||||||
assert utils._subscription_label(fresh) == "ChatGPT subscription"
|
assert utils.subscription_label(fresh) == "ChatGPT subscription"
|
||||||
|
|
||||||
|
|
||||||
def test_persisted_run_record_carries_provider(tmp_path, monkeypatch) -> None: # type: ignore[no-untyped-def]
|
def test_persisted_run_record_carries_provider(tmp_path, monkeypatch) -> None: # type: ignore[no-untyped-def]
|
||||||
|
|||||||
@@ -110,6 +110,28 @@ def test_state_populates_model_warning_for_non_frontier_model() -> None:
|
|||||||
assert "not a recommended frontier model" in warning
|
assert "not a recommended frontier model" in warning
|
||||||
|
|
||||||
|
|
||||||
|
def test_snapshot_carries_the_subscription_provider_label() -> None:
|
||||||
|
os.environ["STRIX_LLM"] = "grok/grok-4"
|
||||||
|
loader._cached = None
|
||||||
|
|
||||||
|
snapshot = TuiController(args()).snapshot()
|
||||||
|
|
||||||
|
# The TUI renders this label, so it must name the actual provider rather
|
||||||
|
# than assuming ChatGPT.
|
||||||
|
assert snapshot["subscription"] is True
|
||||||
|
assert snapshot["subscription_label"] == "Grok subscription"
|
||||||
|
|
||||||
|
|
||||||
|
def test_snapshot_has_no_subscription_label_for_api_key_runs() -> None:
|
||||||
|
os.environ["STRIX_LLM"] = "openai/gpt-5.4"
|
||||||
|
loader._cached = None
|
||||||
|
|
||||||
|
snapshot = TuiController(args()).snapshot()
|
||||||
|
|
||||||
|
assert snapshot["subscription"] is False
|
||||||
|
assert snapshot["subscription_label"] == ""
|
||||||
|
|
||||||
|
|
||||||
def test_setup_restores_prepared_cli_targets() -> None:
|
def test_setup_restores_prepared_cli_targets() -> None:
|
||||||
setup_args = args()
|
setup_args = args()
|
||||||
setup_args.targets_info = [
|
setup_args.targets_info = [
|
||||||
|
|||||||
Reference in New Issue
Block a user