UNPKG

ableton-mcp-server-rag

Version:
636 lines โ€ข 32 kB
// src/mcp/hybrid-processor.ts import { queryRag } from './rag.js'; import { findRelevantTools, toolRegistry } from './tool-vector.js'; export class HybridQueryProcessor { // Enable debug logging for classification pipeline static DEBUG_CLASSIFICATION = false; // Keywords that indicate knowledge queries static KNOWLEDGE_INDICATORS = [ // Question words and phrases 'what is', 'what are', 'what does', 'what do', 'what can', 'what should', 'how does', 'how do', 'how can', 'how to', 'how should', 'how would', 'where is', 'where are', 'where can', 'where do', 'where does', 'when is', 'when are', 'when can', 'when do', 'when does', 'when should', 'which is', 'which are', 'which can', 'which do', 'which does', 'why is', 'why are', 'why can', 'why do', 'why does', 'why should', 'who is', 'who are', 'who can', 'who do', 'who does', // Information retrieval verbs (CRITICAL FIX) 'list', 'show', 'display', 'get', 'fetch', 'retrieve', 'obtain', 'view', 'see', 'check', 'inspect', 'examine', 'look at', 'find', 'search', 'locate', 'discover', 'identify', 'tell me', 'show me', 'give me', 'provide', 'return', // Current state queries (CRITICAL FIX) 'current', 'present', 'now', 'currently', 'at this moment', 'existing', 'active', 'running', 'live', 'real-time', 'status', 'state', 'condition', 'situation', 'position', // Information seeking verbs 'explain', 'describe', 'tell me about', 'inform me', 'clarify', 'define', 'specify', 'detail', 'enumerate', 'outline', 'summarize', 'overview', // Comparison and analysis 'compare', 'contrast', 'difference between', 'differences', 'similar to', 'similarities', 'versus', 'vs', 'compared to', 'better than', 'worse than', 'advantages', 'disadvantages', 'pros and cons', 'benefits', 'drawbacks', 'limitations', // Learning and understanding 'learn about', 'understand', 'know about', 'find out', 'discover', 'explore', 'investigate', 'research', 'tutorial', 'guide', 'help', 'instruction', 'manual', 'documentation', 'reference', 'info', 'information', // Ableton Live specific knowledge terms 'ableton', 'live', 'suite', 'intro', 'standard', 'lite', 'push 2', 'push 3', 'push', 'link', 'max for live', 'operator', 'wavetable', 'simpler', 'impulse', 'drum machine', 'session view', 'arrangement view', 'clip view', 'device view', 'browser', 'library', 'packs', 'samples', 'presets', // Music theory and production concepts 'bpm', 'tempo', 'time signature', 'key signature', 'scale', 'chord', 'harmony', 'melody', 'rhythm', 'beat', 'measure', 'bar', 'quantization', 'swing', 'groove', 'timing', 'sync', 'pitch', 'note', 'octave', 'semitone', 'cent', 'tuning', 'velocity', 'dynamics', 'expression', 'modulation', 'frequency', 'hertz', 'hz', 'spectrum', 'analysis', // Audio concepts 'sample rate', 'bit depth', 'latency', 'buffer size', 'mono', 'stereo', 'surround', 'panning', 'balance', 'gain', 'volume', 'amplitude', 'headroom', 'clipping', 'distortion', 'saturation', 'compression', 'limiting', 'gate', 'expander', 'envelope', 'attack', 'decay', 'sustain', 'release', // Effects and processing knowledge 'reverb', 'echo', 'delay', 'chorus', 'flanger', 'phaser', 'filter', 'equalizer', 'eq', 'low pass', 'high pass', 'band pass', 'resonance', 'cutoff', 'frequency response', 'phase', 'convolution', 'impulse response', 'algorithmic', 'room', 'hall', 'plate', 'spring', 'vocal hall', 'synth hall', // Technical specifications 'specifications', 'spec', 'features', 'capabilities', 'requirements', 'compatibility', 'supported', 'minimum', 'maximum', 'range', 'hardware', 'software', 'system', 'performance', 'cpu', 'memory', 'ram', 'storage', 'disk space', 'installation', // Workflow and methodology 'workflow', 'process', 'method', 'technique', 'approach', 'best practice', 'recommendation', 'tip', 'trick', 'shortcut', 'routing', 'signal flow', 'chain', 'path', 'connection', 'input', 'output', 'send', 'return', 'aux', 'bus', // Properties and attributes (CRITICAL FIX) 'properties', 'attributes', 'parameters', 'settings', 'values', 'configuration', 'setup', 'options', 'preferences', 'name', 'names', 'title', 'label', 'id', 'identifier' ]; // Keywords that indicate action queries static ACTION_INDICATORS = [ // Core action verbs 'create', 'make', 'build', 'generate', 'produce', 'construct', 'add', 'insert', 'place', 'put', 'include', 'append', 'load', 'open', 'import', 'browse', 'select', 'choose', 'set', 'change', 'modify', 'adjust', 'configure', 'setup', 'edit', 'update', 'alter', 'transform', 'convert', 'delete', 'remove', 'clear', 'erase', 'eliminate', 'cut', 'copy', 'paste', 'duplicate', 'clone', 'replicate', 'move', 'drag', 'shift', 'relocate', 'position', 'save', 'export', 'render', 'bounce', 'freeze', 'flatten', // Transport and playback actions 'play', 'start', 'begin', 'launch', 'trigger', 'fire', 'stop', 'pause', 'halt', 'cease', 'end', 'finish', 'record', 'rec', 'capture', 'sample', 'overdub', 'loop', 'repeat', 'cycle', 'continue', 'resume', 'rewind', 'fast forward', 'skip', 'jump', 'seek', 'sync', 'synchronize', 'align', 'lock', 'follow', // Parameter and control actions 'increase', 'raise', 'boost', 'amplify', 'enhance', 'decrease', 'lower', 'reduce', 'attenuate', 'diminish', 'adjust', 'tweak', 'fine tune', 'calibrate', 'balance', 'enable', 'activate', 'turn on', 'switch on', 'engage', 'disable', 'deactivate', 'turn off', 'switch off', 'bypass', 'mute', 'unmute', 'silence', 'solo', 'unsolo', 'arm', 'disarm', 'monitor', 'cue', 'preview', // Specific Ableton Live actions 'quantize', 'unquantize', 'consolidate', 'crop', 'split', 'slice', 'warp', 'stretch', 'transpose', 'reverse', 'normalize', 'fade in', 'fade out', 'crossfade', 'group', 'ungroup', 'fold', 'unfold', 'expand', 'collapse', 'freeze', 'unfreeze', 'flatten', 'unflatten', 'capture', 'extract', 'convert to midi', 'convert to audio', // REMOVED: Generic terms that can be both knowledge and action // 'track', 'clip', 'scene', 'device', 'parameter' - MOVED TO CONTEXT-SENSITIVE // Specific track/clip/device ACTIONS (REFINED) 'create track', 'new track', 'add track', 'insert track', 'delete track', 'remove track', 'duplicate track', 'create clip', 'new clip', 'add clip', 'insert clip', 'delete clip', 'remove clip', 'duplicate clip', 'create device', 'add device', 'insert device', 'load device', 'delete device', 'remove device', 'bypass device', // Device and effect actions 'effect', 'instrument', 'plugin', 'vst', 'au', 'add effect', 'insert effect', 'chain device', 'enable device', 'preset', 'load preset', 'save preset', 'browse preset', 'automate', 'automation', 'envelope', 'modulate', // Specific effects and instruments 'reverb', 'delay', 'echo', 'chorus', 'flanger', 'phaser', 'distortion', 'overdrive', 'saturation', 'bitcrusher', 'compressor', 'limiter', 'gate', 'expander', 'multiband', 'eq', 'equalizer', 'filter', 'low pass', 'high pass', 'band pass', 'operator', 'wavetable', 'simpler', 'impulse', 'drum machine', 'bass', 'collision', 'tension', 'poli', 'analog', 'fm8', // Mixer and routing actions 'volume', 'gain', 'level', 'amplitude', 'loudness', 'pan', 'balance', 'stereo', 'mono', 'width', 'send', 'return', 'aux', 'bus', 'route', 'routing', 'input', 'output', 'monitor', 'cue', 'headphone', 'crossfader', 'crossfade', 'assign', 'unassign', // MIDI and note actions 'midi', 'note', 'chord', 'scale', 'arpeggio', 'sequence', 'velocity', 'pitch', 'bend', 'modulation', 'aftertouch', 'cc', 'control change', 'program change', 'sysex', 'quantization', 'swing', 'groove', 'humanize', 'randomize', 'transpose', 'invert', 'retrograde', 'legato', 'staccato' ]; static async processHybridQuery(query) { // console.log(`๐Ÿ” Processing hybrid query: "${query}"`) const parts = this.splitQuery(query); let knowledgeAnswer = ''; let relevantTools = []; const hasKnowledge = parts.some(p => p.type === 'knowledge'); const hasActions = parts.some(p => p.type === 'action'); // Process knowledge parts if (hasKnowledge) { const knowledgeParts = parts.filter(p => p.type === 'knowledge'); for (const part of knowledgeParts) { // console.log(`๐Ÿ“š Processing knowledge query: "${part.content}"`) try { // Skip RAG for simple conversational queries to prevent token bloat if (this.isSimpleConversationalQuery(part.content)) { knowledgeAnswer += 'Simple conversational query - no knowledge context needed.\n\n'; } else { const ragResult = await queryRag(part.content, 1); // Reduced from 2 to 1 for token management const compressedKnowledge = this.compressKnowledge(ragResult, 500); // Max 500 chars knowledgeAnswer += compressedKnowledge + '\n\n'; } } catch (error) { // console.error('โŒ RAG query failed:', error) knowledgeAnswer += `Knowledge query failed: ${error instanceof Error ? error.message : String(error)}\n\n`; } } } // Process action parts - find relevant tools but don't execute if (hasActions) { const actionParts = parts.filter(p => p.type === 'action'); for (const part of actionParts) { // console.log(`โšก Finding tools for action: "${part.content}"`) try { const tools = await findRelevantTools(part.content, 6); // Increased from 3 to 6 relevantTools.push(...tools); } catch (error) { // console.error('โŒ Tool search failed:', error) // Continue processing other parts } } // ENHANCEMENT: Add context-aware essential tools const contextualTools = this.getContextualTools(query); relevantTools.push(...contextualTools); // Remove duplicates const uniqueTools = Array.from(new Map(relevantTools.map(tool => [tool.name, tool])).values()); relevantTools = uniqueTools.slice(0, 15); // Smart limit - increased from 4 } else if (hasKnowledge) { // For knowledge-only queries, add essential GET tools const allTools = Array.from(toolRegistry.values()); const essentialNames = [ 'get_song_properties', 'get_track_properties', 'get_song_view_properties', 'get_application_info', 'get_device_properties', 'get_clip_properties' ]; relevantTools = essentialNames .map(name => allTools.find(t => t.name === name)) .filter(tool => tool !== undefined); } const combinedResponse = this.generateCombinedResponse(knowledgeAnswer, relevantTools, query); return { knowledgeAnswer: knowledgeAnswer.trim(), relevantTools, combinedResponse, queryAnalysis: { hasKnowledge, hasActions, detectedParts: parts } }; } static splitQuery(query) { // Use the enhanced classification pipeline return this.classifyWithPipeline(query); } /** * Multi-stage classification pipeline for 100% accuracy */ static classifyWithPipeline(query) { const results = []; // Stage 1: Verb Analysis const verbResult = this.classifyByVerbAnalysis(query); results.push(verbResult); // Stage 2: Pattern Matching const patternResult = this.classifyByPatterns(query); results.push(patternResult); // Stage 3: Tool-Aware Classification const toolResult = this.classifyByToolAnalysis(query); results.push(toolResult); // Weighted voting with confidence scoring const knowledgeScore = results .filter(r => r.result === 'knowledge') .reduce((sum, r) => sum + r.confidence, 0); const actionScore = results .filter(r => r.result === 'action') .reduce((sum, r) => sum + r.confidence, 0); const finalType = knowledgeScore > actionScore ? 'knowledge' : 'action'; const finalConfidence = Math.max(knowledgeScore, actionScore) / results.length; // Debug logging (can be enabled for troubleshooting) if (this.DEBUG_CLASSIFICATION) { console.log(`๐Ÿ” Classification Pipeline Results for: "${query}"`); results.forEach(r => console.log(` ${r.method}: ${r.result} (${r.confidence}) - ${r.reasoning}`)); console.log(` Final: ${finalType} (${finalConfidence.toFixed(2)})`); } return [{ type: finalType, content: query, confidence: finalConfidence }]; } /** * Stage 1: Verb-based classification with context analysis */ static classifyByVerbAnalysis(query) { const words = query.toLowerCase().split(/\s+/); // Get/Read verbs (knowledge) - prioritized by position const getVerbs = ['list', 'show', 'display', 'get', 'fetch', 'view', 'check', 'see', 'tell', 'find', 'retrieve', 'obtain']; const actionVerbs = ['create', 'set', 'change', 'delete', 'add', 'modify', 'record', 'play', 'stop', 'make', 'build', 'generate']; // Find the main verb (usually first or second word, sometimes third) for (let i = 0; i < Math.min(4, words.length); i++) { if (getVerbs.includes(words[i])) { return { method: 'verb', result: 'knowledge', confidence: 0.9, reasoning: `Found GET verb: "${words[i]}" at position ${i}` }; } if (actionVerbs.includes(words[i])) { return { method: 'verb', result: 'action', confidence: 0.9, reasoning: `Found ACTION verb: "${words[i]}" at position ${i}` }; } } // Context-based classification for edge cases if (query.includes('current') || query.includes('what is') || query.includes('properties') || query.includes('name')) { return { method: 'verb', result: 'knowledge', confidence: 0.7, reasoning: 'Found context indicators for knowledge query' }; } if (query.includes('set to') || query.includes('make it') || query.includes('change to')) { return { method: 'verb', result: 'action', confidence: 0.7, reasoning: 'Found context indicators for action query' }; } return { method: 'verb', result: 'knowledge', confidence: 0.3, reasoning: 'No clear verb indicators found, defaulting to knowledge' }; } /** * Stage 2: Pattern matching for query structures */ static classifyByPatterns(query) { const lowerQuery = query.toLowerCase(); // Knowledge patterns with confidence scores const knowledgePatterns = [ { pattern: /^(list|show|display|get|what|which|how many)\s+.*$/, confidence: 0.95, name: 'GET command pattern' }, { pattern: /^(tell me|show me|give me)\s+.*$/, confidence: 0.9, name: 'Request information pattern' }, { pattern: /.*\s+(name|names|properties|info|information)\s*$/, confidence: 0.85, name: 'Property query pattern' }, { pattern: /^(what is|what are|how does|where is)\s+.*$/, confidence: 0.9, name: 'Question pattern' }, { pattern: /.*\s+(tempo|time signature|position|track)\s+(name|names|properties)?\s*$/, confidence: 0.8, name: 'Music property pattern' }, // Lower confidence for "current" pattern to avoid overriding action verbs { pattern: /^(show|display|get|list|tell me|give me).*\s+(current|now|present|existing)\s+.*$/, confidence: 0.85, name: 'GET current state pattern' } ]; // Action patterns with confidence scores const actionPatterns = [ { pattern: /^(create|make|add|insert|build|generate)\s+.*$/, confidence: 0.95, name: 'Create command pattern' }, { pattern: /^(set|change|modify|adjust)\s+.*\s+(to|as|with)\s+.*$/, confidence: 0.9, name: 'Set value pattern' }, { pattern: /^(delete|remove|clear|erase)\s+.*$/, confidence: 0.95, name: 'Delete command pattern' }, { pattern: /^(play|stop|record|start|pause)\s*.*$/, confidence: 0.9, name: 'Transport command pattern' }, { pattern: /^(load|import|export|save|render|bounce)\s+.*$/, confidence: 0.85, name: 'File operation pattern' } ]; // Check knowledge patterns first (prioritize GET operations) for (const { pattern, confidence, name } of knowledgePatterns) { if (pattern.test(lowerQuery)) { return { method: 'pattern', result: 'knowledge', confidence, reasoning: `Matched ${name}` }; } } // Check action patterns for (const { pattern, confidence, name } of actionPatterns) { if (pattern.test(lowerQuery)) { return { method: 'pattern', result: 'action', confidence, reasoning: `Matched ${name}` }; } } return { method: 'pattern', result: 'knowledge', confidence: 0.4, reasoning: 'No specific patterns matched, defaulting to knowledge' }; } /** * Stage 3: Tool-aware classification using available tools */ static classifyByToolAnalysis(query) { if (toolRegistry.size === 0) { return { method: 'tool', result: 'knowledge', confidence: 0.3, reasoning: 'Tool registry not available' }; } // Get all available tools const allTools = Array.from(toolRegistry.values()); // Separate GET vs SET/CREATE/MODIFY tools const getTools = allTools.filter(tool => tool.name.startsWith('get_') || tool.description.toLowerCase().includes('get ') || tool.description.toLowerCase().includes('list ') || tool.description.toLowerCase().includes('show ') || tool.description.toLowerCase().includes('display ') || tool.description.toLowerCase().includes('retrieve ') || tool.description.toLowerCase().includes('fetch ')); const actionTools = allTools.filter(tool => tool.name.startsWith('set_') || tool.name.startsWith('create_') || tool.name.startsWith('delete_') || tool.name.startsWith('modify_') || tool.name.startsWith('add_') || tool.name.startsWith('remove_') || tool.description.toLowerCase().includes('set ') || tool.description.toLowerCase().includes('create ') || tool.description.toLowerCase().includes('modify ') || tool.description.toLowerCase().includes('delete ') || tool.description.toLowerCase().includes('change ')); // Simple keyword matching for tool classification const lowerQuery = query.toLowerCase(); let getScore = 0; let actionScore = 0; // Score based on tool name matches getTools.forEach(tool => { const toolWords = tool.name.toLowerCase().split('_'); toolWords.forEach(word => { if (lowerQuery.includes(word)) { getScore += 1; } }); }); actionTools.forEach(tool => { const toolWords = tool.name.toLowerCase().split('_'); toolWords.forEach(word => { if (lowerQuery.includes(word)) { actionScore += 1; } }); }); // Determine result based on scores if (getScore > actionScore) { return { method: 'tool', result: 'knowledge', confidence: Math.min(0.8, 0.5 + (getScore - actionScore) * 0.1), reasoning: `GET tools score: ${getScore}, ACTION tools score: ${actionScore}` }; } else if (actionScore > getScore) { return { method: 'tool', result: 'action', confidence: Math.min(0.8, 0.5 + (actionScore - getScore) * 0.1), reasoning: `ACTION tools score: ${actionScore}, GET tools score: ${getScore}` }; } else { return { method: 'tool', result: 'knowledge', confidence: 0.4, reasoning: `Tied scores (${getScore}), defaulting to knowledge` }; } } static generateCombinedResponse(knowledge, tools, originalQuery) { let response = ''; if (knowledge) { response += `๐Ÿ“š **Knowledge:**\n${knowledge}\n\n`; } if (tools.length > 0) { response += `๐Ÿ”ง **Relevant Tools:**\n`; tools.forEach((tool, i) => { response += `${i + 1}. **${tool.name}** (${tool.category}): ${tool.description}\n`; }); } return response.trim(); } /** * Test function to validate the enhanced classification system * Can be called during development to verify accuracy */ static testClassification() { const testCases = [ // Knowledge queries (should be classified as 'knowledge') { query: "List the current tempo, time signature, playhead position, and every track name in the set", expected: 'knowledge' }, { query: "Show me all track properties", expected: 'knowledge' }, { query: "Get the current song information", expected: 'knowledge' }, { query: "What are the device properties", expected: 'knowledge' }, { query: "Display track names", expected: 'knowledge' }, { query: "Tell me the current tempo", expected: 'knowledge' }, { query: "Retrieve clip properties", expected: 'knowledge' }, // Action queries (should be classified as 'action') { query: "Set the tempo to 120 BPM", expected: 'action' }, { query: "Create a new MIDI track", expected: 'action' }, { query: "Load a reverb effect", expected: 'action' }, { query: "Delete the selected clip", expected: 'action' }, { query: "Record for 30 seconds", expected: 'action' }, { query: "Play the current scene", expected: 'action' }, { query: "Generate a techno kick", expected: 'action' } ]; console.log('๐Ÿงช Testing Enhanced Classification System...\n'); let correct = 0; let total = testCases.length; testCases.forEach((testCase, index) => { const results = this.classifyWithPipeline(testCase.query); const classified = results[0]?.type || 'unknown'; const isCorrect = classified === testCase.expected; if (isCorrect) correct++; console.log(`Test ${index + 1}: ${isCorrect ? 'โœ…' : 'โŒ'}`); console.log(` Query: "${testCase.query}"`); console.log(` Expected: ${testCase.expected}, Got: ${classified} (${results[0]?.confidence.toFixed(2)})`); if (!isCorrect) { console.log(` ๐Ÿ” Debug: Running individual stages...`); const verbResult = this.classifyByVerbAnalysis(testCase.query); const patternResult = this.classifyByPatterns(testCase.query); const toolResult = this.classifyByToolAnalysis(testCase.query); console.log(` Verb: ${verbResult.result} (${verbResult.confidence}) - ${verbResult.reasoning}`); console.log(` Pattern: ${patternResult.result} (${patternResult.confidence}) - ${patternResult.reasoning}`); console.log(` Tool: ${toolResult.result} (${toolResult.confidence}) - ${toolResult.reasoning}`); } console.log(); }); const accuracy = (correct / total * 100).toFixed(1); console.log(`๐ŸŽฏ Classification Accuracy: ${correct}/${total} (${accuracy}%)`); if (accuracy === '100.0') { console.log('๐ŸŽ‰ Perfect accuracy achieved!'); } else { console.log(`๐Ÿ“ˆ Improvement needed: ${total - correct} misclassifications`); } } /** * Enable or disable debug logging for classification pipeline */ static setDebugMode(enabled) { this.DEBUG_CLASSIFICATION = enabled; console.log(`๐Ÿ› Classification debug mode: ${enabled ? 'ENABLED' : 'DISABLED'}`); } /** * Get contextual tools based on query content - ENSURES ESSENTIAL TOOLS ARE INCLUDED */ static getContextualTools(query) { const allTools = Array.from(toolRegistry.values()); const contextualTools = []; const lowerQuery = query.toLowerCase(); // Context-aware tool inclusion based on query content const toolMappings = [ // Track operations { keywords: ['track', 'create track', 'new track'], tools: ['create_track', 'set_tracks_property'] }, { keywords: ['rename', 'name', 'called', 'named'], tools: ['set_tracks_property'] }, // Device/Instrument operations { keywords: ['load', 'preset', 'instrument', 'piano', '808', 'kit', 'effect'], tools: ['load_device', 'list_resources'] }, { keywords: ['grand piano', 'piano', 'keys'], tools: ['load_device', 'list_resources'] }, { keywords: ['808', 'drum', 'kick', 'snare'], tools: ['load_device', 'list_resources'] }, // Clip operations { keywords: ['clip', 'empty clip', 'bar', 'pattern'], tools: ['create_midi_clip', 'add_notes_to_clip'] }, { keywords: ['loop', 'looping'], tools: ['duplicate_clip_loop', 'set_clips_property'] }, // Parameter/Effect operations { keywords: ['fat', 'tweak', 'effect', 'parameter'], tools: ['modify_device_parameter_value', 'load_device'] }, // Audio operations { keywords: ['record', 'audio', 'recording'], tools: ['record_by_time_range', 'create_audio_clip'] }, // Song operations { keywords: ['tempo', 'bpm', 'time signature'], tools: ['set_song_property', 'get_song_properties'] }, { keywords: ['arrangement', 'song', 'length'], tools: ['set_song_property', 'get_song_view_properties'] } ]; // Find matching tools based on query content for (const mapping of toolMappings) { const hasKeyword = mapping.keywords.some(keyword => lowerQuery.includes(keyword)); if (hasKeyword) { for (const toolName of mapping.tools) { const tool = allTools.find(t => t.name === toolName); if (tool && !contextualTools.some(ct => ct.name === toolName)) { contextualTools.push(tool); } } } } return contextualTools; } /** * Detect simple conversational queries that don't need RAG knowledge */ static isSimpleConversationalQuery(query) { const lowerQuery = query.toLowerCase().trim(); // Simple conversational patterns that don't need knowledge base const conversationalPatterns = [ /^what'?s (the )?error\??$/, /^what happened\??$/, /^what went wrong\??$/, /^why did (it|that) fail\??$/, /^is (it|that) working\??$/, /^did (it|that) work\??$/, /^(are we|am i) done\??$/, /^(is it|are we) complete\??$/, /^what now\??$/, /^next step\??$/, /^continue$/, /^ok$/, /^yes$/, /^no$/ ]; return conversationalPatterns.some(pattern => pattern.test(lowerQuery)); } /** * Compress knowledge to prevent token bloat */ static compressKnowledge(knowledge, maxLength) { if (!knowledge || knowledge.length <= maxLength) return knowledge; // Extract most important sentences const sentences = knowledge.split(/[.!?]+/).filter(s => s.trim().length > 10); let compressed = ''; // Prioritize sentences with key terms const keyTerms = ['ableton', 'live', 'track', 'tempo', 'device', 'clip', 'mcp', 'tool']; const scored = sentences.map(sentence => ({ text: sentence.trim(), score: keyTerms.reduce((score, term) => score + (sentence.toLowerCase().includes(term) ? 1 : 0), 0) })); // Sort by relevance scored.sort((a, b) => b.score - a.score); for (const item of scored) { const nextLength = compressed.length + item.text.length + 2; if (nextLength <= maxLength) { compressed += item.text + '. '; } else { break; } } return compressed.trim() || knowledge.substring(0, maxLength); } /** * Add this method to find functionally correct tools */ static async findFunctionallyRelevantTools(query, queryType) { const allTools = Array.from(toolRegistry.values()); if (queryType === 'knowledge') { // For knowledge queries, prioritize GET tools return allTools .filter(tool => tool.name.startsWith('get_') || tool.description.toLowerCase().includes('get ') || tool.description.toLowerCase().includes('list ') || tool.description.toLowerCase().includes('retrieve ')) .slice(0, 8); // Allow more GET tools } else { // For action queries, use vector search but filter by action verbs const vectorTools = await findRelevantTools(query, 6); return vectorTools.filter(tool => tool.name.startsWith('set_') || tool.name.startsWith('create_') || tool.name.startsWith('load_') || tool.name.startsWith('modify_') || tool.name.startsWith('delete_')); } } } //# sourceMappingURL=hybrid-processor.js.map