Skip to content
Fabrik UI

Streaming

SSE protocol and StreamEvent types

Fabrik uses Server-Sent Events (SSE) over HTTP POST for real-time streaming. The server emits StreamEvent objects as newline-delimited JSON.

Wire format

POST /api/chat
Content-Type: application/json

{ "messages": [...], "systemPrompt": "...", "tools": [...] }

The response is text/event-stream:

data: {"type":"start","runId":"run-abc123"}
data: {"type":"text","delta":"Hello "}
data: {"type":"text","delta":"world!"}
data: {"type":"done"}

StreamEvent types

Lifecycle

EventFieldsDescription
startrunIdStream has started
doneStream completed successfully
errormessageStream failed with an error

Text

EventFieldsDescription
textdeltaIncremental text chunk

Components (generative UI)

EventFieldsDescription
component_startid, nameLLM is generating component props
component_deltaid, deltaPartial JSON of component props
component_doneid, propsFinal validated props

Thinking

EventFieldsDescription
thinking_startidLLM started chain-of-thought
thinking_deltaid, deltaThinking text chunk
thinking_doneid, durationMsThinking complete

Steps

EventFieldsDescription
step_startid, titleTool call started
step_doneid, stepStatus, durationMsTool call finished

Artifacts

EventFieldsDescription
artifact_startid, title, languageArtifact generation started
artifact_deltaid, deltaArtifact content chunk
artifact_doneidArtifact complete

Elicitation

EventFieldsDescription
askid, configLLM is asking the user a question

Internal (tool calls)

These events are used internally by the client and are not exposed to developers:

EventFieldsDescription
tool_call_startid, toolNameRaw tool call started
tool_call_deltaid, deltaTool call argument chunk
tool_call_doneid, toolName, argsTool call complete

TypeScript type

The full StreamEvent union type:

import type { StreamEvent } from "@fabrik-sdk/ui"

This is a discriminated union — switch on event.type to narrow the type and get full type safety for each event's fields.

Custom streaming

If you build a custom provider, yield StreamEvent objects from an async generator:

lib/custom-stream.ts
import type { Provider, StreamEvent, StreamOptions } from "@fabrik-sdk/ui"

export function customProvider(): Provider {
  return {
    name: "custom",
    async *stream(options: StreamOptions): AsyncIterable<StreamEvent> {
      yield { type: "start", runId: "run-1" }

      // Stream text
      for (const word of ["Hello", " ", "world!"]) {
        yield { type: "text", delta: word }
      }

      // Render a component
      yield { type: "component_start", id: "c1", name: "greeting_card" }
      yield {
        type: "component_done",
        id: "c1",
        props: { message: "Hello!" },
      }

      yield { type: "done" }
    },
  }
}

On this page