mcp-reasoner
by Jacck
- mcp-reasoner
- dist
- strategies
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();
}
}