Skip to main content
Glama

inferwatch

ci python license

Echtzeit- und Verlaufsmetriken für lokal betriebene LLMs — Ollama und vLLM — mit einem Browser-Dashboard, einem Einstellungsbildschirm und einem MCP-Server, damit ein Agent dieselben Daten abfragen kann.

Ein Python-Prozess, eine SQLite-Datei. Kein Docker, kein Node, kein Prometheus, keine externen Dienste. Es sitzt nie im Request-Pfad und kann die Inferenz daher weder verlangsamen noch unterbrechen.

┌── Ollama ──────────────┐        ┌── vLLM ────────────────┐
│ journald / file /      │        │ GET /metrics           │
│ docker logs            │        │ (native Prometheus)    │
└──────────┬─────────────┘        └──────────┬─────────────┘
           │ per-request rows                │ pre-aggregated
           ▼                                 ▼
        ┌──────────────── SQLite (WAL) ────────────────┐
        │  requests · rollups · vllm_samples/hist      │
        └───────┬──────────────────────────┬───────────┘
                ▼                          ▼
         dashboard :7070            MCP server (stdio)

Die beiden Engines sind nicht symmetrisch, und das Tool tut nicht so, als wären sie es

Das ist die zentrale Design-Tatsache, daher lohnt es sich, sie klar auszusprechen.

Ollama

vLLM

Quelle

sein Log

/metrics

Zeilen pro Request

ja

nein — es existieren keine, die gesammelt werden könnten

TTFT / Latenz

exakt, pro Request

nur Histogramme

Tokens

pro Request

kumulative Zähler

Fehler

HTTP-Status pro Request

request_success_total{finished_reason}

Client-Adresse

ja

nein

Perzentile

exakt innerhalb der Aufbewahrungsfrist

Bucket-Obergrenzen; Mittelwerte sind exakt

Einzigartige Extras

Prompt-Cache-Wiederverwendung, Draft-Akzeptanz, Kaltstartzeit

KV-Cache-Belegung, Preemptions, Batch-Belegung, Wartezeit nach Grund

Beide Tabs zeigen GPU-Auslastung, VRAM, Temperatur und Stromverbrauch, da diese von nvidia-smi gemessen werden und nicht von einer der beiden Engines. Temperatur und Stromverbrauch erhalten separate Diagramme statt sich eine Achse zu teilen, und jedes wird so aggregiert, wie es seine Einheit verlangt: Die Auslastung wird über die Karten gemittelt, VRAM und Watt werden summiert, die Temperatur meldet die heißeste Karte. Die Temperatur ist die einzige Reihe, die nicht bei null beginnt — ein Bereich von 33–68 °C, der bei 0 startet, würde den Großteil des Diagramms verschwenden.

Daher gibt es separate Dashboard-Tabs, separate Tabellen und separate MCP-Tools. Es wird nicht versucht, Zeilen pro Request für vLLM durch Differenzbildung der Zähler zu rekonstruieren: Man kann nicht wiederherstellen, welcher TTFT zu welchem Request gehörte, und eine Fälschung würde erfundene Zeilen neben echte setzen.

Ollama: Woher die Zahlen kommen

Ollama bietet keinen /metrics-Endpunkt (verifiziert — die Route ist nicht in der Binärdatei). Mit OLLAMA_DEBUG=1 gibt das eingebettete llama.cpp pro Request einen Timing-Block aus, der mit der Zugriffszeile und der Scheduler-Zeile kombiniert wird:

slot print_timing: id 0 | task 6763 | prompt eval time = 1254.52 ms /  55 tokens
slot print_timing: id 0 | task 6763 |        eval time = 14591.31 ms / 416 tokens
[GIN] ... | 200 | 16.862061865s | 192.0.2.10 | POST "/v1/chat/completions"
time=... msg="context for request finished" runner.name=.../llama3.2:3b

