SSE-Wire-Contract
Die AI-Chat-Endpoints streamen Server-Sent Events (text/event-stream).
Diese Seite dokumentiert den Control-Stream-Contract, der von
POST /chat/control/stream verwendet wird. Die maßgebliche Quelle sind Parser + Typen in
@kaleidr/inference-ui (packages/inference-ui/src/control/sse.ts und
types.ts). Wenn du kaleidr.js nutzt, parst das Chat-Bundle dies für dich —
diese Seite brauchst du nur für einen eigenen Client.
/chat/summary/stream und /chat/button/stream verwenden andere SSE-
Contracts und werden hier nicht behandelt. Verwende den Control-Stream-
Parser nicht für diese Endpoints.
Kompatibilitätsregel
Clients müssen unbekannte Event-Namen ignorieren. Neue Events können zum stabilen Contract hinzugefügt werden, ohne die Version zu erhöhen. Downstream-Events in der Tabelle "diagnostic / experimental" unten sind kein Contract — der offizielle Client ignoriert sie und sie können verschwinden.
Stabile Events
Die Payloads entsprechen den Annahmen des Control-Parsers. start besitzt kein phase-Feld
(dieses erscheint nur bei phase-Events).
event: | Payload | Meaning |
|---|---|---|
| (unnamed) | { text } | Inkrementelles Prosa-Token. Kann [[Place]]-Marker enthalten. |
phase | { type: "phase", phase, session_id } | Lifecycle vor dem Stream. |
start | { type: "start", session_id, purpose? } | Stream geöffnet. |
place | { name, coordinates: { lat, lng }, address?, category?, source?, id? } | Aufgelöster Ort (merge-by-name → Map-Pin). source: "vendor" kennzeichnet einen Ort aus hochgeladenen Org-Daten. |
place_linked | place fields + is_update? | Einen bereits ausgegebenen Ort mit Daten anreichern. |
early_actions | { type: "early_actions", actions: [ { type, payload } ], places_count?, is_partial? } | Kartenaktionen (fitBounds / route / highlight) vor der vollständigen Antwort. |
prompt_options | { type: "prompt_options", schema_version, kind, question, allow_free_text, options: [ { label, send_text } ] } | Auswahlchips zur Disambiguierung — als antippbare Optionen darstellen und send_text als nächste Benutzernachricht senden. |
route_clear | {} | Alle Overlays löschen, bevor neue Routen eintreffen. Stable. |
route | { type: "route", schema_version, id?, profile, origin, destination, primary, alternatives[], comparison[], stops?, connectors?, color?, label? } | Journey-bezogener Turn — Engine-berechnete Route, vor der Prosa ausgegeben, damit die Karte während des Streams zeichnen kann. |
quota | { type: "quota", product, meter, tier, used, limit, reset_at, period } | Meter während des Streams — verbleibendes Budget anzeigen. |
end | { type: "end", full_text, places[], actions[], question_type, response_time, total_tokens? } | Terminal Envelope — vollständiges Ergebnis. |
error | { type: "error", message, code?, partial_text? } | Terminaler Fehler. |
: keep-alive | comment line | Proxy-Keep-Alive (~10s). Ignorieren. |
Entfernt: grounding (2026-09-05). Grounding beeinflusst die Antwort weiterhin,
setzt aber keinen Frame mehr auf den Wire.
Diagnostisch / experimentell (kein Contract)
Dies sind ausgegebene Implementierungsdetails. Der offizielle Client ignoriert sie; nicht darauf verlassen.
event: | Payload | Notes |
|---|---|---|
debug | { scope, … } | Interne Diagnose. Kann sich ändern oder verschwinden. |
place_name_found | { name, is_early? } | Kein Contract; offizieller Client ignoriert es. |
Tokens und strukturierte Daten
Unbenannte Events streamen einfache Prosa. Sie können inline [[Place]]-
Marker enthalten, wenn ein Ort erwähnt wird (z. B. …cafes near [[Louvre]]…) —
entferne sie für eine saubere Anzeige oder ignoriere die Prosa vollständig und steuere deine
UI über die strukturierten Events.
Strukturierte Daten kommen als Events, niemals inline in der Prosa. Aufgelöste
Orte streamen über place (und place_linked für spätere Metadaten), während sie
geokodiert werden; Kartenaktionen kommen über early_actions; Routen über route
nach einem route_clear; der maßgebliche finale Payload ist das end-Event.
Ein typischer Client rendert Pins aus place-Events, sobald sie eintreffen, und
behandelt end als Source of Truth.
Minimaler Client
const res = await fetch(`${API}/inference-api/b2b/v1/chat/control/stream`, {
method: 'POST',
headers: { 'X-Api-Key': key, 'Content-Type': 'application/json', Accept: 'text/event-stream' },
body: JSON.stringify({ messages: [{ role: 'user', content: 'cafes near the Louvre' }] }),
});
const reader = res.body.getReader();
// …decode chunks, split on \n\n, parse `event:` + `data:` lines
// IMPORTANT: ignore any unknown event name (forward-compat rule above).
Siehe Endpoints für Request-Bodies und Errors für die Fehlerbehandlung.