feat: migrate embeddings to Candle WASM + remove semantic type inference
Major architectural changes: 1. EMBEDDINGS ENGINE (ONNX → Candle WASM): - Replace ONNX Runtime with Rust Candle compiled to WASM - Embedded model in WASM binary (no external downloads) - Quantized Q8 precision with <50MB memory footprint - Zero-download, offline-first operation - Same embedding quality (all-MiniLM-L6-v2) 2. REMOVE SEMANTIC TYPE INFERENCE: - Delete embeddedKeywordEmbeddings.ts (14MB of pre-computed embeddings) - Remove typeAwareQueryPlanner.ts and semanticTypeInference.ts - Remove VerbExactMatchSignal (uses keyword embeddings) - Update SmartRelationshipExtractor to 3 signals (55%/30%/15% weights) API CHANGES (requires v7.0.0): - Removed: inferTypes(), inferNouns(), inferVerbs(), inferIntent() - Removed: getSemanticTypeInference(), SemanticTypeInference class - Removed: TypeInference, SemanticTypeInferenceOptions types Users can still use natural language queries in find() - they just need to specify type explicitly for type-optimized searches. PACKAGE SIZE IMPACT: - Compressed: 90.1 MB → 86.2 MB (-4.3%) - Uncompressed: 114.4 MB → 100.3 MB (-12%) - ~448K lines of code removed 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
81cd16e41b
commit
da7d2ed29d
60 changed files with 3887 additions and 448557 deletions
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* Debug script to analyze vector similarity scores for threshold tuning
|
||||
*/
|
||||
|
||||
import { TypeInferenceSystem } from '../dist/query/typeInference.js';
|
||||
|
||||
async function debugVectorSimilarity() {
|
||||
console.log('🔍 Vector Similarity Threshold Analysis\n');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// Create hybrid system with debug enabled
|
||||
const system = new TypeInferenceSystem({
|
||||
enableVectorFallback: true,
|
||||
fallbackConfidenceThreshold: 0.7,
|
||||
vectorThreshold: 0.3, // Lower threshold to see more matches
|
||||
debug: true
|
||||
});
|
||||
|
||||
// Test cases: unknown words, typos, medical terms
|
||||
const testQueries = [
|
||||
'Find documnets', // Typo: document
|
||||
'Find cardiologists', // Medical: person
|
||||
'Find oncologists', // Medical: person
|
||||
'Find pysicians', // Typo: physician -> person
|
||||
'Find organiztions', // Typo: organization
|
||||
'Find kompanies', // Severe typo: companies -> organization
|
||||
'Find xyzabc', // Completely unknown
|
||||
'neurologist', // Medical single word
|
||||
'cardiologist' // Medical single word
|
||||
];
|
||||
|
||||
console.log('\n📊 Testing vector similarity with threshold = 0.3\n');
|
||||
|
||||
for (const query of testQueries) {
|
||||
console.log(`\nQuery: "${query}"`);
|
||||
const start = performance.now();
|
||||
|
||||
const results = await system.inferTypesAsync(query);
|
||||
|
||||
const elapsed = performance.now() - start;
|
||||
|
||||
if (results.length > 0) {
|
||||
console.log(` ✅ Matched ${results.length} types in ${elapsed.toFixed(2)}ms:`);
|
||||
for (const result of results.slice(0, 3)) {
|
||||
console.log(` - ${result.type}: ${(result.confidence * 100).toFixed(1)}% (${result.matchedKeywords.join(', ')})`);
|
||||
}
|
||||
} else {
|
||||
console.log(` ❌ No matches in ${elapsed.toFixed(2)}ms`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('✅ Analysis complete! Use these insights to tune thresholds.');
|
||||
console.log('='.repeat(60));
|
||||
}
|
||||
|
||||
debugVectorSimilarity().catch(err => {
|
||||
console.error('❌ Error:', err.message);
|
||||
console.error(err.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
/**
|
||||
* Hybrid Type Inference Demo - REAL WORKING EXAMPLE
|
||||
*
|
||||
* Demonstrates the hybrid TypeInference system with vector similarity fallback
|
||||
* actually working end-to-end through the TypeAwareQueryPlanner.
|
||||
*/
|
||||
|
||||
import { TypeAwareQueryPlanner } from '../dist/query/typeAwareQueryPlanner.js';
|
||||
import { NounType } from '../dist/types/graphTypes.js';
|
||||
|
||||
async function main() {
|
||||
console.log('🎯 Hybrid Type Inference Demo\n');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// ========== Test 1: Synchronous Mode (Keyword Only) ==========
|
||||
console.log('\n📌 Test 1: Synchronous Mode (Keyword Matching Only)\n');
|
||||
|
||||
const syncPlanner = new TypeAwareQueryPlanner();
|
||||
|
||||
console.log('Query: "Find engineers"');
|
||||
const plan1 = syncPlanner.planQuery('Find engineers');
|
||||
console.log(`✅ Result: ${plan1.routing} routing`);
|
||||
console.log(` Types: ${plan1.targetTypes.join(', ')}`);
|
||||
console.log(` Confidence: ${(plan1.confidence * 100).toFixed(0)}%`);
|
||||
console.log(` Speedup: ${plan1.estimatedSpeedup.toFixed(1)}x`);
|
||||
|
||||
console.log('\nQuery: "Find physicians" (unknown word - will fail in sync mode)');
|
||||
const plan2 = syncPlanner.planQuery('Find physicians');
|
||||
console.log(`⚠️ Result: ${plan2.routing} routing`);
|
||||
console.log(` Types: ${plan2.targetTypes.length} types (searches ALL)`);
|
||||
console.log(` Reason: ${plan2.reasoning}`);
|
||||
|
||||
// ========== Test 2: Hybrid Mode (Keyword + Vector Fallback) ==========
|
||||
console.log('\n\n📌 Test 2: Hybrid Mode (Keyword + Vector Fallback)\n');
|
||||
|
||||
const hybridPlanner = new TypeAwareQueryPlanner(undefined, {
|
||||
enableVectorFallback: true,
|
||||
debug: true,
|
||||
typeInferenceConfig: {
|
||||
fallbackConfidenceThreshold: 0.7,
|
||||
vectorThreshold: 0.5,
|
||||
debug: true
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Query: "Find engineers" (known keyword - fast path)');
|
||||
const plan3 = await hybridPlanner.planQueryAsync('Find engineers');
|
||||
console.log(`✅ Result: ${plan3.routing} routing`);
|
||||
console.log(` Types: ${plan3.targetTypes.join(', ')}`);
|
||||
console.log(` Confidence: ${(plan3.confidence * 100).toFixed(0)}%`);
|
||||
|
||||
console.log('\n\nQuery: "Find physicians" (unknown word - vector fallback!)');
|
||||
const plan4 = await hybridPlanner.planQueryAsync('Find physicians');
|
||||
console.log(`✅ Result: ${plan4.routing} routing`);
|
||||
console.log(` Types: ${plan4.targetTypes.join(', ')}`);
|
||||
console.log(` Confidence: ${(plan4.confidence * 100).toFixed(0)}%`);
|
||||
console.log(` Speedup: ${plan4.estimatedSpeedup.toFixed(1)}x`);
|
||||
|
||||
console.log('\n\nQuery: "Find documnets" (typo - vector fallback handles it!)');
|
||||
const plan5 = await hybridPlanner.planQueryAsync('Find documnets');
|
||||
console.log(`✅ Result: ${plan5.routing} routing`);
|
||||
console.log(` Types: ${plan5.targetTypes.join(', ')}`);
|
||||
console.log(` Confidence: ${(plan5.confidence * 100).toFixed(0)}%`);
|
||||
|
||||
// ========== Performance Comparison ==========
|
||||
console.log('\n\n📌 Performance Comparison\n');
|
||||
|
||||
console.log('Synchronous (keyword-only):');
|
||||
const start1 = performance.now();
|
||||
syncPlanner.planQuery('Find engineers');
|
||||
const elapsed1 = performance.now() - start1;
|
||||
console.log(` Latency: ${elapsed1.toFixed(2)}ms (fast path)`);
|
||||
|
||||
console.log('\nHybrid with known keyword (should use fast path):');
|
||||
const start2 = performance.now();
|
||||
await hybridPlanner.planQueryAsync('Find engineers');
|
||||
const elapsed2 = performance.now() - start2;
|
||||
console.log(` Latency: ${elapsed2.toFixed(2)}ms (fast path)`);
|
||||
|
||||
console.log('\nHybrid with unknown word (triggers vector fallback):');
|
||||
const start3 = performance.now();
|
||||
await hybridPlanner.planQueryAsync('Find cardiologists');
|
||||
const elapsed3 = performance.now() - start3;
|
||||
console.log(` Latency: ${elapsed3.toFixed(2)}ms (includes vector similarity)`);
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('✅ Demo Complete! The hybrid system is ACTUALLY WORKING!');
|
||||
console.log('='.repeat(60));
|
||||
}
|
||||
|
||||
// Run the demo
|
||||
main().catch(error => {
|
||||
console.error('\n❌ Demo failed:', error.message);
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
/**
|
||||
* Production-Ready Hybrid Type Inference - Comprehensive Demo
|
||||
*
|
||||
* Demonstrates all three inference modes:
|
||||
* 1. Fast path: Keyword matching (0.01-0.1ms)
|
||||
* 2. Fuzzy path: Edit distance matching for typos (0.1-0.5ms)
|
||||
* 3. Vector path: Semantic similarity fallback (50-150ms first call, 2-5ms cached)
|
||||
*/
|
||||
|
||||
import { TypeAwareQueryPlanner } from '../dist/query/typeAwareQueryPlanner.js';
|
||||
|
||||
async function comprehensiveDemo() {
|
||||
console.log('🎯 Production-Ready Hybrid Type Inference Demo\n');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// Initialize hybrid planner
|
||||
const planner = new TypeAwareQueryPlanner(undefined, {
|
||||
enableVectorFallback: true,
|
||||
debug: false,
|
||||
typeInferenceConfig: {
|
||||
fallbackConfidenceThreshold: 0.7,
|
||||
vectorThreshold: 0.3
|
||||
}
|
||||
});
|
||||
|
||||
console.log('\n📌 Test Suite: All Three Inference Modes\n');
|
||||
|
||||
// ========== Fast Path: Exact Keywords ==========
|
||||
console.log('1️⃣ FAST PATH - Exact keyword matches (0.01-0.1ms)');
|
||||
console.log('-'.repeat(60));
|
||||
|
||||
const fastTests = [
|
||||
'Find engineers in San Francisco',
|
||||
'Show cardiologists',
|
||||
'List oncologists and neurologists',
|
||||
'Search documents about AI'
|
||||
];
|
||||
|
||||
for (const query of fastTests) {
|
||||
const start = performance.now();
|
||||
const plan = await planner.planQueryAsync(query);
|
||||
const elapsed = performance.now() - start;
|
||||
|
||||
console.log(` "${query}"`);
|
||||
console.log(` → ${plan.routing}: [${plan.targetTypes.join(', ')}]`);
|
||||
console.log(` → Confidence: ${(plan.confidence * 100).toFixed(0)}%, Speedup: ${plan.estimatedSpeedup.toFixed(1)}x, Latency: ${elapsed.toFixed(2)}ms\n`);
|
||||
}
|
||||
|
||||
// ========== Fuzzy Path: Typo Correction ==========
|
||||
console.log('\n2️⃣ FUZZY PATH - Typo correction via edit distance (0.1-0.5ms)');
|
||||
console.log('-'.repeat(60));
|
||||
|
||||
const fuzzyTests = [
|
||||
'Find pysicians', // physician (1 char substitution)
|
||||
'Show organiztions', // organization (2 chars: missing 'a', extra 't')
|
||||
'List enginners', // engineer (1 char: extra 'n')
|
||||
'Search documnets' // documents (1 char: swapped 'n'/'m')
|
||||
];
|
||||
|
||||
for (const query of fuzzyTests) {
|
||||
const start = performance.now();
|
||||
const plan = await planner.planQueryAsync(query);
|
||||
const elapsed = performance.now() - start;
|
||||
|
||||
console.log(` "${query}"`);
|
||||
console.log(` → ${plan.routing}: [${plan.targetTypes.join(', ')}]`);
|
||||
console.log(` → Confidence: ${(plan.confidence * 100).toFixed(0)}%, Speedup: ${plan.estimatedSpeedup.toFixed(1)}x, Latency: ${elapsed.toFixed(2)}ms\n`);
|
||||
}
|
||||
|
||||
// ========== Vector Path: Semantic Fallback ==========
|
||||
console.log('\n3️⃣ VECTOR PATH - Semantic similarity fallback (2-150ms)');
|
||||
console.log('-'.repeat(60));
|
||||
|
||||
const vectorTests = [
|
||||
'Find cardiovascular specialists', // Should match via vector similarity
|
||||
'Search publications', // Should match document
|
||||
'List facilities' // Should match location/organization
|
||||
];
|
||||
|
||||
for (const query of vectorTests) {
|
||||
const start = performance.now();
|
||||
const plan = await planner.planQueryAsync(query);
|
||||
const elapsed = performance.now() - start;
|
||||
|
||||
console.log(` "${query}"`);
|
||||
console.log(` → ${plan.routing}: [${plan.targetTypes.slice(0, 3).join(', ')}${plan.targetTypes.length > 3 ? '...' : ''}]`);
|
||||
console.log(` → Confidence: ${(plan.confidence * 100).toFixed(0)}%, Speedup: ${plan.estimatedSpeedup.toFixed(1)}x, Latency: ${elapsed.toFixed(2)}ms\n`);
|
||||
}
|
||||
|
||||
// ========== Performance Summary ==========
|
||||
console.log('\n📊 Performance Summary');
|
||||
console.log('-'.repeat(60));
|
||||
console.log(' Fast Path (exact match): < 0.1ms ✅ 95% of queries');
|
||||
console.log(' Fuzzy Path (typo correction): 0.1-0.5ms ✅ 3-4% of queries');
|
||||
console.log(' Vector Path (semantic): 2-150ms ✅ 1-2% of queries');
|
||||
console.log(' Weighted Average Latency: ~0.5ms ✅ Production-ready!');
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('✅ All three inference modes working in production!');
|
||||
console.log('='.repeat(60));
|
||||
}
|
||||
|
||||
comprehensiveDemo().catch(error => {
|
||||
console.error('\n❌ Demo failed:', error.message);
|
||||
console.error(error.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/**
|
||||
* Test different vector thresholds to find optimal balance
|
||||
*/
|
||||
|
||||
import { TypeInferenceSystem } from '../dist/query/typeInference.js';
|
||||
|
||||
async function testThresholds() {
|
||||
console.log('🎯 Vector Threshold Tuning Test\n');
|
||||
console.log('='.repeat(60));
|
||||
|
||||
// Test queries: typos and edge cases
|
||||
const testCases = [
|
||||
{ query: 'Find pysicians', expected: 'person', description: 'Typo: physician' },
|
||||
{ query: 'Find documnets', expected: 'document', description: 'Typo: documents' },
|
||||
{ query: 'Find organiztions', expected: 'organization', description: 'Typo: organizations' },
|
||||
{ query: 'Find kompanies', expected: 'organization', description: 'Severe typo: companies' },
|
||||
{ query: 'Find enginners', expected: 'person', description: 'Typo: engineers' },
|
||||
{ query: 'Find xyzabc', expected: null, description: 'Nonsense word (should fail)' }
|
||||
];
|
||||
|
||||
// Test with different thresholds
|
||||
const thresholds = [0.35, 0.30, 0.25, 0.20];
|
||||
|
||||
for (const threshold of thresholds) {
|
||||
console.log(`\n📊 Testing with vectorThreshold = ${threshold}`);
|
||||
console.log('-'.repeat(60));
|
||||
|
||||
const system = new TypeInferenceSystem({
|
||||
enableVectorFallback: true,
|
||||
fallbackConfidenceThreshold: 0.7,
|
||||
vectorThreshold: threshold,
|
||||
debug: false
|
||||
});
|
||||
|
||||
let successes = 0;
|
||||
let falsePositives = 0;
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const results = await system.inferTypesAsync(testCase.query);
|
||||
const matched = results.length > 0;
|
||||
const correctType = results.length > 0 && results[0].type === testCase.expected;
|
||||
|
||||
if (testCase.expected === null) {
|
||||
// Should NOT match
|
||||
if (!matched) {
|
||||
successes++;
|
||||
console.log(` ✅ "${testCase.query}" correctly returned no matches`);
|
||||
} else {
|
||||
falsePositives++;
|
||||
console.log(` ❌ "${testCase.query}" false positive: ${results[0].type} (${(results[0].confidence * 100).toFixed(1)}%)`);
|
||||
}
|
||||
} else {
|
||||
// Should match expected type
|
||||
if (correctType) {
|
||||
successes++;
|
||||
console.log(` ✅ "${testCase.query}" → ${results[0].type} (${(results[0].confidence * 100).toFixed(1)}%)`);
|
||||
} else if (matched) {
|
||||
console.log(` ⚠️ "${testCase.query}" → ${results[0].type} (expected ${testCase.expected})`);
|
||||
} else {
|
||||
console.log(` ❌ "${testCase.query}" no match (expected ${testCase.expected})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const accuracy = (successes / testCases.length) * 100;
|
||||
console.log(`\n Accuracy: ${successes}/${testCases.length} (${accuracy.toFixed(0)}%), False positives: ${falsePositives}`);
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(60));
|
||||
console.log('✅ Threshold tuning complete!');
|
||||
console.log('='.repeat(60));
|
||||
}
|
||||
|
||||
testThresholds().catch(err => {
|
||||
console.error('❌ Error:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue