Back to catalog

hermes-cost-monitoring

Monitor & track LLM API costs from Hermes state.db — queries, wiki pages, cron auto-updates, and cost dashboards.

Category 🧠 MLOps
Version v1.0.0
hermescosttrackingmonitoringopenrouterstate-db

Hermes Cost Monitoring

Track LLM API costs using Hermes' built-in state.db telemetry. Hermes already records

per-session, per-model token usage and estimated costs in real time — this skill shows

how to surface that data into wiki pages, cron auto-updates, and cost dashboards.

When This Skill Activates

Use this skill when the user:

  • Asks about API costs, token usage, or spending on any provider
  • Wants to track, monitor, or log costs over time
  • Asks to create a cost dashboard or cost-tracking page
  • Wants to understand their LLM spending patterns
  • Asks about state.db internals related to usage/cost

The Data: session_model_usage Table

Hermes writes to state.db after every API call. The session_model_usage table has:

Column Description
session_id Links to sessions table
model Full model ID (e.g. deepseek/deepseek-v4-pro)
billing_provider openrouter, anthropic, etc.
api_call_count Number of API calls for this session+model pair
input_tokens Prompt tokens sent
output_tokens Completion tokens received
reasoning_tokens Thinking/reasoning tokens (o1, deepseek-r1, claude thinking)
cache_read_tokens Prompt caching hits (tokens served from cache — already paid for)
cache_write_tokens Tokens written to cache
estimated_cost_usd Cost estimate from provider's pricing API
actual_cost_usd Actual cost if returned by API (rare — OpenRouter returns $0)
cost_status estimated, actual, or null
first_seen / last_seen Unix timestamps for the session+model activity window
Key insight: Cache reads are NOT free — they're tokens that were already paid for

on first write. But they do reduce total cost vs. re-sending the full prompt each time.

A high cache-read ratio means prompt caching is working well.

Pattern 1: One-Time Cost Snapshot

Query state.db directly and write results to a wiki page:

import sqlite3, os
from datetime import datetime, timezone

HERMES_HOME = os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes"))
WIKI = os.environ.get("WIKI_PATH", os.path.expanduser("~/wiki"))
DB = os.path.join(HERMES_HOME, "state.db")

db = sqlite3.connect(DB)
db.row_factory = sqlite3.Row

All-time totals

totals = dict(db.execute(""" SELECT COUNT(DISTINCT session_id) as sessions, SUM(api_call_count) as calls, SUM(input_tokens) as input_tokens, SUM(output_tokens) as output_tokens, SUM(reasoning_tokens) as reasoning_tokens, SUM(estimated_cost_usd) as est_cost FROM session_model_usage """).fetchone())

Per-model breakdown

by_model = db.execute(""" SELECT model, COUNT(DISTINCT session_id) as sessions, SUM(api_call_count) as calls, SUM(input_tokens) as input_tokens, SUM(output_tokens) as output_tokens, SUM(reasoning_tokens) as reasoning_tokens, SUM(estimated_cost_usd) as est_cost FROM session_model_usage GROUP BY model ORDER BY est_cost DESC """).fetchall()

Daily breakdown

daily = db.execute(""" SELECT DATE(datetime(first_seen, 'unixepoch')) as date, COUNT(DISTINCT session_id) as sessions, SUM(api_call_count) as calls, SUM(estimated_cost_usd) as est_cost FROM session_model_usage GROUP BY date ORDER BY date DESC LIMIT 30 """).fetchall() db.close()

Then build a markdown page with tables for each section.

Pattern 2: Auto-Updating Cron Job

Use a no_agent cron with a Python script that regenerates the wiki page each tick.

Zero LLM tokens — the script IS the job.

Step 1: Write the script (see scripts/openrouter-cost-update.py for a production example)

The script should:

1. Query state.db for all the data sections

2. Format as markdown with YAML frontmatter

3. Write to $WIKI_PATH/wealth/expense/openrouter-costs.md

4. Print a single-line confirmation to stdout

Step 2: Create the cron job
cronjob(action='create',
    name='OpenRouter cost tracker',
    schedule='0 6   *',      # daily at 6am UTC
    no_agent=True,               # pure Python, zero tokens
    script='scripts/openrouter-cost-update.py',
    deliver='local')             # stdout only; wiki page is the real deliverable
Why no_agent: The script does pure data → markdown transformation. No reasoning

needed. Zero token cost. The cron scheduler runs the script, captures stdout, and

delivers it. Empty stdout = silent run.

