import { describe, it, expect, beforeEach } from 'vitest' import { SmartExtractor } from '../../../src/neural/SmartExtractor.js' import { NounType } from '../../../src/types/graphTypes.js' import type { Brainy } from '../../../src/brainy.js' // Mock brain instance const mockBrain = { embed: async (text: string) => { return new Array(384).fill(0) } } as unknown as Brainy describe('SmartExtractor', () => { let extractor: SmartExtractor beforeEach(() => { extractor = new SmartExtractor(mockBrain) }) describe('initialization', () => { it('should initialize with default options', () => { const extractor = new SmartExtractor(mockBrain) const stats = extractor.getStats() expect(stats.calls).toBe(0) expect(stats.cacheSize).toBe(0) }) it('should initialize with custom options', () => { const extractor = new SmartExtractor(mockBrain, { minConfidence: 0.70, enableFormatHints: false, enableEnsemble: true, cacheSize: 5000 }) const stats = extractor.getStats() expect(stats.calls).toBe(0) }) it('should validate signal weights sum to 1.0', () => { expect(() => { new SmartExtractor(mockBrain, { weights: { exactMatch: 0.50, embedding: 0.30, pattern: 0.10, context: 0.05 // Sum = 0.95, should error } }) }).toThrow('Signal weights must sum to 1.0') }) it('should accept valid custom weights', () => { const extractor = new SmartExtractor(mockBrain, { weights: { exactMatch: 0.30, embedding: 0.30, pattern: 0.30, context: 0.10 } }) const stats = extractor.getStats() expect(stats.calls).toBe(0) }) }) describe('basic extraction', () => { it('should extract person type from title', async () => { const result = await extractor.extract('Dr. Dr. Sarah Johnson', { definition: 'Chief Medical Officer at General Hospital' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) expect(result?.confidence).toBeGreaterThan(0.60) }) it('should extract organization type', async () => { const result = await extractor.extract('Microsoft Corporation', { definition: 'Technology company founded in 1975' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Organization) }) it('should extract location type', async () => { const result = await extractor.extract('Seattle, WA', { definition: 'Major city in Pacific Northwest' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Location) }) it('should extract event type', async () => { const result = await extractor.extract('Annual Conference 2024', { definition: 'Yearly industry gathering held in June' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Event) }) it('should extract concept type', async () => { const result = await extractor.extract('machine learning algorithm', { definition: 'Computational method for pattern recognition' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Concept) }) }) describe('format hint extraction - Excel', () => { it('should use Excel column header hints', async () => { const result = await extractor.extract('Ms. Jennifer Martinez', { definition: 'Software engineer on frontend team', formatContext: { format: 'excel', columnHeader: 'Employee Name' } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) expect(result?.metadata?.formatHints).toBeDefined() expect(result?.metadata?.formatHints).toContain('Employee Name') }) it('should extract type keywords from headers', async () => { const result = await extractor.extract('Global Tech Inc', { definition: 'Software company', formatContext: { format: 'excel', columnHeader: 'Company Name' } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Organization) expect(result?.metadata?.formatHints).toContain('company') }) it('should use sheet name as hint', async () => { const result = await extractor.extract('Bob Wilson', { definition: 'Team member', formatContext: { format: 'excel', columnHeader: 'Name', sheetName: 'Employees' } }) expect(result).toBeDefined() expect(result?.metadata?.formatHints).toContain('Employees') }) }) describe('format hint extraction - CSV', () => { it('should parse CSV header patterns', async () => { const result = await extractor.extract('Dr. Alice Chen', { definition: 'Research lead', formatContext: { format: 'csv', columnHeader: 'author_name' } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) expect(result?.metadata?.formatHints).toContain('author_name') }) it('should split underscore patterns', async () => { const result = await extractor.extract('Acme Corp', { definition: 'Business entity', formatContext: { format: 'csv', columnHeader: 'company_name' } }) expect(result).toBeDefined() expect(result?.metadata?.formatHints).toContain('company') expect(result?.metadata?.formatHints).toContain('name') }) }) describe('format hint extraction - YAML', () => { it('should use YAML key as hint', async () => { const result = await extractor.extract('Dr. John Smith', { definition: 'Lead researcher', formatContext: { format: 'yaml', yamlKey: 'author' } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) expect(result?.metadata?.formatHints).toContain('author') }) it('should parse hyphenated YAML keys', async () => { const result = await extractor.extract('TechVentures Inc', { definition: 'Startup company', formatContext: { format: 'yaml', yamlKey: 'company-name' } }) expect(result).toBeDefined() expect(result?.metadata?.formatHints).toContain('company') }) }) describe('format hint extraction - PDF', () => { it('should extract hints from PDF field names', async () => { const result = await extractor.extract('Jane Doe', { definition: 'Applicant', formatContext: { format: 'pdf', fieldName: 'applicant_name' } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) expect(result?.metadata?.formatHints).toContain('applicant_name') }) it('should parse camelCase field names', async () => { const result = await extractor.extract('Boston, MA', { definition: 'City location', formatContext: { format: 'pdf', fieldName: 'cityName' } }) expect(result).toBeDefined() expect(result?.metadata?.formatHints).toContain('city') }) }) describe('format hint extraction - DOCX', () => { it('should use heading level hints', async () => { const result = await extractor.extract('Project Phoenix', { definition: 'Digital transformation initiative', formatContext: { format: 'docx', headingLevel: 1 } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Project) expect(result?.metadata?.formatHints).toBeDefined() }) }) describe('ensemble voting', () => { it('should combine multiple signals', async () => { extractor.resetStats() const result = await extractor.extract('CEO Dr. Sarah Johnson', { definition: 'Chief executive officer and board member since 2018' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) expect(result?.source).toBe('ensemble') // Should have multiple signals agreeing expect(result?.metadata?.signalResults).toBeDefined() expect(result?.metadata?.signalResults!.length).toBeGreaterThan(1) }) it('should apply agreement boost', async () => { const result = await extractor.extract('Dr. Emily Chen', { definition: 'Medical researcher and professor at Stanford University' }) expect(result).toBeDefined() if (result?.metadata?.signalResults && result.metadata.signalResults.length > 1) { expect(result.metadata.agreementBoost).toBeGreaterThan(0) } }) it('should respect minimum confidence threshold', async () => { const strictExtractor = new SmartExtractor(mockBrain, { minConfidence: 0.95 }) const result = await strictExtractor.extract('ambiguous entity', { definition: 'Not clear what this is' }) // High threshold should filter out low-confidence results if (result) { expect(result.confidence).toBeGreaterThanOrEqual(0.95) } }) }) describe('single signal mode', () => { it('should use best signal when ensemble disabled', async () => { const singleSignalExtractor = new SmartExtractor(mockBrain, { enableEnsemble: false }) const result = await singleSignalExtractor.extract('Dr. Smith', { definition: 'Medical professional' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) expect(['exact-match', 'pattern', 'embedding', 'context']).toContain(result?.source) }) }) describe('statistics tracking', () => { it('should track call statistics', async () => { extractor.resetStats() await extractor.extract('Entity 1', { definition: 'Test' }) await extractor.extract('Entity 2', { definition: 'Test' }) const stats = extractor.getStats() expect(stats.calls).toBe(2) }) it('should track cache hits', async () => { extractor.resetStats() await extractor.extract('Dr. Test', { definition: 'Doctor' }) await extractor.extract('Dr. Test', { definition: 'Doctor' }) const stats = extractor.getStats() expect(stats.cacheHits).toBe(1) expect(stats.cacheHitRate).toBeGreaterThan(0) }) it('should track signal wins', async () => { extractor.resetStats() // This should trigger exact match or pattern await extractor.extract('Microsoft Corporation', { definition: 'Software company' }) const stats = extractor.getStats() const totalWins = stats.exactMatchWins + stats.patternWins + stats.embeddingWins + stats.contextWins + stats.ensembleWins expect(totalWins).toBeGreaterThan(0) }) it('should calculate average confidence', async () => { extractor.resetStats() await extractor.extract('Dr. Smith', { definition: 'Doctor' }) await extractor.extract('Acme Corp', { definition: 'Company' }) const stats = extractor.getStats() expect(stats.averageConfidence).toBeGreaterThan(0) expect(stats.averageConfidence).toBeLessThanOrEqual(1) }) it('should track format hint usage', async () => { extractor.resetStats() await extractor.extract('Test Entity', { definition: 'Test', formatContext: { format: 'excel', columnHeader: 'Name' } }) const stats = extractor.getStats() expect(stats.formatHintsUsed).toBeGreaterThanOrEqual(1) }) it('should provide signal-level statistics', () => { const stats = extractor.getStats() expect(stats.signalStats).toBeDefined() expect(stats.signalStats.exactMatch).toBeDefined() expect(stats.signalStats.pattern).toBeDefined() expect(stats.signalStats.embedding).toBeDefined() expect(stats.signalStats.context).toBeDefined() }) it('should reset all statistics', async () => { await extractor.extract('Test', { definition: 'Test' }) extractor.resetStats() const stats = extractor.getStats() expect(stats.calls).toBe(0) expect(stats.cacheHits).toBe(0) expect(stats.averageConfidence).toBe(0) }) }) describe('cache operations', () => { it('should cache extraction results', async () => { const result1 = await extractor.extract('Dr. Test', { definition: 'Medical professional' }) const result2 = await extractor.extract('Dr. Test', { definition: 'Medical professional' }) expect(result1).toEqual(result2) }) it('should clear all caches', async () => { await extractor.extract('Dr. Test', { definition: 'Doctor' }) extractor.clearCache() const stats = extractor.getStats() expect(stats.cacheSize).toBe(0) }) it('should respect cache size limit', async () => { const smallCacheExtractor = new SmartExtractor(mockBrain, { cacheSize: 10 }) // Add more than cache size for (let i = 0; i < 20; i++) { await smallCacheExtractor.extract(`Entity ${i}`, { definition: `Test entity ${i}` }) } const stats = smallCacheExtractor.getStats() expect(stats.cacheSize).toBeLessThanOrEqual(10) }) }) describe('real-world scenarios', () => { it('should classify employee from HR spreadsheet', async () => { const result = await extractor.extract('Ms. Jennifer Martinez', { definition: 'Software Engineer, Frontend Team, employed since 2020', formatContext: { format: 'excel', columnHeader: 'Employee Name', sheetName: 'Staff Directory' } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) expect(result?.confidence).toBeGreaterThan(0.70) }) it('should classify company from business database', async () => { const result = await extractor.extract('Global Innovations Inc', { definition: 'Fortune 500 technology company founded in 1998', formatContext: { format: 'csv', columnHeader: 'company_name' }, metadata: { industry: 'Technology', founded: 1998 } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Organization) }) it('should classify location from travel itinerary', async () => { const result = await extractor.extract('Tokyo, Japan', { definition: 'Meeting location for Q2 2024 conference', formatContext: { format: 'docx', headingLevel: 2 } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Location) }) it('should classify event from conference program', async () => { const result = await extractor.extract('DevConf 2024', { definition: 'Annual developer conference held in June', formatContext: { format: 'yaml', yamlKey: 'event-name' }, metadata: { date: '2024-06-15', venue: 'Convention Center' } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Event) }) it('should classify project from roadmap', async () => { const result = await extractor.extract('Project Phoenix', { definition: 'Digital transformation initiative launched Q1 2024', formatContext: { format: 'markdown', headingLevel: 1 } }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Project) }) it('should classify concept from knowledge base', async () => { const result = await extractor.extract('microservices architecture', { definition: 'Design pattern for building distributed systems using independent services', formatContext: { format: 'markdown', headingLevel: 2 }, allTerms: ['architecture', 'pattern', 'distributed', 'design'] }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Concept) }) }) describe('edge cases', () => { it('should handle entities without context', async () => { const result = await extractor.extract('Microsoft Corporation') expect(result).toBeDefined() expect(result?.type).toBe(NounType.Organization) }) it('should handle entities with minimal definition', async () => { const result = await extractor.extract('Dr. Smith', { definition: 'Medical professional' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Person) }) it('should handle format hints disabled', async () => { const noHintsExtractor = new SmartExtractor(mockBrain, { enableFormatHints: false }) const result = await noHintsExtractor.extract('Test Entity', { definition: 'Test', formatContext: { format: 'excel', columnHeader: 'Name' } }) if (result) { expect(result.metadata?.formatHints).toBeUndefined() } }) it('should handle null results gracefully', async () => { const result = await extractor.extract('xyzabc', { definition: 'Completely ambiguous gibberish with no patterns' }) // May return null if confidence too low if (!result) { expect(result).toBeNull() } }) it('should handle special characters', async () => { const result = await extractor.extract('C++', { definition: 'Programming language developed by Bjarne Stroustrup' }) expect(result).toBeDefined() expect(result?.type).toBe(NounType.Thing) }) }) describe('history management', () => { it('should add entities to history', () => { const vector = new Array(384).fill(0.1) extractor.addToHistory('Test Entity', NounType.Person, vector) // History is internal, just ensure no errors expect(true).toBe(true) }) it('should clear history', () => { const vector = new Array(384).fill(0.1) extractor.addToHistory('Test Entity', NounType.Person, vector) extractor.clearHistory() // History cleared, no errors expect(true).toBe(true) }) }) })