|
| 1 | +#!/bin/bash |
| 2 | +# Per-vertical OpenRouter spend -> VictoriaMetrics (homepage cards + weekly |
| 3 | +# reconciliation ritual). One OpenRouter key per vertical (verified separate |
| 4 | +# 2026-08-05): pi lab / opencode / fleet gateway / podcast workspace. |
| 5 | +# |
| 6 | +# Keys live in openrouter-verticals.env next to this script (gitignored; the |
| 7 | +# operator home dir is chmod 700 so the claude workbench user can't read it): |
| 8 | +# OR_KEY_PI=sk-or-... |
| 9 | +# OR_KEY_OPENCODE=sk-or-... |
| 10 | +# OR_KEY_GATEWAY=sk-or-... (same value as infra/litellm OPENROUTER_API_KEY) |
| 11 | +# OR_KEY_PODCAST=sk-or-... (same value as OPENROUTER_API_KEY_PODCAST) |
| 12 | +# |
| 13 | +# /auth/key returns usage for THE KEY MAKING THE CALL — that's why every key |
| 14 | +# must be present here; there is no list-all endpoint on a normal key. |
| 15 | +# Metrics (gauges, absolute USD as OpenRouter reports them): |
| 16 | +# openrouter_vertical_usd{vertical, window="day|week|month|total"} |
| 17 | +VM=http://localhost:8428/api/v1/import/prometheus |
| 18 | +HERE="$(cd "$(dirname "$0")" && pwd)" |
| 19 | +ENVF="$HERE/openrouter-verticals.env" |
| 20 | + |
| 21 | +poll() { |
| 22 | + [ -f "$ENVF" ] || { echo "no $ENVF — nothing to do"; return; } |
| 23 | + # shellcheck disable=SC1090 |
| 24 | + . "$ENVF" |
| 25 | + local LINES="" |
| 26 | + for V in pi:$OR_KEY_PI opencode:$OR_KEY_OPENCODE gateway:$OR_KEY_GATEWAY podcast:$OR_KEY_PODCAST; do |
| 27 | + local NAME="${V%%:*}" KEY="${V#*:}" |
| 28 | + [ -n "$KEY" ] || continue |
| 29 | + local J |
| 30 | + J=$(curl -s -m 15 https://openrouter.ai/api/v1/auth/key -H "Authorization: Bearer $KEY") |
| 31 | + local T D W M |
| 32 | + T=$(echo "$J" | /usr/bin/python3 -c "import json,sys; d=json.load(sys.stdin).get('data',{}); print(d.get('usage') or 0)" 2>/dev/null) |
| 33 | + D=$(echo "$J" | /usr/bin/python3 -c "import json,sys; d=json.load(sys.stdin).get('data',{}); print(d.get('usage_daily') or 0)" 2>/dev/null) |
| 34 | + W=$(echo "$J" | /usr/bin/python3 -c "import json,sys; d=json.load(sys.stdin).get('data',{}); print(d.get('usage_weekly') or 0)" 2>/dev/null) |
| 35 | + M=$(echo "$J" | /usr/bin/python3 -c "import json,sys; d=json.load(sys.stdin).get('data',{}); print(d.get('usage_monthly') or 0)" 2>/dev/null) |
| 36 | + [ -n "$T" ] || continue # auth/network failure: skip silently, keep last sample |
| 37 | + LINES+="openrouter_vertical_usd{vertical=\"$NAME\",window=\"total\",service=\"openrouter-spend\",environment=\"operations\"} $T |
| 38 | +openrouter_vertical_usd{vertical=\"$NAME\",window=\"day\",service=\"openrouter-spend\",environment=\"operations\"} $D |
| 39 | +openrouter_vertical_usd{vertical=\"$NAME\",window=\"week\",service=\"openrouter-spend\",environment=\"operations\"} $W |
| 40 | +openrouter_vertical_usd{vertical=\"$NAME\",window=\"month\",service=\"openrouter-spend\",environment=\"operations\"} $M |
| 41 | +" |
| 42 | + done |
| 43 | + [ -n "$LINES" ] && printf '%s' "$LINES" | curl -s -o /dev/null --data-binary @- "$VM" |
| 44 | +} |
| 45 | + |
| 46 | +while true; do |
| 47 | + poll |
| 48 | + sleep 600 |
| 49 | +done |
0 commit comments