Das ergibt TTFT, Prefill/Decode-Aufteilung, Token-Anzahl, Decode-Rate, Status, Client, Endpunkt und Modell — für jeden Request, von jedem Client, ohne den Request-Pfad zu berühren. Aus der Kombination ergeben sich zwei Zahlen, die keine der beiden Quellen allein hat:

  • Warteschlangenwartezeit = Wandlatenz − Runner-Zeit: Zeit, die mit Warten statt mit Generieren verbracht wurde. Ein Proxy kann diese nicht trennen.

  • Prompt-Cache-Wiederverwendung = volle Prompt-Länge − tatsächlich ausgewertete Tokens.

OLLAMA_DEBUG=1 ist erforderlich. Ohne sie druckt llama.cpp keine Timing-Zeilen: Request-Raten, Status und GPU-Metriken funktionieren weiterhin, aber TTFT und Token-Anzahl bleiben leer. Das Dashboard zeigt dies in einem Banner an, statt Nullen anzuzeigen.

# /etc/systemd/system/ollama.service.d/override.conf
[Service]
Environment="OLLAMA_DEBUG=1"

vLLM: Woher die Zahlen kommen

Der native Prometheus-Endpunkt von vLLM wird alle collection.scrape_interval_s (Standard 10s) abgerufen. Kumulative Zähler werden differenziert; Histogramm-Buckets werden pro Bucket differenziert; beide werden pro Minute aggregiert geschrieben, denn das Speichern jedes Scrapes würde Millionen von Zeilen pro Monat erzeugen, in Auflösungen, die kein Diagramm nutzt.

Drei Details, die man kennen sollte:

  • Die eigenen Bucket-Grenzen von vLLM werden mit den Zählwerten gespeichert. Seine Grenzen schreiten 1ms/20ms/250ms/2.5s/40s/640s fort; die von Ollama 25ms/200ms/1.5s/15s/60s. Keine ist eine Verfeinerung der anderen, daher würde das Umsortieren der einen in die andere eine Interpolation zwischen den Grenzen erfordern — das Erfinden von Zahlen. Perzentile werden gegen die eigenen Grenzen jeder Quelle berechnet und als Bucket-Obergrenzen gemeldet.

  • _sum und _count sind exakt, daher ist der Mittelwert exakt. Da die Buckets von vLLM im Sekundenbereich grob sind, führen Dashboard und MCP-Tools mit dem Mittelwert und kennzeichnen Perzentile als „höchstens".

  • Neustarts werden erkannt über process_start_time_seconds (und durch einen Zähler, der rückwärts läuft). Das Intervall, das einen Neustart umspannt, wird verworfen, statt als falsches Delta ausgegeben zu werden.

Die Inter-Token-Latenz wurde über vLLM-Versionen hinweg als time_per_output_token_seconds, inter_token_latency_seconds und request_time_per_output_token_seconds geschrieben. Alle werden gesammelt und diejenige mit Daten wird verwendet, sodass dies mit alten und neuen Servern ohne Konfiguration funktioniert.


Related MCP server: System Monitor MCP Server

Installation

Erfordert Python 3.10 oder neuer — nicht wegen dieses Codes (der 3.9-sauber ist), sondern weil fastapi, uvicorn, starlette und mcp alle dies erfordern.

pip install git+https://github.com/floatsmyboat/inferwatch     # or:
git clone https://github.com/floatsmyboat/inferwatch && cd inferwatch
python3 -m venv --upgrade-deps .venv && .venv/bin/pip install -e ".[dev]"

Die Installation gibt Ihnen zwei Befehle:

Befehl

Was es ist

inferwatch

der Collector, das Dashboard und die API (serve, ingest, stats, sources)

inferwatch-mcp

der MCP-Server, über stdio

Noch nicht auf PyPI; installieren Sie vorerst aus Git.

Backfill aus einem Journal, das Sie bereits haben, dann schauen Sie sich die Datenbank an:

inferwatch ingest --since 2d      # or: python -m inferwatch.main ingest
inferwatch stats

Führen Sie es aus:

inferwatch serve                  # http://127.0.0.1:7070

