mcp-reasoner

import { v4 as uuidv4 } from 'uuid'; import { CONFIG } from '../types.js'; import { BaseStrategy } from './base.js'; export class BeamSearchStrategy extends BaseStrategy { constructor(stateManager, beamWidth = CONFIG.beamWidth) { super(stateManager); this.beamWidth = beamWidth; this.beams = new Map(); } async processThought(request) { const nodeId = uuidv4(); const parentNode = request.parentId ? await this.getNode(request.parentId) : undefined; const node = { id: nodeId, thought: request.thought, depth: request.thoughtNumber - 1, score: 0, children: [], parentId: request.parentId, isComplete: !request.nextThoughtNeeded }; // Evaluate and score the node node.score = this.evaluateThought(node, parentNode); await this.saveNode(node); // Update parent if exists if (parentNode) { parentNode.children.push(node.id); await this.saveNode(parentNode); } // Manage beam at current depth let currentBeam = this.beams.get(node.depth) || []; currentBeam.push(node); currentBeam.sort((a, b) => b.score - a.score); // Prune beam to maintain beam width if (currentBeam.length > this.beamWidth) { currentBeam = currentBeam.slice(0, this.beamWidth); } this.beams.set(node.depth, currentBeam); // Calculate path statistics const currentPath = await this.stateManager.getPath(nodeId); const pathScore = currentPath.reduce((acc, n) => acc + n.score, 0) / currentPath.length; // Get best path score from all beams const bestBeamScore = Math.max(...Array.from(this.beams.values()) .flat() .map(n => n.score)); return { nodeId: node.id, thought: node.thought, score: node.score, depth: node.depth, isComplete: node.isComplete, nextThoughtNeeded: request.nextThoughtNeeded, possiblePaths: this.calculatePossiblePaths(), bestScore: Math.max(pathScore, bestBeamScore) }; } calculatePossiblePaths() { let totalPaths = 0; this.beams.forEach((beam, depth) => { const nextBeam = this.beams.get(depth + 1); if (nextBeam) { totalPaths += beam.length * nextBeam.length; } else { totalPaths += beam.length; } }); return totalPaths; } async getBestPath() { // Find the deepest beam const maxDepth = Math.max(...Array.from(this.beams.keys())); const deepestBeam = this.beams.get(maxDepth) || []; if (deepestBeam.length === 0) return []; // Get the best scoring node from deepest beam const bestNode = deepestBeam.reduce((a, b) => a.score > b.score ? a : b); // Reconstruct path const path = await this.stateManager.getPath(bestNode.id); return path; } async getMetrics() { const baseMetrics = await super.getMetrics(); return { ...baseMetrics, beamWidth: this.beamWidth, activeBeams: this.beams.size, totalBeamNodes: Array.from(this.beams.values()).flat().length }; } async clear() { await super.clear(); this.beams.clear(); } }