@yk1028-test/ai-chat-supporter
Version:
AI Chat Supporter - TypeScript library for intelligent chat processing with LangChain integration
271 lines • 10.2 kB
JavaScript
;
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