AbsoluteJS

Streaming & Protocol

AI responses stream over WebSocket using a typed message protocol. The client hooks handle parsing and state management automatically, but understanding the protocol is useful for custom integrations.

#Client Messages

Client → Server
TypePurposeKey Fields
messageSend a message with optional file attachmentscontent, attachments?
cancelStop a streaming responseconversationId
branchFork conversation from a specific messagemessageId, content
TS
// Client → Server messages (sent over WebSocket)

type AIMessageRequest = {
  type: 'message';
  content: string;
  conversationId?: string;
  attachments?: AIAttachment[];
};

type AICancelRequest = {
  type: 'cancel';
  conversationId: string;
};

type AIBranchRequest = {
  type: 'branch';
  messageId: string;
  content: string;
  conversationId: string;
};

#Server Messages

Server → Client
chunkStreamed text content fragment
thinkingExtended reasoning token stream
tool_statusTool execution running or complete with result
imageGenerated image data (may be partial during streaming)
completeResponse finished with model name, duration, and token usage
errorError message from the provider or plugin
TS
// Server → Client messages (received over WebSocket)

type AIChunkMessage = {
  type: 'chunk';
  content: string;
  messageId: string;
  conversationId: string;
};

type AIThinkingMessage = {
  type: 'thinking';
  content: string;
  messageId: string;
  conversationId: string;
};

type AIToolStatusMessage = {
  type: 'tool_status';
  name: string;
  status: 'running' | 'complete';
  input?: unknown;
  result?: string;
  messageId: string;
  conversationId: string;
};

type AIImageMessage = {
  type: 'image';
  data: string;
  format: string;
  isPartial: boolean;
  revisedPrompt?: string;
  imageId?: string;
  messageId: string;
  conversationId: string;
};

type AICompleteMessage = {
  type: 'complete';
  durationMs?: number;
  messageId: string;
  model?: string;
  conversationId: string;
  usage?: AIUsage;
};

type AIErrorMessage = {
  type: 'error';
  message: string;
  messageId?: string;
  conversationId?: string;
};

#AIMessage Type

On the client, streamed data accumulates into AIMessage objects. This is the shape returned by all framework hooks in the messages array.

TS
type AIMessage = {
  id: string;
  role: 'user' | 'assistant' | 'system';
  content: string;
  conversationId: string;
  parentId?: string;
  attachments?: AIAttachment[];
  thinking?: string;
  toolCalls?: AIToolCall[];
  images?: AIImageData[];
  isStreaming?: boolean;
  model?: string;
  usage?: AIUsage;
  durationMs?: number;
  timestamp: number;
};

type AIUsage = {
  inputTokens: number;
  outputTokens: number;
};

type AIAttachment = {
  data: string; // base64-encoded
  media_type:
    | 'image/png'
    | 'image/jpeg'
    | 'image/gif'
    | 'image/webp'
    | 'application/pdf';
  name?: string;
};

#Extended Thinking

Reasoning models can stream their thought process alongside the final response. Enable it with a token budget. Thinking content arrives as thinking messages and is available on AIMessage.thinking.

ModelProvider
Claude Opus 4.6Anthropic
Claude Sonnet 4.6Anthropic
o3OpenAI
o4-miniOpenAI
DeepSeek ReasonerDeepSeek

Enable statically for all requests:

TS
new Elysia().use(
  aiChat({
    provider: getProvider,
    thinking: { budgetTokens: 10000 },
  })
);

Or dynamically per model:

TS
new Elysia().use(
  aiChat({
    provider: getProvider,
    thinking: (providerName, model) => {
      const THINKING_MODELS = new Set([
        'claude-opus-4-6',
        'claude-sonnet-4-6',
        'o3',
        'o4-mini',
        'deepseek-reasoner',
      ]);
      return THINKING_MODELS.has(model)
        ? { budgetTokens: 8000 }
        : undefined;
    },
  })
);

#Connection Management

The client hooks manage the WebSocket connection lifecycle automatically. You get these behaviors out of the box:

Auto-Reconnect
Exponential backoff on disconnect
Keep-Alive
Ping interval every 30s by default
Max Retries
Up to 60 reconnect attempts
Type Validation
All messages validated with type guards
Cleanup
Graceful teardown on component unmount
Backpressure
1MB threshold for stream buffering
TS
type AIConnectionOptions = {
  protocols?: string[];
  reconnect?: boolean;
  pingInterval?: number;
  maxReconnectAttempts?: number;
};