mindcraft/src/agent/tasks.js

148 lines
5.2 KiB
JavaScript
Raw Normal View History

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);
}
}