See your AI subscription quotas and manage your local Ollama models from the KDE Plasma panel. One tab per AI provider with live usage, plus an Ollama tab to load/unload models from memory — no browser, no dashboard.
- AI neural-network panel icon in the plain theme color, so it sits in the icon row like every other tray icon — with a small amber/red badge once your 5-hour window crosses 70% / 90%; dimmed when the daemon is offline
- Tabs separating providers, one per AI plus an Ollama tab, each with a health dot
- Claude tab — live quota (the same numbers
/usageshows inside Claude Code):- The first window is a hero readout — the number you opened the widget for, not one row among many
- Bars carry 70 / 90 threshold marks, the same cut-offs that drive the color (teal → amber → red)
- "reinicia en Xh Ym" reset hint plus an "a este ritmo llegas al X%" projection per window
- Opus / Sonnet 7-day windows shown when the API reports them
- Plan (
PRO) and rate-limit tier as chips
- Follows your Plasma theme — the vintage palette ships in a light and a dark variant, picked from
Kirigami.Theme, and every size derives fromKirigami.Unitsso it scales on HiDPI - Status bar showing how stale the numbers are (
actualizado hace 6 s) and whether the daemon is answering - Actualizar forces a real re-query (
POST /refresh) instead of re-reading the daemon's cache, and spins while it is in flight - Ollama tab — local models:
- Lists installed models with params, quantization and size on disk
- CARGAR / LIBERAR button loads a model into RAM/VRAM (
keep_alive) or evicts it — with an EN RAM badge for resident models - ★ mark a preferred model; the choice is persisted and readable by other apps
- Instant feedback — actions respond immediately (optimistic UI); confirmed by the next poll
- Extensible provider architecture — Codex, Gemini, etc. can be added by dropping one module in
daemon/providers/ - Daemon runs as a systemd user service (starts at boot)
- Reads Claude's OAuth token from
~/.claude/.credentials.json— read-only, never written, so it can't race Claude Code over single-use refresh tokens - Rate-limit aware: backs off when Anthropic's usage endpoint throttles, and retries fast at boot when the network isn't up yet
| Provider | Source | Data |
|---|---|---|
| Claude | ~/.claude/.credentials.json + api.anthropic.com/api/oauth/usage |
Live 5h / 7d utilization, resets, plan |
| Ollama | http://127.0.0.1:11434 (/api/tags, /api/ps, /api/generate) |
Installed models, resident state, load/unload, preferred |
Codex and Gemini are not included yet: their per-plan quota has no clean local endpoint. The daemon is built so they can be added later without a rewrite — see Adding a provider.
- KDE Plasma 6
- Python 3.11+ (standard library only — no extra pip packages)
- Claude tab: Claude Code installed and signed in at least once (so
~/.claude/.credentials.jsonexists) - Ollama tab: Ollama running locally (
ollama serve)
git clone https://github.com/Gu7i/kde-ai-quota-widget.git
cd kde-ai-quota-widgetchmod +x install.sh
./install.shThe installer will:
- Install and enable the
ai-quota-daemonsystemd user service - Install the Plasma plasmoid
- Add the widget to your panel
No credentials to enter: Claude's token is read from ~/.claude, and Ollama is auto-detected at http://127.0.0.1:11434.
./uninstall.shYour Claude Code credentials (~/.claude) are never touched by uninstall.
# 1. Start the daemon
systemctl --user enable --now ai-quota-daemon.service
# 2. Install the plasmoid
cp -r plasmoid/ai-quota ~/.local/share/plasma/plasmoids/org.kde.ai-quota
# 3. Add to panel
# Right-click panel → Add widgets → search "AI Quota"┌─────────────────────────────────────┐
│ KDE Panel │
│ ┌──────────────────────────────┐ │
│ │ Plasmoid (QML) │ │
│ │ polls GET /providers + │ │
│ │ /ollama/models every 8s │ │
│ │ POST /ollama/load|unload │ │
│ └──────────────┬───────────────┘ │
└─────────────────┼───────────────────┘
│ HTTP localhost:7183
┌─────────────────┼───────────────────┐
│ ai-quota-daemon│(Python) │
│ ┌──────────────┴───────────────┐ │
│ │ ai_quota_service.py │ │
│ │ refreshes quotas every 180s │ │
│ │ providers/ │ │
│ │ claude_provider.py ───────┼───┼──▶ api.anthropic.com/api/oauth/usage
│ │ ollama_provider.py ───────┼───┼──▶ localhost:11434 (Ollama)
│ └──────────────────────────────┘ │
└─────────────────────────────────────┘
The daemon exposes a local REST API at http://127.0.0.1:7183:
| Method | Path | Description |
|---|---|---|
| GET | /status |
Daemon health + list of active provider ids |
| GET | /providers |
Quota snapshot for every provider (windows, meta) |
| GET | /ollama/models |
Installed models + which are resident in memory + preferred |
| POST | /ollama/load |
Load a model into memory {"name": "qwen3:8b"} (async) |
| POST | /ollama/unload |
Evict a model from memory {"name": "qwen3:8b"} |
| POST | /ollama/preferred |
Mark a model as preferred {"name": "qwen3:8b"} |
| POST | /refresh |
Force an immediate quota refresh |
Example /providers response:
[
{
"id": "claude",
"label": "Claude",
"ok": true,
"windows": [
{"label": "5 horas", "utilization": 67.0, "resets_at": "2026-07-14T06:29:59+00:00", "window_seconds": 18000},
{"label": "7 días", "utilization": 26.0, "resets_at": "2026-07-16T04:59:59+00:00", "window_seconds": 604800}
],
"meta": {"plan": "pro", "tier": "default_claude_ai", "extra_usage": false}
}
]The daemon reads the Claude Code OAuth token from ~/.claude/.credentials.json and calls Anthropic's usage endpoint — the same data the /usage command shows inside Claude Code:
GET https://api.anthropic.com/api/oauth/usagewithAuthorization: Bearer <token>,anthropic-beta: oauth-2025-04-20,anthropic-version: 2023-06-01- Returns
five_hour/seven_day(andseven_day_opus/seven_day_sonnetwhen applicable) withutilization(%) andresets_at
Token handling is read-only. The file is read fresh on every poll, and never written. Claude Code owns the token and keeps it refreshed whenever it runs; if the access token is expired the widget shows token caducado (abre Claude Code) and recovers by itself on the next poll after Claude Code rotates it.
This is deliberate. An earlier version refreshed the token itself and wrote the rotated pair back. OAuth refresh tokens are single-use, so the daemon and Claude Code raced over the same file and whichever redeemed second was left holding a dead token — a 403 loop that only ended when the other process rewrote the credentials.
Rate limiting: the usage endpoint allows roughly 30 requests/hour, so the poll loop runs at 180s. On a 429 or 5xx the provider backs off (120s, doubling to a 900s cap, reset on the first success) and keeps serving the last good numbers rather than flapping to offline. The Actualizar button skips the backoff — a human pressing a button is not what causes throttling.
- Installed models come from
/api/tags; resident models from/api/ps - Load =
POST /api/generate {"model": ..., "prompt": "", "keep_alive": -1}— runs in the background on the daemon and returns instantly, since a cold load of a multi-GB model can take a while. The EN RAM badge appears on the next poll (≤8 s) - Unload = the same with
"keep_alive": 0 - Preferred is stored in
~/.config/ai-quota-widget/config.json:
{
"ollama_url": "http://127.0.0.1:11434",
"ollama_preferred": "qwen3:8b"
}Point ollama_url elsewhere in this file to track a remote Ollama host.
The daemon discovers quota providers in daemon/ai_quota_service.py → build_providers(). To add one (e.g. Codex or Gemini):
-
Create
daemon/providers/<name>_provider.pywith a class exposingid,label, and asnapshot()method returning:{ "id": "codex", "label": "Codex", "ok": True, "error": None, "windows": [{"label": "5 horas", "utilization": 40.0, "resets_at": "...", "window_seconds": 18000}], "meta": {"plan": "plus"}, }window_seconds(the length of the window) is optional: the API only says when a window resets, so the widget needs the span to work out how much of it is already spent. Omit it and the "at this pace" projection is simply hidden for that window; everything else still renders. -
Register it in
build_providers():_quota_providers = [ClaudeProvider(), CodexProvider()]
The plasmoid builds one tab per provider automatically — no QML changes needed.
# Daemon
systemctl --user status ai-quota-daemon
systemctl --user restart ai-quota-daemon
journalctl --user -u ai-quota-daemon -f
# Test API
curl http://127.0.0.1:7183/providers | python3 -m json.tool
curl http://127.0.0.1:7183/ollama/models | python3 -m json.tool
curl -X POST http://127.0.0.1:7183/ollama/load \
-H "Content-Type: application/json" -d '{"name":"qwen3:8b"}'daemon/
ai_quota_service.py HTTP daemon on :7183 + 180s quota refresh loop
providers/
__init__.py Provider contract docs
claude_provider.py Reads ~/.claude token (read-only), live usage, backoff
ollama_provider.py /api/tags, /api/ps, load/unload, preferred
plasmoid/ai-quota/
contents/ui/
main.qml Plasmoid root: palette, polling, tabs, panel icon
QuotaBar.qml One utilization window — hero or compact row
Gauge.qml Utilization track with the 70 / 90 threshold marks
ModelRow.qml One Ollama model row (load/unload/prefer)
metadata.json Plasma plugin descriptor
install.sh One-shot installer
uninstall.sh Clean removal
A sibling of kde-ewelink-widget: the same industrial monospace chrome and LLM CORP™ mark, in an American-vintage palette — charcoal #201F1D / sand #C1AB85 / teal / rust on dark, parchment #F6F2E8 / #6B5B41 on light. The variant is chosen from Kirigami.Theme.backgroundColor, so the popup no longer sits as a black slab in a light desktop.
Layout follows one rule: the number you opened the widget for is the largest thing in the window. The first quota window is a hero readout; the rest are compact rows under it. Bars shift teal → amber (≥70%) → rust (≥90%) and carry marks at those two thresholds, so a bar says how close you are without you having to remember where the line is — amber is its own hue rather than the body sand, which made "warning" and "label" identical. Chrome is demoted throughout: tabs are underlined rather than filled, and the footer trades decoration for the freshness of the data.
Every dimension derives from Kirigami.Units.gridUnit, which tracks the desktop's font metrics — there are no hardcoded pixel sizes left to break on HiDPI.
GPL-2.0-or-later — consistent with the KDE Plasma ecosystem.