Woran ich in llm-d gearbeitet habe

Meine gemergten Pull Requests im Projekt llm-d: überwiegend CI-Härtung, Tests und Observability, dazu eine Konfigurationsoption für Flow Control und einige kleine Bugfixes.

llm-d ist ein Open-Source-Projekt, mit dem LLM-Inferenz auf Kubernetes betrieben wird. Es wird von Entwicklern von Red Hat, IBM und Google gepflegt. Der Router sitzt zwischen Clients und Modellservern wie vLLM und entscheidet für jede Anfrage, welches Backend sie bearbeitet, anhand von Signalen wie Last und gecachten Präfixen. Eine Flow-Control-Schicht im Router reiht Anfragen nach Priorität ein.

Zwischen Mai und Juli 2026 wurden Pull Requests von mir in mehreren llm-d-Repositories gemergt. Ich betreibe selbst kein LLM-Serving. Die meisten dieser Pull Requests sind CI-, Test-, Observability- und Härtungsarbeit; der Rest sind ein Konfigurations-Feature, einige Bugfixes und Dokumentation. Dieser Beitrag listet auf, was jeder davon geändert hat.

CI- und Supply-Chain-Härtung

Im Router-Projekt gibt es einen RFC mit einer Checkliste für CI/CD-Verbesserungen. Mehrere meiner Pull Requests setzen Punkte daraus um.

  • llm-d-router #960 pinnt eine veränderliche GitHub-Action-Referenz auf einen Commit-SHA, ergänzt permissions-Blöcke mit minimalen Rechten in neun Workflows, setzt ein Timeout von 30 Minuten für den Lint-und-Test-Job (ein hängender kind-Cluster konnte sonst einen Runner bis zu sechs Stunden belegen), ergänzt Concurrency-Gruppen, damit neue Pushes veraltete Runs abbrechen, ersetzt einen Automerge-Cron im Fünf-Minuten-Takt durch Event-Trigger und einen selteneren Fallback-Cron, lädt Trivy-Ergebnisse als SARIF in den GitHub-Security-Tab hoch und ergänzt govulncheck für bekannte Schwachstellen in Go-Abhängigkeiten.
  • llm-d-router #1424 lässt den Job fehlschlagen, wenn die Testabdeckung sinkt, ergänzt einen Dependency-Review-Workflow für Pull Requests und einen Workflow, der das CI-Builder-Image bei Änderungen baut und beim Merge pusht.
  • llm-d-router #1579 pinnt :latest-Image-Tags in den GPU-Beispielmanifesten auf feste vLLM- und SGLang-Versionen und macht den Presubmit-Check für :latest-Tags blockierend.
  • llm-d-router #1572 reicht ein Hugging-Face-Token an die End-to-End-Tests weiter, die sporadisch an Rate Limits des Hubs (HTTP 429) scheiterten.
  • llm-d-kv-cache #623 wendet dieselbe erste Checkliste auf das KV-Cache-Repository an: SARIF-Upload, eine gepinnte Action, eine Integritätsprüfung der Go-Module, Concurrency-Gruppen, Job-Timeouts und ein fehlendes ZeroMQ-Paket im Beispiel-Workflow.

Flow-Control-Konfiguration

llm-d-router #1099 ist der einzige Pull Request in Feature-Größe. Die Flow-Control-Schicht weist jeder Anfrage eine numerische Priorität zu, und eine negative Priorität kennzeichnet Traffic, der verworfen werden darf. Kommt eine Anfrage auf einer Prioritätsstufe ohne konfiguriertes Band an, erzeugt der Router ein Band aus einer einzigen Default-Vorlage. Positive und negative Prioritäten bekamen deshalb dasselbe Queue-Budget, und man konnte zum Beispiel nicht 1 GB Queue-Budget für normalen Traffic und keines für verwerfbaren Traffic konfigurieren.

Der Pull Request ergänzt eine zweite Vorlage, DefaultNegativePriorityBand, die nur verwendet wird, wenn ein Band für eine Priorität unter null erzeugt wird. Ist sie nicht gesetzt, nutzt der Router den bisherigen Default, sodass bestehende Konfigurationen sich wie vorher verhalten. Die eigentliche Änderung sind wenige Zeilen in der Provisionierungslogik. Der größte Teil des Diffs ist die nutzerseitige API-Config, die interne Config mit Validierung und Deep Copy sowie Tests für Konstruktion, API-Übersetzung, Clone-Isolation und dynamische Provisionierung für positive, negative und Fallback-Fälle.

Metriken

llm-d-router #1429 exportiert drei Prometheus-Metriken für den Data Producer des multimodalen Encoder-Caches: ein Histogramm der Trefferquote, einen Zähler für Abfragen und einen Zähler für Treffer. Sie folgen den Metriken, die das Prefix-Cache-Plugin bereits exportierte.

Bugfixes

  • llm-d-router #1426: Anfragen ohne model-Feld wurden abgewiesen, bevor generische Rewrite-Regeln einen Modellnamen eintragen konnten. Das Rewrite läuft jetzt zuerst.
  • llm-d-router #1580: Der Concurrency-Detector meldete volle Auslastung, wenn noch kein Endpoint Metadaten hatte, etwa direkt nach dem Start von Pods oder für einen passiven Pod in einem HA-Setup. Er zählt jetzt die Kapazität dieser Endpoints mit und meldet für sie keine Last.
  • llm-d-router #1695: Ohne angelegten InferencePool und ohne modelServers.matchLabels erzeugte das Standalone-Helm-Chart einen leeren Endpoint-Selector, und der Router scheiterte beim Start. Das Chart bricht jetzt schon beim Rendern mit einer Fehlermeldung ab.
  • llm-d-inference-sim #530: Alle HTTP-Endpoints des Inferenz-Simulators geben Fehler jetzt im JSON-Fehlerformat von OpenAI zurück, das OpenAI-SDK-Clients auswerten können.
  • llm-d-kv-cache #589: entfernt eine Mindestgröße von 16 MB für Staging-Buffer, die jede auf die Platte geschriebene KV-Cache-Datei aufgefüllt hat.

Kleinere Änderungen

  • llm-d-inference-payload-processor #151 ergänzt GetModels(predicate) im Datastore-Interface, sodass Aufrufer alle passenden Modelle mit einem Aufruf unter einem Read-Lock bekommen.
  • Dokumentation: Logging-Konventionen in der AGENTS.md des Routers (#1571), ein Tippfehler in llm-d-inference-sim (#529) und veraltete kgateway-Verweise in llm-d-benchmark (#1437).