Skip to content

Cache tokens not tracked — significant cost undercount with prompt caching #4

@jmangansf-415

Description

@jmangansf-415

Bug

CostClaw significantly undercounts token usage and costs when OpenClaw has prompt caching enabled (cacheRetention: "short" or similar). In my setup with ~92% cache hit rates, the dashboard was showing a fraction of actual usage.

Root Cause

The llm_output hook in src/index.ts only captures event.usage.input and event.usage.output:

const inputTokens = event.usage.input ?? 0;
const outputTokens = event.usage.output ?? 0;

event.usage.input contains only the non-cached input tokens. With prompt caching, the bulk of input tokens come via cacheRead and cacheCreation fields, which are ignored entirely. In practice this meant input token counts of 3–6 per call instead of thousands.

Additionally, computeCost() in src/pricing/calculator.ts has no cache pricing logic. Anthropic charges different rates for cached tokens:

  • Cache reads: 0.1x input price (90% discount)
  • Cache creation: 1.25x input price (25% surcharge)

Impact

On a system with 92% cache hit rates:

  • CostClaw reported ~$0.85 total cost
  • Actual cost was significantly higher
  • Token counts were off by orders of magnitude on the input side

Fix

I've applied the following changes locally and confirmed they work:

1. src/index.ts — capture cache tokens from the event (with fallbacks for multiple field naming conventions):

const cacheReadTokens =
  event.usage.cacheRead ??
  event.usage.cache_read ??
  event.usage.cache_read_input_tokens ??
  event.usage.cacheReadInputTokens ?? 0;
const cacheCreationTokens =
  event.usage.cacheCreation ??
  event.usage.cache_creation ??
  event.usage.cache_creation_input_tokens ??
  event.usage.cacheCreationInputTokens ?? 0;

2. src/pricing/calculator.ts — add cache-aware cost calculation:

const cacheReadCost = (cacheReadTokens / 1_000_000) * price.inputPer1M * 0.1;
const cacheCreationCost = (cacheCreationTokens / 1_000_000) * price.inputPer1M * 1.25;

3. src/storage/db.ts — migration v2 adding columns:

ALTER TABLE llm_events ADD COLUMN cache_read_tokens INTEGER NOT NULL DEFAULT 0;
ALTER TABLE llm_events ADD COLUMN cache_creation_tokens INTEGER NOT NULL DEFAULT 0;

4. src/storage/queries.ts — update LlmRecord interface, upsertLlmRecord, and all aggregate queries to include cache tokens in totals.

Secondary issue: plugin ID mismatch

package.json declares name "costclaw-telemetry" but openclaw.plugin.json declares id "costclaw". This causes a warning on every OpenClaw startup:

plugins.entries.costclaw: plugin costclaw: plugin id mismatch (manifest uses "costclaw", entry hints "costclaw-telemetry")

Tertiary issue: missing tsconfig.json in npm package

tsconfig.json is in the repo but not in the files array in package.json, so it doesn't ship with the npm package. This prevents users from rebuilding from source after patching.


Happy to submit a PR with these fixes if helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions