Streaming
The ChatComponent automatically classifies AI message content and routes it to the appropriate renderer. This page explains how the streaming pipeline works and how to use the classification APIs directly for custom integrations.
Content Classification
Each AI message is processed by a ContentClassifier that examines the content as it streams token-by-token. The classifier determines the content type from the first non-whitespace character:
| Trigger | Content Type | What Happens |
|---|---|---|
First non-whitespace is { | json-render | Parsed as a JSON spec via @ngaf/partial-json |
| Any other text | markdown | Rendered as markdown prose |
Each message gets its own classifier instance. Classification happens once per message โ the type is determined by the first meaningful character and never changes.
The Streaming Pipeline
For JSON spec messages, the pipeline is:
Structural sharing means that when a new token arrives, only the affected element's object reference changes. Sibling elements keep the same reference, so Angular's change detection skips them entirely. This makes streaming efficient even for large specs with many elements.
Using ContentClassifier Directly
For custom message rendering outside of ChatComponent, use createContentClassifier():
Signals
| Signal | Type | Description |
|---|---|---|
type | Signal<ContentType> | 'undetermined', 'markdown', 'json-render', 'a2ui', or 'mixed' |
markdown | Signal<string> | Accumulated markdown prose (empty for pure JSON) |
spec | Signal<Spec | null> | Materialized JSON-render spec with structural sharing |
elementStates | Signal<Map<string, ElementAccumulationState>> | Per-element tracking of which properties have been received |
streaming | Signal<boolean> | true while content is still arriving |
ContentType
Using ParseTreeStore Directly
For lower-level control over JSON-to-Spec materialization:
ElementAccumulationState
A2UI Content Detection
A2UI content uses a different detection trigger than JSON-render specs. Instead of detecting the first non-whitespace { character, the classifier looks for the ---a2ui_JSON--- prefix at the start of the message.
Once detected, the classifier switches to A2UI mode and parses the remaining content as JSONL โ one JSON object per line โ rather than a single JSON object. Each line represents an A2UI message that builds up surfaces with components and data models.
The resulting surfaces are available via classifier.a2uiSurfaces(), which returns a Map<string, A2uiSurface> keyed by surface ID. See the A2UI guide for full details on the A2UI protocol and surface structure.
Error Handling
Parse errors are captured in the errors signal and do not crash the rendering pipeline. When a malformed token arrives, the classifier records the error and continues processing subsequent tokens โ partial results keep rendering.
This makes the errors signal useful for diagnostics and debugging without disrupting the user-facing chat experience.