From d9b99bdda1e9cac78375e324ec5a0dd96db45efc Mon Sep 17 00:00:00 2001 From: Rin Date: Wed, 21 Jan 2026 02:54:42 +0800 Subject: [PATCH] feat(minecraft): improve error handling and auto-craft intermediates...but it doesn't work very well 1. Wrap neuri tool invocations to catch ActionError and return as result strings instead of throwing. This allows the LLM to learn from tool failures during its reasoning phase. 2. Enhance craftRecipe to use the recipe planner for auto-crafting intermediate materials. If the bot has raw materials (e.g., logs) but not direct ingredients (e.g., planks), it will automatically craft the intermediates first. 3. Add memory note about the dual tool interface discovery (neuri native tool calls vs JSON actions through TaskExecutor) for future optimization work. --- .../minecraft/src/agents/action/adapter.ts | 15 ++++- services/minecraft/src/skills/crafting.ts | 59 ++++++++++++++++--- 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/services/minecraft/src/agents/action/adapter.ts b/services/minecraft/src/agents/action/adapter.ts index 60fdd245c..83cea2286 100644 --- a/services/minecraft/src/agents/action/adapter.ts +++ b/services/minecraft/src/agents/action/adapter.ts @@ -8,6 +8,7 @@ import { agent } from 'neuri' import { system, user } from 'neuri/openai' import { BaseLLMHandler } from '../../cognitive/conscious/handler' +import { ActionError } from '../../utils/errors' import { useLogger } from '../../utils/logger' import { generateActionSystemPrompt } from './system-prompt' import { actionsList } from './tools' @@ -25,7 +26,19 @@ export async function createActionNeuriAgent(mineflayer: Mineflayer): Promise "ensurePlanks" -> infinite loop. + // check if we can craft from raw materials using the recipe planner. if (itemName.includes('planks') || itemName === 'stick' || itemName === 'crafting_table') { - logger.log(`Recursion Guard: Skipping crafting table search for basic item: ${itemName}`) + logger.log(`Recursion Guard: Checking if we can craft ${itemName} from raw materials`) + + // Use the recipe planner to see if we can craft this item + const plan = planRecipe(mineflayer.bot, itemName, num) + + if (plan.status === 'unknown_item') { + throw new ActionError('UNKNOWN', `Unknown item: ${itemName}`) + } + + // If we can craft now (have all materials including intermediates), do it + if (plan.canCraftNow && plan.steps.length > 0) { + logger.log(`Recipe planner found craftable path with ${plan.steps.length} steps`) + + // Craft all intermediate steps first (in reverse order = base materials first) + for (const step of [...plan.steps].reverse()) { + if (step.action === 'craft') { + logger.log(`Auto-crafting intermediate: ${step.amount}x ${step.item}`) + // Use direct bot.craft for intermediates to avoid infinite recursion + const stepItemId = mcData.getItemId(step.item) + if (!stepItemId) { + throw new ActionError('UNKNOWN', `Unknown intermediate item: ${step.item}`) + } + const stepRecipes = mineflayer.bot.recipesFor(stepItemId, null, 1, null) + if (stepRecipes && stepRecipes.length > 0) { + const outputPerCraft = stepRecipes[0].result?.count ?? 1 + const craftCount = Math.ceil(step.amount / outputPerCraft) + await mineflayer.bot.craft(stepRecipes[0], craftCount) + logger.log(`Successfully crafted ${craftCount}x ${step.item}`) + } + } + } + + return true + } + + // Can't craft - provide helpful error message + if (Object.keys(plan.missing).length > 0) { + const missingList = Object.entries(plan.missing) + .map(([item, count]) => `${count}x ${item}`) + .join(', ') + throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing: ${missingList}`, { + item: itemName, + missing: plan.missing, + }) + } + throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing ingredients`, { item: itemName }) }