Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 101 additions & 1 deletion agent/insights.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,39 @@ def _estimate_cost(
return float(result.amount_usd or 0.0), result.status


def _estimate_included_market_cost(session: Dict) -> float:
"""At-market USD value of a subscription-included session (hypothetical).

``estimate_usage_cost`` prices subscription-included routes (e.g.
``openai-codex``) at zero — correct for per-session billing, but the
aggregate view wants to know what the same token load would cost at
market rates. Re-run the estimate against the underlying market route
(``openai-codex`` → ``openai``) using the same price table; returns
0.0 when no market price is computable (the count/token buckets still
surface the usage).
"""
model = session.get("model") or ""
usage = CanonicalUsage(
input_tokens=session.get("input_tokens") or 0,
output_tokens=session.get("output_tokens") or 0,
cache_read_tokens=session.get("cache_read_tokens") or 0,
cache_write_tokens=session.get("cache_write_tokens") or 0,
)
provider = session.get("billing_provider")
base_url = session.get("billing_base_url")
if (provider or "").strip().lower() == "openai-codex" or model.lower().startswith(
"openai-codex/"
):
# resolve_billing_route short-circuits this provider to the
# zero-priced subscription route; price it off the official docs
# snapshot like any other openai model instead.
provider = "openai"
result = estimate_usage_cost(model, usage, provider=provider, base_url=base_url)
if result.status == "estimated" and result.amount_usd is not None:
return float(result.amount_usd)
return 0.0




def _bar_chart(values: List[int], max_width: int = 20) -> List[str]:
Expand Down Expand Up @@ -422,9 +455,15 @@ def _compute_overview(
total_tool_calls = sum(s.get("tool_call_count") or 0 for s in sessions)
total_messages = sum(s.get("message_count") or 0 for s in sessions)

# Cost estimation (weighted by model)
# Cost estimation (weighted by model). Sessions are bucketed by
# cost_status semantics: subscription-included usage prices at zero
# (correct per-session) but is surfaced separately so the aggregate
# ledger doesn't silently collapse to $0 (#77223).
total_cost = 0.0
actual_cost = 0.0
included_cost = 0.0
included_cost_tokens = 0
estimated_cost_sessions = 0
models_with_pricing = set()
models_without_pricing = set()
unknown_cost_sessions = 0
Expand All @@ -437,8 +476,17 @@ def _compute_overview(
display = model.split("/")[-1] if "/" in model else (model or "unknown")
if status == "included":
included_cost_sessions += 1
included_cost += _estimate_included_market_cost(s)
included_cost_tokens += (
(s.get("input_tokens") or 0)
+ (s.get("output_tokens") or 0)
+ (s.get("cache_read_tokens") or 0)
+ (s.get("cache_write_tokens") or 0)
)
elif status == "unknown":
unknown_cost_sessions += 1
else:
estimated_cost_sessions += 1
if has_known_pricing(model, s.get("billing_provider"), s.get("billing_base_url")):
models_with_pricing.add(display)
else:
Expand Down Expand Up @@ -485,7 +533,14 @@ def _compute_overview(
"total_cache_write_tokens": total_cache_write,
"total_tokens": total_tokens,
"estimated_cost": total_cost,
"estimated_cost_sessions": estimated_cost_sessions,
"actual_cost": actual_cost,
"included_cost": included_cost,
"included_cost_tokens": included_cost_tokens,
# No pricing signal exists for the unknown bucket — the dollar
# figure is not computable; consumers should treat the count as
# the signal (formatters render "n/a", never "$0.00").
"unknown_cost": 0.0,
"total_hours": total_hours,
"avg_session_duration": avg_duration,
"avg_messages_per_session": total_messages / len(sessions) if sessions else 0,
Expand Down Expand Up @@ -937,6 +992,32 @@ def format_terminal(self, report: Dict) -> str:
lines.append(f" Avg msgs/session: {o['avg_messages_per_session']:.1f}")
lines.append("")

# Cost buckets (estimated / included / unknown). Subscription-included
# usage prices at zero per-session but is surfaced here so the ledger
# doesn't collapse to $0; the included figure is a hypothetical
# at-market comparison, clearly labeled as such (#77223).
lines.append(" 💰 Cost")
lines.append(" " + "─" * 56)
est_cost = f"${o['estimated_cost']:.2f}"
if o.get("estimated_cost_sessions"):
est_cost += f" ({o['estimated_cost_sessions']} sessions)"
lines.append(f" Estimated: {est_cost}")
if o.get("included_cost_sessions"):
if o.get("included_cost") > 0:
market = f"~${o['included_cost']:.2f} at market rates (hypothetical)"
else:
market = "market price unavailable"
lines.append(
f" Included: {o['included_cost_tokens']:,} tokens "
f"({o['included_cost_sessions']} sessions, subscription) — {market}"
)
if o.get("unknown_cost_sessions"):
lines.append(
f" Unknown: {o['unknown_cost_sessions']} sessions "
f"(no pricing signal)"
)
lines.append("")

# Model breakdown
if report["models"]:
lines.append(" 🤖 Models Used")
Expand Down Expand Up @@ -1047,6 +1128,25 @@ def format_gateway(self, report: Dict) -> str:
# Overview
lines.append(f"**Sessions:** {o['total_sessions']} | **Messages:** {o['total_messages']:,} | **Tool calls:** {o['total_tool_calls']:,}")
lines.append(f"**Tokens:** {o['total_tokens']:,} (in: {o['total_input_tokens']:,} / out: {o['total_output_tokens']:,})")

# Cost buckets (estimated / included / unknown). Subscription-included
# usage prices at zero per-session but is surfaced here so the ledger
# doesn't collapse to $0; the included figure is a hypothetical
# at-market comparison, clearly labeled as such (#77223).
cost_parts = [f"estimated ${o['estimated_cost']:.2f}"]
if o.get("included_cost_sessions"):
if o.get("included_cost") > 0:
market = f"~${o['included_cost']:.2f} at market (hypothetical)"
else:
market = "market price unavailable"
cost_parts.append(
f"{o['included_cost_sessions']} included / "
f"{o['included_cost_tokens']:,} tokens ({market})"
)
if o.get("unknown_cost_sessions"):
cost_parts.append(f"{o['unknown_cost_sessions']} unknown (no pricing signal)")
lines.append(f"**Cost:** {' | '.join(cost_parts)}")

if o["total_hours"] > 0:
lines.append(f"**Active time:** ~{format_duration_compact(o['total_hours'] * 3600)} | **Avg session:** ~{format_duration_compact(o['avg_session_duration'])}")
lines.append("")
Expand Down
110 changes: 104 additions & 6 deletions tests/agent/test_insights.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,101 @@ def test_activity_patterns(self, populated_db):
assert activity["busiest_hour"] is not None


# =========================================================================
# Cost buckets (estimated / included / unknown) — #77223
# =========================================================================

class TestCostBuckets:

def test_overview_buckets_included_sessions(self, db):
"""Subscription-included sessions land in the included bucket with
token counts, and never inflate estimated_cost."""
db.create_session(session_id="inc", source="cli", model="gpt-5.2-codex")
db.update_token_counts(
"inc", input_tokens=2_000_000, output_tokens=500_000,
billing_provider="openai-codex",
)
db.create_session(session_id="est", source="cli", model="gpt-4o")
db.update_token_counts(
"est", input_tokens=100_000, output_tokens=50_000,
billing_provider="openai",
)
db.create_session(session_id="unk", source="cli", model="my-custom-model")
db.update_token_counts("unk", input_tokens=10_000, output_tokens=5_000)
db._conn.commit()

overview = InsightsEngine(db).generate(days=30)["overview"]

assert overview["included_cost_sessions"] == 1
assert overview["included_cost_tokens"] == 2_500_000
assert overview["estimated_cost_sessions"] == 1
assert overview["unknown_cost_sessions"] == 1
# gpt-4o: $2.50/M in + $10.00/M out → 0.25 + 0.50
assert overview["estimated_cost"] == pytest.approx(0.75, abs=0.01)
# gpt-5.2-codex has no market entry → hypothetical figure is 0
# (the count/token buckets still surface the usage).
assert overview["included_cost"] == 0.0
assert overview["unknown_cost"] == 0.0

def test_included_bucket_prices_known_models_at_market(self, db):
"""When the underlying model has market pricing, the included bucket
carries a hypothetical at-market figure (#77223)."""
db.create_session(session_id="inc", source="cli", model="gpt-4o")
db.update_token_counts(
"inc", input_tokens=1_000_000, output_tokens=1_000_000,
billing_provider="openai-codex",
)
db._conn.commit()

overview = InsightsEngine(db).generate(days=30)["overview"]

assert overview["included_cost_sessions"] == 1
assert overview["estimated_cost"] == 0.0
# $2.50/M in + $10.00/M out
assert overview["included_cost"] == pytest.approx(12.50, abs=0.01)

def test_terminal_format_renders_cost_buckets(self, db):
db.create_session(session_id="inc", source="cli", model="gpt-4o")
db.update_token_counts(
"inc", input_tokens=1_000_000, output_tokens=1_000_000,
billing_provider="openai-codex",
)
db.create_session(session_id="est", source="cli", model="gpt-4o")
db.update_token_counts(
"est", input_tokens=100_000, output_tokens=50_000,
billing_provider="openai",
)
db._conn.commit()

engine = InsightsEngine(db)
text = engine.format_terminal(engine.generate(days=30))

assert "💰 Cost" in text
assert "Estimated:" in text
assert "Included:" in text
assert "subscription" in text
assert "at market rates (hypothetical)" in text
assert "2,000,000 tokens" in text

def test_gateway_format_renders_cost_buckets(self, db):
db.create_session(session_id="inc", source="cli", model="gpt-4o")
db.update_token_counts(
"inc", input_tokens=1_000_000, output_tokens=1_000_000,
billing_provider="openai-codex",
)
db.create_session(session_id="unk", source="cli", model="my-custom-model")
db.update_token_counts("unk", input_tokens=10_000, output_tokens=5_000)
db._conn.commit()

engine = InsightsEngine(db)
text = engine.format_gateway(engine.generate(days=30))

assert "**Cost:**" in text
assert "1 included" in text
assert "at market (hypothetical)" in text
assert "1 unknown (no pricing signal)" in text





Expand Down Expand Up @@ -390,8 +485,8 @@ def test_terminal_format_has_sections(self, populated_db):



def test_terminal_format_hides_cost_for_custom_models(self, db):
"""Cost display is hidden entirely — custom models no longer show 'N/A' either."""
def test_terminal_format_flags_unknown_cost_for_custom_models(self, db):
"""Custom/self-hosted models surface as an 'unknown' cost bucket with no fake dollar figure."""
db.create_session(session_id="s1", source="cli", model="my-custom-model")
db.update_token_counts("s1", input_tokens=1000, output_tokens=500)
db._conn.commit()
Expand All @@ -402,7 +497,9 @@ def test_terminal_format_hides_cost_for_custom_models(self, db):

assert "N/A" not in text
assert "custom/self-hosted" not in text
assert "Cost" not in text
# The unknown bucket is counted and flagged, never priced.
assert "Unknown:" in text
assert "no pricing signal" in text


class TestGatewayFormatting:
Expand All @@ -415,13 +512,14 @@ def test_gateway_format_is_shorter(self, populated_db):
assert len(gateway_text) < len(terminal_text)


def test_gateway_format_hides_cost(self, populated_db):
"""Gateway format omits dollar figures and internal cache details."""
def test_gateway_format_surfaces_cost_buckets(self, populated_db):
"""Gateway format surfaces cost buckets but omits internal cache details."""
engine = InsightsEngine(populated_db)
report = engine.generate(days=30)
text = engine.format_gateway(report)

assert "$" not in text
assert "**Cost:**" in text
assert "estimated $" in text
assert "cache" not in text.lower()


Expand Down
Loading