117 lines
4.7 KiB
Markdown
117 lines
4.7 KiB
Markdown
---
|
||
name: usage
|
||
description: >
|
||
How much of the Ollama Cloud plan has been spent — current session and weekly
|
||
usage per model, and reports over the continuously sampled history.
|
||
Triggers on: "ollama usage", "usage history".
|
||
---
|
||
|
||
# Usage
|
||
|
||
Shows Ollama Cloud credit usage for the current API key.
|
||
|
||
## Run
|
||
|
||
```bash
|
||
uv run skills/usage/scripts/ollama_usage.py
|
||
```
|
||
|
||
The script reads `OLLAMA_API_KEY` from the `workspace/.env` file (created by
|
||
the user). If it is missing or the key fails (401/403), tell the user —
|
||
never scrape the website.
|
||
|
||
## Output
|
||
|
||
Format (script prints it, present it to the user as-is — same lines, same
|
||
order; translate the labels into the user's language, keep the numbers
|
||
exact; no extra model info on the Session/Weekly lines):
|
||
|
||
```text
|
||
Ollama Cloud usage
|
||
Session: <pct> %, resets HH:MM TZ (in H h M min)
|
||
Weekly: <pct> %, resets in Y days
|
||
Models (request count, weekly window):
|
||
<model>: <count>
|
||
```
|
||
|
||
The per-model breakdown lives only in the "Models" section — never inline
|
||
on the Session/Weekly lines.
|
||
|
||
Times are printed in the **server's local zone** (`Europe/Prague`), taken from
|
||
the system — no zone is hardcoded. The session line loses its reset clause when
|
||
the history holds no rollover to anchor the window; that is correct output,
|
||
not a failure.
|
||
|
||
## Reset times
|
||
|
||
`/api/usage` carries **no reset timestamps**, neither in the body nor in the
|
||
response headers (re-checked 2026-09-15). Both are derived.
|
||
|
||
**Weekly:** next Monday 00:00 UTC, `until_next_monday()`. Matches the dashboard.
|
||
|
||
**Session: a 5-hour window anchored by the first request after the previous one
|
||
ran out** — not a fixed grid. The length comes from
|
||
[ollama.com/blog/transparent-pricing](https://ollama.com/blog/transparent-pricing):
|
||
the new plans dropped the "5-hour or weekly limits" this key still has.
|
||
|
||
How the anchoring was established on 2026-09-15: usage sat unchanged at
|
||
0.077/21 requests through 05:00 UTC — a fixed grid would have zeroed it there
|
||
and the poller would have recorded it — and only reset when a request arrived
|
||
at 06:00, after a 93-minute pause. Reconstructing the agent's activity gives a
|
||
consistent chain: window 00:00–05:00, then 06:00–11:00, each opened by the
|
||
first request after the previous expiry. A fixed grid would additionally
|
||
require that request to land exactly on a boundary by chance.
|
||
|
||
So `session_window_end()` takes the **newest rollover in `samples`** and adds
|
||
5 h. A rollover sample marks the start of a new window, not a boundary that was
|
||
due anyway, which is why nothing is ever extrapolated past it: once the window
|
||
runs out, the output says the next one starts with the next request rather than
|
||
naming a time.
|
||
|
||
That it is a window and not a rolling counter was measured too — usage dropped
|
||
from 0.077/21 to 0.0/`{}` at once; a rolling counter decays gradually.
|
||
|
||
**The reset is never guessed.** An earlier version assumed a calendar hour and
|
||
printed "resets in 31 minutes" while the dashboard said "Resets in 2 hours" —
|
||
a confident wrong number is worse than none. With no rollover in the history,
|
||
the Session line carries the percentage alone.
|
||
|
||
Do not compare our countdown against the dashboard's to the hour: the dashboard
|
||
rounds an unknown way (it showed "4 hours" and "3 hours" seven minutes apart),
|
||
which is why the output prints the wall-clock time too.
|
||
|
||
**If the model is wrong, the report shows it.** `Rollover gap:` lines compare
|
||
consecutive rollovers against the 5 h window — gaps longer than the block
|
||
confirm request-anchoring, a gap exactly equal to it across a long idle stretch
|
||
would point back to a fixed grid.
|
||
|
||
## Continuous sampling
|
||
|
||
A cron job runs `scripts/ollama_usage_poll.py` every minute and appends to
|
||
`db/ollama_usage.sqlite` whenever anything changed (table `samples`; table `meta`
|
||
records every poll, so a gap can be told apart from a failed poll).
|
||
|
||
For a delta report over that data:
|
||
|
||
```bash
|
||
uv run skills/usage/scripts/ollama_usage_report.py [--since ISO] [--until ISO]
|
||
```
|
||
|
||
Default window is the last 24 hours. Per-model **request counts** are the exact
|
||
figure there — `limits.*.usage` has a resolution of 0.1 %, so short-interval
|
||
percentage deltas are noise.
|
||
|
||
The report ends with `Rollover gap:` lines and a `Session window:` line —
|
||
when the window in progress started, when it ends, and how the observed
|
||
rollovers line up against the 5 h length.
|
||
|
||
## Notes
|
||
|
||
- Endpoint: `GET https://ollama.com/api/usage`, header
|
||
`Authorization: Bearer <key>` (verified 2026-09; issue #15132 is stale).
|
||
- `limits.*.usage` is a fraction of the plan limit (× 100 = % as on the
|
||
dashboard).
|
||
- `activity.cost` returns $0.00000 on the Pro plan — broken, omitted from
|
||
the output.
|
||
- The `~/.ollama/id_ed25519` key does not work — only an API key minted at
|
||
ollama.com/settings/keys. |