UNPKG

contextual-agent-sdk

Version:

SDK for building AI agents with seamless voice-text context switching

397 lines 15.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.KnowledgeBaseProvider = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); class KnowledgeBaseProvider { id; name; source = 'knowledge_base'; priority; enabled; config; documentCache = new Map(); constructor(config) { this.config = config; this.id = config.id || 'knowledge_base'; this.name = config.name || 'Knowledge Base Provider'; this.priority = config.priority || 80; this.enabled = config.enabled ?? true; if (this.config.options?.autoDiscoverDocs?.enabled) { this.autoDiscoverDocuments(); } } async getContext(params) { try { const results = []; if (this.config.options?.customSearch && params.query) { const customResults = await this.config.options.customSearch(params.query); results.push(...customResults); } if (this.config.options?.sources) { for (const source of this.config.options.sources) { const sourceResults = await this.searchDocumentSource(source, params.query); results.push(...sourceResults); } } if (results.length === 0) { return null; } return this.formatResults(results); } catch (error) { console.error('Knowledge base search error:', error); return null; } } formatContext(context) { if (typeof context.content === 'string') { return `Documentation:\n${context.content}`; } if (Array.isArray(context.content)) { return `Documentation:\n${context.content.map((item, i) => { const title = item.title || item.filename || `Document ${i + 1}`; const content = item.content || item.text || JSON.stringify(item); const source = item.source ? ` (${item.source})` : ''; return `\n## ${title}${source}\n${content}`; }).join('\n')}`; } return JSON.stringify(context.content, null, 2); } async searchDocumentSource(source, query) { const results = []; try { switch (source.type) { case 'file': const fileResult = await this.loadFile(source.path, source); if (fileResult && this.matchesQuery(fileResult.content, query)) { results.push(fileResult); } break; case 'directory': const dirResults = await this.loadDirectory(source.path, source); results.push(...dirResults.filter(result => this.matchesQuery(result.content, query))); break; case 'url': const urlResult = await this.loadUrl(source.path, source); if (urlResult && this.matchesQuery(urlResult.content, query)) { results.push(urlResult); } break; case 'custom': break; } } catch (error) { console.error(`Error loading source ${source.path}:`, error); } return results; } async loadFile(filePath, source) { try { const cached = this.getCachedDocument(filePath); if (cached) { return { ...cached, source: filePath, type: 'file', tags: source.tags || [], weight: source.weight || 1 }; } const stats = fs.statSync(filePath); const content = fs.readFileSync(filePath, { encoding: source.encoding || 'utf8' }); const parsed = this.parseDocument(content, source.format || this.detectFormat(filePath)); this.cacheDocument(filePath, parsed.content, stats.mtimeMs); return { title: parsed.title || path.basename(filePath), content: parsed.content, filename: path.basename(filePath), source: filePath, type: 'file', tags: source.tags || [], weight: source.weight || 1, lastModified: stats.mtimeMs }; } catch (error) { console.error(`Error loading file ${filePath}:`, error); return null; } } async loadDirectory(dirPath, source) { const results = []; try { const files = fs.readdirSync(dirPath); for (const file of files) { const fullPath = path.join(dirPath, file); const stats = fs.statSync(fullPath); if (stats.isFile() && this.isDocumentFile(file)) { const fileResult = await this.loadFile(fullPath, source); if (fileResult) { results.push(fileResult); } } } } catch (error) { console.error(`Error loading directory ${dirPath}:`, error); } return results; } async loadUrl(url, source) { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const content = await response.text(); const parsed = this.parseDocument(content, source.format || this.detectFormat(url)); return { title: parsed.title || url, content: parsed.content, source: url, type: 'url', tags: source.tags || [], weight: source.weight || 1 }; } catch (error) { console.error(`Error loading URL ${url}:`, error); return null; } } parseDocument(content, format) { switch (format.toLowerCase()) { case 'markdown': case 'md': return this.parseMarkdown(content); case 'json': return this.parseJson(content); case 'text': case 'txt': default: return { content }; } } parseMarkdown(content) { const titleMatch = content.match(/^#\s+(.+)$/m); const title = titleMatch ? titleMatch[1].trim() : undefined; const cleanContent = content .replace(/^#{1,6}\s+/gm, '') .replace(/\*\*(.*?)\*\*/g, '$1') .replace(/\*(.*?)\*/g, '$1') .replace(/`(.*?)`/g, '$1') .replace(/```[\s\S]*?```/g, '[CODE BLOCK]') .replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1') .trim(); return { title, content: cleanContent }; } parseJson(content) { try { const data = JSON.parse(content); if (data.title && data.content) { return { title: data.title, content: data.content }; } if (data.sections) { const title = data.title || data.name; const content = data.sections.map((section) => `${section.title || section.name}: ${section.content || section.description}`).join('\n\n'); return { title, content }; } return { content: JSON.stringify(data, null, 2) }; } catch (error) { return { content }; } } detectFormat(filePath) { const ext = path.extname(filePath).toLowerCase(); const formatMap = { '.md': 'markdown', '.markdown': 'markdown', '.txt': 'text', '.json': 'json', '.readme': 'markdown' }; return formatMap[ext] || 'auto'; } isDocumentFile(filename) { const docExtensions = ['.md', '.txt', '.json', '.markdown']; const docNames = ['readme', 'changelog', 'license', 'contributing', 'docs', 'documentation']; const ext = path.extname(filename).toLowerCase(); const name = path.basename(filename, ext).toLowerCase(); return docExtensions.includes(ext) || docNames.includes(name); } autoDiscoverDocuments() { const autoConfig = this.config.options?.autoDiscoverDocs; if (!autoConfig?.enabled) return; const rootPath = autoConfig.rootPath || process.cwd(); const patterns = autoConfig.patterns || [ 'README.md', 'README.txt', 'README', 'CHANGELOG.md', 'CHANGELOG.txt', 'LICENSE', 'LICENSE.md', 'LICENSE.txt', 'CONTRIBUTING.md', 'CONTRIBUTING.txt', 'docs/**/*.md', 'documentation/**/*.md', '*.md' ]; const discoveredSources = []; patterns.forEach(pattern => { try { if (!pattern.includes('*')) { const filePath = path.join(rootPath, pattern); if (fs.existsSync(filePath)) { discoveredSources.push({ type: 'file', path: filePath, format: 'auto', weight: this.getDocumentWeight(pattern), tags: ['auto-discovered', this.getDocumentType(pattern)] }); } } else { const dirPath = pattern.includes('/') ? path.dirname(pattern) : rootPath; if (fs.existsSync(path.join(rootPath, dirPath))) { discoveredSources.push({ type: 'directory', path: path.join(rootPath, dirPath), format: 'auto', weight: 0.7, tags: ['auto-discovered', 'directory'] }); } } } catch (error) { console.warn(`Could not auto-discover pattern ${pattern}:`, error); } }); if (!this.config.options) { this.config.options = {}; } if (!this.config.options.sources) { this.config.options.sources = []; } this.config.options.sources.push(...discoveredSources); console.log(`Auto-discovered ${discoveredSources.length} documentation sources`); } getDocumentWeight(filename) { const name = filename.toLowerCase(); if (name.includes('readme')) return 1.0; if (name.includes('docs') || name.includes('documentation')) return 0.9; if (name.includes('changelog')) return 0.7; if (name.includes('contributing')) return 0.6; if (name.includes('license')) return 0.5; return 0.8; } getDocumentType(filename) { const name = filename.toLowerCase(); if (name.includes('readme')) return 'readme'; if (name.includes('changelog')) return 'changelog'; if (name.includes('license')) return 'license'; if (name.includes('contributing')) return 'contributing'; if (name.includes('docs')) return 'documentation'; return 'document'; } matchesQuery(content, query) { if (!query) return true; const searchType = this.config.options?.searchType || 'fuzzy'; const lowerContent = content.toLowerCase(); const lowerQuery = query.toLowerCase(); switch (searchType) { case 'exact': return lowerContent.includes(lowerQuery); case 'fuzzy': const queryWords = lowerQuery.split(' ').filter(w => w.length > 2); const matchCount = queryWords.filter(word => lowerContent.includes(word)).length; return matchCount >= Math.ceil(queryWords.length * 0.6); case 'semantic': return this.matchesQuery(content, query); default: return true; } } getCachedDocument(filePath) { const cached = this.documentCache.get(filePath); if (!cached) return null; try { const stats = fs.statSync(filePath); if (stats.mtimeMs > cached.lastModified) { this.documentCache.delete(filePath); return null; } return { content: cached.content }; } catch (error) { this.documentCache.delete(filePath); return null; } } cacheDocument(filePath, content, lastModified) { this.documentCache.set(filePath, { content, lastModified }); } formatResults(results) { const maxResults = this.config.options?.maxResults || 5; const sortedResults = results .sort((a, b) => (b.weight || 0) - (a.weight || 0)) .slice(0, maxResults); return { content: sortedResults, metadata: { source: this.source, timestamp: new Date(), tags: ['knowledge_base', 'documentation'], resultCount: sortedResults.length, sources: sortedResults.map(r => r.source) } }; } } exports.KnowledgeBaseProvider = KnowledgeBaseProvider; //# sourceMappingURL=KnowledgeBaseProvider.js.map