Skip to main content

Chat History

The History class manages conversation history for Baleybots agents. It stores segments (the canonical UI representation) and derives API messages for LLM calls.

Creating a history

In-memory (default)

import { History } from '@baleybots/core';

const history = History.inMemory();

// With a message limit
const history = History.inMemory(50); // keeps last 50 messages

File-based

import { History } from '@baleybots/core';

const history = History.file('./chat-history.json');

Custom storage

Use the helpers for the common cases, or implement HistoryStorage yourself.

import {
History,
createHistoryStorage,
kvLikeHistoryStorage,
} from '@baleybots/core';

// Callback adapter
const storage = createHistoryStorage({
load: async () => db.getHistory(sessionId),
save: async (data) => db.putHistory(sessionId, data),
});

// KV / Redis / LocalStorage-shaped get/set (stores JSON StoredHistoryV2)
const kvStorage = kvLikeHistoryStorage({
key: `chat:${sessionId}`,
get: (k) => redis.get(k),
set: (k, v) => redis.set(k, v),
});

const history = new History(kvStorage);

Or implement the interface directly:

import { History } from '@baleybots/core';
import type { HistoryStorage, StoredHistoryV2 } from '@baleybots/core';

class RedisStorage implements HistoryStorage {
constructor(private redis: RedisClient, private key: string) {}

async load(): Promise<StoredHistoryV2 | null> {
const data = await this.redis.get(this.key);
return data ? JSON.parse(data) : null;
}

async save(history: StoredHistoryV2): Promise<void> {
await this.redis.set(this.key, JSON.stringify(history));
}
}

const history = new History(new RedisStorage(redis, 'chat:user-123'));

Flat-message migration (one-way)

If you have legacy { role, content } rows, migrate once into segments — do not keep flat rows as the restore source:

import {
migrateFlatMessagesToHistory,
projectSegmentsToFlatMessages,
} from '@baleybots/core';

const v2 = migrateFlatMessagesToHistory(legacyRows);
// persist v2 via HistoryStorage

// Lossy projection for analytics / legacy UIs only:
const flat = projectSegmentsToFlatMessages(v2.segments);

See also Chat with tools for createChatRoute on the edge.

Using history with bot.process()

Pass history via the conversationHistory option:

import { Baleybot, History } from '@baleybots/core';

const bot = Baleybot.create({
name: 'chat',
goal: 'Answer the user\'s questions using conversation context',
});

const history = History.inMemory();

// First message
const response1 = await bot.process('My name is Alice', {
conversationHistory: history,
});

// Second message -- the bot remembers the first
const response2 = await bot.process('What is my name?', {
conversationHistory: history,
});
// "Your name is Alice."

The history automatically records user messages and assistant responses as segments. On each call, it derives the API messages that get sent to the LLM.

Using history with useChat

In React, useChat manages history internally. You can provide a custom storage backend:

import { useChat } from '@baleybots/react';

const { segments, sendStreaming } = useChat({
model: 'openai|gpt-5.6-luna',
storage: new RedisStorage(redis, 'chat:user-123'),
});

Storage format

History is stored as a V2 format with segments interleaved:

interface StoredHistory {
version: 2;
segments: StreamSegment[];
}

Segments include UserSegment entries (the user's messages) interleaved with assistant segments (TextSegment, ToolCallSegment, etc.), preserving the full conversation flow.