From 1f7cb0dd55db8516089ccc735babeed57cee7ef1 Mon Sep 17 00:00:00 2001 From: Kolby Nottingham Date: Mon, 4 Dec 2023 23:26:21 -0800 Subject: [PATCH 1/7] routine init --- agent.js | 149 +++++++++++++++++++++++++++++++---------------- utils/history.js | 13 ++++- utils/queries.js | 11 ++++ utils/world.js | 5 +- 4 files changed, 122 insertions(+), 56 deletions(-) diff --git a/agent.js b/agent.js index e6491c3..2715cd1 100644 --- a/agent.js +++ b/agent.js @@ -16,11 +16,35 @@ export class Agent { if (!restart_memory) { this.history.load(); } - + // Add the default behaviors and events + else { + this.history.default = `let blocks = world.getNearbyBlockTypes(bot, 4); + let block_type = blocks[Math.floor(Math.random() * blocks.length)]; + await skills.collectBlock(bot, block_type); + await new Promise(r => setTimeout(r, 1000)); + + let players = world.getNearbyPlayerNames(bot); + let player_name = players[Math.floor(Math.random() * players.length)]; + await skills.goToPlayer(bot, player_name); + await new Promise(r => setTimeout(r, 1000));`; + this.history.events.push(['finished_executing', this.executeCode, 'default']); + // this.history.events.push(['finished_executing', this.sendThought, 'What should I do next? I will make a plan and execute it.']); + this.history.events.push(['health', this.sendThought, 'I may be under attack or need to eat! I will stop what I am doing to check my health and take action.']); + this.history.events.push(['sunrise', null, null]); + this.history.events.push(['noon', null, null]); + this.history.events.push(['sunset', null, null]); + this.history.events.push(['midnight', null, null]); + } + this.bot.on('login', () => { this.bot.chat('Hello world! I am ' + this.name); console.log(`${this.name} logged in.`); }); + + for (let [event, callback, params] of this.history.events) { + if (callback != null) + this.bot.on(event, callback.bind(this, params)); + } } async start() { @@ -30,59 +54,82 @@ export class Agent { if (username === this.name) return; console.log('received message from', username, ':', message); - this.respond(username, message); - this.history.save(); + this.history.add(username, message); + this.handleMessage(); }); - this.bot.on('finished_executing', () => { - setTimeout(() => { - if (!this.coder.executing) { - // return to default behavior - } - }, 10000); - }) - } + this.bot.on('time', () => { + if (this.bot.time.timeOfDay == 0) + this.bot.emit('sunrise'); + else if (this.bot.time.timeOfDay == 6000) + this.bot.emit('noon'); + else if (this.bot.time.timeOfDay == 12000) + this.bot.emit('sunset'); + else if (this.bot.time.timeOfDay == 18000) + this.bot.emit('midnight'); + }); - async respond(username, message) { - await this.history.add(username, message); - for (let i=0; i<5; i++) { - let res = await sendRequest(this.history.getHistory(), this.history.getSystemMessage()); - this.history.add(this.name, res); - let query_cmd = containsQuery(res); - if (query_cmd) { // contains query - let message = res.substring(0, res.indexOf(query_cmd)).trim(); - if (message) - this.bot.chat(message); - let query = getQuery(query_cmd); - let query_res = query.perform(this); - console.log('Agent used query:', query_cmd, 'and got:', query_res) - this.history.add('system', query_res); - } - else if (containsCodeBlock(res)) { // contains code block - console.log('Agent is executing code:', res) - - let message = res.substring(0, res.indexOf('```')).trim(); - if (message) - this.bot.chat(message); - let code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```')); - if (code) { - this.coder.queueCode(code); - let code_return = await this.coder.execute(); - let message = code_return.message; - if (code_return.interrupted) - break; // can only be interrupted by another chat, so this chat is over. - if (!code_return.success) { - message += "\n Write code to fix the problem and try again."; - } - console.log('code return:', message); - this.history.add('system', message); - } - } - else { // conversation response - this.bot.chat(res); - console.log('Purely conversational response:', res) - break; - } + if (this.history.default) { + this.executeCode(this.history.default); } } + + async sendThought(message) { + this.history.add(this.name, message); + await this.handleMessage(); + } + + async executeCode(code, triesRemaining=5) { + if (code == 'default') + code = this.history.default; + + if (code) { + this.coder.queueCode(code); + let code_return = await this.coder.execute(); + let message = code_return.message; + if (code_return.interrupted) + return; + if (!code_return.success) + message += "\n Write code to fix the problem and try again."; + console.log('code return:', message); + this.history.add('system', message); + if (!code_return.success) + await this.handleMessage(triesRemaining-1); + } + } + + async handleMessage(triesRemaining=5) { + if (triesRemaining == 0) { + console.log('Quitting response loop.'); + return; + } + + let res = await sendRequest(this.history.getHistory(), this.history.getSystemMessage()); + this.history.add(this.name, res); + let query_cmd = containsQuery(res); + if (query_cmd) { // contains query + let message = res.substring(0, res.indexOf(query_cmd)).trim(); + if (message) + this.bot.chat(message); + let query = getQuery(query_cmd); + let query_res = query.perform(this); + console.log('Agent used query:', query_cmd, 'and got:', query_res) + this.history.add('system', query_res); + await this.handleMessage(triesRemaining-1); + } + else if (containsCodeBlock(res)) { // contains code block + console.log('Agent is executing code:', res) + + let message = res.substring(0, res.indexOf('```')).trim(); + if (message) + this.bot.chat(message); + let code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```')); + await this.executeCode(code, triesRemaining); + } + else { // conversation response + this.bot.chat(res); + console.log('Purely conversational response:', res) + } + this.history.save(); + } } diff --git a/utils/history.js b/utils/history.js index b14a909..3289b7f 100644 --- a/utils/history.js +++ b/utils/history.js @@ -14,6 +14,10 @@ export class History { this.bio = ''; this.memory = ''; + // The bot's default behaviors + this.default = ''; + this.events = []; + // Variables for controlling the agent's memory and knowledge this.max_messages = 20; this.fewshot = 5; @@ -135,7 +139,7 @@ export class History { if (this.turns.length >= this.max_messages) { console.log('summarizing memory') let to_summarize = [this.turns.shift()]; - while (this.turns[0].role != 'user' && this.turns.length > 0) + while (this.turns[0].role != 'user' && this.turns.length > 1) to_summarize.push(this.turns.shift()); await this.storeMemories(to_summarize); } @@ -152,6 +156,8 @@ export class History { 'name': this.name, 'bio': this.bio, 'memory': this.memory, + 'default': this.default, + 'events': this.events, 'turns': this.turns }; const json_data = JSON.stringify(data, null, 4); @@ -169,10 +175,11 @@ export class History { // load history object from json file const data = readFileSync(this.save_path, 'utf8'); const obj = JSON.parse(data); - this.turns = obj.turns; this.bio = obj.bio; this.memory = obj.memory; - this.num_saved_turns = obj.num_saved_turns; + this.default = obj.default; + this.events = obj.events; + this.turns = obj.turns; } catch (err) { console.log('No history file found for ' + this.name + '.'); } diff --git a/utils/queries.js b/utils/queries.js index 417b235..e2cbdb4 100644 --- a/utils/queries.js +++ b/utils/queries.js @@ -47,6 +47,17 @@ const queryList = [ return pad("Current code:\n`" + agent.coder.current_code +"`"); } }, + { + name: "!events", + description: "Get the bot's events and callbacks.", + perform: function (agent) { + let res = "Events:"; + for (let [event, callback, params] of agent.history.events) { + res += `\n- ${event} -> ${callback.name}(${params})`; + } + return pad(res); + } + } ]; const queryMap = {}; diff --git a/utils/world.js b/utils/world.js index cc6fbd1..9cf6749 100644 --- a/utils/world.js +++ b/utils/world.js @@ -183,15 +183,16 @@ export function getNearbyPlayerNames(bot) { } -export function getNearbyBlockTypes(bot) { +export function getNearbyBlockTypes(bot, distance=16) { /** * Get a list of all nearby block names. * @param {Bot} bot - The bot to get nearby blocks for. + * @param {number} distance - The maximum distance to search, default 16. * @returns {string[]} - A list of all nearby blocks. * @example * let blocks = world.getNearbyBlockTypes(bot); **/ - let blocks = getNearbyBlocks(bot, 16); + let blocks = getNearbyBlocks(bot, distance); let found = []; for (let i = 0; i < blocks.length; i++) { if (!found.includes(blocks[i].name)) { From 8042a483295cb254662cc44443e169eb09688483 Mon Sep 17 00:00:00 2001 From: Kolby Nottingham Date: Tue, 12 Dec 2023 19:50:52 -0800 Subject: [PATCH 2/7] bot profiles --- .gitignore | 1 - agent.js | 41 ++--------------------------------------- utils/history.js | 5 +---- 3 files changed, 3 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 433a15e..f7aae85 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .vscode/ node_modules/ -bots/ package-lock.json temp.js scratch.js diff --git a/agent.js b/agent.js index 2715cd1..672463d 100644 --- a/agent.js +++ b/agent.js @@ -4,6 +4,7 @@ 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 { Events } from './utils/events.js'; export class Agent { @@ -16,35 +17,13 @@ export class Agent { if (!restart_memory) { this.history.load(); } - // Add the default behaviors and events - else { - this.history.default = `let blocks = world.getNearbyBlockTypes(bot, 4); - let block_type = blocks[Math.floor(Math.random() * blocks.length)]; - await skills.collectBlock(bot, block_type); - await new Promise(r => setTimeout(r, 1000)); - let players = world.getNearbyPlayerNames(bot); - let player_name = players[Math.floor(Math.random() * players.length)]; - await skills.goToPlayer(bot, player_name); - await new Promise(r => setTimeout(r, 1000));`; - this.history.events.push(['finished_executing', this.executeCode, 'default']); - // this.history.events.push(['finished_executing', this.sendThought, 'What should I do next? I will make a plan and execute it.']); - this.history.events.push(['health', this.sendThought, 'I may be under attack or need to eat! I will stop what I am doing to check my health and take action.']); - this.history.events.push(['sunrise', null, null]); - this.history.events.push(['noon', null, null]); - this.history.events.push(['sunset', null, null]); - this.history.events.push(['midnight', null, null]); - } + this.events = Events(this, this.history.events) this.bot.on('login', () => { this.bot.chat('Hello world! I am ' + this.name); console.log(`${this.name} logged in.`); }); - - for (let [event, callback, params] of this.history.events) { - if (callback != null) - this.bot.on(event, callback.bind(this, params)); - } } async start() { @@ -58,27 +37,11 @@ export class Agent { this.handleMessage(); }); - this.bot.on('time', () => { - if (this.bot.time.timeOfDay == 0) - this.bot.emit('sunrise'); - else if (this.bot.time.timeOfDay == 6000) - this.bot.emit('noon'); - else if (this.bot.time.timeOfDay == 12000) - this.bot.emit('sunset'); - else if (this.bot.time.timeOfDay == 18000) - this.bot.emit('midnight'); - }); - if (this.history.default) { this.executeCode(this.history.default); } } - async sendThought(message) { - this.history.add(this.name, message); - await this.handleMessage(); - } - async executeCode(code, triesRemaining=5) { if (code == 'default') code = this.history.default; diff --git a/utils/history.js b/utils/history.js index 3289b7f..b155a83 100644 --- a/utils/history.js +++ b/utils/history.js @@ -14,8 +14,7 @@ export class History { this.bio = ''; this.memory = ''; - // The bot's default behaviors - this.default = ''; + // The bot's events this.events = []; // Variables for controlling the agent's memory and knowledge @@ -156,7 +155,6 @@ export class History { 'name': this.name, 'bio': this.bio, 'memory': this.memory, - 'default': this.default, 'events': this.events, 'turns': this.turns }; @@ -177,7 +175,6 @@ export class History { const obj = JSON.parse(data); this.bio = obj.bio; this.memory = obj.memory; - this.default = obj.default; this.events = obj.events; this.turns = obj.turns; } catch (err) { From d6136a77e4c47afd1bd280f91ab1a4324f7a08fb Mon Sep 17 00:00:00 2001 From: Kolby Nottingham Date: Tue, 12 Dec 2023 21:35:39 -0800 Subject: [PATCH 3/7] finished merge and error fixing --- agent.js | 29 ++++++++++++++------------ bots/assist.json | 13 ++++++++++++ bots/survive.json | 19 +++++++++++++++++ controller/agent-process.js | 3 ++- controller/init-agent.js | 12 ++++++++--- utils/events.js | 41 +++++++++++++++++++++++++++++++++++++ utils/history.js | 24 ++++++++++++++-------- utils/queries.js | 3 +-- utils/skill-library.js | 3 +-- 9 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 bots/assist.json create mode 100644 bots/survive.json create mode 100644 utils/events.js diff --git a/agent.js b/agent.js index 5dda00a..b741c36 100644 --- a/agent.js +++ b/agent.js @@ -8,34 +8,37 @@ import { Events } from './utils/events.js'; export class Agent { - constructor(name, save_path, clear_memory=false, autostart=false) { + constructor(name, save_path, load_path=null, init_message=null) { this.name = name; this.bot = initBot(name); this.history = new History(this, save_path); this.history.loadExamples(); this.coder = new Coder(this); - if (!clear_memory) { - this.history.load(); + if (load_path) { + this.history.load(load_path); } - this.events = Events(this, this.history.events) + this.events = new Events(this, this.history.events) 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 (username === this.name) return; + console.log('received message from', username, ':', message); - if (autostart) - this.respond('system', 'Agent process restarted. Notify the user and decide what to do.'); - + this.history.add(username, message); + this.handleMessage(); + }); + + if (init_message) { + this.history.add('system', init_message); + this.handleMessage(); + } else { + this.bot.emit('finished_executing'); + } }); } diff --git a/bots/assist.json b/bots/assist.json new file mode 100644 index 0000000..079fe4b --- /dev/null +++ b/bots/assist.json @@ -0,0 +1,13 @@ +{ + "name": "andy", + "bio": "You are playing minecraft and assisting other players in tasks.", + "memory": "", + "events": [ + [ + "finished_executing", + "executeCode", + "let blocks = world.getNearbyBlockTypes(bot, 4);\nlet block_type = blocks[Math.floor(Math.random() * blocks.length)];\nawait skills.collectBlock(bot, block_type);\nawait new Promise(r => setTimeout(r, 1000));\nlet players = world.getNearbyPlayerNames(bot);\nlet player_name = players[Math.floor(Math.random() * players.length)];\nawait skills.goToPlayer(bot, player_name);\nawait new Promise(r => setTimeout(r, 1000));" + ] + ], + "turns": [] +} \ No newline at end of file diff --git a/bots/survive.json b/bots/survive.json new file mode 100644 index 0000000..ec69983 --- /dev/null +++ b/bots/survive.json @@ -0,0 +1,19 @@ +{ + "name": "andy", + "bio": "You are playing minecraft. Your goal is to collect materials and survive for as long as possible. Follow instructions from other players when appropriate.", + "memory": "", + "default": "", + "events": [ + [ + "finished_executing", + "sendThought", + "I need keep collecting and crafting to survive. I should plan what to do next and execute it." + ], + [ + "damaged", + "sendThought", + "I may be under attack or need to eat! I will stop what I am doing to check my health and take action." + ] + ], + "turns": [] +} \ No newline at end of file diff --git a/controller/agent-process.js b/controller/agent-process.js index 53fdf1b..3bc4fb7 100644 --- a/controller/agent-process.js +++ b/controller/agent-process.js @@ -4,8 +4,9 @@ export class AgentProcess { constructor(name) { this.name = name; } - start(clear_memory=false, autostart=false) { + start(clear_memory=false, autostart=false, profile='survive') { let args = ['controller/init-agent.js', this.name]; + args.push('-p', profile); if (clear_memory) args.push('-c'); if (autostart) diff --git a/controller/init-agent.js b/controller/init-agent.js index e632064..b9329d9 100644 --- a/controller/init-agent.js +++ b/controller/init-agent.js @@ -8,6 +8,11 @@ if (args.length < 1) { } const argv = yargs(args) + .option('profile', { + alias: 'p', + type: 'string', + description: 'profile to use for agent' + }) .option('clear_memory', { alias: 'c', type: 'boolean', @@ -20,8 +25,9 @@ const argv = yargs(args) }).argv const name = argv._[0]; -const clear_memory = !!argv.clear_memory; -const autostart = !!argv.autostart; const save_path = './bots/'+name+'.json'; +const profile = argv.profile; +const load_path = !!argv.clear_memory ? './bots/'+profile+'.json' : save_path; +const init_message = !!argv.autostart ? 'Agent process restarted. Notify the user and decide what to do.' : null; -new Agent(name, save_path, clear_memory, autostart); +new Agent(name, save_path, load_path, init_message); diff --git a/utils/events.js b/utils/events.js new file mode 100644 index 0000000..1da99ab --- /dev/null +++ b/utils/events.js @@ -0,0 +1,41 @@ +export class Events { + constructor(agent, events) { + this.events = events; + if (agent != null) + this.init(agent, events); + } + + init(agent, events) { + this.events = events; + for (let [event, callback, params] of events) { + if (callback != null) + agent.bot.on(event, this[callback].bind(this, agent, params)); + } + + agent.bot.on('time', () => { + if (agent.bot.time.timeOfDay == 0) + agent.bot.emit('sunrise'); + else if (agent.bot.time.timeOfDay == 6000) + agent.bot.emit('noon'); + else if (agent.bot.time.timeOfDay == 12000) + agent.bot.emit('sunset'); + else if (agent.bot.time.timeOfDay == 18000) + agent.bot.emit('midnight'); + }); + + agent.bot.on('health', () => { + if (agent.bot.health < 20) + agent.bot.emit('damaged'); + }); + } + + executeCode(agent, code) { + agent.executeCode(code); + } + + sendThought(agent, message) { + agent.history.add(agent.name, message); + agent.handleMessage(); + } + +} \ No newline at end of file diff --git a/utils/history.js b/utils/history.js index f1d4d60..93d46be 100644 --- a/utils/history.js +++ b/utils/history.js @@ -37,8 +37,7 @@ export class History { } getSystemMessage() { - let system_message = `You are a playful Minecraft bot named '${this.name}' that can communicate with players, see, move, mine, build, and interact with the world by writing and executing code. - Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in your responses, omit needless words, and do not give instructions unless asked.`; + let system_message = `You are a playful Minecraft bot named '${this.name}' that can communicate with players, see, move, mine, build, and interact with the world by writing and executing code. Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in your responses, omit needless words, and do not give instructions unless asked.`; system_message += getQueryDocs(); system_message += getSkillDocs(); if (this.bio != '') @@ -104,6 +103,8 @@ export class History { const embedding = await embed(messages); this.examples.push({'embedding': embedding, 'turns': example}); } + + await this.setExamples(); } async setExamples() { @@ -143,12 +144,14 @@ export class History { await this.storeMemories(to_summarize); } - if (role === 'user') + if (role != 'assistant') await this.setExamples(); } - save() { - if (this.save_path === '' || this.save_path == null) return; + save(save_path=null) { + if (save_path == null) + save_path = this.save_path; + if (save_path === '' || save_path == null) return; // save history object to json file mkdirSync('bots', { recursive: true }); let data = { @@ -159,7 +162,7 @@ export class History { 'turns': this.turns }; const json_data = JSON.stringify(data, null, 4); - writeFileSync(this.save_path, json_data, (err) => { + writeFileSync(save_path, json_data, (err) => { if (err) { throw err; } @@ -167,11 +170,13 @@ export class History { }); } - load() { - if (this.save_path === '' || this.save_path == null) return; + load(load_path=null) { + if (load_path == null) + load_path = this.save_path; + if (load_path === '' || load_path == null) return; try { // load history object from json file - const data = readFileSync(this.save_path, 'utf8'); + const data = readFileSync(load_path, 'utf8'); const obj = JSON.parse(data); this.bio = obj.bio; this.memory = obj.memory; @@ -179,6 +184,7 @@ export class History { this.turns = obj.turns; } catch (err) { console.log('No history file found for ' + this.name + '.'); + console.log(load_path); } } } \ No newline at end of file diff --git a/utils/queries.js b/utils/queries.js index e2cbdb4..2f732d6 100644 --- a/utils/queries.js +++ b/utils/queries.js @@ -79,8 +79,7 @@ export function containsQuery(message) { } export function getQueryDocs() { - let docs = `\n*QUERY DOCS\n You can use the following commands to query for information about the world. - Use the query name in your response and the next input will have the requested information.\n`; + let docs = `\n*QUERY DOCS\n You can use the following commands to query for information about the world. Use the query name in your response and the next input will have the requested information.\n`; for (let query of queryList) { docs += query.name + ': ' + query.description + '\n'; } diff --git a/utils/skill-library.js b/utils/skill-library.js index e9830dd..246e3ab 100644 --- a/utils/skill-library.js +++ b/utils/skill-library.js @@ -2,8 +2,7 @@ import * as skills from './skills.js'; import * as world from './world.js'; export function getSkillDocs() { - let docstring = "\n*SKILL DOCS\nThese skills are javascript functions that can be called with a js function by writing a code block. Ex: '```// write description comment and code here```' \n\ - Your code block should return a bool indicating if the task was completed successfully. It will return true if you don't write a return statement.\n"; + let docstring = "\n*SKILL DOCS\nThese skills are javascript functions that can be called with a js function by writing a code block. Ex: '```// write description comment and code here```' \nYour code block should return a bool indicating if the task was completed successfully. It will return true if you don't write a return statement.\n"; docstring += docHelper(Object.values(skills), 'skills'); docstring += docHelper(Object.values(world), 'world'); return docstring + '*\n'; From 38bc5454b683bb2748c0b5aa5e92026c7d9afa63 Mon Sep 17 00:00:00 2001 From: Kolby Nottingham Date: Tue, 12 Dec 2023 21:39:50 -0800 Subject: [PATCH 4/7] remove event query --- utils/queries.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/utils/queries.js b/utils/queries.js index 2f732d6..1820197 100644 --- a/utils/queries.js +++ b/utils/queries.js @@ -46,17 +46,6 @@ const queryList = [ perform: function (agent) { return pad("Current code:\n`" + agent.coder.current_code +"`"); } - }, - { - name: "!events", - description: "Get the bot's events and callbacks.", - perform: function (agent) { - let res = "Events:"; - for (let [event, callback, params] of agent.history.events) { - res += `\n- ${event} -> ${callback.name}(${params})`; - } - return pad(res); - } } ]; From 4213208172023374e5c46169b2b31473cf917380 Mon Sep 17 00:00:00 2001 From: Kolby Nottingham Date: Sat, 16 Dec 2023 12:08:47 -0600 Subject: [PATCH 5/7] moved finished executing --- agent.js | 1 + utils/coder.js | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/agent.js b/agent.js index b741c36..2fab852 100644 --- a/agent.js +++ b/agent.js @@ -94,5 +94,6 @@ export class Agent { console.log('Purely conversational response:', res) } this.history.save(); + this.bot.emit('finished_executing'); } } diff --git a/utils/coder.js b/utils/coder.js index f6040d1..96ad275 100644 --- a/utils/coder.js +++ b/utils/coder.js @@ -96,7 +96,6 @@ export class Coder { 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; @@ -106,7 +105,6 @@ export class Coder { this.executing = false; clearTimeout(TIMEOUT); - this.agent.bot.emit('finished_executing'); console.error("Code execution triggered catch: " + err); let message = this.formatOutput(this.agent.bot); message += '!!Code threw exception!! Error: ' + err; From 85e7a508c2d0e7ab4f88f5d1ef7807034cbbd744 Mon Sep 17 00:00:00 2001 From: Kolby Nottingham Date: Wed, 20 Dec 2023 16:30:05 -0800 Subject: [PATCH 6/7] simplify profiles and events --- agent.js | 93 +++++++++++++++++-------------------- bots/assist.json | 8 +--- bots/survive.json | 19 -------- controller/agent-process.js | 2 +- controller/init-agent.js | 3 +- utils/coder.js | 2 + utils/events.js | 11 ++++- 7 files changed, 56 insertions(+), 82 deletions(-) delete mode 100644 bots/survive.json diff --git a/agent.js b/agent.js index 2fab852..a6beffd 100644 --- a/agent.js +++ b/agent.js @@ -29,70 +29,61 @@ export class Agent { if (username === this.name) return; console.log('received message from', username, ':', message); - this.history.add(username, message); - this.handleMessage(); + this.handleMessage(username, message); }); if (init_message) { - this.history.add('system', init_message); - this.handleMessage(); + this.handleMessage('system', init_message); } else { this.bot.emit('finished_executing'); } }); } - async executeCode(code, triesRemaining=5) { - if (code == 'default') - code = this.history.default; + async handleMessage(source, message) { + await this.history.add(source, message); - if (code) { - this.coder.queueCode(code); - let code_return = await this.coder.execute(); - let message = code_return.message; - if (code_return.interrupted) - return; - if (!code_return.success) - message += "\n Write code to fix the problem and try again."; - console.log('code return:', message); - this.history.add('system', message); - if (!code_return.success) - await this.handleMessage(triesRemaining-1); - } - } + for (let i=0; i<5; i++) { + let res = await sendRequest(this.history.getHistory(), this.history.getSystemMessage()); + this.history.add(this.name, res); + let query_cmd = containsQuery(res); + if (query_cmd) { // contains query + let message = res.substring(0, res.indexOf(query_cmd)).trim(); + if (message) + this.bot.chat(message); + let query = getQuery(query_cmd); + let query_res = query.perform(this); + console.log('Agent used query:', query_cmd, 'and got:', query_res) + this.history.add('system', query_res); + } + else if (containsCodeBlock(res)) { // contains code block + console.log('Agent is executing code:', res) - async handleMessage(triesRemaining=5) { - if (triesRemaining == 0) { - console.log('Quitting response loop.'); - return; + let message = res.substring(0, res.indexOf('```')).trim(); + if (message) + this.bot.chat(message); + let code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```')); + + if (code) { + this.coder.queueCode(code); + let code_return = await this.coder.execute(); + let message = code_return.message; + if (code_return.interrupted && !code_return.timedout) + break; + if (!code_return.success) { + message += "\nWrite code to fix the problem and try again."; + } + console.log('code return:', message); + this.history.add('system', message); + } + } + else { // conversation response + this.bot.chat(res); + console.log('Purely conversational response:', res); + break; + } } - let res = await sendRequest(this.history.getHistory(), this.history.getSystemMessage()); - this.history.add(this.name, res); - let query_cmd = containsQuery(res); - if (query_cmd) { // contains query - let message = res.substring(0, res.indexOf(query_cmd)).trim(); - if (message) - this.bot.chat(message); - let query = getQuery(query_cmd); - let query_res = query.perform(this); - console.log('Agent used query:', query_cmd, 'and got:', query_res) - this.history.add('system', query_res); - await this.handleMessage(triesRemaining-1); - } - else if (containsCodeBlock(res)) { // contains code block - console.log('Agent is executing code:', res) - - let message = res.substring(0, res.indexOf('```')).trim(); - if (message) - this.bot.chat(message); - let code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```')); - await this.executeCode(code, triesRemaining); - } - else { // conversation response - this.bot.chat(res); - console.log('Purely conversational response:', res) - } this.history.save(); this.bot.emit('finished_executing'); } diff --git a/bots/assist.json b/bots/assist.json index 079fe4b..2307682 100644 --- a/bots/assist.json +++ b/bots/assist.json @@ -2,12 +2,6 @@ "name": "andy", "bio": "You are playing minecraft and assisting other players in tasks.", "memory": "", - "events": [ - [ - "finished_executing", - "executeCode", - "let blocks = world.getNearbyBlockTypes(bot, 4);\nlet block_type = blocks[Math.floor(Math.random() * blocks.length)];\nawait skills.collectBlock(bot, block_type);\nawait new Promise(r => setTimeout(r, 1000));\nlet players = world.getNearbyPlayerNames(bot);\nlet player_name = players[Math.floor(Math.random() * players.length)];\nawait skills.goToPlayer(bot, player_name);\nawait new Promise(r => setTimeout(r, 1000));" - ] - ], + "events": [], "turns": [] } \ No newline at end of file diff --git a/bots/survive.json b/bots/survive.json deleted file mode 100644 index ec69983..0000000 --- a/bots/survive.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "andy", - "bio": "You are playing minecraft. Your goal is to collect materials and survive for as long as possible. Follow instructions from other players when appropriate.", - "memory": "", - "default": "", - "events": [ - [ - "finished_executing", - "sendThought", - "I need keep collecting and crafting to survive. I should plan what to do next and execute it." - ], - [ - "damaged", - "sendThought", - "I may be under attack or need to eat! I will stop what I am doing to check my health and take action." - ] - ], - "turns": [] -} \ No newline at end of file diff --git a/controller/agent-process.js b/controller/agent-process.js index 3bc4fb7..f3439f6 100644 --- a/controller/agent-process.js +++ b/controller/agent-process.js @@ -4,7 +4,7 @@ export class AgentProcess { constructor(name) { this.name = name; } - start(clear_memory=false, autostart=false, profile='survive') { + start(clear_memory=false, autostart=false, profile='assist') { let args = ['controller/init-agent.js', this.name]; args.push('-p', profile); if (clear_memory) diff --git a/controller/init-agent.js b/controller/init-agent.js index b9329d9..96d9381 100644 --- a/controller/init-agent.js +++ b/controller/init-agent.js @@ -26,8 +26,7 @@ const argv = yargs(args) const name = argv._[0]; const save_path = './bots/'+name+'.json'; -const profile = argv.profile; -const load_path = !!argv.clear_memory ? './bots/'+profile+'.json' : save_path; +const load_path = !!argv.clear_memory ? './bots/'+argv.profile+'.json' : save_path; const init_message = !!argv.autostart ? 'Agent process restarted. Notify the user and decide what to do.' : null; new Agent(name, save_path, load_path, init_message); diff --git a/utils/coder.js b/utils/coder.js index 96ad275..0b6e405 100644 --- a/utils/coder.js +++ b/utils/coder.js @@ -100,6 +100,7 @@ export class Coder { let interrupted = this.agent.bot.interrupt_code; let timedout = this.timedout; this.clear(); + this.agent.bot.emit("code_terminated"); return {success:true, message: output, interrupted, timedout}; } catch (err) { this.executing = false; @@ -110,6 +111,7 @@ export class Coder { message += '!!Code threw exception!! Error: ' + err; let interrupted = this.agent.bot.interrupt_code; await this.stop(); + this.agent.bot.emit("code_terminated"); return {success: false, message, interrupted, timedout: false}; } } diff --git a/utils/events.js b/utils/events.js index 1da99ab..4a64c0e 100644 --- a/utils/events.js +++ b/utils/events.js @@ -29,8 +29,12 @@ export class Events { }); } - executeCode(agent, code) { - agent.executeCode(code); + async executeCode(agent, code) { + console.log('responding to event with code.'); + agent.coder.queueCode(code); + let code_return = await agent.coder.execute(); + console.log('code return:', code_return.message); + agent.history.add('system', code_return.message); } sendThought(agent, message) { @@ -38,4 +42,7 @@ export class Events { agent.handleMessage(); } + sendChat(agent, message) { + agent.bot.chat(message); + } } \ No newline at end of file From c8f2cb5a3977d5434be4b83b45e875d9502a60c5 Mon Sep 17 00:00:00 2001 From: Kolby Nottingham Date: Wed, 20 Dec 2023 20:41:15 -0800 Subject: [PATCH 7/7] sendThouhgt fix --- controller/agent-process.js | 4 ++-- utils/events.js | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/controller/agent-process.js b/controller/agent-process.js index f3439f6..929317e 100644 --- a/controller/agent-process.js +++ b/controller/agent-process.js @@ -22,8 +22,8 @@ export class AgentProcess { 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) { + // agent must run for at least 10 seconds before restarting + if (Date.now() - last_restart < 10000) { console.error('Agent process exited too quickly. Killing entire process. Goodbye.'); process.exit(1); } diff --git a/utils/events.js b/utils/events.js index 4a64c0e..015cf17 100644 --- a/utils/events.js +++ b/utils/events.js @@ -38,8 +38,7 @@ export class Events { } sendThought(agent, message) { - agent.history.add(agent.name, message); - agent.handleMessage(); + agent.handleMessage(agent.name, message); } sendChat(agent, message) {