skill usage
This commit is contained in:
@@ -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: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
|
## 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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user