- Rust 99.4%
- Dockerfile 0.6%
- Regex-Scraping, Burn-Rate-Analyse, Keyword-Interception, Cloud-Key-Failover - 26 Tests, darunter die Python-Fixtures aus test_parse_page.py und im Original erzeugte Text-Literale (summary/help byte-gleich) - Dockerfile + compose als Ersatz fuer den Python-Container |
||
|---|---|---|
| src | ||
| tests | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
ollama-usage-monitor-rs
Rust-Port von ollama-usage-monitor: transparenter Proxy vor Ollama,
der nach jedem Request die Cloud-Auslastung von ollama.com/settings nachlädt, analysiert und
über /usage* sowie als Chat-Keyword ausgibt.
Warum portiert
Der Python-Dienst (FastAPI + httpx + uvicorn) hält einen kompletten ASGI-Stack für einen Proxy
vor, der im Kern Bytes durchreicht. Dazu Regex-Scraping, das nur läuft, wenn jemand chattet.
Ein Rust-Binary mit axum erledigt das ohne Interpreter, ohne venv, ohne pip — und der
Durchreichpfad hat keine Kopie über einen Event-Loop mehr.
Bauen
cargo build --release # target/release/ollama-usage-monitor
cargo test # 26 Tests, kein Netz, kein Ollama
docker compose up -d --build
Konfiguration
Alles wie im Original, dieselben Namen und Defaults:
OLLAMA_COOKIE, OLLAMA_BASE_URL (http://localhost:11434), OLLAMA_CLOUD_URL
(https://ollama.com), OLLAMA_CLOUD_KEYS, OLLAMA_DEBOUNCE_SECONDS (auch DEBOUNCE_SECONDS,
30), HISTORY_PATH (/data/usage_history.json), WEBHOOK_URL, WEBHOOK_ALERTS,
PROXY_TOKEN, CORS_ORIGINS (*), PORT (9462), HOST (0.0.0.0). Dazu HISTORY_SIZE (100).
Endpunkte
GET /health— immer offen, auch mitPROXY_TOKENGET /usage,GET /usage/raw,GET /usage/history,GET /usage/summaryPOST /usage/refreshGET /info- alles andere: transparenter Proxy nach
OLLAMA_BASE_URL(alle Methoden)
Chat-Keywords (usage, summary, history, refresh, info, help) werden abgefangen,
wenn sie die letzte User-Nachricht sind, und in beiden Formaten beantwortet: Ollama (NDJSON
für /api/chat) und OpenAI (SSE für /v1/chat/completions). *:cloud-Modelle gehen bei
gesetzten OLLAMA_CLOUD_KEYS direkt an ollama.com und fallen bei 401/402/403/429 auf den
nächsten Key zurück (_preferred_key bleibt sticky).
Parität — was geprüft ist
cargo test (26 Tests):
tests/parse_page.rs(10) — 1:1 die Fälle austest_parse_page.py, inklusive der Eigenheiten:42,5%wird als5.0gelesen,42,5 %rutscht auf den Wochenwert12.0, Login-Seite liefert Nullen stattNone, Tool-Zeilen werden gefiltert, Modelle dedupliziert und nach Requests sortiert.tests/summary_parity.rs(4) — die Texte sind mitmain._summary_linesaus dem Python-Original erzeugt und hier als Literale hinterlegt;info_answer()ist byte-gleich zum Python-_table-Ergebnis (live verglichen, siehe unten).tests/proxy.rs(12) — Bearer-Auth (401 ohne/mit falschem Token,/healthoffen), Cloud-Key-Failover mit Stickiness, Fehlerdurchreichung bei erschöpften Keys, Keyword-Interception (nur letzte User-Nachricht), Streaming-Durchreichung inkl. GPU-Zähler auseval_duration, GET-Weiterleitung bei leerem Body,HISTORY_SIZE.
Live-Vergleich gegen das Original (beide mit TestClient bzw. echtem Port, leere History):
/info : JSON-Objekte gleich (commands, endpoints, chat_keywords)
"help" : Keyword-Antwort byte-identisch zu main._summary_lines/_table
Abweichungen
datetime-Format bleibt:reset_atundtssind weiter…+00:00mit Mikrosekunden.- Streaming-Status ist immer 200, wie im Original — der Upstream-Status wird hier bewusst nicht durchgereicht (Kommentar im Python-Code nennt das einen eigenen Change).
- Chunk-Grenzen beim Streaming werden nicht garantiert:
test_proxy_stream.pyprüfte, dass drei Upstream-Reads drei Chunks ergeben. Über axum+reqwest kann ein Read zusammengelegt werden; geprüft wird die Byte-Gleichheit des Gesamtbodys und der Content-Type. _load_history-Fehlerfälle: kaputte History-Datei wird wie im Original still verworfen.PROXY_TOKENwird pro Request aus der Umgebung gelesen (wie im Original), damit die Tests ihn umschalten können — nicht beim Start gecacht.- Kein
/v1/models, kein Streaming-usage-Objekt: war im Original auch nicht da.
Herkunft
Portiert am 2026-11-05 aus ollama-usage-monitor (Python, 881 + 208 + 193 Zeilen). Das Original
bleibt unverändert liegen; der Wechsel ist ein docker compose auf das neue Repo.