skill usage

This commit is contained in:
lachtan
2026-09-15 12:18:52 +02:00
parent ea70c10ea9
commit 2ee633f5ee
2 changed files with 40 additions and 60 deletions

View File

@@ -1,9 +1,10 @@
--- ---
name: usage name: usage
description: > description: >
How much of the Ollama Cloud plan has been spent — current session and weekly How much of the Ollama Cloud plan has been spent — session and weekly usage,
usage per model, and reports over the continuously sampled history. per-model request counts, and delta reports over the continuously sampled
Triggers on: "ollama usage", "usage history". history. Triggers on: "ollama usage", "ollama quota", "ollama credits",
"ollama limits", "how much of the ollama plan is left", "ollama usage report".
--- ---
# Usage # Usage
@@ -16,80 +17,57 @@ Shows Ollama Cloud credit usage for the current API key.
uv run skills/usage/scripts/ollama_usage.py uv run skills/usage/scripts/ollama_usage.py
``` ```
The script reads `OLLAMA_API_KEY` from the `workspace/.env` file (created by The key comes from `OLLAMA_API_KEY` the environment first, then the
the user). If it is missing or the key fails (401/403), tell the user — `workspace/.env` file (created by the user). If it is missing or fails
never scrape the website. (401/403), tell the user — never scrape the website.
## Output ## Output
Format (script prints it, present it to the user as-is — same lines, same Present the script's lines as-is — same lines, same order; translate the labels
order; translate the labels into the user's language, keep the numbers into the user's language, keep the numbers exact.
exact; no extra model info on the Session/Weekly lines):
```text ```text
Ollama Cloud usage Ollama Cloud usage
Session: <pct> %, resets HH:MM TZ (in H h M min) Session: <pct> %, resets HH:MM TZ (in H h M min)
Weekly: <pct> %, resets in Y days Weekly: <pct> %, resets in <countdown>
Models (request count, weekly window): Models (request count, weekly window):
<model>: <count> <model>: <count>
``` ```
The per-model breakdown lives only in the "Models" section — never inline Per-model numbers belong only under "Models", never inline on the Session or
on the Session/Weekly lines. Weekly line.
Times are printed in the **server's local zone** (`Europe/Prague`), taken from The Session line has two other shapes, both correct output and not a failure:
the system — no zone is hardcoded. The session line loses its reset clause when the percentage alone (the history holds no rollover to anchor the window), and
the history holds no rollover to anchor the window; that is correct output, `window expired — the next one starts with the next request`.
not a failure.
Times are printed in the server's local zone, taken from the system — no zone
is hardcoded.
## Reset times ## Reset times
`/api/usage` carries **no reset timestamps**, neither in the body nor in the `/api/usage` carries **no reset timestamps**, neither in the body nor in the
response headers (re-checked 2026-09-15). Both are derived. response headers (re-checked 2026-09-15). Both countdowns are derived.
**Weekly:** next Monday 00:00 UTC, `until_next_monday()`. Matches the dashboard. - **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. So `session_window_end()` takes the newest
rollover in `samples` and adds 5 h, and nothing is extrapolated past it — once
the window runs out there is no next time until a request opens one.
**Session: a 5-hour window anchored by the first request after the previous one **The reset is never guessed.** With no rollover in the history the Session line
ran out** — not a fixed grid. The length comes from carries the percentage alone; a confident wrong number is worse than none.
[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 Do not calibrate our countdown against the dashboard's: it rounds an unknown way
0.077/21 requests through 05:00 UTC — a fixed grid would have zeroed it there (it showed "4 hours" and "3 hours" seven minutes apart), which is why the output
and the poller would have recorded it — and only reset when a request arrived prints the wall-clock time too.
at 06:00, after a 93-minute pause. Reconstructing the agent's activity gives a
consistent chain: window 00:0005:00, then 06:0011: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 ## Continuous sampling
A cron job runs `scripts/ollama_usage_poll.py` every minute and appends to 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` `db/ollama_usage.sqlite` (table `samples`) whenever anything changed. Table
records every poll, so a gap can be told apart from a failed poll). `meta` holds a single row — timestamp and status of the last poll — so a report
that has not moved can be told apart from a dead poller.
For a delta report over that data: For a delta report over that data:
@@ -101,9 +79,10 @@ 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 figure there — `limits.*.usage` has a resolution of 0.1 %, so short-interval
percentage deltas are noise. percentage deltas are noise.
The report ends with `Rollover gap:` lines and a `Session window:` line — The report closes with a `Session window:` line and `Last poll:`. From the second
when the window in progress started, when it ends, and how the observed recorded rollover on it also prints `Rollover gap:` lines, which test the window
rollovers line up against the 5 h length. model: a gap longer than 5 h confirms request-anchoring, one exactly equal to it
across a long idle stretch would point back to a fixed grid.
## Notes ## Notes
@@ -114,4 +93,4 @@ rollovers line up against the 5 h length.
- `activity.cost` returns $0.00000 on the Pro plan — broken, omitted from - `activity.cost` returns $0.00000 on the Pro plan — broken, omitted from
the output. the output.
- The `~/.ollama/id_ed25519` key does not work — only an API key minted at - The `~/.ollama/id_ed25519` key does not work — only an API key minted at
ollama.com/settings/keys. ollama.com/settings/keys.

View File

@@ -1,8 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Sample Ollama Cloud usage into db/ollama_usage.sqlite. Run from cron every minute. """Sample Ollama Cloud usage into db/ollama_usage.sqlite. Run from cron every minute.
Writes a `samples` row only when something changed; `meta` records every poll so a Writes a `samples` row only when something changed; `meta` holds a single row with
gap in `samples` can be told apart from a poll that failed or never ran. the last poll's timestamp and status, so a history that stopped moving can be told
apart from a poller that stopped running.
""" """
from __future__ import annotations from __future__ import annotations