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 handlerschat/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:
ChatMessageChatMessageContentChatMessageHeaderChatMessagePartsChatMessageFooter
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.