Skip to main content

Architecture

This page describes how Baleybots fits together internally. Baleybots is a composition layer on AI SDK v7 — it adds goal-oriented processors, typed composition (pipeline, parallel, graph), and a rich streaming event model on top of AI SDK's models, streamText/generateObject, tools, and stopWhen.

Processable interface

Everything in Baleybots implements the Processable interface:

interface Processable<TInput, TOutput> {
process(input: TInput, options?: ProcessOptions): Promise<TOutput>;
getId(): string;
getBotNames(): string[];
subscribeToAll(options?): Subscription;
}

A single Baleybot, a pipeline(), a parallel() composition, and a ParallelMerge all implement this interface. Any composition can be used anywhere a single processor is expected.

Data flow

When you call bot.process(input), the request flows through these stages:

input
-> Baleybot.process()
-> streamWithAISDK() (adapters/ai-sdk/stream-helper.ts)
-> AI SDK (streamText / generateObject / generateText)
-> AI SDK provider (OpenAI, Anthropic, etc.)
-> BaleybotStreamEvent stream (provider-agnostic events)
-> segment reducer (segments/core-reducer.ts)
-> StreamSegment[] (UI-canonical representation)
-> final output (extracted from segments)

streamWithAISDK

All LLM calls route through streamWithAISDK() in adapters/ai-sdk/stream-helper.ts. This function:

  1. Resolves the model — from a model string, ModelConfig, or a direct AI SDK LanguageModel passthrough
  2. Translates multimodal inputs via translateToContentParts()
  3. Calls AI SDK streamText() or structured-output APIs (generateObject / Output helpers)
  4. Runs the tool loop with AI SDK stopWhen (default: stepCountIs(50))
  5. Transforms the AI SDK stream into BaleybotStreamEvent events

Structured output

Output.object(), Output.array(), and Output.choice() from @baleybots/core are thin wrappers over AI SDK structured output. They map to AI SDK Output helpers via toAISDKOutput(), giving you typed .process() results without reimplementing schema validation.

BaleybotStreamEvent

The streaming event format is provider-agnostic. All providers transform their native format into this unified type:

type BaleybotStreamEvent =
| { type: 'text_delta'; content: string }
| { type: 'tool_call_stream_start'; id: string; toolName: string }
| { type: 'tool_execution_output'; toolName: string; result: unknown }
| { type: 'error'; error: Error }
// ... more event types

Segment reducer

Stream events are accumulated into StreamSegment[] by the segment reducer in segments/core-reducer.ts. Each event updates the current segment state. For example, text_delta events append to the current TextSegment, while tool_call_stream_start creates a new ToolCallSegment.

Pipeline composition

Pipelines chain Processable instances sequentially. Each step receives the output of the previous step as input:

pipeline(researcher, writer, editor)

input -> researcher.process() -> writer.process() -> editor.process() -> output

Pipeline steps can also include conditional branching (when), loops (loop, recursiveLoop), parallel execution (parallel), and routing (route).

Multimodal inputs

Multimodal inputs (images, audio, video, files) are handled by the builder functions (text(), image(), audio(), etc.) which produce UnifiedMessageInput objects. These are translated to AI SDK content parts by translateToContentParts() in adapters/ai-sdk/multimodal-translator.ts before being sent to the provider.