@restnfeel/agentc-starter-kit
Version:
한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템
1,136 lines (1,133 loc) • 43.6 kB
JavaScript
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