Als Dienst — die Unit wird aus systemd/inferwatch.service.in für den aktuellen Benutzer, den Checkout-Pfad und den Interpreter gerendert, sodass nichts fest verdrahtet ist:

./scripts/install-systemd.sh          # system service (uses sudo)
sudo systemctl enable --now inferwatch

./scripts/install-systemd.sh --user   # or per-user, no sudo
systemctl --user enable --now inferwatch

Überschreiben Sie mit HOST=0.0.0.0 PORT=7070 DATADIR=... ./scripts/install-systemd.sh.

Im Netzwerk verfügbar machen

Es gibt keine Authentifizierung. Das Dashboard ist schreibgeschützt (nur GET) und speichert keinen Prompt- oder Antworttext — nur Zählwerte, Zeitmessungen, Modellnamen und Client-Adressen. Beschränken Sie es in der Firewall:

sudo ufw allow from 192.168.1.0/24 to any port 7070 proto tcp comment "inferwatch"

Konfigurieren, was überwacht wird

Alles ist über den Tab Einstellungen im Dashboard oder über die CLI bearbeitbar:

python -m inferwatch.main sources                       # list
python -m inferwatch.main sources add --kind vllm --name qwen \
    --set url=http://127.0.0.1:8000
python -m inferwatch.main sources add --kind ollama --name box \
    --set reader=file --set path=~/.ollama/logs/server.log
python -m inferwatch.main sources disable qwen

Ollama-Log-Reader. Nicht jeder betreibt Ollama unter systemd:

Reader

für

Zeitstempel-Genauigkeit

journald

ollama.service

Mikrosekunde, von journald

file

ollama serve in einem Terminal oder jede Installation, die in eine Datei protokolliert

abgeleitet; siehe unten

docker

Ollama in einem Container

pro Zeile, von docker logs -t

Der file-Reader folgt wie tail -F, übersteht Rotation (Inode-Änderung) und Kürzung und speichert einen Offset, sodass ein Neustart nicht erneut abspielt. Ollamas Go-Zeilen tragen time=, aber die slot-Zeilen von llama.cpp — diejenigen mit den Token-Anzahlen — tragen keinen Zeitstempel, daher wird der zuletzt gesehene weitergetragen. Die Reihenfolge, von der die Joins abhängen, gilt immer; die absolute Präzision ist geringer als bei journald, und eine [GIN]-Zeile mit Sekundenauflösung wird nach vorne geklemmt, sodass die Zeit nie rückwärts zu laufen scheint.

Einstellungs-Priorität

spec default  <  database (Settings tab)  <  environment  <  command line

Ein Schlüssel, der über die Umgebung oder ein Flag bereitgestellt wird, wird im Tab Einstellungen schreibgeschützt mit seiner Herkunft angezeigt, weil dem Prozess gesagt wurde, ihn zu verwenden, und ein Browser dies nicht stillschweigend überschreiben darf. Das Speichern ist Alles-oder-nichts, sodass ein Tippfehler in einem Feld keine halb angewendete Konfiguration hinterlassen kann. Änderungen an Quellen, Intervallen und Aufbewahrung gelten ohne Neustart; server.host und server.port sind als neustartpflichtig markiert, und die API sagt dies nach dem Speichern.

Konfigurationsreferenz

Jede unten stehende Einstellung ist im Tab Einstellungen bearbeitbar, als Umgebungsvariable setzbar, und einige sind mit einem Flag fixierbar. Die Tabellen werden aus dem Code generiert (scripts/gen-config-docs.py), sodass sie nicht von dem abweichen können, was das Programm tatsächlich akzeptiert.

Generiert von scripts/gen-config-docs.py — nicht von Hand bearbeiten.

Sammlung

Einstellung

Standard

Akzeptiert

Umgebungsvariable

Hinweise

collection.poll_interval_s

5.0

1–300

INFERWATCH_COLLECTION_POLL_INTERVAL_S

