Chat · API Reference

createContentClassifier()

Factory function that creates a ContentClassifier — a stateful, per-message service that detects content type from the token stream and routes to the appropriate parser.

Import:

import { createContentClassifier } from '@threadplane/chat';
import type { ContentClassifier, ContentType } from '@threadplane/chat';

#Signature

function createContentClassifier(): ContentClassifier

Returns: ContentClassifier — a stateful classifier instance. Must be called within an Angular injection context (signals require it).

#ContentClassifier Interface

interface ContentClassifier {
  /** Feed the full message content snapshot. Internally computes delta. */
  update(content: string): void;
 
  /** Detected content type. */
  readonly type: Signal<ContentType>;
 
  /** Accumulated markdown prose. Empty for pure JSON messages. */
  readonly markdown: Signal<string>;
 
  /** Materialized JSON-render spec. Null until JSON is detected. */
  readonly spec: Signal<Spec | null>;
 
  /** Per-element accumulation tracking. */
  readonly elementStates: Signal<Map<string, ElementAccumulationState>>;
 
  /** True while content is still arriving. */
  readonly streaming: Signal<boolean>;
 
  /** A2UI surfaces parsed from the stream. */
  readonly a2uiSurfaces: Signal<Map<string, A2uiSurface>>;
 
  /** Per-surface A2UI accumulation state, keyed by surface ID. */
  readonly a2uiSurfaceStates: Signal<Map<string, A2uiSurfaceState>>;
 
  /** Parse errors encountered (non-fatal). */
  readonly errors: Signal<string[]>;
 
  /** Clean up resources. */
  dispose(): void;
}

#ContentType

type ContentType = 'pending' | 'markdown' | 'json-render' | 'a2ui' | 'mixed';
ValueMeaning
pendingNo content received yet
markdownPlain text / markdown prose
json-renderJSON spec detected (first non-whitespace is {)
a2uiA2UI payload detected via ---a2ui_JSON--- prefix, parsed as JSONL messages
mixedMarkdown followed by structured content

#a2uiSurfaces

When the content type is a2ui, this signal contains all parsed A2UI surfaces with their components and data models. The map is keyed by surface ID and updates incrementally as JSONL lines arrive during streaming.

#errors

Parse errors are captured but don't stop the classifier. Partial results continue to render. Useful for diagnostics — check this signal when debugging unexpected rendering behavior or malformed agent output.

#Usage

const classifier = createContentClassifier();
 
// Feed full content snapshots — delta is computed internally
classifier.update('Hello world');
classifier.type();      // 'markdown'
classifier.markdown();  // 'Hello world'
 
// For JSON content
const jsonClassifier = createContentClassifier();
jsonClassifier.update('{"root":"r1","elements":{}}');
jsonClassifier.type();  // 'json-render'
jsonClassifier.spec();  // { root: 'r1', elements: {} }
 
// Always dispose when done
classifier.dispose();

#A2UI content

When the snapshot starts with the ---a2ui_JSON--- prefix, the classifier switches to A2UI mode and parses the remaining lines as JSONL. Read type() (=> 'a2ui') and a2uiSurfaces() for the parsed surfaces:

const a2uiClassifier = createContentClassifier();
 
a2uiClassifier.update(
  '---a2ui_JSON---\n' +
  '{"surfaceId":"s1","components":[{"id":"root","component":{"Text":{"text":{"literalString":"Hi"}}}}]}',
);
 
a2uiClassifier.type();          // 'a2ui'
a2uiClassifier.a2uiSurfaces();  // Map<string, A2uiSurface> keyed by surface ID
 
a2uiClassifier.dispose();
Delta processing

Pass the full message content each time, not just new characters. The classifier tracks processedLength internally and only processes the delta.