123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- import { createStorage } from '../base/base';
- import { StorageEnum } from '../base/enums';
- import type { ChatSession, ChatMessage, ChatHistoryStorage, Message, ChatSessionMetadata } from './types';
- // Key for storing chat session metadata
- const CHAT_SESSIONS_META_KEY = 'chat_sessions_meta';
- // Create storage for session metadata
- const chatSessionsMetaStorage = createStorage<ChatSessionMetadata[]>(CHAT_SESSIONS_META_KEY, [], {
- storageEnum: StorageEnum.Local,
- liveUpdate: true,
- });
- // Helper function to get storage key for a specific session's messages
- const getSessionMessagesKey = (sessionId: string) => `chat_messages_${sessionId}`;
- // Helper function to create storage for a specific session's messages
- const getSessionMessagesStorage = (sessionId: string) => {
- return createStorage<ChatMessage[]>(getSessionMessagesKey(sessionId), [], {
- storageEnum: StorageEnum.Local,
- liveUpdate: true,
- });
- };
- // Helper function to get current timestamp in milliseconds
- const getCurrentTimestamp = (): number => Date.now();
- /**
- * Creates a chat history storage instance with optimized operations
- */
- export function createChatHistoryStorage(): ChatHistoryStorage {
- return {
- getAllSessions: async (): Promise<ChatSession[]> => {
- const sessionsMeta = await chatSessionsMetaStorage.get();
- // For listing purposes, we can return sessions without loading messages
- // This makes the list view very fast
- return sessionsMeta.map(meta => ({
- ...meta,
- messages: [], // Empty array as we don't load messages for listing
- }));
- },
- clearAllSessions: async (): Promise<void> => {
- const sessionsMeta = await chatSessionsMetaStorage.get();
- for (const sessionMeta of sessionsMeta) {
- const messagesStorage = getSessionMessagesStorage(sessionMeta.id);
- await messagesStorage.set([]);
- }
- await chatSessionsMetaStorage.set([]);
- },
- // Get session metadata without messages (for UI listing)
- getSessionsMetadata: async (): Promise<ChatSessionMetadata[]> => {
- return await chatSessionsMetaStorage.get();
- },
- getSession: async (sessionId: string): Promise<ChatSession | null> => {
- const sessionsMeta = await chatSessionsMetaStorage.get();
- const sessionMeta = sessionsMeta.find(session => session.id === sessionId);
- if (!sessionMeta) return null;
- // Load messages only when a specific session is requested
- const messagesStorage = getSessionMessagesStorage(sessionId);
- const messages = await messagesStorage.get();
- return {
- ...sessionMeta,
- messages,
- };
- },
- createSession: async (title: string): Promise<ChatSession> => {
- const newSessionId = crypto.randomUUID();
- const currentTime = getCurrentTimestamp();
- const newSessionMeta: ChatSessionMetadata = {
- id: newSessionId,
- title,
- createdAt: currentTime,
- updatedAt: currentTime,
- messageCount: 0,
- };
- // Create empty messages array for the new session
- const messagesStorage = getSessionMessagesStorage(newSessionId);
- await messagesStorage.set([]);
- // Add session metadata to the index
- await chatSessionsMetaStorage.set(prevSessions => [...prevSessions, newSessionMeta]);
- return {
- ...newSessionMeta,
- messages: [],
- };
- },
- updateTitle: async (sessionId: string, title: string): Promise<ChatSessionMetadata> => {
- let updatedSessionMeta: ChatSessionMetadata | undefined;
- // Update the title and capture the updated session in a single pass
- await chatSessionsMetaStorage.set(prevSessions => {
- return prevSessions.map(session => {
- if (session.id === sessionId) {
- // Create the updated session
- const updated = {
- ...session,
- title,
- updatedAt: getCurrentTimestamp(),
- };
- // Capture it for return value
- updatedSessionMeta = updated;
- return updated;
- }
- return session;
- });
- });
- // Check if we found and updated the session
- if (!updatedSessionMeta) {
- throw new Error('Session not found');
- }
- // Return the already captured metadata
- return updatedSessionMeta;
- },
- deleteSession: async (sessionId: string): Promise<void> => {
- // Remove session from metadata
- await chatSessionsMetaStorage.set(prevSessions => prevSessions.filter(session => session.id !== sessionId));
- // Remove the session's messages
- const messagesStorage = getSessionMessagesStorage(sessionId);
- await messagesStorage.set([]);
- },
- addMessage: async (sessionId: string, message: Message): Promise<ChatMessage> => {
- const newMessage: ChatMessage = {
- ...message,
- id: crypto.randomUUID(),
- };
- // First check if session exists and update metadata in a single operation
- let sessionFound = false;
- await chatSessionsMetaStorage.set(prevSessions => {
- return prevSessions.map(session => {
- if (session.id === sessionId) {
- sessionFound = true;
- return {
- ...session,
- updatedAt: getCurrentTimestamp(),
- messageCount: session.messageCount + 1,
- };
- }
- return session;
- });
- });
- // Throw error if session wasn't found
- if (!sessionFound) {
- throw new Error(`Session with ID ${sessionId} not found`);
- }
- // Only add the message if the session exists
- const messagesStorage = getSessionMessagesStorage(sessionId);
- await messagesStorage.set(prevMessages => [...prevMessages, newMessage]);
- return newMessage;
- },
- deleteMessage: async (sessionId: string, messageId: string): Promise<void> => {
- // Get the messages storage for this session
- const messagesStorage = getSessionMessagesStorage(sessionId);
- // Get current messages to calculate the new count
- const currentMessages = await messagesStorage.get();
- const messageToDelete = currentMessages.find(msg => msg.id === messageId);
- if (!messageToDelete) return; // Message not found
- // Remove the message directly from the messages storage
- await messagesStorage.set(prevMessages => prevMessages.filter(msg => msg.id !== messageId));
- // Update the session's metadata (updatedAt timestamp and messageCount)
- await chatSessionsMetaStorage.set(prevSessions => {
- return prevSessions.map(session => {
- if (session.id === sessionId) {
- return {
- ...session,
- updatedAt: getCurrentTimestamp(),
- messageCount: Math.max(0, session.messageCount - 1),
- };
- }
- return session;
- });
- });
- },
- };
- }
- // Export the storage instance for direct use
- export const chatHistoryStore = createChatHistoryStorage();
|