mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-04-22 06:02:07 +02:00
Merge branch 'main' into routine
This commit is contained in:
commit
72aa92352e
9 changed files with 133 additions and 38 deletions
35
agent.js
35
agent.js
|
@ -3,18 +3,19 @@ import { sendRequest } from './utils/gpt.js';
|
|||
import { History } from './utils/history.js';
|
||||
import { Coder } from './utils/coder.js';
|
||||
import { getQuery, containsQuery } from './utils/queries.js';
|
||||
import { containsCodeBlock } from './utils/skill_library.js';
|
||||
import { containsCodeBlock } from './utils/skill-library.js';
|
||||
import { Events } from './utils/events.js';
|
||||
|
||||
|
||||
export class Agent {
|
||||
constructor(name, save_path, restart_memory=false) {
|
||||
constructor(name, save_path, clear_memory=false, autostart=false) {
|
||||
this.name = name;
|
||||
this.bot = initBot(name);
|
||||
this.history = new History(this, save_path);
|
||||
this.history.loadExamples();
|
||||
this.coder = new Coder(this);
|
||||
|
||||
if (!restart_memory) {
|
||||
if (!clear_memory) {
|
||||
this.history.load();
|
||||
}
|
||||
|
||||
|
@ -23,25 +24,21 @@ export class Agent {
|
|||
this.bot.on('login', () => {
|
||||
this.bot.chat('Hello world! I am ' + this.name);
|
||||
console.log(`${this.name} logged in.`);
|
||||
|
||||
this.bot.on('chat', (username, message) => {
|
||||
if (username === this.name) return;
|
||||
console.log('received message from', username, ':', message);
|
||||
|
||||
this.history.add(username, message);
|
||||
this.handleMessage();
|
||||
});
|
||||
|
||||
if (autostart)
|
||||
this.respond('system', 'Agent process restarted. Notify the user and decide what to do.');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
async start() {
|
||||
await this.history.loadExamples();
|
||||
|
||||
this.bot.on('chat', (username, message) => {
|
||||
if (username === this.name) return;
|
||||
console.log('received message from', username, ':', message);
|
||||
|
||||
this.history.add(username, message);
|
||||
this.handleMessage();
|
||||
});
|
||||
|
||||
if (this.history.default) {
|
||||
this.executeCode(this.history.default);
|
||||
}
|
||||
}
|
||||
|
||||
async executeCode(code, triesRemaining=5) {
|
||||
if (code == 'default')
|
||||
code = this.history.default;
|
||||
|
|
39
controller/agent-process.js
Normal file
39
controller/agent-process.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { spawn } from 'child_process';
|
||||
|
||||
export class AgentProcess {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
}
|
||||
start(clear_memory=false, autostart=false) {
|
||||
let args = ['controller/init-agent.js', this.name];
|
||||
if (clear_memory)
|
||||
args.push('-c');
|
||||
if (autostart)
|
||||
args.push('-a');
|
||||
|
||||
const agentProcess = spawn('node', args, {
|
||||
stdio: 'inherit',
|
||||
stderr: 'inherit',
|
||||
});
|
||||
|
||||
let last_restart = Date.now();
|
||||
agentProcess.on('exit', (code, signal) => {
|
||||
console.log(`Agent process exited with code ${code} and signal ${signal}`);
|
||||
|
||||
if (code !== 0) {
|
||||
// agent must run for at least 30 seconds before restarting
|
||||
if (Date.now() - last_restart < 30 * 1000) {
|
||||
console.error('Agent process exited too quickly. Killing entire process. Goodbye.');
|
||||
process.exit(1);
|
||||
}
|
||||
console.log('Restarting agent...');
|
||||
this.start(false, true);
|
||||
last_restart = Date.now();
|
||||
}
|
||||
});
|
||||
|
||||
agentProcess.on('error', (err) => {
|
||||
console.error('Failed to start agent process:', err);
|
||||
});
|
||||
}
|
||||
}
|
27
controller/init-agent.js
Normal file
27
controller/init-agent.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { Agent } from '../agent.js';
|
||||
import yargs from 'yargs';
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 1) {
|
||||
console.log('Usage: node init_agent.js <agent_name> [-c] [-a]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const argv = yargs(args)
|
||||
.option('clear_memory', {
|
||||
alias: 'c',
|
||||
type: 'boolean',
|
||||
description: 'restart memory from scratch'
|
||||
})
|
||||
.option('autostart', {
|
||||
alias: 'a',
|
||||
type: 'boolean',
|
||||
description: 'automatically prompt the agent on startup'
|
||||
}).argv
|
||||
|
||||
const name = argv._[0];
|
||||
const clear_memory = !!argv.clear_memory;
|
||||
const autostart = !!argv.autostart;
|
||||
const save_path = './bots/'+name+'.json';
|
||||
|
||||
new Agent(name, save_path, clear_memory, autostart);
|
5
main.js
5
main.js
|
@ -1,4 +1,3 @@
|
|||
import { Agent } from './agent.js';
|
||||
import { AgentProcess } from './controller/agent-process.js';
|
||||
|
||||
let agent = new Agent('andy', './bots/andy.json', true);
|
||||
agent.start();
|
||||
new AgentProcess('andy').start(true, false);
|
|
@ -7,6 +7,7 @@
|
|||
"mineflayer-pathfinder": "^2.4.4",
|
||||
"mineflayer-pvp": "^1.3.2",
|
||||
"openai": "^4.4.0",
|
||||
"patch-package": "^8.0.0"
|
||||
"patch-package": "^8.0.0",
|
||||
"yargs": "^17.7.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ export class Coder {
|
|||
this.executing = false;
|
||||
this.agent.bot.output = '';
|
||||
this.code_template = '';
|
||||
this.timedout = false;
|
||||
|
||||
readFile(this.fp+'template.js', 'utf8', (err, data) => {
|
||||
if (err) throw err;
|
||||
|
@ -47,10 +48,10 @@ export class Coder {
|
|||
}
|
||||
|
||||
|
||||
// returns {success: bool, message: string, interrupted: bool}
|
||||
// returns {success: bool, message: string, interrupted: bool, timedout: false}
|
||||
async execute() {
|
||||
if (!this.queued_code) return {success: false, message: "No code to execute.", interrupted: false};
|
||||
if (!this.code_template) return {success: false, message: "Code template not loaded.", interrupted: false};
|
||||
if (!this.queued_code) return {success: false, message: "No code to execute.", interrupted: false, timedout: false};
|
||||
if (!this.code_template) return {success: false, message: "Code template not loaded.", interrupted: false, timedout: false};
|
||||
let src = '';
|
||||
|
||||
let code = this.queued_code;
|
||||
|
@ -80,9 +81,9 @@ export class Coder {
|
|||
|
||||
if (write_result) {
|
||||
console.error('Error writing code execution file: ' + result);
|
||||
return {success: false, message: result, interrupted: false};
|
||||
return {success: false, message: result, interrupted: false, timedout: false};
|
||||
}
|
||||
|
||||
let TIMEOUT;
|
||||
try {
|
||||
console.log('executing code...\n');
|
||||
let execution_file = await import('.'+filename);
|
||||
|
@ -90,28 +91,33 @@ export class Coder {
|
|||
this.current_code = this.queued_code;
|
||||
|
||||
this.executing = true;
|
||||
await execution_file.main(this.agent.bot);
|
||||
TIMEOUT = this._startTimeout(10);
|
||||
await execution_file.main(this.agent.bot); // open fire
|
||||
this.executing = false;
|
||||
clearTimeout(TIMEOUT);
|
||||
|
||||
this.agent.bot.emit('finished_executing');
|
||||
let output = this.formatOutput(this.agent.bot);
|
||||
let interrupted = this.agent.bot.interrupt_code;
|
||||
let timedout = this.timedout;
|
||||
this.clear();
|
||||
return {success:true, message: output, interrupted};
|
||||
return {success:true, message: output, interrupted, timedout};
|
||||
} catch (err) {
|
||||
this.executing = false;
|
||||
clearTimeout(TIMEOUT);
|
||||
|
||||
this.agent.bot.emit('finished_executing');
|
||||
console.error("Code execution triggered catch:" + err);
|
||||
console.error("Code execution triggered catch: " + err);
|
||||
let message = this.formatOutput(this.agent.bot);
|
||||
message += '!!Code threw exception!! Error: ' + err;
|
||||
let interrupted = this.agent.bot.interrupt_code;
|
||||
await this.stop();
|
||||
return {success: false, message, interrupted};
|
||||
return {success: false, message, interrupted, timedout: false};
|
||||
}
|
||||
}
|
||||
|
||||
formatOutput(bot) {
|
||||
if (bot.interrupt_code) return '';
|
||||
if (bot.interrupt_code && !this.timedout) return '';
|
||||
let output = bot.output;
|
||||
const MAX_OUT = 1000;
|
||||
if (output.length > MAX_OUT) {
|
||||
|
@ -125,6 +131,7 @@ export class Coder {
|
|||
}
|
||||
|
||||
async stop() {
|
||||
if (!this.executing) return;
|
||||
while (this.executing) {
|
||||
this.agent.bot.interrupt_code = true;
|
||||
this.agent.bot.collectBlock.cancelTask();
|
||||
|
@ -140,5 +147,26 @@ export class Coder {
|
|||
this.current_code = '';
|
||||
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.bot.output += `\nAction performed for ${TIMEOUT_MINS} minutes and then timed out and stopped. You may want to continue or do something else.`;
|
||||
this.stop(); // last attempt to stop
|
||||
await new Promise(resolve => setTimeout(resolve, 5 * 1000)); // wait 5 seconds
|
||||
if (this.executing) {
|
||||
console.error(`Failed to stop. Killing process. Goodbye.`);
|
||||
this.agent.bot.output += `\nForce stop failed! Process was killed and will be restarted. Goodbye world.`;
|
||||
this.agent.bot.chat('Goodbye world.');
|
||||
let output = this.formatOutput(this.agent.bot);
|
||||
this.agent.history.add('system', output);
|
||||
this.agent.history.save();
|
||||
process.exit(1); // force exit program
|
||||
}
|
||||
console.log('Code execution stopped successfully.');
|
||||
}, TIMEOUT_MINS*60*1000);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { writeFileSync, readFileSync, mkdirSync } from 'fs';
|
||||
import { getQueryDocs } from './queries.js';
|
||||
import { getSkillDocs } from './skill_library.js';
|
||||
import { getSkillDocs } from './skill-library.js';
|
||||
import { sendRequest, embed, cosineSimilarity } from './gpt.js';
|
||||
|
||||
|
||||
|
|
|
@ -57,13 +57,12 @@ export async function smeltItem(bot, itemName, num=1) {
|
|||
|
||||
let furnaceBlock = undefined;
|
||||
furnaceBlock = getNearestBlock(bot, 'furnace', 6);
|
||||
if (furnaceBlock === null){
|
||||
if (!furnaceBlock){
|
||||
log(bot, `There is no furnace nearby.`)
|
||||
return false;
|
||||
}
|
||||
await bot.lookAt(furnaceBlock.position);
|
||||
|
||||
|
||||
console.log('smelting...');
|
||||
const furnace = await bot.openFurnace(furnaceBlock);
|
||||
// check if the furnace is already smelting something
|
||||
|
@ -227,6 +226,10 @@ export async function collectBlock(bot, blockType, num=1) {
|
|||
* @example
|
||||
* await skills.collectBlock(bot, "oak_log");
|
||||
**/
|
||||
if (num < 1) {
|
||||
log(bot, `Invalid number of blocks to collect: ${num}.`);
|
||||
return false;
|
||||
}
|
||||
let collected = 0;
|
||||
const blocks = getNearestBlocks(bot, blockType, 64, num);
|
||||
if (blocks.length === 0) {
|
||||
|
@ -256,6 +259,8 @@ export async function collectBlock(bot, blockType, num=1) {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if (bot.interrupt_code)
|
||||
break;
|
||||
}
|
||||
log(bot, `Collected ${collected} ${blockType}.`);
|
||||
return true;
|
||||
|
@ -565,7 +570,6 @@ export async function followPlayer(bot, username) {
|
|||
log(bot, `You are now actively following player ${username}.`);
|
||||
|
||||
while (!bot.interrupt_code) {
|
||||
console.log('followPlayer waiting for interrupt...', bot.interrupt_code);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue