Files
stack/infra/grafana/dashboards/llm.json
kert 1843c0a9a0
All checks were successful
CI / lint (push) Successful in 33s
CI / test (push) Successful in 2m6s
Deploy / notebooks (push) Has been skipped
CI / notebooks-smoke (push) Successful in 1m31s
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Deploy / llm (push) Successful in 1m21s
Deploy / mc (push) Has been skipped
Deploy / api (push) Successful in 1m46s
Infra CI / docs (push) Successful in 21s
Infra CI / llm (push) Successful in 15s
Infra CI / mc (push) Successful in 14s
Deploy / report (push) Successful in 11s
Infra CI / zotero (push) Successful in 17s
Infra CI / notebooks (push) Successful in 50s
Infra CI / api (push) Successful in 19s
fix(perf,infra): actually export the metrics — /metrics route, perf extra in the api/llm images, scrape targets, telemetry on (refs #579)
The P26 telemetry path never reached Prometheus: setup_meter_provider
only installs an in-process PrometheusMetricReader, nothing served the
registry (api and llm answered 404 on /metrics), the images never
installed the perf extra, STACK_TELEMETRY was off, and no scrape target
existed — so the data-pipelines request-rate panel was empty from the
day it was written. Now: perf.middleware.instrument mounts GET /metrics
(prometheus_client registry), the api and llm images install --extra
perf, compose sets STACK_TELEMETRY=true on both, services.yml scrapes
api:8000 and llm:8000, and both dashboards' request-rate panels query
http_server_duration_milliseconds_count (what the FastAPI instrumentor
emits; stack_http_server_requests_total never existed). Verified live:
stack_llm_dispatch_total is queryable in Prometheus with job=llm.
2026-09-11 18:59:40 -04:00

257 lines
5.4 KiB
JSON

{
"title": "LLM service",
"uid": "llm",
"tags": [
"homelab",
"stack"
],
"timezone": "browser",
"schemaVersion": 39,
"time": {
"from": "now-6h",
"to": "now"
},
"refresh": "30s",
"panels": [
{
"id": 1,
"title": "HTTP request rate (status)",
"type": "timeseries",
"gridPos": {
"x": 0,
"y": 0,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"targets": [
{
"expr": "sum by (http_status_code) (rate(http_server_duration_milliseconds_count{job=\"llm\"}[1m]))",
"legendFormat": "{{http_status_code}}"
}
]
},
{
"id": 2,
"title": "Chat answers by outcome",
"type": "timeseries",
"gridPos": {
"x": 12,
"y": 0,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"targets": [
{
"expr": "sum by (outcome) (rate(stack_llm_chat_total[5m]))",
"legendFormat": "{{outcome}}"
}
]
},
{
"id": 3,
"title": "Dispatch rate per host",
"type": "timeseries",
"gridPos": {
"x": 0,
"y": 8,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"targets": [
{
"expr": "sum by (host, kind) (rate(stack_llm_dispatch_total[5m]))",
"legendFormat": "{{host}} {{kind}}"
}
]
},
{
"id": 4,
"title": "In-flight requests per host",
"type": "timeseries",
"gridPos": {
"x": 12,
"y": 8,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"targets": [
{
"expr": "sum by (host) (stack_llm_inflight)",
"legendFormat": "{{host}}"
}
]
},
{
"id": 5,
"title": "Dispatch latency p50/p95 (host, kind)",
"type": "timeseries",
"gridPos": {
"x": 0,
"y": 16,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"unit": "s"
},
"overrides": []
},
"targets": [
{
"expr": "histogram_quantile(0.50, sum by (le, host, kind) (rate(stack_llm_dispatch_seconds_bucket[5m])))",
"legendFormat": "p50 {{host}} {{kind}}"
},
{
"expr": "histogram_quantile(0.95, sum by (le, host, kind) (rate(stack_llm_dispatch_seconds_bucket[5m])))",
"legendFormat": "p95 {{host}} {{kind}}"
}
]
},
{
"id": 6,
"title": "Chat latency by stage (p50/p95)",
"type": "timeseries",
"gridPos": {
"x": 12,
"y": 16,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"fieldConfig": {
"defaults": {
"unit": "s"
},
"overrides": []
},
"targets": [
{
"expr": "histogram_quantile(0.50, sum by (le, stage) (rate(stack_llm_chat_seconds_bucket[5m])))",
"legendFormat": "p50 {{stage}}"
},
{
"expr": "histogram_quantile(0.95, sum by (le, stage) (rate(stack_llm_chat_seconds_bucket[5m])))",
"legendFormat": "p95 {{stage}}"
}
]
},
{
"id": 7,
"title": "Embedding throughput per host (texts/s)",
"type": "timeseries",
"gridPos": {
"x": 0,
"y": 24,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"targets": [
{
"expr": "sum by (host) (rate(stack_llm_embedded_texts_total[5m]))",
"legendFormat": "{{host}}"
}
]
},
{
"id": 8,
"title": "Chat tokens by direction",
"type": "timeseries",
"gridPos": {
"x": 12,
"y": 24,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"targets": [
{
"expr": "sum by (direction) (rate(stack_llm_chat_tokens_total[5m]))",
"legendFormat": "{{direction}}"
}
]
},
{
"id": 9,
"title": "Indexed chunks per collection",
"type": "timeseries",
"gridPos": {
"x": 0,
"y": 32,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"targets": [
{
"expr": "sum by (collection) (rate(stack_llm_indexed_chunks_total[5m]))",
"legendFormat": "{{collection}}"
}
]
},
{
"id": 10,
"title": "Indexed items (indexed vs skipped)",
"type": "timeseries",
"gridPos": {
"x": 12,
"y": 32,
"w": 12,
"h": 8
},
"datasource": {
"uid": "prometheus"
},
"targets": [
{
"expr": "sum by (collection, outcome) (rate(stack_llm_indexed_items_total[5m]))",
"legendFormat": "{{collection}} {{outcome}}"
}
]
},
{
"id": 11,
"title": "LLM service log tail",
"type": "logs",
"gridPos": {
"x": 0,
"y": 40,
"w": 24,
"h": 8
},
"datasource": {
"uid": "loki"
},
"targets": [
{
"expr": "{container=\"llm\"}"
}
]
}
]
}