mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-04-21 21:52:07 +02:00
refactor: add TaskManager, move task mgmt out of coder
This commit is contained in:
parent
e8e3cb7116
commit
b11416d0fc
10 changed files with 233 additions and 195 deletions
|
@ -4,6 +4,7 @@ import { Prompter } from './prompter.js';
|
||||||
import { initModes } from './modes.js';
|
import { initModes } from './modes.js';
|
||||||
import { initBot } from '../utils/mcdata.js';
|
import { initBot } from '../utils/mcdata.js';
|
||||||
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction } from './commands/index.js';
|
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction } from './commands/index.js';
|
||||||
|
import { TaskManager } from './tasks.js';
|
||||||
import { NPCContoller } from './npc/controller.js';
|
import { NPCContoller } from './npc/controller.js';
|
||||||
import { MemoryBank } from './memory_bank.js';
|
import { MemoryBank } from './memory_bank.js';
|
||||||
import { SelfPrompter } from './self_prompter.js';
|
import { SelfPrompter } from './self_prompter.js';
|
||||||
|
@ -13,6 +14,7 @@ import settings from '../../settings.js';
|
||||||
|
|
||||||
export class Agent {
|
export class Agent {
|
||||||
async start(profile_fp, load_mem=false, init_message=null, count_id=0) {
|
async start(profile_fp, load_mem=false, init_message=null, count_id=0) {
|
||||||
|
this.tasks = new TaskManager(this);
|
||||||
this.prompter = new Prompter(this, profile_fp);
|
this.prompter = new Prompter(this, profile_fp);
|
||||||
this.name = this.prompter.getName();
|
this.name = this.prompter.getName();
|
||||||
this.history = new History(this);
|
this.history = new History(this);
|
||||||
|
@ -40,7 +42,7 @@ export class Agent {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
console.log(`${this.name} spawned.`);
|
console.log(`${this.name} spawned.`);
|
||||||
this.coder.clear();
|
this.clearBotLogs();
|
||||||
|
|
||||||
const ignore_messages = [
|
const ignore_messages = [
|
||||||
"Set own game mode to",
|
"Set own game mode to",
|
||||||
|
@ -91,6 +93,17 @@ export class Agent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interruptBot() {
|
||||||
|
this.bot.interrupt_code = true;
|
||||||
|
this.bot.collectBlock.cancelTask();
|
||||||
|
this.bot.pathfinder.stop();
|
||||||
|
this.bot.pvp.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearBotLogs() {
|
||||||
|
this.bot.output = '';
|
||||||
|
this.bot.interrupt_code = false;
|
||||||
|
}
|
||||||
|
|
||||||
async cleanChat(message, translate_up_to=-1) {
|
async cleanChat(message, translate_up_to=-1) {
|
||||||
let to_translate = message;
|
let to_translate = message;
|
||||||
|
@ -250,8 +263,8 @@ export class Agent {
|
||||||
this.cleanKill('Bot disconnected! Killing agent process.');
|
this.cleanKill('Bot disconnected! Killing agent process.');
|
||||||
});
|
});
|
||||||
this.bot.on('death', () => {
|
this.bot.on('death', () => {
|
||||||
this.coder.cancelResume();
|
this.tasks.cancelResume();
|
||||||
this.coder.stop();
|
this.tasks.stop();
|
||||||
});
|
});
|
||||||
this.bot.on('kicked', (reason) => {
|
this.bot.on('kicked', (reason) => {
|
||||||
console.warn('Bot kicked!', reason);
|
console.warn('Bot kicked!', reason);
|
||||||
|
@ -267,7 +280,7 @@ export class Agent {
|
||||||
this.bot.clearControlStates();
|
this.bot.clearControlStates();
|
||||||
this.bot.pathfinder.stop(); // clear any lingering pathfinder
|
this.bot.pathfinder.stop(); // clear any lingering pathfinder
|
||||||
this.bot.modes.unPauseAll();
|
this.bot.modes.unPauseAll();
|
||||||
this.coder.executeResume();
|
this.tasks.resumeTask();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Init NPC controller
|
// Init NPC controller
|
||||||
|
@ -297,7 +310,7 @@ export class Agent {
|
||||||
}
|
}
|
||||||
|
|
||||||
isIdle() {
|
isIdle() {
|
||||||
return !this.coder.executing && !this.coder.generating;
|
return !this.tasks.executing && !this.coder.generating;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanKill(msg='Killing agent process...') {
|
cleanKill(msg='Killing agent process...') {
|
||||||
|
|
|
@ -7,11 +7,8 @@ export class Coder {
|
||||||
this.agent = agent;
|
this.agent = agent;
|
||||||
this.file_counter = 0;
|
this.file_counter = 0;
|
||||||
this.fp = '/bots/'+agent.name+'/action-code/';
|
this.fp = '/bots/'+agent.name+'/action-code/';
|
||||||
this.executing = false;
|
|
||||||
this.generating = false;
|
this.generating = false;
|
||||||
this.code_template = '';
|
this.code_template = '';
|
||||||
this.timedout = false;
|
|
||||||
this.cur_action_name = '';
|
|
||||||
|
|
||||||
readFile('./bots/template.js', 'utf8', (err, data) => {
|
readFile('./bots/template.js', 'utf8', (err, data) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
|
@ -83,7 +80,7 @@ export class Coder {
|
||||||
|
|
||||||
async generateCode(agent_history) {
|
async generateCode(agent_history) {
|
||||||
// wrapper to prevent overlapping code generation loops
|
// wrapper to prevent overlapping code generation loops
|
||||||
await this.stop();
|
await this.agent.tasks.stop();
|
||||||
this.generating = true;
|
this.generating = true;
|
||||||
let res = await this.generateCodeLoop(agent_history);
|
let res = await this.generateCodeLoop(agent_history);
|
||||||
this.generating = false;
|
this.generating = false;
|
||||||
|
@ -119,7 +116,7 @@ export class Coder {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failures >= 3) {
|
if (failures >= 3) {
|
||||||
return {success: false, message: 'Action failed, agent would not write code.', interrupted: false, timedout: false};
|
return { success: false, message: 'Action failed, agent would not write code.', interrupted: false, timedout: false };
|
||||||
}
|
}
|
||||||
messages.push({
|
messages.push({
|
||||||
role: 'system',
|
role: 'system',
|
||||||
|
@ -137,22 +134,22 @@ export class Coder {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const execution_file = await this.stageCode(code);
|
const executionModuleExports = await this.stageCode(code);
|
||||||
if (!execution_file) {
|
if (!executionModuleExports) {
|
||||||
agent_history.add('system', 'Failed to stage code, something is wrong.');
|
agent_history.add('system', 'Failed to stage code, something is wrong.');
|
||||||
return {success: false, message: null, interrupted: false, timedout: false};
|
return {success: false, message: null, interrupted: false, timedout: false};
|
||||||
}
|
}
|
||||||
|
|
||||||
code_return = await this.execute(async ()=>{
|
code_return = await this.agent.tasks.runTask('newAction', async () => {
|
||||||
return await execution_file.main(this.agent.bot);
|
return await executionModuleExports.main(this.agent.bot);
|
||||||
}, settings.code_timeout_mins);
|
}, { timeout: settings.code_timeout_mins });
|
||||||
if (code_return.interrupted && !code_return.timedout)
|
if (code_return.interrupted && !code_return.timedout)
|
||||||
return {success: false, message: null, interrupted: true, timedout: false};
|
return { success: false, message: null, interrupted: true, timedout: false };
|
||||||
console.log("Code generation result:", code_return.success, code_return.message);
|
console.log("Code generation result:", code_return.success, code_return.message);
|
||||||
|
|
||||||
if (code_return.success) {
|
if (code_return.success) {
|
||||||
const summary = "Summary of newAction\nAgent wrote this code: \n```" + this.sanitizeCode(code) + "```\nCode Output:\n" + code_return.message;
|
const summary = "Summary of newAction\nAgent wrote this code: \n```" + this.sanitizeCode(code) + "```\nCode Output:\n" + code_return.message;
|
||||||
return {success: true, message: summary, interrupted: false, timedout: false};
|
return { success: true, message: summary, interrupted: false, timedout: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
messages.push({
|
messages.push({
|
||||||
|
@ -164,114 +161,7 @@ export class Coder {
|
||||||
content: code_return.message + '\nCode failed. Please try again:'
|
content: code_return.message + '\nCode failed. Please try again:'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return {success: false, message: null, interrupted: false, timedout: true};
|
return { success: false, message: null, interrupted: false, timedout: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeResume(func=null, timeout=10) {
|
|
||||||
const new_resume = func != null;
|
|
||||||
if (new_resume) { // start new resume
|
|
||||||
this.resume_func = func;
|
|
||||||
this.resume_name = this.cur_action_name;
|
|
||||||
}
|
|
||||||
if (this.resume_func != null && this.agent.isIdle() && (!this.agent.self_prompter.on || new_resume)) {
|
|
||||||
this.cur_action_name = this.resume_name;
|
|
||||||
let res = await this.execute(this.resume_func, timeout);
|
|
||||||
this.cur_action_name = '';
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
return {success: false, message: null, interrupted: false, timedout: false};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelResume() {
|
|
||||||
this.resume_func = null;
|
|
||||||
this.resume_name = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurActionName(name) {
|
|
||||||
this.cur_action_name = name.replace(/!/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns {success: bool, message: string, interrupted: bool, timedout: false}
|
|
||||||
async execute(func, timeout=10) {
|
|
||||||
if (!this.code_template) return {success: false, message: "Code template not loaded.", interrupted: false, timedout: false};
|
|
||||||
|
|
||||||
let TIMEOUT;
|
|
||||||
try {
|
|
||||||
console.log('executing code...\n');
|
|
||||||
await this.stop();
|
|
||||||
this.clear();
|
|
||||||
|
|
||||||
this.executing = true;
|
|
||||||
if (timeout > 0)
|
|
||||||
TIMEOUT = this._startTimeout(timeout);
|
|
||||||
await func(); // open fire
|
|
||||||
this.executing = false;
|
|
||||||
clearTimeout(TIMEOUT);
|
|
||||||
|
|
||||||
let output = this.formatOutput(this.agent.bot);
|
|
||||||
let interrupted = this.agent.bot.interrupt_code;
|
|
||||||
let timedout = this.timedout;
|
|
||||||
this.clear();
|
|
||||||
if (!interrupted && !this.generating) this.agent.bot.emit('idle');
|
|
||||||
return {success:true, message: output, interrupted, timedout};
|
|
||||||
} catch (err) {
|
|
||||||
this.executing = false;
|
|
||||||
clearTimeout(TIMEOUT);
|
|
||||||
this.cancelResume();
|
|
||||||
console.error("Code execution triggered catch: " + err);
|
|
||||||
await this.stop();
|
|
||||||
|
|
||||||
let message = this.formatOutput(this.agent.bot) + '!!Code threw exception!! Error: ' + err;
|
|
||||||
let interrupted = this.agent.bot.interrupt_code;
|
|
||||||
this.clear();
|
|
||||||
if (!interrupted && !this.generating) this.agent.bot.emit('idle');
|
|
||||||
return {success: false, message, interrupted, timedout: false};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
formatOutput(bot) {
|
|
||||||
if (bot.interrupt_code && !this.timedout) return '';
|
|
||||||
let output = bot.output;
|
|
||||||
const MAX_OUT = 500;
|
|
||||||
if (output.length > MAX_OUT) {
|
|
||||||
output = `Code output is very long (${output.length} chars) and has been shortened.\n
|
|
||||||
First outputs:\n${output.substring(0, MAX_OUT/2)}\n...skipping many lines.\nFinal outputs:\n ${output.substring(output.length - MAX_OUT/2)}`;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
output = 'Code output:\n' + output;
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
async stop() {
|
|
||||||
if (!this.executing) return;
|
|
||||||
const start = Date.now();
|
|
||||||
while (this.executing) {
|
|
||||||
this.agent.bot.interrupt_code = true;
|
|
||||||
this.agent.bot.collectBlock.cancelTask();
|
|
||||||
this.agent.bot.pathfinder.stop();
|
|
||||||
this.agent.bot.pvp.stop();
|
|
||||||
console.log('waiting for code to finish executing...');
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
if (Date.now() - start > 10 * 1000) {
|
|
||||||
this.agent.cleanKill('Code execution refused stop after 10 seconds. Killing process.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.agent.bot.output = '';
|
|
||||||
this.agent.bot.interrupt_code = false;
|
|
||||||
this.timedout = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_startTimeout(TIMEOUT_MINS=10) {
|
|
||||||
return setTimeout(async () => {
|
|
||||||
console.warn(`Code execution timed out after ${TIMEOUT_MINS} minutes. Attempting force stop.`);
|
|
||||||
this.timedout = true;
|
|
||||||
this.agent.history.add('system', `Code execution timed out after ${TIMEOUT_MINS} minutes. Attempting force stop.`);
|
|
||||||
await this.stop(); // last attempt to stop
|
|
||||||
}, TIMEOUT_MINS*60*1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,17 +1,12 @@
|
||||||
import * as skills from '../library/skills.js';
|
import * as skills from '../library/skills.js';
|
||||||
import settings from '../../../settings.js';
|
import settings from '../../../settings.js';
|
||||||
|
|
||||||
function wrapExecution(func, resume=false, timeout=-1) {
|
function runAsTask (taskLabel, taskFn, resume = false, timeout = -1) {
|
||||||
return async function (agent, ...args) {
|
return async function (agent, ...args) {
|
||||||
let code_return;
|
const taskFnWithAgent = async () => {
|
||||||
const wrappedFunction = async () => {
|
await taskFn(agent, ...args);
|
||||||
await func(agent, ...args);
|
|
||||||
};
|
};
|
||||||
if (resume) {
|
const code_return = await agent.tasks.runTask(`action:${taskLabel}`, taskFnWithAgent, { timeout, resume });
|
||||||
code_return = await agent.coder.executeResume(wrappedFunction, timeout);
|
|
||||||
} else {
|
|
||||||
code_return = await agent.coder.execute(wrappedFunction, timeout);
|
|
||||||
}
|
|
||||||
if (code_return.interrupted && !code_return.timedout)
|
if (code_return.interrupted && !code_return.timedout)
|
||||||
return;
|
return;
|
||||||
return code_return.message;
|
return code_return.message;
|
||||||
|
@ -36,9 +31,9 @@ export const actionsList = [
|
||||||
name: '!stop',
|
name: '!stop',
|
||||||
description: 'Force stop all actions and commands that are currently executing.',
|
description: 'Force stop all actions and commands that are currently executing.',
|
||||||
perform: async function (agent) {
|
perform: async function (agent) {
|
||||||
await agent.coder.stop();
|
await agent.tasks.stop();
|
||||||
agent.coder.clear();
|
agent.clearBotLogs();
|
||||||
agent.coder.cancelResume();
|
agent.tasks.cancelResume();
|
||||||
agent.bot.emit('idle');
|
agent.bot.emit('idle');
|
||||||
let msg = 'Agent stopped.';
|
let msg = 'Agent stopped.';
|
||||||
if (agent.self_prompter.on)
|
if (agent.self_prompter.on)
|
||||||
|
@ -78,7 +73,7 @@ export const actionsList = [
|
||||||
'player_name': {type: 'string', description: 'The name of the player to go to.'},
|
'player_name': {type: 'string', description: 'The name of the player to go to.'},
|
||||||
'closeness': {type: 'float', description: 'How close to get to the player.', domain: [0, Infinity]}
|
'closeness': {type: 'float', description: 'How close to get to the player.', domain: [0, Infinity]}
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, player_name, closeness) => {
|
perform: runAsTask('goToPlayer', async (agent, player_name, closeness) => {
|
||||||
return await skills.goToPlayer(agent.bot, player_name, closeness);
|
return await skills.goToPlayer(agent.bot, player_name, closeness);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -89,7 +84,7 @@ export const actionsList = [
|
||||||
'player_name': {type: 'string', description: 'name of the player to follow.'},
|
'player_name': {type: 'string', description: 'name of the player to follow.'},
|
||||||
'follow_dist': {type: 'float', description: 'The distance to follow from.', domain: [0, Infinity]}
|
'follow_dist': {type: 'float', description: 'The distance to follow from.', domain: [0, Infinity]}
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, player_name, follow_dist) => {
|
perform: runAsTask('followPlayer', async (agent, player_name, follow_dist) => {
|
||||||
await skills.followPlayer(agent.bot, player_name, follow_dist);
|
await skills.followPlayer(agent.bot, player_name, follow_dist);
|
||||||
}, true)
|
}, true)
|
||||||
},
|
},
|
||||||
|
@ -101,7 +96,7 @@ export const actionsList = [
|
||||||
'closeness': { type: 'float', description: 'How close to get to the block.', domain: [0, Infinity] },
|
'closeness': { type: 'float', description: 'How close to get to the block.', domain: [0, Infinity] },
|
||||||
'search_range': { type: 'float', description: 'The distance to search for the block.', domain: [0, Infinity] }
|
'search_range': { type: 'float', description: 'The distance to search for the block.', domain: [0, Infinity] }
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, type, closeness, range) => {
|
perform: runAsTask('goToBlock', async (agent, type, closeness, range) => {
|
||||||
await skills.goToNearestBlock(agent.bot, type, closeness, range);
|
await skills.goToNearestBlock(agent.bot, type, closeness, range);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -109,7 +104,7 @@ export const actionsList = [
|
||||||
name: '!moveAway',
|
name: '!moveAway',
|
||||||
description: 'Move away from the current location in any direction by a given distance.',
|
description: 'Move away from the current location in any direction by a given distance.',
|
||||||
params: {'distance': { type: 'float', description: 'The distance to move away.', domain: [0, Infinity] }},
|
params: {'distance': { type: 'float', description: 'The distance to move away.', domain: [0, Infinity] }},
|
||||||
perform: wrapExecution(async (agent, distance) => {
|
perform: runAsTask('moveAway', async (agent, distance) => {
|
||||||
await skills.moveAway(agent.bot, distance);
|
await skills.moveAway(agent.bot, distance);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -127,11 +122,11 @@ export const actionsList = [
|
||||||
name: '!goToPlace',
|
name: '!goToPlace',
|
||||||
description: 'Go to a saved location.',
|
description: 'Go to a saved location.',
|
||||||
params: {'name': { type: 'string', description: 'The name of the location to go to.' }},
|
params: {'name': { type: 'string', description: 'The name of the location to go to.' }},
|
||||||
perform: wrapExecution(async (agent, name) => {
|
perform: runAsTask('goToPlace', async (agent, name) => {
|
||||||
const pos = agent.memory_bank.recallPlace(name);
|
const pos = agent.memory_bank.recallPlace(name);
|
||||||
if (!pos) {
|
if (!pos) {
|
||||||
skills.log(agent.bot, `No location named "${name}" saved.`);
|
skills.log(agent.bot, `No location named "${name}" saved.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await skills.goToPosition(agent.bot, pos[0], pos[1], pos[2], 1);
|
await skills.goToPosition(agent.bot, pos[0], pos[1], pos[2], 1);
|
||||||
})
|
})
|
||||||
|
@ -144,7 +139,7 @@ export const actionsList = [
|
||||||
'item_name': { type: 'ItemName', description: 'The name of the item to give.' },
|
'item_name': { type: 'ItemName', description: 'The name of the item to give.' },
|
||||||
'num': { type: 'int', description: 'The number of items to give.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
'num': { type: 'int', description: 'The number of items to give.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, player_name, item_name, num) => {
|
perform: runAsTask('givePlayer', async (agent, player_name, item_name, num) => {
|
||||||
await skills.giveToPlayer(agent.bot, item_name, player_name, num);
|
await skills.giveToPlayer(agent.bot, item_name, player_name, num);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -152,7 +147,7 @@ export const actionsList = [
|
||||||
name: '!consume',
|
name: '!consume',
|
||||||
description: 'Eat/drink the given item.',
|
description: 'Eat/drink the given item.',
|
||||||
params: {'item_name': { type: 'ItemName', description: 'The name of the item to consume.' }},
|
params: {'item_name': { type: 'ItemName', description: 'The name of the item to consume.' }},
|
||||||
perform: wrapExecution(async (agent, item_name) => {
|
perform: runAsTask('consume', async (agent, item_name) => {
|
||||||
await agent.bot.consume(item_name);
|
await agent.bot.consume(item_name);
|
||||||
skills.log(agent.bot, `Consumed ${item_name}.`);
|
skills.log(agent.bot, `Consumed ${item_name}.`);
|
||||||
})
|
})
|
||||||
|
@ -161,7 +156,7 @@ export const actionsList = [
|
||||||
name: '!equip',
|
name: '!equip',
|
||||||
description: 'Equip the given item.',
|
description: 'Equip the given item.',
|
||||||
params: {'item_name': { type: 'ItemName', description: 'The name of the item to equip.' }},
|
params: {'item_name': { type: 'ItemName', description: 'The name of the item to equip.' }},
|
||||||
perform: wrapExecution(async (agent, item_name) => {
|
perform: runAsTask('equip', async (agent, item_name) => {
|
||||||
await skills.equip(agent.bot, item_name);
|
await skills.equip(agent.bot, item_name);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -172,7 +167,7 @@ export const actionsList = [
|
||||||
'item_name': { type: 'ItemName', description: 'The name of the item to put in the chest.' },
|
'item_name': { type: 'ItemName', description: 'The name of the item to put in the chest.' },
|
||||||
'num': { type: 'int', description: 'The number of items to put in the chest.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
'num': { type: 'int', description: 'The number of items to put in the chest.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, item_name, num) => {
|
perform: runAsTask('putInChest', async (agent, item_name, num) => {
|
||||||
await skills.putInChest(agent.bot, item_name, num);
|
await skills.putInChest(agent.bot, item_name, num);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -183,7 +178,7 @@ export const actionsList = [
|
||||||
'item_name': { type: 'ItemName', description: 'The name of the item to take.' },
|
'item_name': { type: 'ItemName', description: 'The name of the item to take.' },
|
||||||
'num': { type: 'int', description: 'The number of items to take.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
'num': { type: 'int', description: 'The number of items to take.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, item_name, num) => {
|
perform: runAsTask('takeFromChest', async (agent, item_name, num) => {
|
||||||
await skills.takeFromChest(agent.bot, item_name, num);
|
await skills.takeFromChest(agent.bot, item_name, num);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -191,7 +186,7 @@ export const actionsList = [
|
||||||
name: '!viewChest',
|
name: '!viewChest',
|
||||||
description: 'View the items/counts of the nearest chest.',
|
description: 'View the items/counts of the nearest chest.',
|
||||||
params: { },
|
params: { },
|
||||||
perform: wrapExecution(async (agent) => {
|
perform: runAsTask('viewChest', async (agent) => {
|
||||||
await skills.viewChest(agent.bot);
|
await skills.viewChest(agent.bot);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -202,7 +197,7 @@ export const actionsList = [
|
||||||
'item_name': { type: 'ItemName', description: 'The name of the item to discard.' },
|
'item_name': { type: 'ItemName', description: 'The name of the item to discard.' },
|
||||||
'num': { type: 'int', description: 'The number of items to discard.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
'num': { type: 'int', description: 'The number of items to discard.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, item_name, num) => {
|
perform: runAsTask('discard', async (agent, item_name, num) => {
|
||||||
const start_loc = agent.bot.entity.position;
|
const start_loc = agent.bot.entity.position;
|
||||||
await skills.moveAway(agent.bot, 5);
|
await skills.moveAway(agent.bot, 5);
|
||||||
await skills.discard(agent.bot, item_name, num);
|
await skills.discard(agent.bot, item_name, num);
|
||||||
|
@ -216,7 +211,7 @@ export const actionsList = [
|
||||||
'type': { type: 'BlockName', description: 'The block type to collect.' },
|
'type': { type: 'BlockName', description: 'The block type to collect.' },
|
||||||
'num': { type: 'int', description: 'The number of blocks to collect.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
'num': { type: 'int', description: 'The number of blocks to collect.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, type, num) => {
|
perform: runAsTask('collectBlocks', async (agent, type, num) => {
|
||||||
await skills.collectBlock(agent.bot, type, num);
|
await skills.collectBlock(agent.bot, type, num);
|
||||||
}, false, 10) // 10 minute timeout
|
}, false, 10) // 10 minute timeout
|
||||||
},
|
},
|
||||||
|
@ -226,10 +221,10 @@ export const actionsList = [
|
||||||
params: {
|
params: {
|
||||||
'type': { type: 'BlockName', description: 'The block type to collect.' }
|
'type': { type: 'BlockName', description: 'The block type to collect.' }
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, type) => {
|
perform: runAsTask('collectAllBlocks', async (agent, type) => {
|
||||||
let success = await skills.collectBlock(agent.bot, type, 1);
|
let success = await skills.collectBlock(agent.bot, type, 1);
|
||||||
if (!success)
|
if (!success)
|
||||||
agent.coder.cancelResume();
|
agent.tasks.cancelResume();
|
||||||
}, true, 3) // 3 minute timeout
|
}, true, 3) // 3 minute timeout
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -239,7 +234,7 @@ export const actionsList = [
|
||||||
'recipe_name': { type: 'ItemName', description: 'The name of the output item to craft.' },
|
'recipe_name': { type: 'ItemName', description: 'The name of the output item to craft.' },
|
||||||
'num': { type: 'int', description: 'The number of times to craft the recipe. This is NOT the number of output items, as it may craft many more items depending on the recipe.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
'num': { type: 'int', description: 'The number of times to craft the recipe. This is NOT the number of output items, as it may craft many more items depending on the recipe.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
||||||
},
|
},
|
||||||
perform: wrapExecution(async (agent, recipe_name, num) => {
|
perform: runAsTask('craftRecipe', async (agent, recipe_name, num) => {
|
||||||
await skills.craftRecipe(agent.bot, recipe_name, num);
|
await skills.craftRecipe(agent.bot, recipe_name, num);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -250,32 +245,29 @@ export const actionsList = [
|
||||||
'item_name': { type: 'ItemName', description: 'The name of the input item to smelt.' },
|
'item_name': { type: 'ItemName', description: 'The name of the input item to smelt.' },
|
||||||
'num': { type: 'int', description: 'The number of times to smelt the item.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
'num': { type: 'int', description: 'The number of times to smelt the item.', domain: [1, Number.MAX_SAFE_INTEGER] }
|
||||||
},
|
},
|
||||||
perform: async function (agent, item_name, num) {
|
perform: runAsTask('smeltItem', async (agent, item_name, num) => {
|
||||||
let response = await wrapExecution(async (agent) => {
|
let response = await skills.smeltItem(agent.bot, item_name, num);
|
||||||
console.log('smelting item');
|
|
||||||
return await skills.smeltItem(agent.bot, item_name, num);
|
|
||||||
})(agent);
|
|
||||||
if (response.indexOf('Successfully') !== -1) {
|
if (response.indexOf('Successfully') !== -1) {
|
||||||
// there is a bug where the bot's inventory is not updated after smelting
|
// there is a bug where the bot's inventory is not updated after smelting
|
||||||
// only updates after a restart
|
// only updates after a restart
|
||||||
agent.cleanKill(response + ' Safely restarting to update inventory.');
|
agent.cleanKill(response + ' Safely restarting to update inventory.');
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '!clearFurnace',
|
|
||||||
description: 'Tak all items out of the nearest furnace.',
|
|
||||||
params: { },
|
|
||||||
perform: wrapExecution(async (agent) => {
|
|
||||||
await skills.clearNearestFurnace(agent.bot);
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: '!clearFurnace',
|
||||||
|
description: 'Take all items out of the nearest furnace.',
|
||||||
|
params: { },
|
||||||
|
perform: runAsTask('clearFurnace', async (agent) => {
|
||||||
|
await skills.clearNearestFurnace(agent.bot);
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
name: '!placeHere',
|
name: '!placeHere',
|
||||||
description: 'Place a given block in the current location. Do NOT use to build structures, only use for single blocks/torches.',
|
description: 'Place a given block in the current location. Do NOT use to build structures, only use for single blocks/torches.',
|
||||||
params: {'type': { type: 'BlockName', description: 'The block type to place.' }},
|
params: {'type': { type: 'BlockName', description: 'The block type to place.' }},
|
||||||
perform: wrapExecution(async (agent, type) => {
|
perform: runAsTask('placeHere', async (agent, type) => {
|
||||||
let pos = agent.bot.entity.position;
|
let pos = agent.bot.entity.position;
|
||||||
await skills.placeBlock(agent.bot, type, pos.x, pos.y, pos.z);
|
await skills.placeBlock(agent.bot, type, pos.x, pos.y, pos.z);
|
||||||
})
|
})
|
||||||
|
@ -284,14 +276,14 @@ export const actionsList = [
|
||||||
name: '!attack',
|
name: '!attack',
|
||||||
description: 'Attack and kill the nearest entity of a given type.',
|
description: 'Attack and kill the nearest entity of a given type.',
|
||||||
params: {'type': { type: 'string', description: 'The type of entity to attack.'}},
|
params: {'type': { type: 'string', description: 'The type of entity to attack.'}},
|
||||||
perform: wrapExecution(async (agent, type) => {
|
perform: runAsTask('attack', async (agent, type) => {
|
||||||
await skills.attackNearest(agent.bot, type, true);
|
await skills.attackNearest(agent.bot, type, true);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '!goToBed',
|
name: '!goToBed',
|
||||||
description: 'Go to the nearest bed and sleep.',
|
description: 'Go to the nearest bed and sleep.',
|
||||||
perform: wrapExecution(async (agent) => {
|
perform: runAsTask('goToBed', async (agent) => {
|
||||||
await skills.goToBed(agent.bot);
|
await skills.goToBed(agent.bot);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -299,7 +291,7 @@ export const actionsList = [
|
||||||
name: '!activate',
|
name: '!activate',
|
||||||
description: 'Activate the nearest object of a given type.',
|
description: 'Activate the nearest object of a given type.',
|
||||||
params: {'type': { type: 'BlockName', description: 'The type of object to activate.' }},
|
params: {'type': { type: 'BlockName', description: 'The type of object to activate.' }},
|
||||||
perform: wrapExecution(async (agent, type) => {
|
perform: runAsTask('activate', async (agent, type) => {
|
||||||
await skills.activateNearestBlock(agent.bot, type);
|
await skills.activateNearestBlock(agent.bot, type);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -307,7 +299,7 @@ export const actionsList = [
|
||||||
name: '!stay',
|
name: '!stay',
|
||||||
description: 'Stay in the current location no matter what. Pauses all modes.',
|
description: 'Stay in the current location no matter what. Pauses all modes.',
|
||||||
params: {'type': { type: 'int', description: 'The number of seconds to stay. -1 for forever.', domain: [-1, Number.MAX_SAFE_INTEGER] }},
|
params: {'type': { type: 'int', description: 'The number of seconds to stay. -1 for forever.', domain: [-1, Number.MAX_SAFE_INTEGER] }},
|
||||||
perform: wrapExecution(async (agent, seconds) => {
|
perform: runAsTask('stay', async (agent, seconds) => {
|
||||||
await skills.stay(agent.bot, seconds);
|
await skills.stay(agent.bot, seconds);
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -321,9 +313,9 @@ export const actionsList = [
|
||||||
perform: async function (agent, mode_name, on) {
|
perform: async function (agent, mode_name, on) {
|
||||||
const modes = agent.bot.modes;
|
const modes = agent.bot.modes;
|
||||||
if (!modes.exists(mode_name))
|
if (!modes.exists(mode_name))
|
||||||
return `Mode ${mode_name} does not exist.` + modes.getDocs();
|
return `Mode ${mode_name} does not exist.` + modes.getDocs();
|
||||||
if (modes.isOn(mode_name) === on)
|
if (modes.isOn(mode_name) === on)
|
||||||
return `Mode ${mode_name} is already ${on ? 'on' : 'off'}.`;
|
return `Mode ${mode_name} is already ${on ? 'on' : 'off'}.`;
|
||||||
modes.setOn(mode_name, on);
|
modes.setOn(mode_name, on);
|
||||||
return `Mode ${mode_name} is now ${on ? 'on' : 'off'}.`;
|
return `Mode ${mode_name} is now ${on ? 'on' : 'off'}.`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,7 +207,6 @@ export async function executeCommand(agent, message) {
|
||||||
else {
|
else {
|
||||||
console.log('parsed command:', parsed);
|
console.log('parsed command:', parsed);
|
||||||
const command = getCommand(parsed.commandName);
|
const command = getCommand(parsed.commandName);
|
||||||
const is_action = isAction(command.name);
|
|
||||||
let numArgs = 0;
|
let numArgs = 0;
|
||||||
if (parsed.args) {
|
if (parsed.args) {
|
||||||
numArgs = parsed.args.length;
|
numArgs = parsed.args.length;
|
||||||
|
@ -215,11 +214,7 @@ export async function executeCommand(agent, message) {
|
||||||
if (numArgs !== numParams(command))
|
if (numArgs !== numParams(command))
|
||||||
return `Command ${command.name} was given ${numArgs} args, but requires ${numParams(command)} args.`;
|
return `Command ${command.name} was given ${numArgs} args, but requires ${numParams(command)} args.`;
|
||||||
else {
|
else {
|
||||||
if (is_action)
|
|
||||||
agent.coder.setCurActionName(command.name);
|
|
||||||
const result = await command.perform(agent, ...parsed.args);
|
const result = await command.perform(agent, ...parsed.args);
|
||||||
if (is_action)
|
|
||||||
agent.coder.setCurActionName('');
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,9 +260,9 @@ async function execute(mode, agent, func, timeout=-1) {
|
||||||
if (agent.self_prompter.on)
|
if (agent.self_prompter.on)
|
||||||
agent.self_prompter.stopLoop();
|
agent.self_prompter.stopLoop();
|
||||||
mode.active = true;
|
mode.active = true;
|
||||||
let code_return = await agent.coder.execute(async () => {
|
let code_return = await agent.tasks.runTask(`mode:${mode.name}`, async () => {
|
||||||
await func();
|
await func();
|
||||||
}, timeout);
|
}, { timeout });
|
||||||
mode.active = false;
|
mode.active = false;
|
||||||
console.log(`Mode ${mode.name} finished executing, code_return: ${code_return.message}`);
|
console.log(`Mode ${mode.name} finished executing, code_return: ${code_return.message}`);
|
||||||
}
|
}
|
||||||
|
@ -328,7 +328,7 @@ class ModeController {
|
||||||
this.unPauseAll();
|
this.unPauseAll();
|
||||||
}
|
}
|
||||||
for (let mode of this.modes_list) {
|
for (let mode of this.modes_list) {
|
||||||
let interruptible = mode.interrupts.some(i => i === 'all') || mode.interrupts.some(i => i === this.agent.coder.cur_action_name);
|
let interruptible = mode.interrupts.some(i => i === 'all') || mode.interrupts.some(i => `action:${i}` === this.agent.tasks.currentTaskLabel);
|
||||||
if (mode.on && !mode.paused && !mode.active && (this.agent.isIdle() || interruptible)) {
|
if (mode.on && !mode.paused && !mode.active && (this.agent.isIdle() || interruptible)) {
|
||||||
await mode.update(this.agent);
|
await mode.update(this.agent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export class BuildGoal {
|
||||||
async wrapSkill(func) {
|
async wrapSkill(func) {
|
||||||
if (!this.agent.isIdle())
|
if (!this.agent.isIdle())
|
||||||
return false;
|
return false;
|
||||||
let res = await this.agent.coder.execute(func);
|
let res = await this.agent.tasks.runTask('BuildGoal', func);
|
||||||
return !res.interrupted;
|
return !res.interrupted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ export class NPCContoller {
|
||||||
if (!this.agent.isIdle()) return;
|
if (!this.agent.isIdle()) return;
|
||||||
|
|
||||||
// Persue goal
|
// Persue goal
|
||||||
if (!this.agent.coder.resume_func) {
|
if (!this.agent.tasks.resume_func) {
|
||||||
this.executeNext();
|
this.executeNext();
|
||||||
this.agent.history.save();
|
this.agent.history.save();
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ export class NPCContoller {
|
||||||
|
|
||||||
async executeNext() {
|
async executeNext() {
|
||||||
if (!this.agent.isIdle()) return;
|
if (!this.agent.isIdle()) return;
|
||||||
await this.agent.coder.execute(async () => {
|
await this.agent.tasks.runTask('npc:moveAway', async () => {
|
||||||
await skills.moveAway(this.agent.bot, 2);
|
await skills.moveAway(this.agent.bot, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ export class NPCContoller {
|
||||||
if (building == this.data.home) {
|
if (building == this.data.home) {
|
||||||
let door_pos = this.getBuildingDoor(building);
|
let door_pos = this.getBuildingDoor(building);
|
||||||
if (door_pos) {
|
if (door_pos) {
|
||||||
await this.agent.coder.execute(async () => {
|
await this.agent.tasks.runTask('npc:exitBuilding', async () => {
|
||||||
await skills.useDoor(this.agent.bot, door_pos);
|
await skills.useDoor(this.agent.bot, door_pos);
|
||||||
await skills.moveAway(this.agent.bot, 2); // If the bot is too close to the building it will try to enter again
|
await skills.moveAway(this.agent.bot, 2); // If the bot is too close to the building it will try to enter again
|
||||||
});
|
});
|
||||||
|
@ -132,13 +132,13 @@ export class NPCContoller {
|
||||||
let building = this.currentBuilding();
|
let building = this.currentBuilding();
|
||||||
if (this.data.home !== null && (building === null || building != this.data.home)) {
|
if (this.data.home !== null && (building === null || building != this.data.home)) {
|
||||||
let door_pos = this.getBuildingDoor(this.data.home);
|
let door_pos = this.getBuildingDoor(this.data.home);
|
||||||
await this.agent.coder.execute(async () => {
|
await this.agent.tasks.runTask('npc:returnHome', async () => {
|
||||||
await skills.useDoor(this.agent.bot, door_pos);
|
await skills.useDoor(this.agent.bot, door_pos);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go to bed
|
// Go to bed
|
||||||
await this.agent.coder.execute(async () => {
|
await this.agent.tasks.runTask('npc:bed', async () => {
|
||||||
await skills.goToBed(this.agent.bot);
|
await skills.goToBed(this.agent.bot);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,7 +322,7 @@ export class ItemGoal {
|
||||||
// If the bot has failed to obtain the block before, explore
|
// If the bot has failed to obtain the block before, explore
|
||||||
if (this.failed.includes(next.name)) {
|
if (this.failed.includes(next.name)) {
|
||||||
this.failed = this.failed.filter((item) => item !== next.name);
|
this.failed = this.failed.filter((item) => item !== next.name);
|
||||||
await this.agent.coder.execute(async () => {
|
await this.agent.tasks.runTask('itemGoal:explore', async () => {
|
||||||
await skills.moveAway(this.agent.bot, 8);
|
await skills.moveAway(this.agent.bot, 8);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -339,7 +339,7 @@ export class ItemGoal {
|
||||||
|
|
||||||
// Execute the next goal
|
// Execute the next goal
|
||||||
let init_quantity = world.getInventoryCounts(this.agent.bot)[next.name] || 0;
|
let init_quantity = world.getInventoryCounts(this.agent.bot)[next.name] || 0;
|
||||||
await this.agent.coder.execute(async () => {
|
await this.agent.tasks.runTask('itemGoal:next', async () => {
|
||||||
await next.execute(quantity);
|
await next.execute(quantity);
|
||||||
});
|
});
|
||||||
let final_quantity = world.getInventoryCounts(this.agent.bot)[next.name] || 0;
|
let final_quantity = world.getInventoryCounts(this.agent.bot)[next.name] || 0;
|
||||||
|
|
|
@ -87,7 +87,7 @@ export class SelfPrompter {
|
||||||
async stop(stop_action=true) {
|
async stop(stop_action=true) {
|
||||||
this.interrupt = true;
|
this.interrupt = true;
|
||||||
if (stop_action)
|
if (stop_action)
|
||||||
await this.agent.coder.stop();
|
await this.agent.tasks.stop();
|
||||||
await this.stopLoop();
|
await this.stopLoop();
|
||||||
this.on = false;
|
this.on = false;
|
||||||
}
|
}
|
||||||
|
|
148
src/agent/tasks.js
Normal file
148
src/agent/tasks.js
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
export class TaskManager {
|
||||||
|
constructor(agent) {
|
||||||
|
this.agent = agent;
|
||||||
|
this.executing = false;
|
||||||
|
this.currentTaskLabel = '';
|
||||||
|
this.currentTaskFn = null;
|
||||||
|
this.timedout = false;
|
||||||
|
this.resume_func = null;
|
||||||
|
this.resume_name = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async resumeTask(taskFn, timeout) {
|
||||||
|
return this._executeResume(taskFn, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
async runTask(taskLabel, taskFn, { timeout, resume = false } = {}) {
|
||||||
|
if (resume) {
|
||||||
|
return this._executeResume(taskFn, timeout);
|
||||||
|
} else {
|
||||||
|
return this._executeTask(taskLabel, taskFn, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async stop() {
|
||||||
|
if (!this.executing) return;
|
||||||
|
console.trace();
|
||||||
|
const start = Date.now();
|
||||||
|
while (this.executing) {
|
||||||
|
this.agent.interruptBot();
|
||||||
|
console.log('waiting for code to finish executing...');
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
if (Date.now() - start > 10 * 1000) {
|
||||||
|
this.agent.cleanKill('Code execution refused stop after 10 seconds. Killing process.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelResume() {
|
||||||
|
this.resume_func = null;
|
||||||
|
this.resume_name = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _executeResume(taskFn = null, timeout = 10) {
|
||||||
|
const new_resume = taskFn != null;
|
||||||
|
if (new_resume) { // start new resume
|
||||||
|
this.resume_func = taskFn;
|
||||||
|
this.resume_name = this.currentTaskLabel;
|
||||||
|
}
|
||||||
|
if (this.resume_func != null && this.agent.isIdle() && (!this.agent.self_prompter.on || new_resume)) {
|
||||||
|
this.currentTaskLabel = this.resume_name;
|
||||||
|
let res = await this._executeTask(this.resume_name, this.resume_func, timeout);
|
||||||
|
this.currentTaskLabel = '';
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return { success: false, message: null, interrupted: false, timedout: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _executeTask(taskLabel, taskFn, timeout = 10) {
|
||||||
|
let TIMEOUT;
|
||||||
|
try {
|
||||||
|
console.log('executing code...\n');
|
||||||
|
|
||||||
|
// await current task to finish (executing=false), with 10 seconds timeout
|
||||||
|
// also tell agent.bot to stop various actions
|
||||||
|
if (this.executing) {
|
||||||
|
console.log(`new task "${taskLabel}" trying to interrupt current task "${this.currentTaskLabel}"`);
|
||||||
|
}
|
||||||
|
await this.stop();
|
||||||
|
|
||||||
|
// clear bot logs and reset interrupt code
|
||||||
|
this.agent.clearBotLogs();
|
||||||
|
|
||||||
|
this.executing = true;
|
||||||
|
this.currentTaskLabel = taskLabel;
|
||||||
|
this.currentTaskFn = taskFn;
|
||||||
|
|
||||||
|
// timeout in minutes
|
||||||
|
if (timeout > 0) {
|
||||||
|
TIMEOUT = this._startTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the task
|
||||||
|
await taskFn();
|
||||||
|
|
||||||
|
// mark task as finished + cleanup
|
||||||
|
this.executing = false;
|
||||||
|
this.currentTaskLabel = '';
|
||||||
|
this.currentTaskFn = null;
|
||||||
|
clearTimeout(TIMEOUT);
|
||||||
|
|
||||||
|
// get bot activity summary
|
||||||
|
let output = this._getBotOutputSummary();
|
||||||
|
let interrupted = this.agent.bot.interrupt_code;
|
||||||
|
let timedout = this.timedout;
|
||||||
|
this.agent.clearBotLogs();
|
||||||
|
|
||||||
|
// if not interrupted and not generating, emit idle event
|
||||||
|
if (!interrupted && !this.agent.coder.generating) {
|
||||||
|
this.agent.bot.emit('idle');
|
||||||
|
}
|
||||||
|
|
||||||
|
// return task status report
|
||||||
|
return { success: true, message: output, interrupted, timedout };
|
||||||
|
} catch (err) {
|
||||||
|
this.executing = false;
|
||||||
|
this.currentTaskLabel = '';
|
||||||
|
this.currentTaskFn = null;
|
||||||
|
clearTimeout(TIMEOUT);
|
||||||
|
this.cancelResume();
|
||||||
|
console.error("Code execution triggered catch: " + err);
|
||||||
|
await this.stop();
|
||||||
|
|
||||||
|
let message = this._getBotOutputSummary() + '!!Code threw exception!! Error: ' + err;
|
||||||
|
let interrupted = this.agent.bot.interrupt_code;
|
||||||
|
this.agent.clearBotLogs();
|
||||||
|
if (!interrupted && !this.agent.coder.generating) {
|
||||||
|
this.agent.bot.emit('idle');
|
||||||
|
}
|
||||||
|
return { success: false, message, interrupted, timedout: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_getBotOutputSummary() {
|
||||||
|
const { bot } = this.agent;
|
||||||
|
if (bot.interrupt_code && !this.timedout) return '';
|
||||||
|
let output = bot.output;
|
||||||
|
const MAX_OUT = 500;
|
||||||
|
if (output.length > MAX_OUT) {
|
||||||
|
output = `Code output is very long (${output.length} chars) and has been shortened.\n
|
||||||
|
First outputs:\n${output.substring(0, MAX_OUT / 2)}\n...skipping many lines.\nFinal outputs:\n ${output.substring(output.length - MAX_OUT / 2)}`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
output = 'Code output:\n' + output;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
_startTimeout(TIMEOUT_MINS = 10) {
|
||||||
|
return setTimeout(async () => {
|
||||||
|
console.warn(`Code execution timed out after ${TIMEOUT_MINS} minutes. Attempting force stop.`);
|
||||||
|
this.timedout = true;
|
||||||
|
this.agent.history.add('system', `Code execution timed out after ${TIMEOUT_MINS} minutes. Attempting force stop.`);
|
||||||
|
await this.stop(); // last attempt to stop
|
||||||
|
}, TIMEOUT_MINS * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue