mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 22:07:03 +01:00
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
225 lines
7.8 KiB
Python
225 lines
7.8 KiB
Python
"""Render the audit as markdown plus a machine-readable snapshot.
|
|
|
|
Two outputs, two consumers. ``WEEKLY.md`` is for reading; ``weekly.json`` is
|
|
for the next run, which diffs against it to produce a week-over-week delta —
|
|
the part that makes running this repeatedly worth anything, since a single
|
|
week's absolute numbers cannot tell you whether a change helped.
|
|
|
|
Both land outside the repo, in ``~/.claude/token-report/``. The report names
|
|
paths and tools but never contains prompt or file *content*, so it stays safe
|
|
to keep on disk; it is still never committed, because the code repo is public
|
|
and the working-directory list alone maps out private projects.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from datetime import UTC, datetime
|
|
import json
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
from python_pkg.token_audit.attribute import LONG_SESSION_TURNS, Axes, Totals
|
|
from python_pkg.token_audit.model import TOKEN_KINDS
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Sequence
|
|
|
|
from python_pkg.token_audit.model import Session
|
|
|
|
REPORT_DIR = Path.home() / ".claude" / "token-report"
|
|
MARKDOWN_NAME = "WEEKLY.md"
|
|
JSON_NAME = "weekly.json"
|
|
TOP_N = 12
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Window:
|
|
"""The time span a report covers."""
|
|
|
|
since: float
|
|
until: float
|
|
|
|
def describe(self) -> str:
|
|
"""Human-readable window bounds."""
|
|
fmt = "%Y-%m-%d %H:%M"
|
|
start = datetime.fromtimestamp(self.since, tz=UTC).strftime(fmt)
|
|
end = datetime.fromtimestamp(self.until, tz=UTC).strftime(fmt)
|
|
return f"{start} - {end} UTC"
|
|
|
|
|
|
def snapshot(totals: Totals, axes: Axes, window: Window, sessions: int) -> dict:
|
|
"""Build the JSON structure persisted for next week's delta."""
|
|
return {
|
|
"generated": datetime.now(tz=UTC).isoformat(),
|
|
"window": {"since": window.since, "until": window.until},
|
|
"sessions": sessions,
|
|
"turns": totals.turns,
|
|
"weighted_total": round(totals.weighted),
|
|
"raw": dict(totals.by_kind),
|
|
"image_cost": round(axes.image_cost),
|
|
"image_reads": axes.images.count,
|
|
"cold_start_tokens": int(axes.cold_start.total),
|
|
"cold_starts": axes.cold_start.count,
|
|
"long_sessions": axes.long_sessions.count,
|
|
"long_session_cost": round(axes.long_sessions.total),
|
|
"sidechain_cost": round(axes.sidechain.total),
|
|
"projects": dict(axes.project.most_common(TOP_N)),
|
|
"mcp_calls": dict(axes.mcp_calls),
|
|
"skills": dict(axes.skills),
|
|
}
|
|
|
|
|
|
def _pct(part: float, whole: float) -> str:
|
|
"""Format ``part`` as a percentage of ``whole``, guarding divide-by-zero."""
|
|
return f"{part / whole * 100:.1f}%" if whole else "n/a"
|
|
|
|
|
|
def _delta_line(current: dict, previous: dict | None) -> str:
|
|
"""Describe the change in weighted spend since the previous snapshot."""
|
|
if previous is None:
|
|
return "_No previous report on disk - next run will show a delta._"
|
|
before = previous.get("weighted_total", 0)
|
|
now = current["weighted_total"]
|
|
if not before:
|
|
return "_Previous report had no spend recorded._"
|
|
change = (now - before) / before * 100
|
|
arrow = "up" if change >= 0 else "down"
|
|
return f"**Week over week: {arrow} {abs(change):.1f}%** ({before:,} -> {now:,})"
|
|
|
|
|
|
def _table(rows: Sequence[tuple[str, str]], headers: tuple[str, str]) -> list[str]:
|
|
"""Render a two-column markdown table."""
|
|
out = [f"| {headers[0]} | {headers[1]} |", "|---|---|"]
|
|
out += [f"| {left} | {right} |" for left, right in rows]
|
|
return out
|
|
|
|
|
|
def render(
|
|
totals: Totals,
|
|
axes: Axes,
|
|
window: Window,
|
|
sessions: Sequence[Session],
|
|
current: dict,
|
|
previous: dict | None,
|
|
) -> str:
|
|
"""Render the full markdown report."""
|
|
total = totals.weighted
|
|
lines = [
|
|
"# Claude Code token audit",
|
|
"",
|
|
f"Window: {window.describe()}",
|
|
f"Sessions: {len(sessions)} | assistant turns: {totals.turns:,}",
|
|
"",
|
|
_delta_line(current, previous),
|
|
"",
|
|
"## Totals",
|
|
"",
|
|
f"**Cost-weighted total: {total:,.0f}**",
|
|
"",
|
|
]
|
|
lines += _table(
|
|
[(kind, f"{totals.by_kind.get(kind, 0):,}") for kind in TOKEN_KINDS],
|
|
("raw token class", "count"),
|
|
)
|
|
lines += ["", "## Ranked drivers", ""]
|
|
lines += _table(_driver_rows(axes, total), ("driver", "share of weighted cost"))
|
|
lines += ["", "## Projects", ""]
|
|
lines += _table(
|
|
[
|
|
(path, f"{_pct(cost, total)} ({cost:,})")
|
|
for path, cost in axes.project.most_common(TOP_N)
|
|
],
|
|
("working directory", "weighted cost"),
|
|
)
|
|
lines += ["", "## Tool result payloads (estimated tokens returned)", ""]
|
|
lines += _table(
|
|
[
|
|
(name, f"{tok:,} over {axes.tool_calls[name]:,} calls")
|
|
for name, tok in axes.tool_tokens.most_common(TOP_N)
|
|
],
|
|
("tool", "result tokens"),
|
|
)
|
|
lines += ["", "## MCP servers invoked", ""]
|
|
lines += _table(
|
|
[(name, f"{n:,}") for name, n in axes.mcp_calls.most_common()]
|
|
or [("(none invoked)", "0")],
|
|
("server", "calls"),
|
|
)
|
|
lines += ["", "## Skills invoked", ""]
|
|
lines += _table(
|
|
[(name, f"{n:,}") for name, n in axes.skills.most_common()]
|
|
or [("(none invoked)", "0")],
|
|
("skill", "invocations"),
|
|
)
|
|
lines += ["", "## Most expensive sessions", ""]
|
|
lines += _table(_session_rows(sessions, total), ("session", "weighted cost"))
|
|
lines += ["", _footer(axes)]
|
|
return "\n".join(lines) + "\n"
|
|
|
|
|
|
def _driver_rows(axes: Axes, total: float) -> list[tuple[str, str]]:
|
|
"""Rows for the headline driver table."""
|
|
long_cost = axes.long_sessions.total
|
|
return [
|
|
(
|
|
f"Images re-sent in context ({axes.images.count:,} reads)",
|
|
f"{_pct(axes.image_cost, total)} ({axes.image_cost:,.0f})",
|
|
),
|
|
(
|
|
f"Sessions over {LONG_SESSION_TURNS} turns ({axes.long_sessions.count})",
|
|
f"{_pct(long_cost, total)} ({long_cost:,.0f})",
|
|
),
|
|
(
|
|
f"Cold-start prefix ({axes.cold_start.count} starts)",
|
|
f"{axes.cold_start.total:,.0f} raw tokens cached",
|
|
),
|
|
(
|
|
f"Subagent sidechains ({axes.sidechain.count} turns)",
|
|
f"{_pct(axes.sidechain.total, total)} ({axes.sidechain.total:,.0f})",
|
|
),
|
|
]
|
|
|
|
|
|
def _session_rows(sessions: Sequence[Session], total: float) -> list[tuple[str, str]]:
|
|
"""Rows for the per-session table, most expensive first."""
|
|
ranked = sorted(sessions, key=lambda s: s.cost, reverse=True)[:TOP_N]
|
|
return [
|
|
(
|
|
f"`{s.session_id[:8]}` ({s.turn_count:,} turns, max ctx {s.max_context:,})",
|
|
f"{_pct(s.cost, total)} ({s.cost:,.0f})",
|
|
)
|
|
for s in ranked
|
|
]
|
|
|
|
|
|
def _footer(axes: Axes) -> str:
|
|
"""Closing note on how the image figure should be read."""
|
|
return (
|
|
f"_Image cost is an estimate: {axes.images.total:,.0f} tokens were read once, "
|
|
"then re-sent as cache-read on every later turn until compaction. "
|
|
"It is excluded from the reconciled totals above, which come only from "
|
|
"the API's reported usage._"
|
|
)
|
|
|
|
|
|
def write(markdown: str, data: dict, directory: Path = REPORT_DIR) -> Path:
|
|
"""Persist both outputs, returning the markdown path."""
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
md_path = directory / MARKDOWN_NAME
|
|
md_path.write_text(markdown, encoding="utf-8")
|
|
(directory / JSON_NAME).write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
return md_path
|
|
|
|
|
|
def load_previous(directory: Path = REPORT_DIR) -> dict | None:
|
|
"""Load the last snapshot, or ``None`` if this is the first run."""
|
|
path = directory / JSON_NAME
|
|
if not path.exists():
|
|
return None
|
|
try:
|
|
loaded = json.loads(path.read_text(encoding="utf-8"))
|
|
except ValueError:
|
|
return None
|
|
return loaded if isinstance(loaded, dict) else None
|