shunt / APCAM: what your local LLM habit actually costs
A shunt is the resistor you drop into a circuit specifically to measure current. Same idea here: APCAM — AI Power Calculation And Monitoring — works out what your local LLM habit actually costs in electricity, from measured GPU wattage rather than a spec-sheet guess, and renders it as a single self-contained HTML page.
Two independent sources, joined. calibrate.ps1 samples nvidia-smi while
running one real inference and records the idle-to-sustained envelope for
your specific card — on the machine this was built for, that came out to
17.9 W idle → 173.5 W generating, pegged against a 175 W limit, which a
spec sheet was never going to tell you. collect.ps1 reads Ollama’s own
server logs for completed requests — timestamp, duration, endpoint, client,
status — plus the generation-rate samples the runner prints, and attributes
each one to a model using the load-event timeline. Energy is
duration x (measured GPU watts + a system-watts estimate); cost is that
against a rate you set with a slider.
The finding that motivated building it
On the reference machine, sitting idle cost 3.3x more than every inference combined, over the same window. The idle floor was the bill, not the workload. Whether that holds for you depends on your hardware and how much you actually generate — which is the whole reason to measure instead of assume.
Quickstart
git clone https://github.com/gmhoward9289-ops/shunt-ai-power.git shunt
cd shunt\apcam
.\calibrate.ps1 # once per machine: measures your GPU's power envelope
.\collect.ps1 # parse Ollama's logs -> dataset.json (+ history.json)
.\build.ps1 -Open # inject data into the page and open it
Keep it current without thinking about it — install-task.ps1 sets up a
scheduled task (default twice daily) that runs as you with an interactive
token: no stored password, no elevation, and it only fires while you’re
logged on.
Honest about what’s measured and what’s guessed
That’s the actual point of the tool, not a footnote:
| Measured | GPU watts, VRAM, temperature. Request timing, status, endpoint, client class. Generation rates. Model inventory and on-disk size. |
| Estimated | Non-GPU system draw (a slider, default 70 W). Short requests spend part of their time loading weights at lower draw, so their energy is mildly overstated. |
| Not measured | Wall power — unless you calibrate against a Tasmota or Shelly smart plug, nothing sees past the GPU to the outlet. |
Two limits are worth knowing before you trust the model-attribution numbers: generation rates come from the runner’s internal task IDs, which are non-contiguous and don’t map one-to-one onto HTTP requests, so rates are attributed only to whichever model the load timeline says was loaded at that moment — zipping them in request order produced confidently wrong numbers in an earlier version of this. And tags that share a weights blob (a model and its long-context variant) are indistinguishable in the logs, so they’re reported together rather than guessed apart.
Beyond Ollama
collect.ps1 -Source adds adapters for llamacpp (a captured stderr log) and
vllm (scraped from its own /metrics endpoint, giving exact cumulative
counters instead of a request timeline). Both are fixture-verified against
real log and metrics formats but haven’t been run against a live server here
yet — the Ollama path is the only one that has. LM Studio isn’t implemented:
its server log has no documented stable format, and a missing adapter beats a
guessed one.
Privacy, and platform support
Nothing generated leaves the machine — dataset.json, history.json,
machine.json, and the built dashboard are all gitignored, client addresses
get reduced to localhost / lan-xxxx / external-xxxx at capture time, and
the dashboard itself is a static file with no network calls. It also handles
more than one machine: point build.ps1 at several dataset.json files and
it renders a machine picker with per-machine sliders plus a combined totals
strip.
Built and tested on Windows + NVIDIA with Ollama. Linux + NVIDIA works under PowerShell 7; Linux + AMD and macOS Apple Silicon are supported with caveats documented in the README — read that section before you start if you’re not on the primary platform.
MIT licensed.
From my swamp to yours.