Chat messages → BubbleList adapters
This guide explains how to turn messages from common OpenAI, OpenClaw, and Ollama requests/sessions into MessageBubbleData for BubbleList.
Implementation lives in src/Bubble/OpenAIMessageBubble/. It does not depend on vendor npm SDKs; types are defined in-repo.
Table of contents
- Design overview
- OpenAI Chat Completions
- OpenClaw session / transcript
- Ollama /api/chat
- Streaming (SSE) and stable ids
- API cheat sheet
- Example with BubbleList
Design overview
- Goal: produce
MessageBubbleData[]for thebubbleListprop onBubbleList. - Two ways in:
- Hooks: recompute when React state changes (
useMemoinside). - Pure functions: tests, non-React usage, or custom memoization.
- Hooks: recompute when React state changes (
- Pipeline: OpenClaw and Ollama messages are normalized to an OpenAI-compatible shape, then share the same OpenAI mapper to avoid duplicated logic.
OpenAI Chat Completions
Typical chat.completions messages: role + content (string or parts), optional tool_calls, tool, function, etc.
Default id: msg.id ?? \openai-msg-${index}`` (content is not hashed, to keep streaming stable).
Notable mapOptions: baseTime / timeStepMs, getMessageId, toolRoleAs (RoleType for tool / function rows), appendToolCallsToContent, preserveRawInExtra (extra.openai.raw), bumpUpdateAtOnLastMessage.
Embedded demo: “OpenAI messages - useOpenAIMessageBubbleData” under Bubble.
OpenClaw session / transcript
Extra fields on top of the OpenAI-like shape:
timestamp(ms): optionally applied tocreateAt/updateAt(useOpenClawTimestamps, default on).toolResult: normalized to an OpenAItoolrow; original role is available underextra.openclaw.raw(preserveOpenClawRawInExtra, default on).
Ollama /api/chat
Aligned with Ollama Chat API ChatMessage: role is system | user | assistant | tool, content is a string; optional images (base64 list), tool_calls, thinking, etc.
thinking and image-count hints are appended as readable placeholders (toggle with appendThinkingToContent, appendImagesPlaceholder). Default id: msg.id ?? \ollama-msg-${index}`; originals under extra.ollama.raw (preserveOllamaRawInExtra`, default on).
Streaming (SSE) and stable ids
The adapters do not parse SSE; they only consume the messages array you keep in state. On each chunk, update state (for example grow the last assistant content).
Avoid using a hash of content as the message id, or keys will change every token and the list will remount. Prefer:
- A stable
idassigned when the turn starts (server- or client-side), or - The default index-based id (index stays fixed while streaming that row).
API cheat sheet
Import from the package entry (also re-exported via ./Bubble):
Per-message overrides: all three paths support mapMessage (signature OpenAIMessagesMapMessage on the OpenAI-shaped step); return an immutable update of draft.
Example with BubbleList
Changelog
See Bubble in changelog.en-US.md / changelog.zh-CN.md for v2.30.22 and later.
