diff --git a/agent/credits_tracker.py b/agent/credits_tracker.py index f2186fb91a56c..929bc34d32624 100644 --- a/agent/credits_tracker.py +++ b/agent/credits_tracker.py @@ -316,12 +316,21 @@ def evaluate_credits_notices( active.discard(CREDITS_USAGE_KEY) if target_band is not None: # Belt-and-suspenders: a producer could set subscription_limit_micros - # without subscription_limit_usd. Render "$? cap" rather than "$None cap". + # without subscription_limit_usd. Render "$?" rather than "$None". _cap_usd = state.subscription_limit_usd or "?" _level = current_band[1] # type: ignore[index] (current_band set when target_band set) + # Report absolute dollars used, not a bare "N% used": the percentage is + # only meaningful against a Nous subscription cap (no cap → never fires), + # so dollars are clearer and don't imply a universal %. Used = cap − + # remaining (micros, money-safe), clamped to [0, cap]. Re-emits on band + # change (50 → 75 → 90), not every turn — a snapshot, not a live ticker. + _lim = state.subscription_limit_micros or 0 + _used_micros = max(0, min(_lim, _lim - state.subscription_micros)) + _used_usd = f"{_used_micros / 1_000_000:.2f}" if _lim else "?" + _glyph = "⚠" if _level == "warn" else "•" to_show.append( AgentNotice( - text=f"{'⚠' if _level == 'warn' else '•'} Credits {target_band}% used · ${_cap_usd} cap", + text=f"{_glyph} You've used ${_used_usd} of your ${_cap_usd} cap", level=_level, kind=CREDITS_NOTICE_KIND, key=CREDITS_USAGE_KEY, diff --git a/tests/agent/test_credits_policy.py b/tests/agent/test_credits_policy.py index de106dadb90e1..ba7142d14aed1 100644 --- a/tests/agent/test_credits_policy.py +++ b/tests/agent/test_credits_policy.py @@ -569,7 +569,8 @@ class TestTopUpSuppression: assert latch["usage_band"] is None to_show, _ = evaluate_credits_notices(state_with_fraction(0.95), latch) n = next(n for n in to_show if n.key == "credits.usage") - assert "90%" in n.text + # uf 0.95 of a $20 cap → used = $19.00 (cap − remaining, clamped). + assert "$19.00" in n.text assert latch["usage_band"] == 90 def test_grant_spent_still_fires_with_topup(self): @@ -645,7 +646,8 @@ class TestUsageBands: evaluate_credits_notices(state_with_fraction(0.10), latch) # prime to_show, _ = evaluate_credits_notices(state_with_fraction(0.55), latch) n = next(n for n in to_show if n.key == "credits.usage") - assert "50%" in n.text and n.level == "info" + # uf 0.55 of a $20 cap → used = $11.00; band 50 fires at info level. + assert "$11.00" in n.text and n.level == "info" assert latch["usage_band"] == 50 def test_75_band_fires_warn(self): @@ -653,7 +655,8 @@ class TestUsageBands: evaluate_credits_notices(state_with_fraction(0.10), latch) to_show, _ = evaluate_credits_notices(state_with_fraction(0.80), latch) n = next(n for n in to_show if n.key == "credits.usage") - assert "75%" in n.text and n.level == "warn" + # uf 0.80 of a $20 cap → used = $16.00; band 75 fires at warn level. + assert "$16.00" in n.text and n.level == "warn" assert latch["usage_band"] == 75 def test_climb_replaces_band(self): @@ -663,15 +666,15 @@ class TestUsageBands: # 55% → 50 band evaluate_credits_notices(state_with_fraction(0.55), latch) assert latch["usage_band"] == 50 - # 80% → climbs to 75, clearing the 50 line + # 80% → climbs to 75, clearing the 50 line (used = $16.00 of $20) to_show, to_clear = evaluate_credits_notices(state_with_fraction(0.80), latch) assert "credits.usage" in to_clear - assert "75%" in self._band_text(to_show) + assert "$16.00" in self._band_text(to_show) assert latch["usage_band"] == 75 - # 95% → climbs to 90 + # 95% → climbs to 90 (used = $19.00 of $20) to_show, to_clear = evaluate_credits_notices(state_with_fraction(0.95), latch) assert "credits.usage" in to_clear - assert "90%" in self._band_text(to_show) + assert "$19.00" in self._band_text(to_show) assert latch["usage_band"] == 90 def test_step_down_on_recovery(self): @@ -680,13 +683,13 @@ class TestUsageBands: evaluate_credits_notices(state_with_fraction(0.10), latch) evaluate_credits_notices(state_with_fraction(0.95), latch) assert latch["usage_band"] == 90 - # drop to 80% → steps down to 75 + # drop to 80% → steps down to 75 (used = $16.00 of $20) to_show, to_clear = evaluate_credits_notices(state_with_fraction(0.80), latch) assert "credits.usage" in to_clear - assert "75%" in self._band_text(to_show) - # drop to 55% → steps down to 50 + assert "$16.00" in self._band_text(to_show) + # drop to 55% → steps down to 50 (used = $11.00 of $20) to_show, _ = evaluate_credits_notices(state_with_fraction(0.55), latch) - assert "50%" in self._band_text(to_show) + assert "$11.00" in self._band_text(to_show) # drop below 50% → clears entirely to_show, to_clear = evaluate_credits_notices(state_with_fraction(0.10), latch) assert "credits.usage" in to_clear