fix(usage-window): name the allowance that stopped this model

Greptile: "Regular models get monthly resets". A full Large-model allowance
won the limit line, retry time and pool bench even when the refused model
is not top tier and its daily allowance is also full, so the line said
"monthly large-model limit · resets in 30 days" while limit_hit (the card)
said "daily". The stop line, window_stop_resets_at_ms and the pool's
reset_at now take the gauge from plan_limits.blocking_gauge with the
model's top-tier mark, the same rule limit_hit reads; a refusal no
all-scope allowance explains still falls back to the top-tier one.
This commit is contained in:
Teknium committed 2026-10-22 13:50:10 -07:00
1 parent c00f999ba4
commit e3fff39ec7
5 files changed
+51 -20

No files matched your search

+4 -2
View File
@@ -623,8 +623,10 @@ def _usage_window_refusal(agent, classified, api_messages=None) -> tuple[str, Op
return "", None, None
try:
from agent.credits_tracker import _window_hard_stop, window_stop_resets_at_ms
from agent.plan_limits import current_model_is_top_tier
state = getattr(agent, "_credits_state", None)
stop = _window_hard_stop(state, model_is_free=False) if state is not None else None
top_tier = current_model_is_top_tier(agent)
stop = _window_hard_stop(state, model_is_free=False, model_is_top_tier=top_tier) if state is not None else None
# The server may word the refusal itself (``display_message`` on the 402): shown verbatim, so a
# reword ships with the API. Without it, the stop line Hermes builds from the gauges.
display = context.get("display_message")
@@ -641,7 +643,7 @@ def _usage_window_refusal(agent, classified, api_messages=None) -> tuple[str, Op
message = server_message
else:
return "", None, None
resets_ms = window_stop_resets_at_ms(state) if state is not None else None
resets_ms = window_stop_resets_at_ms(state, model_is_top_tier=top_tier) if state is not None else None
limit = None
try:
from agent.model_metadata import estimate_messages_tokens_rough
+28 -17
View File
@@ -243,7 +243,23 @@ def _window_approach_text(g: WindowGauge) -> str:
return f"⚠ {pct}% of your {gauge_limit_name(g)} limit used{_resets(g.resets_at_ms)}"
def _window_hard_stop(state: CreditsState, *, model_is_free: bool) -> Optional[str]:
def _stop_gauge(state: CreditsState, *, model_is_top_tier: bool) -> Optional[WindowGauge]:
"""The full allowance behind the limit line, by :func:`agent.plan_limits.blocking_gauge` (the rule
``limit_hit`` reads, so the line, the retry time and the card agree). A full top-tier allowance counts only on
a refusal: the API stamps ``spend-source`` on every admitted request and never on a 402, so header-sourced
+ no source = this model was refused; an ``included`` source means the model is not top-tier and a seed
cannot know the model. A refusal no all-scope allowance explains is the top-tier one (a cold catalog)."""
if not state.is_usage_window:
return None
from agent.plan_limits import blocking_gauge
refused = state.from_header and state.spend_source is None
g = blocking_gauge("", state.gauges, free=False, top_tier=refused and model_is_top_tier)
if g is None and refused and state.top_tier_exhausted:
return state.top_tier_gauge
return g
def _window_hard_stop(state: CreditsState, *, model_is_free: bool, model_is_top_tier: bool = False) -> Optional[str]:
"""Blocking copy for a usage-window account, or None. ``paid_access`` is the portal's word (dunning);
then: payment paused with no credits (credits keep a paused plan going whatever the setting says; a
card update restores included usage), the large-model allowance used with no pay path, the window used with no
@@ -260,26 +276,21 @@ def _window_hard_stop(state: CreditsState, *, model_is_free: bool) -> Optional[s
return None
from agent.plan_limits import credits_action
action = _CREDITS_STEP[credits_action(state.keep_going_on_credits, purchased)]
# The large-model allowance ran out AND the last response was a block: the API stamps ``spend-source`` on every
# admitted request and never on a 402, so header-sourced + no source = this model was refused. An
# ``included`` source means the current model is not top-tier (still admitted) and a seed cannot know
# which model the session will use — neither is a stop (/usage carries the top-tier line).
if state.top_tier_exhausted and state.from_header and state.spend_source is None:
g = _stop_gauge(state, model_is_top_tier=model_is_top_tier)
if g is None:
return None
if g.scope == SCOPE_TOP_TIER:
auto = _AUTO_TOP_UP_SUFFIX if state.auto_top_up_enabled and action == _ADD_CREDITS_ACTION else ""
return f"✕ {_TOP_TIER_LIMIT}{_resets(state.top_tier_resets_at_ms)} · {action}{auto}"
if state.window_exhausted:
name, resets = _spent_window(state)
return f"✕ You've hit your {name} limit{_resets(resets)} · {action}"
return None
return f"✕ {_TOP_TIER_LIMIT}{_resets(g.resets_at_ms)} · {action}{auto}"
return f"✕ You've hit your {gauge_limit_name(g)} limit{_resets(g.resets_at_ms)} · {action}"
def window_stop_resets_at_ms(state: CreditsState) -> Optional[int]:
"""When the allowance behind :func:`_window_hard_stop`'s limit line frees up (same branch order)."""
def window_stop_resets_at_ms(state: CreditsState, *, model_is_top_tier: bool = False) -> Optional[int]:
"""When the allowance behind :func:`_window_hard_stop`'s limit line frees up (the same gauge)."""
if state.unmetered:
return None
if state.top_tier_exhausted and state.from_header and state.spend_source is None:
return state.top_tier_resets_at_ms
return _spent_window(state)[1] if state.window_exhausted else None
g = _stop_gauge(state, model_is_top_tier=model_is_top_tier)
return g.resets_at_ms if g is not None else None
# "Keep going on credits": the account setting (/topup or Portal billing), off by default on a window plan.
@@ -348,7 +359,7 @@ def _evaluate_window_notices(state: CreditsState, latch: dict, *, model_is_free:
to_clear.append(CREDITS_GRANT_SPENT_KEY)
active.discard(CREDITS_GRANT_SPENT_KEY)
stop = _window_hard_stop(state, model_is_free=model_is_free)
stop = _window_hard_stop(state, model_is_free=model_is_free, model_is_top_tier=model_is_top_tier)
limit = stop is not None and stop not in (_PAUSED_TEXT, _PAUSED_OTHER_TEXT, _DEPLETED_TEXT)
was_stopped = CREDITS_DEPLETED_KEY in active or CREDITS_WINDOW_LIMIT_KEY in active
_reconcile_sticky(CREDITS_WINDOW_LIMIT_KEY, stop if limit else None, "error", latch, to_show, to_clear)
+5
View File
@@ -149,6 +149,11 @@ def blocking_gauge(model: str, gauges: Iterable[Any], *, free: bool, top_tier: b
return max(full, key=lambda g: g.resets_at_ms or 0) if full else None
def current_model_is_top_tier(agent: Any) -> bool:
"""*agent*'s current model is top tier, by its own profile's cached catalog (see :func:`is_top_tier`)."""
return is_top_tier(str(getattr(agent, "model", "") or ""), cached_pricing(str(getattr(agent, "base_url", "") or "")))
def is_blocked(model: str, gauges: Iterable[Any], pricing: Optional[dict]) -> bool:
return blocking_gauge(model, gauges, free=is_free(model, pricing),
top_tier=is_top_tier(model, pricing)) is not None
+3 -1
View File
@@ -632,8 +632,10 @@ def _usage_window_reset_context(agent) -> dict[str, float]:
pool keeps its default bench."""
try:
from agent.credits_tracker import window_stop_resets_at_ms
from agent.plan_limits import current_model_is_top_tier
state = getattr(agent, "_credits_state", None)
resets_ms = window_stop_resets_at_ms(state) if state is not None else None
resets_ms = (window_stop_resets_at_ms(state, model_is_top_tier=current_model_is_top_tier(agent))
if state is not None else None)
except Exception:
logger.debug("usage-window reset unavailable for the pool", exc_info=True)
return {}
+11
View File
@@ -1183,6 +1183,17 @@ class TestRefusalReadsAsTheStopLine:
assert _usage_window_reset_context(self._agent()) == {"reset_at": DAY_RESET / 1000}
assert _usage_window_reset_context(SimpleNamespace(_credits_state=None)) == {}
def test_a_regular_model_refused_by_its_day_names_the_day_everywhere(self):
"""Day and Large-model allowances both full on a 402 for a model that is not top tier: the line, the retry
time, the pool bench and the card all name the day (what stops this model), not the month-away reset."""
from agent.conversation_loop import _usage_window_refusal
from agent.turn_recovery import _usage_window_reset_context
agent = self._agent(window_top_tier_used_bp=10_000, window_top_tier_resets_at_ms=MONTH_RESET)
text, resets_at, limit = _usage_window_refusal(agent, SimpleNamespace(error_context={"usage_window": True}))
assert text.startswith(f"You've hit your daily limit · resets {format_window_reset(DAY_RESET)}")
assert resets_at == DAY_RESET / 1000 and limit["gauge_id"] == "day"
assert _usage_window_reset_context(agent) == {"reset_at": DAY_RESET / 1000}
def test_keep_going_off_with_no_balance_says_both_steps_everywhere(self):
"""A new subscriber's first limit (setting off by default, no top-up): the stop line, the next-steps
payload and the 402 guidance all name both steps, since either one alone is still refused."""