Skip to main content

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:

  • SpawnAgentSegment
  • SequentialThinkingSegment
  • DSLPipelineSegment

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

RepresentationUse 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().