@restnfeel/agentc-starter-kit
Version:
한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템
486 lines (424 loc) • 11.9 kB
text/typescript
/**
* @fileoverview Unified API wrapper for the RAG chatbot system
* @module core/api/ChatbotAPI
*/
import type {
ChatbotConfig,
Document,
Conversation,
ConversationMessage,
LLMConfig,
VectorStoreConfig,
StorageConfig,
ChatbotError,
} from "../contexts/ChatbotContext";
import type {
ApiResponse,
InitializationOptions,
SearchOptions,
FileUploadConstraints,
} from "../../types/api";
import { createChatbotError } from "../../utils";
/**
* Extended configuration interface for API initialization
*/
export interface ChatbotAPIConfig extends Partial<ChatbotConfig> {
vectorStore?: VectorStoreConfig;
llm?: LLMConfig;
storage?: StorageConfig;
}
/**
* Unified API class for the RAG chatbot system
* Provides a clean, consistent interface for all chatbot operations
*/
export class ChatbotAPI {
private initialized = false;
private config: ChatbotAPIConfig | null = null;
private errorHandlers: Array<(error: ChatbotError) => void> = [];
/**
* Initialize the chatbot system
*/
async initialize(
config: ChatbotAPIConfig,
options?: InitializationOptions
): Promise<ApiResponse<void>> {
try {
this.validateConfig(config);
this.config = config;
// Initialize components based on options
const results = await Promise.allSettled([
!options?.skipVectorStore
? this.initializeVectorStore()
: Promise.resolve(),
!options?.skipLLM ? this.initializeLLM() : Promise.resolve(),
!options?.skipStorage ? this.initializeStorage() : Promise.resolve(),
]);
// Check for failures
const failures = results.filter((result) => result.status === "rejected");
if (failures.length > 0 && !options?.retryOnFailure) {
throw new Error(
`Initialization failed: ${failures.length} components failed`
);
}
this.initialized = true;
return {
success: true,
data: undefined,
timestamp: new Date().toISOString(),
};
} catch (error) {
const chatbotError = this.createError(
"INITIALIZATION_FAILED",
"Failed to initialize chatbot system",
{ originalError: error, config }
);
this.handleError(chatbotError);
return {
success: false,
error: chatbotError.message,
details: chatbotError.details,
timestamp: new Date().toISOString(),
};
}
}
/**
* Reset the chatbot system
*/
async reset(): Promise<ApiResponse<void>> {
try {
// Reset all components
await Promise.allSettled([
this.resetVectorStore(),
this.resetLLM(),
this.resetStorage(),
]);
this.initialized = false;
this.config = null;
return {
success: true,
data: undefined,
timestamp: new Date().toISOString(),
};
} catch (error) {
const chatbotError = this.createError(
"RESET_FAILED",
"Failed to reset chatbot system",
{ originalError: error }
);
this.handleError(chatbotError);
return {
success: false,
error: chatbotError.message,
details: chatbotError.details,
timestamp: new Date().toISOString(),
};
}
}
/**
* Send a message and get response
*/
async sendMessage(
content: string,
conversationId: string,
options?: { useRAG?: boolean; context?: any }
): Promise<ApiResponse<ConversationMessage>> {
try {
this.ensureInitialized();
const message: ConversationMessage = {
id: this.generateId("msg"),
role: "user",
content,
timestamp: new Date(),
};
// Process message through the system
const response = options?.useRAG
? await this.processRAGMessage(message, conversationId)
: await this.processDirectMessage(message, conversationId);
return {
success: true,
data: response,
timestamp: new Date().toISOString(),
};
} catch (error) {
const chatbotError = this.createError(
"MESSAGE_PROCESSING_FAILED",
"Failed to process message",
{ originalError: error, content, conversationId }
);
this.handleError(chatbotError);
return {
success: false,
error: chatbotError.message,
details: chatbotError.details,
timestamp: new Date().toISOString(),
};
}
}
/**
* Upload documents to the knowledge base
*/
async uploadDocuments(
files: File[],
constraints?: FileUploadConstraints,
onProgress?: (progress: number) => void
): Promise<ApiResponse<Document[]>> {
try {
this.ensureInitialized();
// Validate files
this.validateFiles(files, constraints);
const documents: Document[] = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
onProgress?.((i / files.length) * 100);
const document = await this.processFileUpload(file);
documents.push(document);
}
onProgress?.(100);
return {
success: true,
data: documents,
timestamp: new Date().toISOString(),
};
} catch (error) {
const chatbotError = this.createError(
"DOCUMENT_UPLOAD_FAILED",
"Failed to upload documents",
{ originalError: error, fileCount: files.length }
);
this.handleError(chatbotError);
return {
success: false,
error: chatbotError.message,
details: chatbotError.details,
timestamp: new Date().toISOString(),
};
}
}
/**
* Search documents in the knowledge base
*/
async searchDocuments(
query: string,
options?: SearchOptions
): Promise<ApiResponse<Document[]>> {
try {
this.ensureInitialized();
const startTime = Date.now();
const results = await this.performSearch(query, options);
const searchTime = Date.now() - startTime;
return {
success: true,
data: results,
timestamp: new Date().toISOString(),
};
} catch (error) {
const chatbotError = this.createError(
"SEARCH_FAILED",
"Failed to search documents",
{ originalError: error, query, options }
);
this.handleError(chatbotError);
return {
success: false,
error: chatbotError.message,
details: chatbotError.details,
timestamp: new Date().toISOString(),
};
}
}
/**
* Get system health status
*/
async getHealthStatus(): Promise<ApiResponse<any>> {
try {
const health = await this.checkSystemHealth();
return {
success: true,
data: health,
timestamp: new Date().toISOString(),
};
} catch (error) {
const chatbotError = this.createError(
"HEALTH_CHECK_FAILED",
"Failed to check system health",
{ originalError: error }
);
this.handleError(chatbotError);
return {
success: false,
error: chatbotError.message,
details: chatbotError.details,
timestamp: new Date().toISOString(),
};
}
}
/**
* Add error handler
*/
onError(handler: (error: ChatbotError) => void): () => void {
this.errorHandlers.push(handler);
// Return unsubscribe function
return () => {
const index = this.errorHandlers.indexOf(handler);
if (index > -1) {
this.errorHandlers.splice(index, 1);
}
};
}
/**
* Get current configuration
*/
getConfig(): ChatbotAPIConfig | null {
return this.config;
}
/**
* Check if system is initialized
*/
isInitialized(): boolean {
return this.initialized;
}
// Private helper methods
private ensureInitialized(): void {
if (!this.initialized) {
throw new Error(
"Chatbot system is not initialized. Call initialize() first."
);
}
}
private validateConfig(config: ChatbotAPIConfig): void {
if (!config) {
throw new Error("Configuration is required");
}
// Add more specific validation logic here
if (!config.llm) {
throw new Error("LLM configuration is required");
}
}
private validateFiles(
files: File[],
constraints?: FileUploadConstraints
): void {
if (!files || files.length === 0) {
throw new Error("No files provided");
}
if (constraints) {
for (const file of files) {
if (file.size > constraints.maxSize) {
throw new Error(
`File ${file.name} exceeds maximum size of ${constraints.maxSize} bytes`
);
}
if (
constraints.allowedTypes &&
!constraints.allowedTypes.includes(file.type)
) {
throw new Error(`File type ${file.type} is not allowed`);
}
}
}
}
private createError(
code: string,
message: string,
details?: any
): ChatbotError {
return createChatbotError(code, message, details);
}
private handleError(error: ChatbotError): void {
this.errorHandlers.forEach((handler) => {
try {
handler(error);
} catch (handlerError) {
console.error("Error in error handler:", handlerError);
}
});
}
private generateId(prefix: string): string {
return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
// Placeholder methods for implementation
private async initializeVectorStore(): Promise<void> {
// Implementation will be connected to existing vector store service
console.log("Initializing vector store...");
}
private async initializeLLM(): Promise<void> {
// Implementation will be connected to existing LLM service
console.log("Initializing LLM...");
}
private async initializeStorage(): Promise<void> {
// Implementation will be connected to existing storage service
console.log("Initializing storage...");
}
private async resetVectorStore(): Promise<void> {
console.log("Resetting vector store...");
}
private async resetLLM(): Promise<void> {
console.log("Resetting LLM...");
}
private async resetStorage(): Promise<void> {
console.log("Resetting storage...");
}
private async processRAGMessage(
message: ConversationMessage,
conversationId: string
): Promise<ConversationMessage> {
// Implementation will use existing RAG chain service
return {
id: this.generateId("msg"),
role: "assistant",
content: "RAG response placeholder",
timestamp: new Date(),
};
}
private async processDirectMessage(
message: ConversationMessage,
conversationId: string
): Promise<ConversationMessage> {
// Implementation will use direct LLM service
return {
id: this.generateId("msg"),
role: "assistant",
content: "Direct response placeholder",
timestamp: new Date(),
};
}
private async processFileUpload(file: File): Promise<Document> {
// Implementation will use existing storage service
return {
id: this.generateId("doc"),
title: file.name,
content: "",
metadata: {
source: file.name,
uploadedAt: new Date(),
size: file.size,
type: file.type,
description: `Uploaded file: ${file.name}`,
},
status: "processing",
};
}
private async performSearch(
query: string,
options?: SearchOptions
): Promise<Document[]> {
// Implementation will use existing vector store search
return [];
}
private async checkSystemHealth(): Promise<any> {
// Implementation will check all components
return {
overall: true,
components: {
vectorStore: true,
llm: true,
storage: true,
},
};
}
}
/**
* Default singleton instance
*/
export const chatbotAPI = new ChatbotAPI();
export default ChatbotAPI;