UNPKG

@yk1028-test/ai-chat-supporter

Version:

AI Chat Supporter - TypeScript library for intelligent chat processing with LangChain integration

271 lines 10.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.LangChainRAGManager = exports.SimpleDocumentStore = exports.SimpleTextSplitter = exports.TextDocumentProcessor = void 0; const documents_1 = require("@langchain/core/documents"); const fs = __importStar(require("fs/promises")); const path = __importStar(require("path")); const uuid_1 = require("uuid"); // 텍스트 파일 처리기 class TextDocumentProcessor { constructor() { this.supportedExtensions = ['.txt', '.md', '.text']; } async process(filePath) { try { const content = await fs.readFile(filePath, 'utf-8'); const stats = await fs.stat(filePath); const fileName = path.basename(filePath, path.extname(filePath)); return { id: this.generateId(filePath), title: fileName, content: content.trim(), filePath, metadata: { fileSize: stats.size, extension: path.extname(filePath), }, lastModified: stats.mtime, }; } catch (error) { throw new Error(`텍스트 파일 처리 중 오류 발생: ${error instanceof Error ? error.message : 'Unknown error'}`); } } generateId(_filePath) { return (0, uuid_1.v4)(); } } exports.TextDocumentProcessor = TextDocumentProcessor; // 간단한 텍스트 분할기 (LangChain의 RecursiveCharacterTextSplitter 대체) class SimpleTextSplitter { constructor(options) { this.chunkSize = options.chunkSize; this.chunkOverlap = options.chunkOverlap; } splitText(text) { const chunks = []; const separators = ['\n\n', '\n', '. ', ' ']; let currentText = text; while (currentText.length > this.chunkSize) { let splitIndex = this.chunkSize; // 적절한 분할 지점 찾기 for (const separator of separators) { const lastIndex = currentText.lastIndexOf(separator, this.chunkSize); if (lastIndex > this.chunkSize * 0.5) { splitIndex = lastIndex + separator.length; break; } } const chunk = currentText.substring(0, splitIndex).trim(); if (chunk) { chunks.push(chunk); } // 오버랩을 고려한 다음 시작점 설정 const overlapStart = Math.max(0, splitIndex - this.chunkOverlap); currentText = currentText.substring(overlapStart); } // 남은 텍스트 추가 if (currentText.trim()) { chunks.push(currentText.trim()); } return chunks; } } exports.SimpleTextSplitter = SimpleTextSplitter; // 간단한 문서 저장소 (벡터 스토어 대체) class SimpleDocumentStore { constructor() { this.documents = []; } async addDocuments(documents) { this.documents.push(...documents); } async searchSimilar(query, k = 5) { const queryLower = query.toLowerCase(); const scored = []; for (const doc of this.documents) { const content = doc.pageContent.toLowerCase(); let score = 0; // 간단한 키워드 매칭 점수 계산 const queryWords = queryLower.split(/\s+/).filter(word => word.length > 2); for (const word of queryWords) { const matches = (content.match(new RegExp(word, 'g')) || []).length; score += matches; } if (score > 0) { scored.push({ doc, score }); } } return scored .sort((a, b) => b.score - a.score) .slice(0, k) .map(item => item.doc); } getDocumentCount() { return this.documents.length; } } exports.SimpleDocumentStore = SimpleDocumentStore; // LangChain 기반 RAG 관리자 (간소화된 버전) class LangChainRAGManager { constructor(config = {}) { this.processors = new Map(); this.config = { chunkSize: 1000, chunkOverlap: 200, maxDocuments: 10, similarityThreshold: 0.5, ...config, }; // 문서 스토어 초기화 this.documentStore = new SimpleDocumentStore(); // 텍스트 분할기 설정 this.textSplitter = new SimpleTextSplitter({ chunkSize: this.config.chunkSize, chunkOverlap: this.config.chunkOverlap, }); // 기본 처리기 등록 this.registerProcessor(new TextDocumentProcessor()); } // 문서 처리기 등록 registerProcessor(processor) { processor.supportedExtensions.forEach(ext => { this.processors.set(ext, processor); }); } // 문서 추가 async addDocument(filePath) { const extension = path.extname(filePath).toLowerCase(); const processor = this.processors.get(extension); if (!processor) { throw new Error(`지원하지 않는 파일 형식: ${extension}`); } try { const document = await processor.process(filePath); // 문서를 청크로 분할 const chunks = this.textSplitter.splitText(document.content); // LangChain Document 형태로 변환 const langchainDocs = chunks.map((chunk, index) => new documents_1.Document({ pageContent: chunk, metadata: { ...document.metadata, documentId: document.id, title: document.title, filePath: document.filePath, chunkIndex: index, totalChunks: chunks.length } })); // 문서 스토어에 추가 await this.documentStore.addDocuments(langchainDocs); return document; } catch (error) { throw new Error(`문서 추가 중 오류 발생: ${error instanceof Error ? error.message : 'Unknown error'}`); } } // 여러 문서 일괄 추가 async addDocuments(filePaths) { const results = []; const errors = []; for (const filePath of filePaths) { try { const document = await this.addDocument(filePath); results.push(document); } catch (error) { errors.push(`${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } if (errors.length > 0) { console.warn('일부 문서 추가 실패:', errors); } return results; } // 텍스트로부터 직접 문서 추가 async addTextDocument(text, metadata = {}) { const chunks = this.textSplitter.splitText(text); const langchainDocs = chunks.map((chunk, index) => new documents_1.Document({ pageContent: chunk, metadata: { ...metadata, chunkIndex: index, totalChunks: chunks.length, documentId: (0, uuid_1.v4)() } })); await this.documentStore.addDocuments(langchainDocs); } // 검색 및 컨텍스트 구축 async buildContext(query, maxResults) { const k = maxResults || this.config.maxDocuments || 5; try { const results = await this.documentStore.searchSimilar(query, k); return results.map(doc => { const title = doc.metadata.title || 'Unknown'; return `[${title}]\n${doc.pageContent}`; }); } catch (error) { console.warn(`컨텍스트 구축 실패: ${error instanceof Error ? error.message : 'Unknown error'}`); return []; } } // 설정 업데이트 updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; // 텍스트 분할기 재설정 if (newConfig.chunkSize || newConfig.chunkOverlap) { this.textSplitter = new SimpleTextSplitter({ chunkSize: this.config.chunkSize, chunkOverlap: this.config.chunkOverlap, }); } } // 지원하는 파일 형식 조회 getSupportedExtensions() { return Array.from(this.processors.keys()); } // 통계 정보 async getStats() { return { documentsCount: this.documentStore.getDocumentCount(), supportedExtensions: this.getSupportedExtensions(), }; } } exports.LangChainRAGManager = LangChainRAGManager; //# sourceMappingURL=LangChainRAGManager.js.map