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)) {