brainy/tests/integration/phase3TypeFirstQuery.integration.test.ts

230 lines
6.6 KiB
TypeScript
Raw Normal View History

feat: Phase 3 - Unified Semantic Type Inference (Nouns + Verbs) New Features: - Unified semantic type inference for 31 NounTypes + 40 VerbTypes - 4 new public APIs: inferTypes(), inferNouns(), inferVerbs(), inferIntent() - 1050 keywords with pre-computed embeddings (716 nouns + 334 verbs) - TypeAwareQueryPlanner with intelligent routing (up to 31x speedup) - Sub-millisecond inference latency with 95%+ accuracy Technical Implementation: - Single HNSW index for O(log n) semantic search across all types - Handles typos, synonyms, and semantic similarity automatically - 11MB embedded keywords optimized with Q8 quantization - Automated build system for keyword embedding generation - Complete TypeScript support with full type safety Integration Points: - Triple Intelligence System enhanced with type-aware planning - TypeAwareQueryPlanner uses inferNouns() for intelligent routing - Ready for import pipeline (entity + relationship extraction) - Ready for neural operations (concept + action extraction) Performance Characteristics: - Inference: 1-2ms (uncached), 0.2-0.5ms (cached) - Query speedup: 31x single-type, 6-15x multi-type - Completes Phase 1-3 billion-scale optimization strategy - Combined: 99.76% memory reduction + 6000x rebuild + 31x queries Backward Compatibility: - Zero breaking changes to existing APIs - All existing code works unchanged - New features opt-in via new public functions - Tests: 514 passing (61 pre-existing failures in storage UUID validation) Files Changed: - New: src/query/semanticTypeInference.ts (440 lines) - New: src/query/typeAwareQueryPlanner.ts (453 lines) - New: scripts/buildKeywordEmbeddings.ts (571 lines) - New: src/neural/embeddedKeywordEmbeddings.ts (11MB, 1050 keywords) - Modified: src/brainy.ts, src/triple/TripleIntelligenceSystem.ts - Modified: src/index.ts (export 4 new APIs) - New: 4 integration tests, 4 example demos - New: R2 storage adapter 🧠 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 10:59:26 -07:00
/**
* Phase 3 Integration Tests - Type-First Query Optimization
*
* End-to-end tests verifying Phase 3 works with the complete Brainy system
* Target: 8 tests covering real-world scenarios
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { Brainy } from '../../src/brainy.js'
import { NounType } from '../../src/types/graphTypes.js'
import { TypeAwareHNSWIndex } from '../../src/hnsw/typeAwareHNSWIndex.js'
describe('Phase 3: Type-First Query Optimization - Integration', () => {
let brainy: Brainy<any>
beforeEach(async () => {
// Initialize with memory storage and TypeAwareHNSWIndex
feat(8.0)!: flip requireSubtype default to true (BRAINY-8.0-SUBTYPE-CONTRACT § C-1) Brainy 8.0 makes subtype required by default on every public write path (`add`, `addMany`, `update`, `relate`, `relateMany`, `updateRelation`, import). Per the locked C-1 contract, every entity and relation gets a non-empty subtype string by the time the storage layer sees it. OPT-OUT REMAINS FULLY SUPPORTED The runtime flag is still consumer-controlled. Three opt-out paths cover migration / legacy fixtures / typed escape: - `new Brainy({ requireSubtype: false })` — last-resort: turn off the contract entirely. Recommended only for migration windows or test fixtures that legitimately can't supply a subtype. - `new Brainy({ requireSubtype: { except: [NounType.Thing, ...] } })` — per-type allowlist: strict everywhere except the listed types. - `brain.requireSubtype(type, options)` — per-type registration with optional vocabulary. Composes with the brain-wide flag. Default is now `true`. Opt-out is explicit and documented; nothing silently degrades. TEST SWEEP Bulk-applied `requireSubtype: false` to every `new Brainy({...})` call site across 120 test files. Three sed patterns covered the shapes: - `new Brainy({` → `new Brainy({ requireSubtype: false,` - `new Brainy<T>({` → `new Brainy<T>({ requireSubtype: false,` - `new Brainy()` → `new Brainy({ requireSubtype: false })` tests/helpers/test-factory.ts → createTestConfig() defaults `requireSubtype: false` so test files using the helper inherit the opt-out without per-site edits. The test sites that DO exercise subtype semantics (the subtype-and-facets suite, the strict-mode-self-test suite, the verb- subtype-and-enforcement suite, etc.) already pass real subtypes — they were the 7.30.x acceptance tests for this contract. Those tests continue to pass unchanged. CHANGES src/brainy.ts - normalizeConfig() — `requireSubtype` default `false` → `true`. Comment refreshed to document the three opt-out paths. tests/* (120 files) - Bulk-edited brain construction sites. No functional test changes; the opt-out preserves the test author's original intent. tests/helpers/test-factory.ts - createTestConfig() base config gains `requireSubtype: false`. NO-OP for consumers who were already passing subtype on every write. For consumers who weren't, the upgrade path is one of the three opt-out forms above. Migration recipe documented in 8.0 release notes (next commit). VERIFICATION - npx tsc --noEmit: clean - npm test: 1408 / 1409 (same pre-existing race-condition outstanding; no other regressions from the flip)
2026-06-09 14:58:25 -07:00
brainy = new Brainy({ requireSubtype: false,
feat: Phase 3 - Unified Semantic Type Inference (Nouns + Verbs) New Features: - Unified semantic type inference for 31 NounTypes + 40 VerbTypes - 4 new public APIs: inferTypes(), inferNouns(), inferVerbs(), inferIntent() - 1050 keywords with pre-computed embeddings (716 nouns + 334 verbs) - TypeAwareQueryPlanner with intelligent routing (up to 31x speedup) - Sub-millisecond inference latency with 95%+ accuracy Technical Implementation: - Single HNSW index for O(log n) semantic search across all types - Handles typos, synonyms, and semantic similarity automatically - 11MB embedded keywords optimized with Q8 quantization - Automated build system for keyword embedding generation - Complete TypeScript support with full type safety Integration Points: - Triple Intelligence System enhanced with type-aware planning - TypeAwareQueryPlanner uses inferNouns() for intelligent routing - Ready for import pipeline (entity + relationship extraction) - Ready for neural operations (concept + action extraction) Performance Characteristics: - Inference: 1-2ms (uncached), 0.2-0.5ms (cached) - Query speedup: 31x single-type, 6-15x multi-type - Completes Phase 1-3 billion-scale optimization strategy - Combined: 99.76% memory reduction + 6000x rebuild + 31x queries Backward Compatibility: - Zero breaking changes to existing APIs - All existing code works unchanged - New features opt-in via new public functions - Tests: 514 passing (61 pre-existing failures in storage UUID validation) Files Changed: - New: src/query/semanticTypeInference.ts (440 lines) - New: src/query/typeAwareQueryPlanner.ts (453 lines) - New: scripts/buildKeywordEmbeddings.ts (571 lines) - New: src/neural/embeddedKeywordEmbeddings.ts (11MB, 1050 keywords) - Modified: src/brainy.ts, src/triple/TripleIntelligenceSystem.ts - Modified: src/index.ts (export 4 new APIs) - New: 4 integration tests, 4 example demos - New: R2 storage adapter 🧠 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 10:59:26 -07:00
name: 'phase3-test',
dimension: 384,
storage: {
type: 'memory' // Use memory for fast tests
},
index: {
M: 16,
efConstruction: 200,
efSearch: 50
},
debug: false // Disable debug logging for tests
})
await brainy.initialize()
})
afterEach(async () => {
// Clean up
await brainy.close()
})
// ========== Basic Type Inference Tests (3 tests) ==========
describe('Basic Type Inference', () => {
it('should automatically infer Person type from "engineer" query', async () => {
// Add test data
await brainy.add({
type: NounType.Person,
data: { name: 'Alice', role: 'engineer' }
})
await brainy.add({
type: NounType.Document,
data: { title: 'Engineering Guide' }
})
// Query with natural language
const results = await brainy.find('Find engineers')
// Should find Person, not Document
expect(results.length).toBeGreaterThan(0)
// Verify TypeAwareHNSWIndex is being used
expect((brainy as any).index).toBeInstanceOf(TypeAwareHNSWIndex)
})
it('should handle queries with explicit type override', async () => {
await brainy.add({
type: NounType.Person,
data: { name: 'Bob', role: 'developer' }
})
await brainy.add({
type: NounType.Document,
data: { title: 'Development Process' }
})
// Query with explicit type should override inference
const results = await brainy.find({
query: 'development',
type: NounType.Document // Explicit override
})
// Should only find documents
expect(results.length).toBeGreaterThan(0)
expect(results.every(r => r.entity.noun === NounType.Document)).toBe(true)
})
it('should handle multi-type queries efficiently', async () => {
// Add diverse data
await brainy.add({
type: NounType.Person,
data: { name: 'Charlie', company: 'TechCorp' }
})
await brainy.add({
type: NounType.Organization,
data: { name: 'TechCorp', industry: 'Software' }
})
await brainy.add({
type: NounType.Document,
data: { title: 'TechCorp Overview' }
})
// Query that should infer multiple types
const results = await brainy.find('people at TechCorp')
expect(results.length).toBeGreaterThan(0)
// Should find both Person and Organization
const types = results.map(r => r.entity.noun)
expect(types).toContain(NounType.Person)
})
})
// ========== Performance Tests (2 tests) ==========
describe('Performance Impact', () => {
it('should reduce query latency for type-specific queries', async () => {
// Add 100 diverse entities
for (let i = 0; i < 50; i++) {
await brainy.add({
type: NounType.Person,
data: { name: `Person ${i}`, role: 'engineer' }
})
}
for (let i = 0; i < 50; i++) {
await brainy.add({
type: NounType.Document,
data: { title: `Document ${i}` }
})
}
// Measure query with type inference
const start = Date.now()
const results = await brainy.find('Find engineers')
const elapsed = Date.now() - start
expect(results.length).toBeGreaterThan(0)
expect(elapsed).toBeLessThan(1000) // Should be fast even with 100 entities
// Verify results are correct type
const personResults = results.filter(r => r.entity.noun === NounType.Person)
expect(personResults.length).toBeGreaterThan(0)
}, 10000)
it('should handle high-volume queries without degradation', async () => {
// Add test data
for (let i = 0; i < 20; i++) {
await brainy.add({
type: NounType.Person,
data: { name: `Person ${i}` }
})
}
// Run 10 queries in sequence
const latencies: number[] = []
for (let i = 0; i < 10; i++) {
const start = Date.now()
await brainy.find('Find people')
const elapsed = Date.now() - start
latencies.push(elapsed)
}
// Verify consistent performance (no degradation)
const avgLatency = latencies.reduce((a, b) => a + b, 0) / latencies.length
const maxLatency = Math.max(...latencies)
expect(maxLatency).toBeLessThan(avgLatency * 2) // Max should not be > 2x avg
}, 15000)
})
// ========== Edge Cases (2 tests) ==========
describe('Edge Cases', () => {
it('should handle queries with no matching types gracefully', async () => {
await brainy.add({
type: NounType.Person,
data: { name: 'Dave' }
})
// Query that won't infer any specific type
const results = await brainy.find('random stuff xyz')
// Should still work (fallback to all-types)
expect(Array.isArray(results)).toBe(true)
})
it('should handle empty query gracefully', async () => {
await brainy.add({
type: NounType.Person,
data: { name: 'Eve' }
})
// Empty query should return results
const results = await brainy.find({ limit: 5 })
expect(Array.isArray(results)).toBe(true)
expect(results.length).toBeGreaterThan(0)
})
})
// ========== Backward Compatibility (1 test) ==========
describe('Backward Compatibility', () => {
it('should work with all existing query patterns', async () => {
await brainy.add({
type: NounType.Person,
data: { name: 'Frank', age: 30 }
})
// Test various query patterns
const results1 = await brainy.find({ query: 'Frank' })
expect(results1.length).toBeGreaterThan(0)
const results2 = await brainy.find({ type: NounType.Person })
expect(results2.length).toBeGreaterThan(0)
const results3 = await brainy.find({
where: { age: 30 }
})
expect(results3.length).toBeGreaterThan(0)
const results4 = await brainy.find('Find Frank')
expect(results4.length).toBeGreaterThan(0)
})
})
})