# The gateway is the only source of per-request LLM latency covering EVERY # model, including the two Ollama ones (ornith:35b, qwen2.5:3b-instruct) that # expose no metrics endpoint at all. Series it emits, per # internal/observability/prometheus.go: # llm_ttft_seconds histogram time-to-first-token # llm_itl_seconds histogram inter-token latency # llm_tokens_total counter SSE chunks generated # queue_full_requests_total counter backpressure rejects # warmup_requests_delayed_total counter vLLM slot contention # vllm_prefix_cache_hit_tokens_total counter prefix-cache hits # request_duration_seconds histogram full request # # Caveat: these are derived from the SSE byte stream, not from the engine. # TTFT is request-start to first byte, ITL is the gap between SSE writes # carrying "data: ", and llm_tokens_total counts SSE chunks rather than true # tokens. They are also streaming-only — non-streaming requests record # nothing. Treat vLLM's own histograms as authoritative for `reasoning`. # # The Deployment already carried prometheus.io/scrape annotations, but # kube-prometheus-stack ignores pod annotations (no additionalScrapeConfigs, # serviceMonitorSelectorNilUsesHelmValues: false), so none of the above was # ever collected. This ServiceMonitor is what actually wires it up. apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: api-gateway namespace: monitoring labels: app.kubernetes.io/name: api-gateway app.kubernetes.io/part-of: api spec: namespaceSelector: matchNames: - api selector: matchLabels: app: api-gateway endpoints: # api-gateway Service port IS named `http` (unlike the KServe ones), so # the name form works here. - port: http interval: 30s scrapeTimeout: 10s path: /metrics scheme: http