Enable real-time streaming responses in chat components
Cedar enables streaming responses by default in your chat interface. Responses appear in real-time as they’re generated. You can disable streaming by setting the stream prop to false on any chat component or ChatInput.
Partial Object Streaming Not Supported: Cedar currently doesn’t support
partial object streaming. For structured data to be handled via Cedar’s
automatic handlers, only complete objects should be streamed or streaming
should be turned off.
OpenAI and AI SDK: Streaming is supported out of the box
Mastra and Custom backend: The backend is required to send a data-only SSE stream of information in either streamed text or entire objects
Uhhh... what is a data-only SSE stream?
Server-Sent Events (SSE) are part of a one-way streaming protocol to deliver a sequence of data: messages over a single HTTP connection. The stream mixes plain text and structured JSON, sent as newline-delimited chunks prefixed with data:.Under the hood, the server emits:Text chunks for incremental message rendering
Our client handlers will parse each data: line as it arrives and handle parsed text or JSON accordingly. This enables real-time, mixed-format updates with minimal overhead.
Sample Backend Streaming Handler Implementation
Feel free to drop these handlers into your backend to take care of Cedar-OS-compatible streaming.1. Example Usage:
Copy
Ask AI
// Return statement of /handleStreamreturn createSSEStream(async (controller) => { // Handle application execution logic with controller variable available // Stream text responses from your agent const streamResult = await agent.stream({ prompt, temperature, maxTokens, systemPrompt, }); // Stream the text result using the helper function await handleTextStream(streamResult, controller); // Example: Stream a tool call result as structured JSON const toolCallResult = { type: 'tool_call', tool: 'plant_seed', result: { status: 'success', message: 'A new Cedar tree has been planted <3', }, timestamp: new Date().toISOString(), }; // Stream the JSON event using the helper function streamJSONEvent(controller, toolCallResult);});/** * Handles streaming of text chunks using data-only format. * Pass your agent's stream result and the controller to stream text chunks to frontend. */export async function handleTextStream( streamResult: StreamTextResult<any, any>, streamController: ReadableStreamDefaultController<Uint8Array>): Promise<string> { const encoder = new TextEncoder(); const chunks: string[] = []; // Stream raw text chunks through data field for await (const chunk of streamResult.textStream) { chunks.push(chunk); // Escape literal newlines for SSE compliance const escaped = chunk.replace(/\n/g, '\\n'); streamController.enqueue(encoder.encode(`data:${escaped}\n\n`)); } return chunks.join('');}/** * Emit any JSON object as a data event. * Use this to stream structured data like tool results, progress updates, or custom events. */export function streamJSONEvent<T>( controller: ReadableStreamDefaultController<Uint8Array>, eventData: T) { const encoder = new TextEncoder(); controller.enqueue(encoder.encode('data: ')); controller.enqueue(encoder.encode(`${JSON.stringify(eventData)}\n\n`));}
2. Core SSE Stream Creator:
Copy
Ask AI
/** * Creates a Server-Sent Events stream response. * Pass a callback function that receives a controller for streaming data. * Use this as your main endpoint return value for streaming responses. */export function createSSEStream( cb: (controller: ReadableStreamDefaultController<Uint8Array>) => Promise<void>): Response { const encoder = new TextEncoder(); const stream = new ReadableStream<Uint8Array>({ async start(controller) { try { // Execute your application logic with streaming controller await cb(controller); // Signal completion with empty data controller.enqueue(encoder.encode('event: done\n')); controller.enqueue(encoder.encode('data:\n\n')); } catch (err) { console.error('Error during SSE stream', err); const message = err instanceof Error ? err.message : 'Internal error'; controller.enqueue(encoder.encode('data: ')); controller.enqueue( encoder.encode(`${JSON.stringify({ type: 'error', message })}\n\n`) ); } finally { controller.close(); } }, }); return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', Connection: 'keep-alive', }, });}