ableton-mcp-server-rag
Version:
Ableton Live MCP depend on Ableton JS
636 lines โข 32 kB
JavaScript
// 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