Wie oft nvidia-smi und der eigene Status-Endpunkt der Engine abgetastet werden.

collection.scrape_interval_s

10.0

1–300

INFERWATCH_COLLECTION_SCRAPE_INTERVAL_S

Wie oft der /metrics-Endpunkt jeder vLLM-Instanz gelesen wird. vLLM-Zähler sind kumulativ, daher setzt dies die Auflösung jeder daraus abgeleiteten Rate und jedes Histogramms.

collection.backfill

2d

7d, oder -2 days / @epoch

INFERWATCH_COLLECTION_BACKFILL

Neustart erforderlich — Wie weit zurück beim ersten Lauf gelesen wird, bevor ein Resume-Zustand existiert. Akzeptiert 7d / 6h oder eine journalctl-Form wie '-2 days'.

collection.rollup_interval_s

60.0

10–3600

INFERWATCH_COLLECTION_ROLLUP_INTERVAL_S

Wie oft die 1-Minuten- und 1-Stunden-Aggregate neu berechnet werden.

Aufbewahrung

Einstellung

Standard

Akzeptiert

Umgebungsvariable

Hinweise

retention.raw_days

7.0

0.5–3650

INFERWATCH_RETENTION_RAW_DAYS

Details pro Request, die älter als dies sind, werden gelöscht. Rollups werden unbegrenzt aufbewahrt, sodass Langzeitdiagramme überleben.

retention.sample_days

30.0

0.5–3650

INFERWATCH_RETENTION_SAMPLE_DAYS

GPU-Stichproben, Engine-Stichproben und das Ereignisprotokoll werden auf dies gekürzt.

Dashboard

Einstellung

Standard

Akzeptiert

Umgebungsvariable

Hinweise

dashboard.default_window

1h

15m, 1h, 6h, 24h, 7d, 30d

INFERWATCH_DASHBOARD_DEFAULT_WINDOW

Bereich, der beim Öffnen des Dashboards ausgewählt ist.

dashboard.include_health

false

INFERWATCH_DASHBOARD_INCLUDE_HEALTH

HEAD- und Status-Polling in die Request-Raten einbeziehen. Standardmäßig deaktiviert, da dies bei einer gepollten Instanz 90 %+ der Treffer ausmachen kann.

dashboard.refresh_s

10.0

2–600

INFERWATCH_DASHBOARD_REFRESH_S

Wie oft das geöffnete Dashboard neu lädt. Der Live-Request-Feed wird separat gepusht und ist davon nicht betroffen.

Server

Einstellung

Standard

Akzeptiert

Umgebungsvariable

Hinweise

server.host

127.0.0.1

INFERWATCH_SERVER_HOST

Neustart erforderlich — 0.0.0.0 macht das Dashboard im Netzwerk verfügbar. Es gibt keine Authentifizierung, daher im Firewall einschränken.

server.port

7070

1–65535

INFERWATCH_SERVER_PORT

Neustart erforderlich — Port, auf dem Dashboard und API lauschen.

Quellfelder

Diese mit --set key=value bei sources add oder im Tab „Einstellungen“ setzen.

Ollama (--kind ollama)

Feld

Standard

Erforderlich wenn

Hinweise

reader

journald

Woher ollamas Log gelesen wird. Pro-Request-Metriken stammen aus llama.cpps Debug-Zeilen, daher ist eine dieser Optionen erforderlich. Eine von journald, file, docker.

unit

ollama

reader=journald

path

reader=file

Wird wie tail -F verfolgt, Rotation und Kürzung werden also behandelt.

container

ollama

reader=docker

url

http://127.0.0.1:11434

Wird verwendet, um /api/ps für residente Modelle abzufragen.

models_dir

Optional. Löst Blob-Digests bei Ladeereignissen in Modellnamen auf. Standard: $OLLAMA_MODELS oder ~/.ollama/models.

vLLM (--kind vllm)

Feld

Standard

Erforderlich wenn

Hinweise

url

http://127.0.0.1:8000

