What Is the AG-UI Protocol? How Tambo AI Uses It for Real-Time Streaming

The AG-UI protocol is an open-source event-based wire format that represents conversational AI interactions as typed events, which Tambo AI uses to stream LLM outputs, tool calls, and UI components in real-time via Server-Sent Events.

The AG-UI protocol powers the streaming architecture of the tambo-ai/tambo repository, enabling low-latency delivery of text deltas, tool invocations, and interactive UI components from backend LLMs to React frontends.

What Is the AG-UI Protocol?

AG-UI (Agent-User Interface) is an open-source wire protocol defined by the @ag-ui/core SDK. It models conversational interactions as a stream of strongly typed events rather than monolithic JSON responses. Standard event types include RUN_STARTED, TEXT_MESSAGE_CONTENT, TOOL_CALL_START, and RUN_FINISHED, each carrying specific payloads that describe the state of an AI run.

This event-based architecture decouples the LLM generation process from the UI rendering layer, allowing clients to incrementally display content as it is produced rather than waiting for complete responses.

How Tambo AI Implements AG-UI for Streaming

Tambo AI adopts AG-UI as the foundational wire protocol for its v1 streaming-first API. The implementation spans three architectural layers: backend event generation, SSE transport, and React SDK consumption.

Backend Event Generation

In packages/backend/src/util/tambo-custom-events.ts, Tambo extends the standard AG-UI specification with custom event types prefixed with tambo.. These extensions handle component streaming, property updates, and input awaiting states. The backend translates LLM token streams, tool call stages, and component generation into AG-UI events, mixing standard types like TEXT_MESSAGE_CONTENT with custom events like tambo.component.start.

SSE Transport Layer

Events flow from the backend to the client via Server-Sent Events (SSE). The API endpoint streams text/event-stream content where each line contains a JSON-encoded AG-UI event. This transport method maintains a persistent connection ideal for real-time incremental updates while remaining compatible with standard HTTP infrastructure.

React SDK Consumption

The React SDK consumes these streams through two core utilities in react-sdk/src/v1/utils/. The handleEventStream function in stream-handler.ts converts the SDK's async iterable into typed AGUIEvent objects with optional debug logging. These events feed into streamReducer in event-accumulator.ts, an immutable state reducer that builds the thread state by processing each event type and updating React state accordingly.

Core Components in the Tambo AI Repository

Responsibility File Path Key Function/Export
Event source conversion react-sdk/src/v1/utils/stream-handler.ts handleEventStream
State accumulation react-sdk/src/v1/utils/event-accumulator.ts streamReducer
Custom event definitions packages/backend/src/util/tambo-custom-events.ts ComponentStartEvent, ComponentPropsDeltaEvent
Hook implementation react-sdk/src/v1/hooks/use-tambo-v1-send-message.ts useTamboV1SendMessage
Type definitions react-sdk/src/v1/types/event.ts TamboCustomEvent

Streaming Flow: From LLM to UI

The complete data flow follows these discrete stages:

  1. Run Initialization – The client calls POST /v1/threads/{id}/runs, triggering the backend to emit a RUN_STARTED event.

  2. Content Generation – As the LLM produces tokens, the backend emits TEXT_MESSAGE_CONTENT events containing text deltas. When the model invokes tools, the system emits TOOL_CALL_START, TOOL_CALL_ARGS, and TOOL_CALL_END events.

  3. Component Streaming – For UI component generation, Tambo emits custom events: tambo.component.start initializes the component, tambo.component.props_delta streams JSON-Patch updates to properties, and tambo.component.end finalizes the state.

  4. SSE Delivery – Each event travels via Server-Sent Events as data: { "type": "...", ... } JSON payloads.

  5. State Reduction – The React SDK's streamReducer processes incoming events immutably, appending text deltas to messages, updating tool call states, and merging component property patches into the thread state.

  6. UI Rendering – React hooks like useTamboV1SendMessage expose the accumulated state to components, enabling real-time rendering of streaming text, tool results, and interactive UI components.

Handling Custom AG-UI Events in Tambo

Tambo extends the standard AG-UI specification with seven custom event types defined in packages/backend/src/util/tambo-custom-events.ts:

Event Name Purpose Payload Structure
tambo.component.start Signals the beginning of a component tool call ComponentStartEventValue with messageId, componentId, componentName
tambo.component.props_delta Streams incremental property updates via JSON-Patch ComponentPropsDeltaEventValue with operations array and streamingStatus
tambo.component.state_delta Streams incremental state updates via JSON-Patch ComponentStateDeltaEventValue with operations array
tambo.component.end Finalizes component streaming with complete props/state ComponentEndEventValue with finalProps and finalState
tambo.run.awaiting_input Pauses the run pending client-side tool execution RunAwaitingInputEventValue with pendingToolCalls
tambo.message.parent Links messages to parent messages for MCP-style threading MessageParentEventValue with messageId and parentMessageId