Pattern 3: Per-Session Cost (for inline display)

Enable inline cost display in the CLI/TUI:

hermes config set display.show_cost true

This shows estimated cost after each turn. Restart required.

Effective Cost Math

OpenRouter pricing (per million tokens) varies by model. Common models as of mid-2026:

Model Input/M Output/M Notes
deepseek-v4-pro $0.40 $1.00 Includes reasoning
deepseek-v4-flash $0.05 $0.21 Fast fallback

Cache reads reduce effective cost — when the system prompt and tool definitions

are cached (which Hermes does aggressively), input costs drop to cache-read rates

(~10% of full price). A healthy session should show 80-90% cache hit rate.

Wiki Page Structure

The tracking page should include (see references/cost-page-template.md for full example):

  • YAML frontmatter with type: summary, tags: [hermes, ai, tracking, finance]
  • All-time summary table
  • By model breakdown table
  • Daily breakdown (last 14-30 days)
  • Recent sessions table
  • Notes section explaining data source, caveats

Pitfalls

  • state.db path: Use os.environ.get("HERMES_HOME", os.path.expanduser("~/.hermes")) — never hardcode ~/.hermes/state.db. The Hermes home can be overridden.
  • WIKI_PATH env var: If set, the wiki is there; otherwise defaults to ~/wiki. The cron script must respect this.
  • actual_cost_usd is usually $0: Most providers don't return actual cost in API responses. estimated_cost_usd is the reliable field.
  • Cron deliver: local with no_agent: The script's stdout becomes the delivery message. Keep it to one line — the wiki page is the real output. Empty stdout = silent delivery (nothing sent to user).
  • Don't hardcode ~/.hermes: Use HERMES_HOME env var — cron jobs may run in different environments.
  • Timeouts: The script runs in <1 second — no timeout concerns. But if the DB grows very large, add LIMIT clauses to queries.
  • Cache tokens ARE counted in input_tokens: They're a subset, not an addition. The cache_read_tokens column tracks how many of the input_tokens were served from cache.
  • JOIN column mismatch: session_model_usage.session_id references sessions.id (not sessions.session_id). Always use smu.session_id = s.id when joining.
  • Cost column index: When using sqlite3.Row, access by name (r['est_cost']), not by index — the column order in SELECT with aggregates is easy to miscount.
  • Duplicate file trap: If a cost page exists in multiple wiki locations (e.g. tracking/ and wealth/expense/), cron scripts writing to different paths create silent data drift. When consolidating, use md5sum + wc -l to pick the most complete version, copy it to the canonical location, then grep -rn "old/path" ~/./scripts/ to update any cron scripts referencing the old path. Always update agents.md after structural changes.

Pattern 4: Weekly Audit with Spike Detection

A focused weekly summary delivered before the user's retro. Separate from the

daily full-page regenerator — this one targets Slack delivery and includes

week-over-week comparison + threshold alerts.

Script: scripts/weekly-cost-audit.py

The script should:

1. Query state.db for this week vs last week (7-day windows)

2. Compute per-model breakdown, daily trend with visual bars

3. Flag expensive sessions (>$0.30)

4. Detect spikes: week total >$5 OR avg per-session >$0.50

5. Print a concise markdown summary to stdout (no wiki page — Slack is the output)

cronjob(action='create',
    name='Weekly cost audit',
    schedule='0 9   0',       # Sunday 9am, 30min before retro
    no_agent=True,
    script='scripts/weekly-cost-audit.py',
    deliver='origin')           # delivers to Slack where retro fires
Why separate from Pattern 2: The daily tracker writes a full wiki page

(dense, for reference). The weekly audit is a short Slack message with trend

analysis and spike alerts — designed for quick scanning at retro time.

Spike thresholds (adjust per user):
  • Week total >$5 → review model mix, delegation patterns
  • Avg per-session >$0.50 → check for long sessions or model upgrades

Supporting Files

  • references/cost-page-template.md — Full markdown template for the auto-generated cost tracking page, including frontmatter, table structures, and the number formatting helper.
  • scripts/weekly-cost-audit.py — Weekly cost audit script (no_agent cron). Queries past 7 days, compares week-over-week, detects spikes, delivers concise Slack summary. Used by the "Weekly cost audit" cron job.
  • $HERMES_HOME/scripts/openrouter-cost-update.py — Production cron script that queries state.db and regenerates wiki/wealth/expense/openrouter-costs.md. Designed for cronjob(no_agent=True). Respects WIKI_PATH and HERMES_HOME env vars.