Die OpenAI-kompatible Server-Root. /metrics wird von hier gelesen.

unit

Wenn gesetzt, wird das Journal zusätzlich auf HTTP-Statuscodes, Client-Adressen und Engine-Fehler gelesen, die /metrics nicht preisgibt.

api_key

Wird als Bearer-Token gesendet, falls der Server eines verlangt.

Befehlszeilen-Flags

Flag

Zweck

Legt die Einstellung fest

--db

--unit

systemd-Unit für die geseedete Ollama-Quelle / für den Ingest

--ollama-url

--models-dir

ollama-Modelle-Verzeichnis (löst Blob-Digests in Modellnamen auf)

--log-file

Ingest: diese Logdatei statt des Journals lesen

--since

Log-Backfill-Fenster, z. B. '-2 days'

collection.backfill

--retention-days

Aufbewahrung roher Requests; Rollups werden dauerhaft behalten

retention.raw_days

--poll-interval

collection.poll_interval_s

--scrape-interval

collection.scrape_interval_s

--host

server.host

--port

server.port

-v, --verbose

Flags, die eine Einstellung festlegen, haben Vorrang vor Umgebung und Einstellungen-Tab; der Tab zeigt diese Schlüssel schreibgeschützt mit ihrer Herkunft.

Umfang: eine Ollama-Quelle, viele vLLM-Quellen

vLLM-Zeilen sind durchgehend nach Quelle verschlüsselt, sodass beliebig viele vLLM-Instanzen nebeneinander überwacht werden können. Die Ollama-Tabellen (requests, events, ps_samples) sind nicht nach Quelle partitioniert, sodass genau eine Ollama-Quelle gleichzeitig läuft; das Aktivieren einer zweiten protokolliert eine Warnung und ignoriert sie, statt zwei Instanzen stillschweigend in einen Zahlensatz zu mischen. Diese Tabellen zu partitionieren ist eine Schemaänderung, die bewusst vorgenommen werden sollte.


Dashboard

http://127.0.0.1:7070 — drei Tabs: Ollama, vLLM, Einstellungen.

Welche GPUs zu welcher Engine gehören

Ein Host betreibt oft mehr als eine Engine, daher würde das Plotten jeder Karte im Bereich einer Instanz implizieren, dass sie alle nutzt. Die GPUs jeder vLLM-Instanz werden durch Verfolgen von Prozessen ermittelt — der Port, den sie bedient → die lauschende PID → ihre Nachkommen → geschnitten mit nvidia-smis Compute-Prozessen → die Karten, die diese halten. Die Karten der Instanz tragen die Serienfarbe und ihre VRAM-Kachel zählt nur diese; die anderen Karten des Hosts bleiben grau sichtbar, beschriftet mit „andere Engine“.

Für die Zuordnung sind ss, eine lokale Instanz und nvidia-smi-Prozesssichtbarkeit erforderlich (in Containern oft nicht vorhanden). Fehlt eine davon, sagt das der Bereich und zeigt jede Karte ohne Hervorhebung, statt zu raten.

Eine Filterzeile begrenzt alles darunter. Jedes Diagramm hat einen Tabellen-Umschalter, der dieselben Serien als Zahlen zeigt, sodass kein Wert nur durch Hovern erreichbar ist. Ein Live-SSE-Feed speist den Request-Ticker und die aktuelle Rate.

URL-Parameter: ?tab=vllm, ?window=6h, ?model=llama3.2:3b, ?source=name, ?nostream=1 (deaktiviert den Live-Feed — nützlich für Kiosk-Displays und Screenshot-Tools, die sonst ewig auf einen offenen Stream warten).

API

Endpoint

Gibt zurück

/api/dashboard?window=1h&model=

alles, was der Ollama-Tab braucht, ein Zeitschnitt

/api/vllm/dashboard?window=1h&source=

dasselbe für eine vLLM-Instanz

/api/summary, /api/timeseries, /api/models, /api/slowest?by=queue_ms

Ollama-Aufschlüsselungen

