Tulip Logo IconTulip
Concepts

Chat

Stream agent output on the server and render composable chat UI on the client.

The chat domain has two halves:

  • chat/server: stream helpers for server procedures and route handlers
  • chat/client: composable React components for threads, messages, composers, and tool cards

Server streaming

import { streamAgentChatEventIterator } from "@tulip-systems/ai/chat/server";

return streamAgentChatEventIterator({
  agent: assistantAgent,
  messages: input.messages,
  abortSignal: signal,
});

Use streamAgentChat() when you need the lower-level stream result directly.

Thread composition

<ChatThread status={status}>
  <ChatThreadContent>
    {messages.map((message) => (
      <ChatThreadMessage key={message.id} message={message} toolRenderers={toolRenderers} />
    ))}
    <ChatThreadActivity />
  </ChatThreadContent>
</ChatThread>

Composer composition

<ChatComposer value={input} onValueChange={setInput} onSubmit={submitPrompt} status={status}>
  <ChatComposerInput placeholder="Ask something..." />
  <ChatComposerToolbar>
    <ChatComposerActions>
      <ChatComposerAttachmentButton />
    </ChatComposerActions>
    <ChatComposerSubmit />
  </ChatComposerToolbar>
</ChatComposer>

ChatComposer keeps typing available while the agent is responding. Submit is disabled whenever status !== "ready".

Message composition

ChatThreadMessage uses the lower-level message slots internally:

  • ChatMessage
  • ChatMessageContent
  • ChatMessageHeader
  • ChatMessageParts
  • ChatMessageFooter

Use the lower-level slots when an app needs custom message chrome.

Tool rendering

Pass ToolClientDefinition[] to ChatThreadMessage or ChatMessageParts:

<ChatThreadMessage message={message} toolRenderers={ASSISTANT_TOOL_CLIENTS} />

Unknown tools fall back to ToolCallCard.

On this page