fix(credits): suppress depleted banner on stealth-preview models

Stealth-preview SKUs (e.g. stealth/ox-alpha) are free-tier but carry no
:free suffix, so is_free_tier_model() returned False for them.  On gateway
sessions (which never run the model picker's pricing fetch), the free-model
suppression of the credits.depleted banner never engaged, and any response
carrying paid_access:false triggered a false "Credit access paused" notice.

Add stealth/ prefix detection to is_free_tier_model() as a zero-network
signal, same design as the existing :free suffix check.  Fail-open to
False (banner still shows) if the prefix changes — recoverable noise,
never a masked depletion on a paid model.

Closes #91843
This commit is contained in:
kshitijk4poor
2026-08-22 03:52:16 +05:30
committed by kshitij
parent b40d85dc81
commit ad96d2e2d9
2 changed files with 55 additions and 2 deletions
+14 -2
View File
@@ -226,12 +226,15 @@ def is_free_tier_model(model: str, base_url: str = "") -> bool:
1. The ``:free`` suffix — the canonical Nous free SKU marker (e.g.
``nvidia/nemotron-3-ultra:free``). Free by construction on the API side
(spend is forced to 0 for ``:free`` ids).
2. A peek into the in-process pricing cache in ``hermes_cli.models``
2. The ``stealth/`` prefix — Nous stealth-preview SKUs (e.g.
``stealth/ox-alpha``) are free-tier but carry no ``:free`` suffix. Spend
is forced to zero server-side, so these are also free by construction.
3. A peek into the in-process pricing cache in ``hermes_cli.models``
(populated when the model picker fetched ``/v1/models`` pricing for
*base_url*). PEEK ONLY — a cache miss never triggers a fetch. This is
CLI/TUI-session best-effort: gateway sessions never run the picker's
pricing fetch, so suppression there rests entirely on the ``:free``
suffix (which all Nous free SKUs carry).
suffix and ``stealth/`` prefix.
Fail-open to False (the depleted notice still shows) on any error: wrongly
showing the warning is recoverable noise; wrongly hiding it on a paid model
@@ -241,6 +244,15 @@ def is_free_tier_model(model: str, base_url: str = "") -> bool:
return False
if model.endswith(":free"):
return True
# Stealth-preview SKUs (e.g. stealth/ox-alpha) are free-tier but carry no
# ``:free`` suffix. Spend is forced to zero server-side, so a ``paid_access:
# false`` header on these models is a false positive for the depleted banner.
# The ``stealth/`` prefix is the Nous naming convention for these SKUs and
# is checked here as a zero-network signal, same design as the ``:free``
# suffix above. Fail-open to False (the banner still shows) if the prefix
# ever changes — recoverable noise, never a masked depletion on a paid model.
if model.startswith("stealth/"):
return True
if not base_url:
return False
try:
+41
View File
@@ -351,6 +351,47 @@ class TestIsFreeTierModel:
monkeypatch.setattr(models_mod, "_pricing_cache", _Exploding())
assert is_free_tier_model("some/model", "https://inference-api.nousresearch.com") is False
def test_stealth_prefix_detected_as_free(self):
"""Stealth-preview SKUs (stealth/...) are free-tier but carry no
:free suffix. Suppression must engage so the depleted banner doesn't
fire on a false paid_access:false from the server's stealth pool."""
from agent.credits_tracker import is_free_tier_model
# No base_url needed — stealth/ is a zero-network signal, same as :free.
assert is_free_tier_model("stealth/ox-alpha", "") is True
assert is_free_tier_model("stealth/ox-alpha", "https://inference-api.nousresearch.com/v1") is True
# Non-stealth model without :free suffix → not free (without pricing cache).
assert is_free_tier_model("some/paid-model", "") is False
def test_depleted_suppressed_for_stealth_model(self):
"""End-to-end: paid_access:false on a stealth/ model must NOT fire
the depleted banner (the exact scenario from issue #91843)."""
from agent.credits_tracker import (
CreditsState, evaluate_credits_notices, is_free_tier_model,
)
state = CreditsState(
version=1,
remaining_micros=0,
remaining_usd="0.00",
subscription_micros=0,
subscription_usd="0.00",
purchased_micros=0,
purchased_usd="0.00",
paid_access=False,
captured_at=1.0,
from_header=True,
)
model = "stealth/ox-alpha"
base_url = "https://inference-api.nousresearch.com/v1"
model_is_free = is_free_tier_model(model, base_url)
assert model_is_free is True
latch = fresh_latch()
to_show, to_clear = evaluate_credits_notices(state, latch, model_is_free=model_is_free)
assert all(n.key != "credits.depleted" for n in to_show)
assert "credits.depleted" not in latch["active"]
# ── Scenario 6: denominator none (uf is None) ────────────────────────────────