/api/vllm/summary, /api/vllm/timeseries, /api/vllm/instances

vLLM-Aufschlüsselungen

/api/requests, /api/errors, /api/events, /api/gpu, /api/ps

Rohzeilen und Zeitverläufe

/api/config (GET/PUT), /api/config/reset

Einstellungen

/api/sources (GET/POST/PUT/DELETE), /api/sources/probe

überwachte Engines

/api/prefs, /api/status, /api/health

Dashboard-Standardwerte, Collector-Zustand

/api/stream

SSE-Live-Feed

/api/sources/probe prüft eine Definition bevor sie gespeichert wird, sodass ein Tippfehler dort auftaucht statt als Stille in den Diagrammen.


MCP-Server

./scripts/install-mcp.sh      # writes .mcp.json for this checkout (gitignored)

oder claude mcp add inferwatch -- /path/to/.venv/bin/python -m inferwatch.mcp_server.

Öffnet dieselbe SQLite-Datei schreibgeschützt (mode=ro plus PRAGMA query_only) und antwortet über dieselbe Abfrageebene wie das Dashboard, sodass eine gemeldete Zahl immer mit der Zahl auf dem Bildschirm übereinstimmt.

Tool

Zweck

get_summary, get_timeseries

Ollama-Kennzahlen und -Serien

compare_models, list_models

Aufschlüsselung pro Modell; was resident ist

recent_requests, slowest_requests, recent_errors

Ollama-Details pro Request

get_events

Kaltladungen, Verdrängungen, Kürzungen, Warnungen

vllm_summary, vllm_timeseries, vllm_instances

vLLM-Metriken, Erreichbarkeit, GPU-Zuordnung

gpu_status

pro Gerät Auslastung/VRAM/Temperatur/Leistung

list_sources, get_settings

was überwacht wird und wie es konfiguriert ist

health

funktioniert die Erfassung, ist Debug-Logging aktiv

run_sql, describe_schema

schreibgeschützte SELECT-Notluke, mit Einheiten


Aufbewahrung

  • Rohzeilen pro Request (Ollama): 7 Tage (retention.raw_days).

  • GPU-Samples, Ereignisse, vLLM-Zeilen: 30 Tage (retention.sample_days).

  • rollup_1m und rollup_1h: unbegrenzt aufbewahrt.

Rollups speichern Histogramme mit festen Buckets von TTFT und Latenz, keine vorberechneten Perzentile. Histogramme addieren sich, sodass ein Perzentil über einen beliebigen Bereich durch Summieren der Buckets und Fortschreiten zum Zielrang berechnet wird. Perzentile von Perzentilen wären bedeutungslos; das ist es nicht.

Anfragen innerhalb des Rohdatenfensters liefern exakte Perzentile; darüber hinaus stammen sie aus Histogrammen und werden als obere Grenze des enthaltenden Buckets gemeldet. Jede Antwort enthält exact: true|false.

Neustarts und Reboots

Der Wiederaufnahmezustand ist pro Quelle — ein journald-Cursor, eine Datei-Inode+Offset oder ein Docker-Zeitstempel — und wird bei SIGTERM geleert. Zwei Dinge machen das sicher statt nur wahrscheinlich:

Schreibvorgänge sind idempotent. Jede Anfrage- und Ereigniszeile trägt einen dedupe_key unter einem UNIQUE-Index, und Einfügungen erfolgen mit INSERT OR IGNORE. Das erneute Lesen von Zeilen, die bereits gespeichert wurden, ist ein No-op, sodass ingest wiederholt ausgeführt werden kann und eine Wiederaufnahme sicher überlappen kann.

Ein Cursor, der nicht verwendet werden kann, wird nicht vertraut. Wenn das Journal, auf das ein Cursor zeigt, rotiert wurde, positioniert journalctl stillschweigend neu, indem es den im Cursor eingebetteten Zeitstempel verwendet, und setzt korrekt fort. Aber ein Cursor, der in der Zukunft gestempelt ist (Clock-Skew, eine wiederhergestellte Datenbank), lässt journalctl auf Einträge warten, die nicht eintreffen werden, was die Sammlung stillschweigend zum Stillstand bringt; ein solcher Cursor wird beim Start abgelehnt. Ein Follow-Versuch, der zweimal hintereinander nichts liefert, bewirkt dasselbe.

