UNPKG

ableton-mcp-server-rag

Version:
196 lines 8.35 kB
// Enhanced RAG tools with backward compatibility // Add this new method to the existing RagTools class smartQueryV2({ query, maxTokenBudget = 180000, clientType = 'swift' }, { query: string, maxTokenBudget: number, clientType: string }); { // console.log("🧠 smart_query_v2 tool invoked with query:", query) try { // Process the hybrid query const result = await HybridQueryProcessor.processHybridQuery(query); // Get all available tools from the registry const { toolRegistry } = await import('./tool-vector.js'); const allTools = Array.from(toolRegistry.values()); // Smart tool selection with token budgeting const selectedTools = this.selectToolsWithTokenBudget(result.relevantTools, allTools, result.queryAnalysis.hasKnowledge, result.queryAnalysis.hasActions, maxTokenBudget, query); // Compress knowledge context const compressedKnowledge = this.compressKnowledgeContext(result.knowledgeAnswer, Math.min(1500, Math.floor(maxTokenBudget * 0.08)) // Max 8% for knowledge ); // Calculate token usage const tokenAnalysis = this.calculateTokenUsage(compressedKnowledge, selectedTools); // Format response based on client type if (clientType === 'swift') { // Swift-compatible format const response = { knowledge: compressedKnowledge, tools: selectedTools.map(tool => tool.name), queryType: result.queryAnalysis.hasKnowledge ? 'knowledge' : 'action', confidence: result.queryAnalysis.detectedParts[0]?.confidence || 0.5, tokenAnalysis, debugInfo: { originalToolCount: result.relevantTools.length, selectedToolCount: selectedTools.length, tokenBudgetUsed: tokenAnalysis.total, tokenBudgetLimit: maxTokenBudget, withinBudget: tokenAnalysis.total <= maxTokenBudget } }; return Result.data(response); } else { // Legacy string format for backward compatibility let response = ''; if (compressedKnowledge) { response += `KNOWLEDGE:\n${compressedKnowledge}\n\n`; } if (selectedTools.length > 0) { response += `TOOLS:\n`; selectedTools.forEach(tool => { response += `${tool.name}\n`; }); } return Result.data(response.trim() || "No relevant knowledge or tools found for this query."); } } catch (error) { // console.error("❌ Error in smart_query_v2:", error) return Result.error(`Smart query failed: ${error instanceof Error ? error.message : String(error)}`); } } selectToolsWithTokenBudget(suggestedTools, any[], allTools, any[], hasKnowledge, boolean, hasActions, boolean, maxTokenBudget, number, query, string); any[]; { const toolTokenBudget = Math.floor(maxTokenBudget * 0.7); // 70% for tools const selected = []; let currentTokens = 0; // Priority 1: Essential tools based on query type const essentialToolNames = this.getEssentialToolNames(hasKnowledge, hasActions, query); for (const toolName of essentialToolNames) { const tool = allTools.find(t => t.name === toolName); if (tool && !selected.some(s => s.name === toolName)) { const tokenCost = this.estimateToolTokens(tool); if (currentTokens + tokenCost <= toolTokenBudget) { selected.push(tool); currentTokens += tokenCost; } } } // Priority 2: Add suggested tools from RAG for (const tool of suggestedTools) { if (!selected.some(s => s.name === tool.name)) { const tokenCost = this.estimateToolTokens(tool); if (currentTokens + tokenCost <= toolTokenBudget && selected.length < 20) { selected.push(tool); currentTokens += tokenCost; } } } // Priority 3: Add complementary tools from different categories const seenCategories = new Set(selected.map(t => t.category)); for (const tool of allTools) { if (selected.length >= 18) break; // Reasonable limit if (!selected.some(s => s.name === tool.name) && !seenCategories.has(tool.category)) { const tokenCost = this.estimateToolTokens(tool); if (currentTokens + tokenCost <= toolTokenBudget) { selected.push(tool); currentTokens += tokenCost; seenCategories.add(tool.category); } } } return selected; } getEssentialToolNames(hasKnowledge, boolean, hasActions, boolean, query, string); string[]; { const essential = []; if (hasKnowledge) { essential.push('get_song_properties', 'get_track_properties', 'get_application_info', 'get_song_view_properties'); } if (hasActions) { const lowerQuery = query.toLowerCase(); // Context-aware tool selection if (lowerQuery.includes('track')) { essential.push('create_track', 'set_tracks_property'); } if (lowerQuery.includes('clip')) { essential.push('create_midi_clip', 'add_notes_to_clip'); } if (lowerQuery.includes('tempo') || lowerQuery.includes('bpm')) { essential.push('set_song_property'); } if (lowerQuery.includes('device') || lowerQuery.includes('effect') || lowerQuery.includes('reverb') || lowerQuery.includes('delay')) { essential.push('load_device', 'modify_device_parameter_value', 'list_resources'); } if (lowerQuery.includes('record')) { essential.push('record_by_time_range'); } // Always include these for action queries essential.push('get_device_properties', 'get_clip_properties'); } return [...new Set(essential)]; // Remove duplicates } estimateToolTokens(tool, any); number; { const baseTokens = 400; // More accurate base estimate const descriptionTokens = (tool.description || '').length / 3.2; const paramsTokens = JSON.stringify(tool.params || {}).length / 3.2; const keywordTokens = (tool.keywords || []).join(' ').length / 3.2; return Math.ceil(baseTokens + descriptionTokens + paramsTokens + keywordTokens); } compressKnowledgeContext(knowledge, string, maxTokens, number); string; { if (!knowledge) return ''; const maxLength = Math.floor(maxTokens * 3.2); // Convert tokens to chars if (knowledge.length <= maxLength) return knowledge; // Smart compression: prioritize relevant content const sentences = knowledge.split(/[.!?]+/).filter(s => s.trim().length > 15); const keyTerms = ['ableton', 'live', 'track', 'tempo', 'device', 'clip', 'effect', 'bpm']; // Score sentences by relevance const scored = sentences.map(sentence => ({ text: sentence.trim(), score: keyTerms.reduce((score, term) => score + (sentence.toLowerCase().includes(term) ? 2 : 0), 0) + (sentence.length < 100 ? 1 : 0) // Prefer shorter sentences })); // Sort by relevance scored.sort((a, b) => b.score - a.score); let compressed = ''; for (const item of scored) { const nextLength = compressed.length + item.text.length + 2; if (nextLength <= maxLength) { compressed += item.text + '. '; } else { break; } } return compressed.trim(); } calculateTokenUsage(knowledge, string, tools, any[]); any; { const knowledgeTokens = Math.ceil(knowledge.length / 3.2); const toolsTokens = tools.reduce((sum, tool) => sum + this.estimateToolTokens(tool), 0); const total = knowledgeTokens + toolsTokens; return { knowledge: knowledgeTokens, tools: toolsTokens, total, breakdown: { knowledgePercent: Math.round((knowledgeTokens / total) * 100), toolsPercent: Math.round((toolsTokens / total) * 100) }, withinBudget: total <= 180000, budgetUsage: Math.round((total / 180000) * 100) }; } export {}; //# sourceMappingURL=rag-tools-enhanced.js.map