diff --git a/agent/insights.py b/agent/insights.py index b1b9a06ef6..c1dec9e073 100644 --- a/agent/insights.py +++ b/agent/insights.py @@ -1022,7 +1022,7 @@ class InsightsEngine: lines.append(" 💰 Cost") lines.append(" " + "─" * 56) if est_cost > 0: - lines.append(f" Estimated: {_fmt_est_cost(est_cost)}") + lines.append(f" Estimated: {_fmt_est_cost(est_cost)}") if included_sessions > 0: lines.append( f" Included: {included_sessions} session(s) " diff --git a/agent/usage_pricing.py b/agent/usage_pricing.py index d3e294f7b4..5cacdb04a9 100644 --- a/agent/usage_pricing.py +++ b/agent/usage_pricing.py @@ -22,14 +22,20 @@ _NOUS_DEFAULT_BASE_URL = "https://inference-api.nousresearch.com/v1" # the display is non-zero (e.g. $0.0046 instead of $0.00). See #79220. _SUBCENT_THRESHOLD = Decimal("0.01") +# Attached to every CostResult with status="included" so consumers can +# distinguish "free because subscription" from "free because $0 pricing". +_INCLUDED_NOTE = "subscription-included; no provider invoice for usage" + def format_cost_label(amount: Decimal) -> str: """Format a cost amount as a display label. Scales precision to magnitude: - Zero → "$0.00" - - Sub-cent (< $0.01) → "~$0.0046" (4 dp; below $0.00005 falls back - to "~$<0.0001" so the label never reads as zero) + - Sub-cent (< $0.01) → "~$0.0046" (4 dp; amounts that ROUND to + 0.0000 at 4 dp — i.e. at or below $0.00005 under banker's + rounding — fall back to "~$<0.0001" so the label never reads + as zero) - Normal → "~$1.23" (2 dp) This fixes #79220 where sub-cent per-turn costs on cheap models @@ -44,8 +50,11 @@ def format_cost_label(amount: Decimal) -> str: return "$0.00" if amount < _SUBCENT_THRESHOLD: label = f"~${amount:.4f}" - # 4dp truncation of a positive amount below $0.00005 would render + # A positive amount that rounds to 0.0000 at 4 dp would render # "~$0.0000" — a zero-looking label, the exact #79220 dishonesty. + # Comparing the rendered label checks the truth directly (a naive + # `< 0.00005` threshold misses the exact boundary under + # ROUND_HALF_EVEN). return label if label != "~$0.0000" else "~$<0.0001" return f"~${amount:.2f}" @@ -1365,7 +1374,7 @@ def estimate_usage_cost( source="none", label="included", pricing_version="included-route", - notes=("subscription-included; no provider invoice for usage",), + notes=(_INCLUDED_NOTE,), ) entry = get_pricing_entry(model_name, provider=provider, base_url=base_url, api_key=api_key) @@ -1414,6 +1423,7 @@ def estimate_usage_cost( if entry.source == "none" and amount == _ZERO: status = "included" label = "included" + notes.append(_INCLUDED_NOTE) if route.provider == "openrouter": notes.append("OpenRouter cost is estimated from the models API until reconciled.") diff --git a/tests/agent/test_insights.py b/tests/agent/test_insights.py index 1c19abf43d..22de263908 100644 --- a/tests/agent/test_insights.py +++ b/tests/agent/test_insights.py @@ -701,7 +701,8 @@ class TestEdgeCases: report = engine.generate(days=30) text = engine.format_terminal(report) - # The cost section should appear with all three buckets + # The cost section should appear with the buckets this DB has + # (estimated + included; no unknown-cost session is created here) assert "💰 Cost" in text assert "~$1.50" in text # estimated assert "included" in text.lower() diff --git a/tests/agent/test_usage_pricing.py b/tests/agent/test_usage_pricing.py index fa0fcef5a6..ddb9d4cfbb 100644 --- a/tests/agent/test_usage_pricing.py +++ b/tests/agent/test_usage_pricing.py @@ -416,7 +416,9 @@ class TestFormatCostLabel: """ label = format_cost_label(Decimal("0.00004")) assert label == "~$<0.0001" - assert "0.0000" not in label.replace("<0.0001", "") + # Exact boundary: $0.00005 rounds to 0.0000 under ROUND_HALF_EVEN + # and must also take the fallback. + assert format_cost_label(Decimal("0.00005")) == "~$<0.0001" def test_sub_cent_deepseek_scenario(self): """Reproduce the #79220 reproduction: DeepSeek at $0.004640."""