systemctl stop ist in deutlich unter einer Sekunde abgeschlossen. systemd zeichnet ExecMainStatus=15 zusammen mit Result=success auf: uvicorn löst das Signal nach dem Herunterfahren bewusst erneut aus, sodass das Beenden durch SIGTERM erwartet wird, kein Absturz.


Ehrliche Einschränkungen

Zuordnung unter Parallelität (Ollama). Die Task-ID in den Timing-Zeilen von llama.cpp und der Status der Zugriffszeile erscheinen nie zusammen, daher werden sie nach Ankunftsreihenfolge verknüpft. Bei einer laufenden Anfrage ist das exakt. Wenn zwei abschließen, bevor eine der Zugriffszeilen gedruckt wird, unterscheidet nichts im Log sie — diese Zeilen werden als attribution='ambiguous' gespeichert statt geraten. Werte: exact, ambiguous, none (fehlgeschlagen, bevor der Runner erreicht wurde — es wird auch kein Modell geraten), orphan (Timings ohne Zugriffszeile). Ein 2-Tage-Backfill auf dem Entwicklungs-Host ergab 174 exact, 9 ambiguous, 4 orphan, mit OLLAMA_NUM_PARALLEL=1 in Kraft; erwarten Sie einen höheren ambiguous-Anteil, je mehr Anfragen gleichzeitig laufen.

vLLMs Token- und Anfragezähler sind nicht pro Anfrage ausgerichtet. generation_tokens_total erhöht sich, während Tokens streamen; request_success_total erhöht sich nur, wenn eine Anfrage abgeschlossen ist. Über ein kurzes Fenster beschreiben sie daher überlappende, aber unterschiedliche Mengen von Anfragen, und das Dividieren des einen durch das andere ergibt nicht Tokens pro Anfrage. Die API kennzeichnet dies mit counters_aligned: false, und das Dashboard sagt dies auf dem vLLM-Tab.

Kein Pro-Anfrage-Anything für vLLM. Oben abgedeckt. Wenn Sie Pro-Anfrage-Details von vLLM benötigen, ist dessen Request-Level-Logging die einzige Quelle, und es protokolliert Prompt-Text — den dieses Tool bewusst nie speichert.

Logs sind die Zufuhr, nicht das Archiv. Ein Journal kann je nach journald.conf nur ein oder zwei Tage halten; die SQLite-Datei ist der Historiker. Wenn Logs schneller rotieren, als inferwatch läuft, ist diese Lücke nicht wiederherstellbar.

Zwei byteidentische Ereignisse in derselben Mikrosekunde kollabieren zu einem. Der Dedupe-Key für Ereignisse wird aus ihren Werten gebildet, sodass eine identische Warnung, die zweimal innerhalb einer Mikrosekunde protokolliert wird, eine Zeile behält. Ein bewusster Tausch für garantierte Idempotenz — das Verwerfen einer wiederholten Warnung schlägt das Duplizieren von Verlauf.

Health-Check-Traffic wird getrennt, nicht gezählt. HEAD / und GET /api/ps machten 96 % der Anfragen auf dem Entwicklungs-Host aus. Sie werden mit class='health' gespeichert und von Inferenzraten ausgeschlossen, es sei denn, dashboard.include_health ist aktiviert; requests_all enthält sie immer.

Hängt von Logformaten und Metriknamen ab. Ollamas Timing-Zeilen sind Debug- Ausgabe, kein Vertrag, und vLLM benennt Metriken zwischen Releases um. tests/test_parse.py enthält wörtliche Fixture-Zeilen und tests/test_vllm.py einen echten /metrics-Auszug; wenn ein Upgrade das Parsen bricht, schlagen diese Tests fehl und zeigen, was sich geändert hat.