The streamReducer in react-sdk/src/v1/utils/event-accumulator.ts handles these via a handleCustomEvent function that applies JSON-Patch operations to component state and manages the component lifecycle.

Practical Example: Consuming AG-UI Streams

The following React component demonstrates how to consume AG-UI events using Tambo's streaming utilities:

import { useEffect, useReducer } from "react";
import { streamReducer, createInitialState } from "@tambo-ai/react/v1";
import { handleEventStream } from "@tambo-ai/react/v1/utils/stream-handler";

export function ChatThread({ threadId, apiKey }) {
  const [state, dispatch] = useReducer(streamReducer, createInitialState());

  useEffect(() => {
    // Initialise the thread locally
    dispatch({ type: "INIT_THREAD", threadId });

    async function startRun() {
      const client = await getTamboClient({ apiKey });
      const stream = await client.threads.runs.run(threadId, {
        message: { 
          role: "user", 
          content: [{ type: "text", text: "Hello" }] 
        },
      });

      // Convert the SDK async iterable into typed AG-UI events
      for await (const ev of handleEventStream(stream, { debug: false })) {
        dispatch({ type: "EVENT", event: ev, threadId });
      }
    }

    startRun().catch(console.error);
  }, [threadId, apiKey]);

  const thread = state.threadMap[threadId]?.thread ?? { messages: [] };

  return (
    <div>
      {thread.messages.map((msg) => (
        <Message key={msg.id} data={msg} />
      ))}
    </div>
  );
}

In this implementation, handleEventStream from react-sdk/src/v1/utils/stream-handler.ts transforms the raw SDK stream into typed AGUIEvent objects, while streamReducer from react-sdk/src/v1/utils/event-accumulator.ts immutably updates the React state based on each event type.

Summary

  • The AG-UI protocol is an open-source, event-based wire format that represents AI interactions as typed events like RUN_STARTED, TEXT_MESSAGE_CONTENT, and TOOL_CALL_START.
  • Tambo AI uses AG-UI as the foundation for its v1 streaming API, transmitting events via Server-Sent Events from backend to client.
  • The React SDK consumes these streams through handleEventStream in react-sdk/src/v1/utils/stream-handler.ts and accumulates state via streamReducer in react-sdk/src/v1/utils/event-accumulator.ts.
  • Custom extensions in packages/backend/src/util/tambo-custom-events.ts enable streaming UI components with events like tambo.component.start and tambo.component.props_delta using JSON-Patch operations.
  • This architecture enables real-time, incremental UI updates where text, tool results, and interactive components stream to the user as they are generated.

Frequently Asked Questions

What is the AG-UI protocol?

The AG-UI protocol is an open-source event-based wire format defined by the @ag-ui/core SDK. It represents conversational AI interactions as a stream of strongly typed events—such as RUN_STARTED, TEXT_MESSAGE_CONTENT, and TOOL_CALL_END—rather than monolithic JSON responses, enabling fine-grained streaming and real-time UI updates.

How does Tambo AI use AG-UI for streaming?

Tambo AI implements AG-UI in its v1 API by translating LLM outputs into AG-UI events on the backend, transmitting them via Server-Sent Events (SSE), and consuming them in the React SDK. The handleEventStream utility in react-sdk/src/v1/utils/stream-handler.ts converts the SSE stream into typed events, while streamReducer in react-sdk/src/v1/utils/event-accumulator.ts updates React state incrementally.

What are Tambo's custom AG-UI events?

Tambo extends the standard AG-UI specification with custom events defined in packages/backend/src/util/tambo-custom-events.ts. These include tambo.component.start to initialize UI components, tambo.component.props_delta and tambo.component.state_delta to stream JSON-Patch updates to component properties, tambo.component.end to finalize rendering, and tambo.run.awaiting_input to pause execution for client-side tool calls.

Where is the AG-UI stream handling implemented in Tambo?

The primary AG-UI stream handling logic resides in two key files within the React SDK: react-sdk/src/v1/utils/stream-handler.ts contains the handleEventStream generator function that parses raw SSE data into AGUIEvent objects, and react-sdk/src/v1/utils/event-accumulator.ts contains the streamReducer that processes these events into immutable React state updates.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →