Skip to content

Commit d9b99bd

Browse files
shinohara-rinnekomeowww
authored andcommitted
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.
1 parent b4f606e commit d9b99bd

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

services/minecraft/src/agents/action/adapter.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { agent } from 'neuri'
88
import { system, user } from 'neuri/openai'
99

1010
import { BaseLLMHandler } from '../../cognitive/conscious/handler'
11+
import { ActionError } from '../../utils/errors'
1112
import { useLogger } from '../../utils/logger'
1213
import { generateActionSystemPrompt } from './system-prompt'
1314
import { actionsList } from './tools'
@@ -25,7 +26,19 @@ export async function createActionNeuriAgent(mineflayer: Mineflayer): Promise<Ag
2526
logger.withFields({ name: action.name, parameters }).log('Calling action')
2627
mineflayer.memory.actions.push(action)
2728
const fn = action.perform(mineflayer)
28-
return await fn(...Object.values(parameters))
29+
try {
30+
return await fn(...Object.values(parameters))
31+
}
32+
catch (error) {
33+
// Return ActionError as a result string instead of throwing
34+
// This allows the LLM to learn from tool failures during its reasoning phase
35+
if (error instanceof ActionError) {
36+
logger.withError(error).warn('Action failed during tool call')
37+
return `[FAILED] ${error.code}: ${error.message}${error.context ? ` (${JSON.stringify(error.context)})` : ''}`
38+
}
39+
// Re-throw non-ActionError errors (unexpected failures)
40+
throw error
41+
}
2942
},
3043
{ description: action.description },
3144
)

services/minecraft/src/skills/crafting.ts

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { Mineflayer } from '../libs/mineflayer'
77
import { ActionError } from '../utils/errors'
88
import { useLogger } from '../utils/logger'
99
import { McData } from '../utils/mcdata'
10+
import { planRecipe } from '../utils/recipe-planner'
1011
import { ensureCraftingTable } from './actions/ensure'
1112
import { collectBlock, placeBlock } from './blocks'
1213
import { goToNearestBlock, goToPosition, moveAway } from './movement'
@@ -40,8 +41,7 @@ export async function craftRecipe(
4041
try {
4142
await mineflayer.bot.craft(recipe, num, craftingTable ?? undefined)
4243
logger.log(
43-
`Successfully crafted ${num} ${itemName}${
44-
craftingTable ? ' using crafting table' : ''
44+
`Successfully crafted ${num} ${itemName}${craftingTable ? ' using crafting table' : ''
4545
}.`,
4646
)
4747
return true
@@ -78,8 +78,7 @@ export async function craftRecipe(
7878
}
7979
catch (err) {
8080
logger.log(
81-
`Attempt ${attempts + 1} to move to crafting table failed: ${
82-
(err as Error).message
81+
`Attempt ${attempts + 1} to move to crafting table failed: ${(err as Error).message
8382
}`,
8483
)
8584
if (err instanceof ActionError)
@@ -137,12 +136,56 @@ export async function craftRecipe(
137136
}
138137
}
139138

140-
// RECURSION GUARD:
139+
// RECURSION GUARD + AUTO-CRAFT INTERMEDIATE MATERIALS:
141140
// If we failed to craft basic items (planks, sticks) without a table,
142-
// do NOT try to find a table. These items do not need a table.
143-
// Seeking a table often triggers "ensureCraftingTable" -> "ensurePlanks" -> infinite loop.
141+
// check if we can craft from raw materials using the recipe planner.
144142
if (itemName.includes('planks') || itemName === 'stick' || itemName === 'crafting_table') {
145-
logger.log(`Recursion Guard: Skipping crafting table search for basic item: ${itemName}`)
143+
logger.log(`Recursion Guard: Checking if we can craft ${itemName} from raw materials`)
144+
145+
// Use the recipe planner to see if we can craft this item
146+
const plan = planRecipe(mineflayer.bot, itemName, num)
147+
148+
if (plan.status === 'unknown_item') {
149+
throw new ActionError('UNKNOWN', `Unknown item: ${itemName}`)
150+
}
151+
152+
// If we can craft now (have all materials including intermediates), do it
153+
if (plan.canCraftNow && plan.steps.length > 0) {
154+
logger.log(`Recipe planner found craftable path with ${plan.steps.length} steps`)
155+
156+
// Craft all intermediate steps first (in reverse order = base materials first)
157+
for (const step of [...plan.steps].reverse()) {
158+
if (step.action === 'craft') {
159+
logger.log(`Auto-crafting intermediate: ${step.amount}x ${step.item}`)
160+
// Use direct bot.craft for intermediates to avoid infinite recursion
161+
const stepItemId = mcData.getItemId(step.item)
162+
if (!stepItemId) {
163+
throw new ActionError('UNKNOWN', `Unknown intermediate item: ${step.item}`)
164+
}
165+
const stepRecipes = mineflayer.bot.recipesFor(stepItemId, null, 1, null)
166+
if (stepRecipes && stepRecipes.length > 0) {
167+
const outputPerCraft = stepRecipes[0].result?.count ?? 1
168+
const craftCount = Math.ceil(step.amount / outputPerCraft)
169+
await mineflayer.bot.craft(stepRecipes[0], craftCount)
170+
logger.log(`Successfully crafted ${craftCount}x ${step.item}`)
171+
}
172+
}
173+
}
174+
175+
return true
176+
}
177+
178+
// Can't craft - provide helpful error message
179+
if (Object.keys(plan.missing).length > 0) {
180+
const missingList = Object.entries(plan.missing)
181+
.map(([item, count]) => `${count}x ${item}`)
182+
.join(', ')
183+
throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing: ${missingList}`, {
184+
item: itemName,
185+
missing: plan.missing,
186+
})
187+
}
188+
146189
throw new ActionError('RESOURCE_MISSING', `Cannot craft ${itemName} - missing ingredients`, { item: itemName })
147190
}
148191

0 commit comments

Comments
 (0)