Tests

python -m unittest discover -s tests -t .

CI führt dies auf Python 3.10 bis 3.14 aus, plus einen Packaging-Job, der das Wheel baut, prüft, dass das Dashboard-HTML darin enthalten ist, und es in eine saubere Umgebung aus einem leeren Verzeichnis installiert, sodass der Quellbaum einen Packaging-Fehler nicht maskieren kann. Kein Netzwerk, keine GPU und keine Engine ist erforderlich. Parser-Fixtures sind wörtliche echte Logzeilen und ein echter /metrics-Auszug. Die Abdeckung umfasst den Join des Korrelators und seine ambiguous/orphan/failed-Fälle, Histogramm-Perzentile und Rollup-Idempotenz, die Schema-Migration, signal-sichere Commits, Cursor-Validierung, Dateirotation und -truncation, Zeitstempel-Monotonie, Counter-Reset-Erkennung sowie Konfigurations- Präzedenz und Sperrung. Die Konfigurationsreferenz in dieser Datei wird aus der Spezifikation generiert, und ein Test schlägt fehl, wenn sie abweicht. Das JavaScript des Dashboards wird mit einem reinen Python-Parser syntaxgeprüft und seine Formatierer in einer echten JS-Engine ausgeführt (beides optional — kein Node nötig).

.venv/bin/python scripts/gen-config-docs.py --check   # docs match the code?

Layout

inferwatch/parse.py        ollama log line parsers (pure, fixture-tested)
inferwatch/readers.py      journald / file / docker log readers
inferwatch/collect.py      correlator, GPU + model pollers, maintainer
inferwatch/vllm.py         Prometheus scraper, delta and reset handling
inferwatch/gpuproc.py      maps GPUs to the process tree holding them
inferwatch/vllm_metrics.py vLLM query layer
inferwatch/metrics.py      ollama query layer (shared by API and MCP)
inferwatch/store.py        SQLite schema, rollups, histograms, retention
inferwatch/config.py       typed settings spec, precedence, source validation
inferwatch/supervisor.py   builds and rebuilds collectors from the sources table
inferwatch/api.py          FastAPI endpoints + SSE
inferwatch/web/index.html  dashboard (single file, no CDN, no build step)
inferwatch/mcp_server.py   MCP server (read-only)
inferwatch/main.py         serve / ingest / stats / sources

Mitwirken

Issues und Pull Requests sind willkommen. Zwei Dinge machen eine Änderung leicht annehmbar:

  • python -m unittest discover -s tests -t . besteht.

  • Wenn Sie inferwatch/config.py angefasst haben, führen Sie python scripts/gen-config-docs.py aus, damit die Konfigurationsreferenz der README mit dem Code übereinstimmt — ein Test erzwingt das.

Parser-Änderungen sollten mit einer Fixture-Zeile kommen, die wörtlich aus echter Engine- Ausgabe kopiert ist, so wie es die bestehenden Tests tun. Logformate und Metriknamen sind keine Verträge, und eine echte Fixture macht einen zukünftigen Bruch offensichtlich.

Lizenz

Apache License 2.0 — siehe LICENSE und NOTICE.

A
license - permissive license
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    A
    quality
    D
    maintenance
    Enables AI agents to query Prometheus metrics and Loki logs for intelligent alert investigation and troubleshooting. Provides service discovery, metric querying, log searching, and correlation tools to help identify root causes of issues.
    9
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to monitor real-time CPU, RAM, and disk usage on the local machine.

View all related MCP servers

Related MCP Connectors

  • Provide real-time data querying and visualization by integrating Tako with your agents. Generate o…

  • Gateway between LLM agents and world data through eight tools and a bundled endpoint catalog.

  • See, price, and control every tool call your AI agents make: policy checks, cost, and audit tools.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/floatsmyboat/inferwatch'

If you have feedback or need assistance with the MCP directory API, please join our Discord server