Segments vs Messages
Baleybots uses two different representations for conversation data: segments (for UI rendering) and messages (for API calls). Understanding the difference is important when building applications.
Segments (UI-canonical)
Segments are the canonical representation for the UI. They are what you render in your chat interface.
type StreamSegment =
| UserSegment // type: 'user' — the user's message
| TextSegment // type: 'text' — assistant text response
| ToolCallSegment // type: 'tool_call' — tool invocation and result
| ReasoningSegment // type: 'reasoning' — model reasoning/thinking
| StructuredOutputSegment // type: 'structured_output' — JSON output
| ErrorSegment // type: 'error' — an error occurred
| DoneSegment // type: 'done' — turn complete
Segments are created by the segment reducer as stream events arrive. They contain everything needed to render the conversation, including streaming state (isStreaming), tool call arguments, results, and errors.
Rendering segments
function renderSegment(segment: StreamSegment) {
switch (segment.type) {
case 'user':
return <UserBubble>{segment.content}</UserBubble>;
case 'text':
return <AssistantBubble>{segment.content}</AssistantBubble>;
case 'tool_call':
return <ToolCallCard name={segment.name} result={segment.result} />;
default:
return null;
}
}
Messages (API-derived)
Messages are what get sent to the LLM provider. They follow the standard chat completion format:
interface ChatMessage {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string | ContentPart[];
}
Messages are derived from segments by the History class when preparing an API call. You rarely need to work with messages directly.
ToolCallSegment and collapsed types
In earlier versions, Baleybots had separate segment types for specific tool patterns:
SpawnAgentSegmentSequentialThinkingSegmentDSLPipelineSegment
These have been collapsed into ToolCallSegment. You can identify them by checking the name property (which holds the tool name):
function renderToolCall(segment: ToolCallSegment) {
switch (segment.name) {
case 'spawn_agent':
return <SpawnAgentCard segment={segment} />;
case 'sequential_thinking':
return <ThinkingCard segment={segment} />;
default:
return <GenericToolCard segment={segment} />;
}
}
For typed access to tool-specific data, use the typedToolCall helpers from @baleybots/core:
import { getSpawnAgentData, getSequentialThinkingData } from '@baleybots/core';
if (segment.name === 'spawn_agent') {
const data = getSpawnAgentData(segment);
// data.agentName, data.agentGoal, data.status, etc.
}
When to use which
| Representation | Use for |
|---|---|
Segments (StreamSegment[]) | Rendering the chat UI, persisting conversation state |
Messages (ChatMessage[]) | Sending to LLM APIs (handled internally by History) |
In most applications, you only work with segments. Messages are managed internally by History and Baleybot.process().