UNPKG

@restnfeel/agentc-starter-kit

Version:

한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템

1,136 lines (1,133 loc) 43.6 kB
import { jsx } from 'react/jsx-runtime'; import { useReducer, useRef, useCallback, useEffect, createContext } from 'react'; // Enums for better type safety var ChatbotStatus; (function (ChatbotStatus) { ChatbotStatus["IDLE"] = "idle"; ChatbotStatus["LOADING"] = "loading"; ChatbotStatus["READY"] = "ready"; ChatbotStatus["ERROR"] = "error"; ChatbotStatus["DISCONNECTED"] = "disconnected"; })(ChatbotStatus || (ChatbotStatus = {})); var VectorStoreType; (function (VectorStoreType) { VectorStoreType["QDRANT"] = "qdrant"; VectorStoreType["PINECONE"] = "pinecone"; VectorStoreType["SUPABASE"] = "supabase"; })(VectorStoreType || (VectorStoreType = {})); var LLMProvider; (function (LLMProvider) { LLMProvider["OPENAI"] = "openai"; LLMProvider["ANTHROPIC"] = "anthropic"; LLMProvider["GOOGLE"] = "google"; LLMProvider["MISTRAL"] = "mistral"; })(LLMProvider || (LLMProvider = {})); // Default configuration const defaultChatbotConfig = { systemPrompt: "You are a helpful AI assistant with access to a knowledge base. Use the provided context to answer questions accurately and helpfully.", welcomeMessage: "Hello! I'm here to help you find information. What would you like to know?", suggestedQuestions: [ "What can you help me with?", "How does this system work?", "Tell me about your capabilities", ], maxConversationHistory: 10, enableRAG: true, ragSettings: { similarityThreshold: 0.7, maxRetrievedDocs: 5, chunkSize: 1000, chunkOverlap: 200, }, uiSettings: { theme: "default", language: "en", showTimestamps: true, enableTypingIndicator: true, }, }; // Initial state const initialState = { isInitialized: false, globalStatus: ChatbotStatus.IDLE, initializationProgress: 0, vectorStore: { status: ChatbotStatus.IDLE, isConnected: false, config: null, error: null, collections: [], documentCount: 0, }, llm: { status: ChatbotStatus.IDLE, isConnected: false, config: null, error: null, availableModels: [], currentModel: null, }, storage: { status: ChatbotStatus.IDLE, isConnected: false, config: null, error: null, uploadProgress: {}, }, config: defaultChatbotConfig, documents: [], conversations: [], currentConversation: null, isProcessing: false, lastError: null, }; // Reducer function function chatbotReducer(state, action) { var _a; switch (action.type) { case "INITIALIZE_START": return { ...state, globalStatus: ChatbotStatus.LOADING, initializationProgress: 0, lastError: null, }; case "INITIALIZE_SUCCESS": return { ...state, isInitialized: true, globalStatus: ChatbotStatus.READY, initializationProgress: 100, }; case "INITIALIZE_ERROR": return { ...state, globalStatus: ChatbotStatus.ERROR, lastError: action.payload, }; case "SET_INITIALIZATION_PROGRESS": return { ...state, initializationProgress: action.payload, }; // Vector store cases case "VECTOR_STORE_CONNECTING": return { ...state, vectorStore: { ...state.vectorStore, status: ChatbotStatus.LOADING, error: null, }, }; case "VECTOR_STORE_CONNECTED": return { ...state, vectorStore: { ...state.vectorStore, status: ChatbotStatus.READY, isConnected: true, collections: action.payload.collections, documentCount: action.payload.documentCount, }, }; case "VECTOR_STORE_ERROR": return { ...state, vectorStore: { ...state.vectorStore, status: ChatbotStatus.ERROR, isConnected: false, error: action.payload, }, }; case "VECTOR_STORE_CONFIG_UPDATE": return { ...state, vectorStore: { ...state.vectorStore, config: state.vectorStore.config ? { ...state.vectorStore.config, ...action.payload } : null, }, }; // LLM cases case "LLM_CONNECTING": return { ...state, llm: { ...state.llm, status: ChatbotStatus.LOADING, error: null, }, }; case "LLM_CONNECTED": return { ...state, llm: { ...state.llm, status: ChatbotStatus.READY, isConnected: true, availableModels: action.payload.availableModels, currentModel: action.payload.currentModel, }, }; case "LLM_ERROR": return { ...state, llm: { ...state.llm, status: ChatbotStatus.ERROR, isConnected: false, error: action.payload, }, }; case "LLM_CONFIG_UPDATE": return { ...state, llm: { ...state.llm, config: state.llm.config ? { ...state.llm.config, ...action.payload } : null, }, }; // Storage cases case "STORAGE_CONNECTING": return { ...state, storage: { ...state.storage, status: ChatbotStatus.LOADING, error: null, }, }; case "STORAGE_CONNECTED": return { ...state, storage: { ...state.storage, status: ChatbotStatus.READY, isConnected: true, }, }; case "STORAGE_ERROR": return { ...state, storage: { ...state.storage, status: ChatbotStatus.ERROR, isConnected: false, error: action.payload, }, }; case "STORAGE_UPLOAD_PROGRESS": return { ...state, storage: { ...state.storage, uploadProgress: { ...state.storage.uploadProgress, [action.payload.fileId]: action.payload.progress, }, }, }; case "STORAGE_CONFIG_UPDATE": return { ...state, storage: { ...state.storage, config: state.storage.config ? { ...state.storage.config, ...action.payload } : null, }, }; // Configuration cases case "CONFIG_UPDATE": return { ...state, config: { ...state.config, ...action.payload }, }; // Document cases case "DOCUMENTS_SET": return { ...state, documents: action.payload, }; case "DOCUMENT_ADD": return { ...state, documents: [...state.documents, action.payload], }; case "DOCUMENT_UPDATE": return { ...state, documents: state.documents.map((doc) => doc.id === action.payload.id ? { ...doc, ...action.payload.updates } : doc), }; case "DOCUMENT_REMOVE": return { ...state, documents: state.documents.filter((doc) => doc.id !== action.payload), }; // Conversation cases case "CONVERSATIONS_SET": return { ...state, conversations: action.payload, }; case "CONVERSATION_START": return { ...state, conversations: [action.payload, ...state.conversations], currentConversation: action.payload, }; case "CONVERSATION_SELECT": return { ...state, currentConversation: state.conversations.find((c) => c.id === action.payload) || null, }; case "CONVERSATION_MESSAGE_ADD": const updatedConversations = state.conversations.map((conv) => conv.id === action.payload.conversationId ? { ...conv, messages: [...conv.messages, action.payload.message], updatedAt: new Date(), } : conv); return { ...state, conversations: updatedConversations, currentConversation: ((_a = state.currentConversation) === null || _a === void 0 ? void 0 : _a.id) === action.payload.conversationId ? updatedConversations.find((c) => c.id === action.payload.conversationId) || null : state.currentConversation, }; // UI cases case "SET_PROCESSING": return { ...state, isProcessing: action.payload, }; case "CLEAR_ERROR": return { ...state, lastError: null, }; case "SET_ERROR": return { ...state, lastError: action.payload, }; default: return state; } } // Create context const ChatbotContext = createContext(undefined); // Provider component (basic structure - will be implemented in subsequent subtasks) function ChatbotProvider({ children, initialConfig = {}, autoInitialize = false, }) { const [state, dispatch] = useReducer(chatbotReducer, { ...initialState, config: { ...defaultChatbotConfig, ...initialConfig }, }); // Vector store instance reference const vectorStoreRef = useRef(null); // LLM instance reference const llmRef = useRef(null); // RAG chain instance reference const ragChainRef = useRef(null); // Storage instance reference const storageRef = useRef(null); // Initialization methods const initialize = useCallback(async () => { dispatch({ type: "INITIALIZE_START" }); dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: 0 }); try { // Clear any previous errors dispatch({ type: "CLEAR_ERROR" }); // Initialize based on configuration const config = state.config; let initProgress = 10; // Step 1: Initialize Vector Store (if enabled) if (config.enableRAG) { try { // Check if we have vector store configuration const vectorStoreConfig = state.vectorStore.config; if (vectorStoreConfig) { await initializeVectorStore(vectorStoreConfig); initProgress = 40; } else { console.warn("RAG is enabled but no vector store configuration found"); } } catch (error) { console.warn("Failed to initialize vector store:", error); // Continue with other initializations } } dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: initProgress }); // Step 2: Initialize LLM (required for basic functionality) try { const llmConfig = state.llm.config; if (llmConfig) { await initializeLLM(llmConfig); initProgress = Math.max(initProgress, 80); } else { throw new Error("LLM configuration is required"); } } catch (error) { console.error("Failed to initialize LLM:", error); throw error; // LLM is required, so fail initialization } dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: initProgress }); // Step 3: Initialize Storage (optional) try { const storageConfig = state.storage.config; if (storageConfig) { await initializeStorage(storageConfig); initProgress = 100; } else { console.warn("No storage configuration found, skipping storage initialization"); initProgress = 100; } } catch (error) { console.warn("Failed to initialize storage:", error); // Storage is optional, continue initProgress = 100; } dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: initProgress }); dispatch({ type: "INITIALIZE_SUCCESS" }); } catch (error) { const chatbotError = { code: "INITIALIZATION_ERROR", message: `Failed to initialize chatbot: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "INITIALIZE_ERROR", payload: chatbotError }); dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } }, [ state.config, state.vectorStore.config, state.llm.config, state.storage.config, ]); const reset = useCallback(async () => { try { dispatch({ type: "SET_PROCESSING", payload: true }); // Disconnect and clear all subsystems if (vectorStoreRef.current) { try { await vectorStoreRef.current.disconnect(); } catch (error) { console.warn("Failed to disconnect vector store:", error); } vectorStoreRef.current = null; } if (llmRef.current) { try { await llmRef.current.disconnect(); } catch (error) { console.warn("Failed to disconnect LLM:", error); } llmRef.current = null; } if (ragChainRef.current) { ragChainRef.current = null; } if (storageRef.current) { try { await storageRef.current.disconnect(); } catch (error) { console.warn("Failed to disconnect storage:", error); } storageRef.current = null; } // Reset state to initial values const resetState = { ...initialState, config: state.config, // Keep the configuration }; // Clear all state except config dispatch({ type: "INITIALIZE_START" }); // This will reset most of the state dispatch({ type: "CLEAR_ERROR" }); dispatch({ type: "SET_PROCESSING", payload: false }); } catch (error) { const chatbotError = { code: "RESET_ERROR", message: `Failed to reset chatbot: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } }, [state.config]); // Vector store methods const initializeVectorStore = useCallback(async (config) => { dispatch({ type: "VECTOR_STORE_CONNECTING" }); dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: 20 }); try { // Dynamic import to avoid bundling issues const { createVectorStore, VectorStoreUtils } = await import('../services/vectorStore.js'); // Validate configuration if (!VectorStoreUtils.validateConfig(config)) { throw new Error("Invalid vector store configuration"); } // Create vector store instance vectorStoreRef.current = createVectorStore(config); // Connect to vector store await vectorStoreRef.current.connect(); // Get initial data const collections = await vectorStoreRef.current.getCollections(); const documentCount = await vectorStoreRef.current.getDocumentCount(); dispatch({ type: "VECTOR_STORE_CONNECTED", payload: { collections, documentCount }, }); dispatch({ type: "VECTOR_STORE_CONFIG_UPDATE", payload: config }); dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: 40 }); } catch (error) { const chatbotError = { code: "VECTOR_STORE_INIT_ERROR", message: `Failed to initialize vector store: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "VECTOR_STORE_ERROR", payload: chatbotError }); dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } }, []); const addDocuments = useCallback(async (documents) => { if (!vectorStoreRef.current) { throw new Error("Vector store is not initialized"); } dispatch({ type: "SET_PROCESSING", payload: true }); try { // Dynamic import for utilities const { VectorStoreUtils } = await import('../services/vectorStore.js'); // Prepare documents with embeddings const preparedDocuments = await VectorStoreUtils.prepareDocumentsForStorage(documents); // Add to vector store await vectorStoreRef.current.addDocuments(preparedDocuments); // Update local state preparedDocuments.forEach((doc) => { dispatch({ type: "DOCUMENT_ADD", payload: doc }); }); // Update document count const documentCount = await vectorStoreRef.current.getDocumentCount(); dispatch({ type: "VECTOR_STORE_CONNECTED", payload: { collections: state.vectorStore.collections, documentCount, }, }); } catch (error) { const chatbotError = { code: "ADD_DOCUMENTS_ERROR", message: `Failed to add documents: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } finally { dispatch({ type: "SET_PROCESSING", payload: false }); } }, [state.vectorStore.collections]); const removeDocuments = useCallback(async (documentIds) => { if (!vectorStoreRef.current) { throw new Error("Vector store is not initialized"); } dispatch({ type: "SET_PROCESSING", payload: true }); try { // Remove from vector store await vectorStoreRef.current.removeDocuments(documentIds); // Update local state documentIds.forEach((id) => { dispatch({ type: "DOCUMENT_REMOVE", payload: id }); }); // Update document count const documentCount = await vectorStoreRef.current.getDocumentCount(); dispatch({ type: "VECTOR_STORE_CONNECTED", payload: { collections: state.vectorStore.collections, documentCount, }, }); } catch (error) { const chatbotError = { code: "REMOVE_DOCUMENTS_ERROR", message: `Failed to remove documents: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } finally { dispatch({ type: "SET_PROCESSING", payload: false }); } }, [state.vectorStore.collections]); const searchSimilarDocuments = useCallback(async (query, limit = 5) => { if (!vectorStoreRef.current) { throw new Error("Vector store is not initialized"); } try { const results = await vectorStoreRef.current.similaritySearch(query, limit); return results; } catch (error) { const chatbotError = { code: "SIMILARITY_SEARCH_ERROR", message: `Failed to search documents: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } }, []); // LLM methods const initializeLLM = useCallback(async (config) => { dispatch({ type: "LLM_CONNECTING" }); dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: 60 }); try { // Dynamic import to avoid bundling issues const { createLLM, LLMUtils, RAGChainService } = await import('../services/llmService.js'); // Validate configuration if (!LLMUtils.validateConfig(config)) { throw new Error("Invalid LLM configuration"); } // Create LLM instance llmRef.current = createLLM(config); // Connect to LLM await llmRef.current.connect(); // Get available models and current model const availableModels = await llmRef.current.listModels(); const currentModel = config.model; dispatch({ type: "LLM_CONNECTED", payload: { availableModels, currentModel }, }); dispatch({ type: "LLM_CONFIG_UPDATE", payload: config }); // Create RAG chain if vector store is available if (vectorStoreRef.current && llmRef.current) { ragChainRef.current = new RAGChainService(llmRef.current, vectorStoreRef.current); } dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: 80 }); } catch (error) { const chatbotError = { code: "LLM_INIT_ERROR", message: `Failed to initialize LLM: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "LLM_ERROR", payload: chatbotError }); dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } }, []); const generateResponse = useCallback(async (prompt, context) => { if (!llmRef.current) { throw new Error("LLM is not initialized"); } dispatch({ type: "SET_PROCESSING", payload: true }); try { const response = await llmRef.current.generateResponse(prompt, context); return response.content; } catch (error) { const chatbotError = { code: "LLM_GENERATE_ERROR", message: `Failed to generate response: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } finally { dispatch({ type: "SET_PROCESSING", payload: false }); } }, []); const generateRAGResponse = useCallback(async (query) => { if (!ragChainRef.current) { // Fallback to regular LLM if RAG chain is not available if (!llmRef.current) { throw new Error("Neither RAG chain nor LLM is initialized"); } dispatch({ type: "SET_PROCESSING", payload: true }); try { const response = await llmRef.current.generateResponse(query); return { response: response.content, sources: [] }; } catch (error) { const chatbotError = { code: "RAG_FALLBACK_ERROR", message: `Failed to generate response: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } finally { dispatch({ type: "SET_PROCESSING", payload: false }); } } dispatch({ type: "SET_PROCESSING", payload: true }); try { const ragResponse = await ragChainRef.current.generateRAGResponse(query, { maxRetrievedDocs: state.config.ragSettings.maxRetrievedDocs, similarityThreshold: state.config.ragSettings.similarityThreshold, }); return { response: ragResponse.response, sources: ragResponse.sources, }; } catch (error) { const chatbotError = { code: "RAG_GENERATE_ERROR", message: `Failed to generate RAG response: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } finally { dispatch({ type: "SET_PROCESSING", payload: false }); } }, [state.config.ragSettings]); // Storage methods const initializeStorage = useCallback(async (config) => { dispatch({ type: "STORAGE_CONNECTING" }); dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: 90 }); try { // Dynamic import to avoid bundling issues const { createStorage, StorageUtils } = await import('../services/storageService.js'); // Validate configuration if (!StorageUtils.validateConfig(config)) { throw new Error("Invalid storage configuration"); } // Create storage instance storageRef.current = createStorage(config); // Connect to storage await storageRef.current.connect(); dispatch({ type: "STORAGE_CONNECTED" }); dispatch({ type: "STORAGE_CONFIG_UPDATE", payload: config }); dispatch({ type: "SET_INITIALIZATION_PROGRESS", payload: 100 }); } catch (error) { const chatbotError = { code: "STORAGE_INIT_ERROR", message: `Failed to initialize storage: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "STORAGE_ERROR", payload: chatbotError }); dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } }, []); const uploadDocument = useCallback(async (file, metadata) => { if (!storageRef.current) { throw new Error("Storage is not initialized"); } dispatch({ type: "SET_PROCESSING", payload: true }); try { // Setup progress tracking const fileId = `upload_${Date.now()}_${Math.random() .toString(36) .substr(2, 9)}`; if (storageRef.current.setProgressCallback) { storageRef.current.setProgressCallback(fileId, (progress) => { dispatch({ type: "STORAGE_UPLOAD_PROGRESS", payload: { fileId: progress.fileId, progress: progress.progress }, }); }); } // Upload file const document = await storageRef.current.uploadFile(file, { description: metadata === null || metadata === void 0 ? void 0 : metadata.description, tags: metadata === null || metadata === void 0 ? void 0 : metadata.tags, extractText: true, }); // Add to local state dispatch({ type: "DOCUMENT_ADD", payload: document }); // Add to vector store if available and document has content if (vectorStoreRef.current && document.content) { try { await vectorStoreRef.current.addDocuments([document]); } catch (error) { console.warn("Failed to add document to vector store:", error); // Continue without vector store indexing } } // Clean up progress tracking if (storageRef.current.removeProgressCallback) { storageRef.current.removeProgressCallback(fileId); } return document; } catch (error) { const chatbotError = { code: "UPLOAD_DOCUMENT_ERROR", message: `Failed to upload document: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } finally { dispatch({ type: "SET_PROCESSING", payload: false }); } }, []); const downloadDocument = useCallback(async (documentId) => { if (!storageRef.current) { throw new Error("Storage is not initialized"); } try { const blob = await storageRef.current.downloadFile(documentId); return blob; } catch (error) { const chatbotError = { code: "DOWNLOAD_DOCUMENT_ERROR", message: `Failed to download document: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } }, []); const deleteDocument = useCallback(async (documentId) => { if (!storageRef.current) { throw new Error("Storage is not initialized"); } dispatch({ type: "SET_PROCESSING", payload: true }); try { // Remove from storage await storageRef.current.deleteFile(documentId); // Remove from vector store if available if (vectorStoreRef.current) { try { await vectorStoreRef.current.removeDocuments([documentId]); } catch (error) { console.warn("Failed to remove document from vector store:", error); // Continue with local removal } } // Remove from local state dispatch({ type: "DOCUMENT_REMOVE", payload: documentId }); } catch (error) { const chatbotError = { code: "DELETE_DOCUMENT_ERROR", message: `Failed to delete document: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } finally { dispatch({ type: "SET_PROCESSING", payload: false }); } }, []); const listDocuments = useCallback(async () => { if (!storageRef.current) { // Return local documents if storage is not initialized return state.documents; } try { const documents = await storageRef.current.listFiles(); // Update local state dispatch({ type: "DOCUMENTS_SET", payload: documents }); return documents; } catch (error) { const chatbotError = { code: "LIST_DOCUMENTS_ERROR", message: `Failed to list documents: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); // Return local documents as fallback return state.documents; } }, [state.documents]); // Configuration methods (placeholders) const updateConfig = useCallback(async (updates) => { dispatch({ type: "CONFIG_UPDATE", payload: updates }); }, []); const exportConfig = useCallback(() => { return state.config; }, [state.config]); const importConfig = useCallback(async (config) => { dispatch({ type: "CONFIG_UPDATE", payload: config }); }, []); // Conversation methods (placeholders) const startConversation = useCallback(async (title) => { // Implementation will be added in subtask 27.5 const conversation = { id: `conv_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, title: title || `Conversation ${new Date().toLocaleDateString()}`, messages: [], createdAt: new Date(), updatedAt: new Date(), }; dispatch({ type: "CONVERSATION_START", payload: conversation }); return conversation; }, []); const selectConversation = useCallback(async (conversationId) => { dispatch({ type: "CONVERSATION_SELECT", payload: conversationId }); }, []); const sendMessage = useCallback(async (content) => { if (!state.currentConversation) { throw new Error("No active conversation. Please start a conversation first."); } if (!llmRef.current) { throw new Error("LLM is not initialized"); } dispatch({ type: "SET_PROCESSING", payload: true }); try { // Create user message const userMessage = { id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, content, role: "user", timestamp: new Date(), }; // Add user message to conversation dispatch({ type: "CONVERSATION_MESSAGE_ADD", payload: { conversationId: state.currentConversation.id, message: userMessage, }, }); // Generate AI response let aiResponse; let retrievedDocs = []; if (state.config.enableRAG && ragChainRef.current) { // Use RAG for response generation try { const ragResult = await generateRAGResponse(content); aiResponse = ragResult.response; retrievedDocs = ragResult.sources; } catch (error) { console.warn("RAG generation failed, falling back to direct LLM:", error); aiResponse = await generateResponse(content); } } else { // Use direct LLM response aiResponse = await generateResponse(content); } // Create assistant message const assistantMessage = { id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, content: aiResponse, role: "assistant", timestamp: new Date(), metadata: { retrievedDocs: retrievedDocs.length > 0 ? retrievedDocs : undefined, confidence: retrievedDocs.length > 0 ? 0.8 : undefined, // Mock confidence }, }; // Add assistant message to conversation dispatch({ type: "CONVERSATION_MESSAGE_ADD", payload: { conversationId: state.currentConversation.id, message: assistantMessage, }, }); return assistantMessage; } catch (error) { const chatbotError = { code: "SEND_MESSAGE_ERROR", message: `Failed to send message: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } finally { dispatch({ type: "SET_PROCESSING", payload: false }); } }, [ state.currentConversation, state.config.enableRAG, llmRef.current, ragChainRef.current, generateRAGResponse, generateResponse, ]); const getConversationHistory = useCallback(async (conversationId) => { var _a; const targetId = conversationId || ((_a = state.currentConversation) === null || _a === void 0 ? void 0 : _a.id); if (!targetId) return []; const conversation = state.conversations.find((c) => c.id === targetId); return (conversation === null || conversation === void 0 ? void 0 : conversation.messages) || []; }, [state.conversations, state.currentConversation]); const deleteConversation = useCallback(async (conversationId) => { var _a; try { // Check if the conversation being deleted is the current one if (((_a = state.currentConversation) === null || _a === void 0 ? void 0 : _a.id) === conversationId) { // Clear current conversation dispatch({ type: "CONVERSATION_SELECT", payload: "" }); } // Remove from conversations list const updatedConversations = state.conversations.filter((c) => c.id !== conversationId); dispatch({ type: "CONVERSATIONS_SET", payload: updatedConversations }); } catch (error) { const chatbotError = { code: "DELETE_CONVERSATION_ERROR", message: `Failed to delete conversation: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); throw error; } }, [state.conversations, state.currentConversation]); // Utility methods const getStatus = useCallback(() => { return { overall: state.globalStatus, subsystems: { vectorStore: state.vectorStore.status, llm: state.llm.status, storage: state.storage.status, }, }; }, [state]); const clearError = useCallback(() => { dispatch({ type: "CLEAR_ERROR" }); }, []); const healthCheck = useCallback(async () => { const results = { vectorStore: false, llm: false, storage: false, }; try { // Check vector store health if (vectorStoreRef.current) { try { await vectorStoreRef.current.healthCheck(); results.vectorStore = true; } catch (error) { console.warn("Vector store health check failed:", error); } } // Check LLM health if (llmRef.current) { try { await llmRef.current.healthCheck(); results.llm = true; } catch (error) { console.warn("LLM health check failed:", error); } } // Check storage health if (storageRef.current) { try { await storageRef.current.healthCheck(); results.storage = true; } catch (error) { console.warn("Storage health check failed:", error); } } return results; } catch (error) { const chatbotError = { code: "HEALTH_CHECK_ERROR", message: `Health check failed: ${error instanceof Error ? error.message : "Unknown error"}`, details: error, timestamp: new Date(), }; dispatch({ type: "SET_ERROR", payload: chatbotError }); return results; // Return partial results even on error } }, []); // Auto-initialize if enabled useEffect(() => { if (autoInitialize && !state.isInitialized) { initialize(); } }, [autoInitialize, state.isInitialized, initialize]); // Context value const contextValue = { ...state, initialize, reset, initializeVectorStore, addDocuments, removeDocuments, searchSimilarDocuments, initializeLLM, generateResponse, generateRAGResponse, initializeStorage, uploadDocument, downloadDocument, deleteDocument, listDocuments, updateConfig, exportConfig, importConfig, startConversation, selectConversation, sendMessage, getConversationHistory, deleteConversation, getStatus, clearError, healthCheck, }; return (jsx(ChatbotContext.Provider, { value: contextValue, children: children })); } export { ChatbotContext, ChatbotProvider, ChatbotStatus, LLMProvider, VectorStoreType, chatbotReducer, ChatbotProvider as default, defaultChatbotConfig }; //# sourceMappingURL=ChatbotContext.js.map