arena-learning / studyArena /lib /utils /chat-storage.ts
Nitish kumar
Upload folder using huggingface_hub
c20f20c verified
/**
* Chat Storage - Persist chat sessions to IndexedDB
*
* Independent from stage/scene storage cycle.
* Handles serialization, truncation, and batch writes.
*/
import type { ChatSession, ChatMessageMetadata, SessionStatus } from '@/lib/types/chat';
import type { UIMessage } from 'ai';
import { db, type ChatSessionRecord } from './database';
/** Maximum messages per session to avoid IndexedDB bloat */
const MAX_MESSAGES_PER_SESSION = 200;
/**
* Save chat sessions for a stage to IndexedDB.
* - Active sessions are saved as 'interrupted' (streaming context lost on refresh)
* - pendingToolCalls are cleared (runtime-only state)
* - Messages are truncated to MAX_MESSAGES_PER_SESSION
*/
export async function saveChatSessions(stageId: string, sessions: ChatSession[]): Promise<void> {
if (!sessions || sessions.length === 0) {
// Delete all sessions for this stage if empty
await db.chatSessions.where('stageId').equals(stageId).delete();
return;
}
const records: ChatSessionRecord[] = sessions.map((session) => ({
id: session.id,
stageId,
type: session.type,
title: session.title,
// Mark active sessions as interrupted (streaming context lost on refresh)
status: (session.status === 'active' ? 'interrupted' : session.status) as SessionStatus,
// Truncate messages and strip non-serializable data
messages: session.messages.slice(-MAX_MESSAGES_PER_SESSION),
config: session.config,
toolCalls: session.toolCalls,
pendingToolCalls: [], // Clear runtime state
createdAt: session.createdAt,
updatedAt: session.updatedAt,
sceneId: session.sceneId,
lastActionIndex: session.lastActionIndex,
}));
await db.transaction('rw', db.chatSessions, async () => {
// Delete old sessions for this stage, then bulk insert new ones
await db.chatSessions.where('stageId').equals(stageId).delete();
await db.chatSessions.bulkPut(records);
});
}
/**
* Load chat sessions for a stage from IndexedDB.
* Returns sessions sorted by createdAt.
*/
export async function loadChatSessions(stageId: string): Promise<ChatSession[]> {
const records = await db.chatSessions.where('stageId').equals(stageId).sortBy('createdAt');
return records.map((record) => ({
id: record.id,
type: record.type,
title: record.title,
status: record.status,
messages: record.messages as UIMessage<ChatMessageMetadata>[],
config: record.config,
toolCalls: record.toolCalls,
pendingToolCalls: record.pendingToolCalls,
createdAt: record.createdAt,
updatedAt: record.updatedAt,
sceneId: record.sceneId,
lastActionIndex: record.lastActionIndex,
}));
}
/**
* Delete all chat sessions for a stage.
*/
export async function deleteChatSessions(stageId: string): Promise<void> {
await db.chatSessions.where('stageId').equals(stageId).delete();
}