From 440ffdd931771f33cbd682493f56a717296cbbe7 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Mon, 3 Jun 2024 18:21:11 -0500 Subject: [PATCH 1/7] ignore user commands, remove message logs --- src/agent/agent.js | 5 ++--- src/models/claude.js | 2 +- src/models/gpt.js | 3 ++- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/agent/agent.js b/src/agent/agent.js index deed4f5..8a0c044 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -79,9 +79,6 @@ export class Agent { } async handleMessage(source, message) { - if (!!source && !!message) - await this.history.add(source, message); - const user_command_name = containsCommand(message); if (user_command_name) { if (!commandExists(user_command_name)) { @@ -101,6 +98,8 @@ export class Agent { return; } + await this.history.add(source, message); + for (let i=0; i<5; i++) { let history = this.history.getHistory(); let res = await this.prompter.promptConvo(history); diff --git a/src/models/claude.js b/src/models/claude.js index 0304714..c97ecb2 100644 --- a/src/models/claude.js +++ b/src/models/claude.js @@ -20,7 +20,7 @@ export class Claude { let res = null; try { console.log('Awaiting anthropic api response...') - console.log('Messages:', messages); + // console.log('Messages:', messages); const resp = await this.anthropic.messages.create({ model: this.model_name || "claude-3-sonnet-20240229", system: systemMessage, diff --git a/src/models/gpt.js b/src/models/gpt.js index 061596b..bf5ca8d 100644 --- a/src/models/gpt.js +++ b/src/models/gpt.js @@ -1,5 +1,6 @@ import OpenAIApi from 'openai'; import { getKey, hasKey } from '../utils/keys.js'; +import { strictFormat } from '../utils/text.js'; export class GPT { constructor(model_name, url) { @@ -24,7 +25,7 @@ export class GPT { let res = null; try { console.log('Awaiting openai api response...') - console.log('Messages:', messages); + // console.log('Messages:', messages); let completion = await this.openai.chat.completions.create({ model: this.model_name || "gpt-3.5-turbo", messages: messages, From af8252cd95e6e8e9d9f5f76627f44ad99c5fce20 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Mon, 3 Jun 2024 18:23:01 -0500 Subject: [PATCH 2/7] added cheat mode, teleport, insta place, better torch placer --- src/agent/library/skills.js | 73 ++++++++++++++++++++++++++++++------- src/agent/modes.js | 33 ++++++++++------- 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index cd9f5b3..ba8c27e 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -10,19 +10,25 @@ export function log(bot, message, chat=false) { bot.chat(message); } +export function shouldPlaceTorch(bot) { + if (!bot.modes.isOn('torch_placing') || bot.interrupt_code) return false; + const pos = world.getPosition(bot); + // TODO: check light level instead of nearby torches, block.light is broken + let nearest_torch = world.getNearestBlock(bot, 'torch', 6); + if (!nearest_torch) { + const block = bot.blockAt(pos); + let has_torch = bot.inventory.items().find(item => item.name === 'torch'); + return has_torch && block.name === 'air'; + } + return false; +} + async function autoLight(bot) { - if (bot.modes.isOn('torch_placing') && !bot.interrupt_code) { - let nearest_torch = world.getNearestBlock(bot, 'torch', 6); - if (!nearest_torch) { - let has_torch = bot.inventory.items().find(item => item.name === 'torch'); - const curr_block = agent.bot.blockAt(pos); - if (has_torch && curr_block.name === 'air') { - try { - log(bot, `Placing torch at ${bot.entity.position}.`); - return await placeBlock(bot, 'torch', bot.entity.position.x, bot.entity.position.y, bot.entity.position.z); - } catch (err) {return true;} - } - } + if (shouldPlaceTorch(bot)) { + try { + const pos = world.getPosition(bot); + return await placeBlock(bot, 'torch', pos.x, pos.y, pos.z, true); + } catch (err) {return false;} } return false; } @@ -455,6 +461,13 @@ export async function breakBlockAt(bot, x, y, z) { if (x == null || y == null || z == null) throw new Error('Invalid position to break block at.'); let block = bot.blockAt(Vec3(x, y, z)); if (block.name !== 'air' && block.name !== 'water' && block.name !== 'lava') { + if (bot.modes.isOn('cheat')) { + let msg = '/setblock ' + Math.floor(x) + ' ' + Math.floor(y) + ' ' + Math.floor(z) + ' air'; + bot.chat(msg); + log(bot, `Used /setblock to break block at ${x}, ${y}, ${z}.`); + return true; + } + if (bot.entity.position.distanceTo(block.position) > 4.5) { let pos = block.position; let movements = new pf.Movements(bot); @@ -482,7 +495,7 @@ export async function breakBlockAt(bot, x, y, z) { } -export async function placeBlock(bot, blockType, x, y, z) { +export async function placeBlock(bot, blockType, x, y, z, no_cheat=false) { /** * Place the given block type at the given position. It will build off from any adjacent blocks. Will fail if there is a block in the way or nothing to build off of. * @param {MinecraftBot} bot, reference to the minecraft bot. @@ -495,7 +508,18 @@ export async function placeBlock(bot, blockType, x, y, z) { * let position = world.getPosition(bot); * await skills.placeBlock(bot, "oak_log", position.x + 1, position.y - 1, position.x); **/ - console.log('placing block...') + if (!mc.getBlockId(blockType)) { + log(bot, `Invalid block type: ${blockType}.`); + return false; + } + + if (bot.modes.isOn('cheat') && !no_cheat) { + let msg = '/setblock ' + Math.floor(x) + ' ' + Math.floor(y) + ' ' + Math.floor(z) + ' ' + blockType; + bot.chat(msg); + log(bot, `Used /setblock to place ${blockType} at ${x}, ${y}, ${z}.`); + return true; + } + let block = bot.inventory.items().find(item => item.name === blockType); if (!block) { log(bot, `Don't have any ${blockType} to place.`); @@ -705,6 +729,13 @@ export async function goToPlayer(bot, username, distance=3) { * @example * await skills.goToPlayer(bot, "player"); **/ + + if (bot.modes.isOn('cheat')) { + bot.chat('/tp @s ' + username); + log(bot, `Teleported to ${username}.`); + return true; + } + bot.modes.pause('self_defense'); bot.modes.pause('cowardice'); let player = bot.players[username].entity @@ -759,6 +790,20 @@ export async function moveAway(bot, distance) { let goal = new pf.goals.GoalNear(pos.x, pos.y, pos.z, distance); let inverted_goal = new pf.goals.GoalInvert(goal); bot.pathfinder.setMovements(new pf.Movements(bot)); + + if (bot.modes.isOn('cheat')) { + const path = await bot.pathfinder.getPathTo(move, inverted_goal, 10000); + let last_move = path.path[path.path.length-1]; + console.log(last_move); + if (last_move) { + let x = Math.floor(last_move.x); + let y = Math.floor(last_move.y); + let z = Math.floor(last_move.z); + bot.chat('/tp @s ' + x + ' ' + y + ' ' + z); + return true; + } + } + await bot.pathfinder.goto(inverted_goal); let new_pos = bot.entity.position; log(bot, `Moved away from nearest entity to ${new_pos}.`); diff --git a/src/agent/modes.js b/src/agent/modes.js index 9a32c0e..4d5b83c 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -149,21 +149,16 @@ const modes = [ interrupts: ['followPlayer'], on: true, active: false, + cooldown: 5, + last_place: Date.now(), update: function (agent) { - // TODO: check light level instead of nearby torches, block.light is broken - const near_torch = world.getNearestBlock(agent.bot, 'torch', 6); - if (!near_torch) { - let torches = agent.bot.inventory.items().filter(item => item.name === 'torch'); - if (torches.length > 0) { - const torch = torches[0]; + if (skills.shouldPlaceTorch(agent.bot)) { + if (Date.now() - this.last_place < this.cooldown * 1000) return; + execute(this, agent, async () => { const pos = agent.bot.entity.position; - const curr_block = agent.bot.blockAt(pos); - if (curr_block.name === 'air') { - execute(this, agent, async () => { - await skills.placeBlock(agent.bot, torch.name, pos.x, pos.y, pos.z); - }); - } - } + await skills.placeBlock(agent.bot, 'torch', pos.x, pos.y, pos.z, true); + }); + this.last_place = Date.now(); } } }, @@ -204,6 +199,14 @@ const modes = [ } } }, + { + name: 'cheat', + description: 'Use cheats to instantly place blocks and teleport.', + interrupts: [], + on: false, + active: false, + update: function (agent) { /* do nothing */ } + } ]; async function execute(mode, agent, func, timeout=-1) { @@ -291,4 +294,8 @@ class ModeController { export function initModes(agent) { // the mode controller is added to the bot object so it is accessible from anywhere the bot is used agent.bot.modes = new ModeController(agent); + let modes = agent.prompter.getInitModes(); + if (modes) { + agent.bot.modes.loadJson(modes); + } } From 25c4f68be7789203556fb236e398825f7e1dba75 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Mon, 3 Jun 2024 18:23:29 -0500 Subject: [PATCH 3/7] add modes to profile --- andy.json | 11 +++++++++++ src/agent/npc/controller.js | 2 +- src/agent/prompter.js | 28 ++++++++++++++++------------ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/andy.json b/andy.json index d4c5fc9..8d2654e 100644 --- a/andy.json +++ b/andy.json @@ -8,6 +8,17 @@ "coding": "You are an intelligent mineflayer bot $NAME that plays minecraft by writing javascript codeblocks. Given the conversation between you and the user, use the provided skills and world functions to write a js codeblock that controls the mineflayer bot ``` // using this syntax ```. The code will be executed and you will recieve it's output. If you are satisfied with the response, respond without a codeblock in a conversational way. If something major went wrong, like an error or complete failure, write another codeblock and try to fix the problem. Minor mistakes are acceptable. Be maximally efficient, creative, and clear. Do not use commands !likeThis, only use codeblocks. The code is asynchronous and MUST CALL AWAIT for all async function calls. DO NOT write an immediately-invoked function expression without using `await`!! DO NOT WRITE LIKE THIS: ```(async () => {console.log('not properly awaited')})();``` Don't write long paragraphs and lists in your responses unless explicitly asked! Only summarize the code you write with a sentence or two when done. This is extremely important to me, take a deep breath and good luck! \n$STATS\n$INVENTORY\n$CODE_DOCS\n$EXAMPLES\nConversation:", "saving_memory": "You are a minecraft bot named $NAME that has been talking and playing minecraft by using commands. Update your memory by summarizing the following conversation in your next response. Store information that will help you improve as a Minecraft bot. Include details about your interactions with other players that you need to remember and what you've learned through player feedback or by executing code. Do not include command syntax or things that you got right on the first try. Be extremely brief and use as few words as possible.\nOld Memory: '$MEMORY'\nRecent conversation: \n$TO_SUMMARIZE\nSummarize your old memory and recent conversation into a new memory, and respond only with the memory text: ", + + "modes": { + "self_preservation": true, + "cowardice": true, + "self_defense": true, + "hunting": true, + "item_collecting": true, + "torch_placing": true, + "idle_staring": true, + "cheat": false + }, "conversation_examples": [ [ diff --git a/src/agent/npc/controller.js b/src/agent/npc/controller.js index d65107c..18658b6 100644 --- a/src/agent/npc/controller.js +++ b/src/agent/npc/controller.js @@ -11,7 +11,7 @@ import * as mc from '../../utils/mcdata.js'; export class NPCContoller { constructor(agent) { this.agent = agent; - this.data = NPCData.fromObject(agent.prompter.prompts.npc); + this.data = NPCData.fromObject(agent.prompter.profile.npc); this.temp_goals = []; this.item_goal = new ItemGoal(agent, this.data); this.build_goal = new BuildGoal(agent); diff --git a/src/agent/prompter.js b/src/agent/prompter.js index 5eb5761..233c4a0 100644 --- a/src/agent/prompter.js +++ b/src/agent/prompter.js @@ -15,12 +15,12 @@ import { Local } from '../models/local.js'; export class Prompter { constructor(agent, fp) { this.agent = agent; - this.prompts = JSON.parse(readFileSync(fp, 'utf8')); + this.profile = JSON.parse(readFileSync(fp, 'utf8')); this.convo_examples = null; this.coding_examples = null; - let name = this.prompts.name; - let chat = this.prompts.model; + let name = this.profile.name; + let chat = this.profile.model; if (typeof chat === 'string' || chat instanceof String) { chat = {model: chat}; if (chat.model.includes('gemini')) @@ -50,7 +50,7 @@ export class Prompter { else throw new Error('Unknown API:', api); - let embedding = this.prompts.embedding; + let embedding = this.profile.embedding; if (embedding === undefined) { if (chat.api !== 'ollama') embedding = {api: chat.api}; @@ -76,7 +76,7 @@ export class Prompter { } mkdirSync(`./bots/${name}`, { recursive: true }); - writeFileSync(`./bots/${name}/last_profile.json`, JSON.stringify(this.prompts, null, 4), (err) => { + writeFileSync(`./bots/${name}/last_profile.json`, JSON.stringify(this.profile, null, 4), (err) => { if (err) { throw err; } @@ -85,15 +85,19 @@ export class Prompter { } getName() { - return this.prompts.name; + return this.profile.name; + } + + getInitModes() { + return this.profile.modes; } async initExamples() { console.log('Loading examples...') this.convo_examples = new Examples(this.embedding_model); - await this.convo_examples.load(this.prompts.conversation_examples); + await this.convo_examples.load(this.profile.conversation_examples); this.coding_examples = new Examples(this.embedding_model); - await this.coding_examples.load(this.prompts.coding_examples); + await this.coding_examples.load(this.profile.coding_examples); console.log('Examples loaded.'); } @@ -149,25 +153,25 @@ export class Prompter { } async promptConvo(messages) { - let prompt = this.prompts.conversing; + let prompt = this.profile.conversing; prompt = await this.replaceStrings(prompt, messages, this.convo_examples); return await this.chat_model.sendRequest(messages, prompt); } async promptCoding(messages) { - let prompt = this.prompts.coding; + let prompt = this.profile.coding; prompt = await this.replaceStrings(prompt, messages, this.coding_examples); return await this.chat_model.sendRequest(messages, prompt); } async promptMemSaving(prev_mem, to_summarize) { - let prompt = this.prompts.saving_memory; + let prompt = this.profile.saving_memory; prompt = await this.replaceStrings(prompt, null, null, prev_mem, to_summarize); return await this.chat_model.sendRequest([], prompt); } async promptGoalSetting(messages, last_goals) { - let system_message = this.prompts.goal_setting; + let system_message = this.profile.goal_setting; system_message = await this.replaceStrings(system_message, messages); let user_message = 'Use the below info to determine what goal to target next\n\n'; From ca4ef4114a06492cecda02852202f3babdd6ad04 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Mon, 3 Jun 2024 18:28:44 -0500 Subject: [PATCH 4/7] added no_cheat to docs --- src/agent/library/skills.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index ba8c27e..2266a2e 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -503,6 +503,7 @@ export async function placeBlock(bot, blockType, x, y, z, no_cheat=false) { * @param {number} x, the x coordinate of the block to place. * @param {number} y, the y coordinate of the block to place. * @param {number} z, the z coordinate of the block to place. + * @param {boolean} no_cheat, overrides cheat mode to place the block normally. Defaults to false. * @returns {Promise} true if the block was placed, false otherwise. * @example * let position = world.getPosition(bot); From baa51ee2c825f5025f744403b46180161d1a9038 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Mon, 3 Jun 2024 18:33:00 -0500 Subject: [PATCH 5/7] added teleport to gotoposition --- src/agent/library/skills.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index 2266a2e..c409d6e 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -713,6 +713,11 @@ export async function goToPosition(bot, x, y, z, min_distance=2) { log(bot, `Missing coordinates, given x:${x} y:${y} z:${z}`); return false; } + if (bot.modes.isOn('cheat')) { + bot.chat('/tp @s ' + x + ' ' + y + ' ' + z); + log(bot, `Teleported to ${x}, ${y}, ${z}.`); + return true; + } bot.pathfinder.setMovements(new pf.Movements(bot)); await bot.pathfinder.goto(new pf.goals.GoalNear(x, y, z, min_distance)); log(bot, `You have reached at ${x}, ${y}, ${z}.`); From 3342a6deb9b76a0b990a2be754caeeb9f4fd31a1 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Mon, 3 Jun 2024 18:40:01 -0500 Subject: [PATCH 6/7] removed strict format --- src/models/gpt.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/models/gpt.js b/src/models/gpt.js index bf5ca8d..7cfbcb7 100644 --- a/src/models/gpt.js +++ b/src/models/gpt.js @@ -1,6 +1,5 @@ import OpenAIApi from 'openai'; import { getKey, hasKey } from '../utils/keys.js'; -import { strictFormat } from '../utils/text.js'; export class GPT { constructor(model_name, url) { From 3f541ae9303fb8631e61d190adf94e8b60090aa5 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Mon, 3 Jun 2024 19:09:22 -0500 Subject: [PATCH 7/7] moved shouldplacetorch to world --- src/agent/library/skills.js | 15 +-------------- src/agent/library/world.js | 13 +++++++++++++ src/agent/modes.js | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/agent/library/skills.js b/src/agent/library/skills.js index c409d6e..b848304 100644 --- a/src/agent/library/skills.js +++ b/src/agent/library/skills.js @@ -10,21 +10,8 @@ export function log(bot, message, chat=false) { bot.chat(message); } -export function shouldPlaceTorch(bot) { - if (!bot.modes.isOn('torch_placing') || bot.interrupt_code) return false; - const pos = world.getPosition(bot); - // TODO: check light level instead of nearby torches, block.light is broken - let nearest_torch = world.getNearestBlock(bot, 'torch', 6); - if (!nearest_torch) { - const block = bot.blockAt(pos); - let has_torch = bot.inventory.items().find(item => item.name === 'torch'); - return has_torch && block.name === 'air'; - } - return false; -} - async function autoLight(bot) { - if (shouldPlaceTorch(bot)) { + if (world.shouldPlaceTorch(bot)) { try { const pos = world.getPosition(bot); return await placeBlock(bot, 'torch', pos.x, pos.y, pos.z, true); diff --git a/src/agent/library/world.js b/src/agent/library/world.js index 9d6be62..0e32014 100644 --- a/src/agent/library/world.js +++ b/src/agent/library/world.js @@ -256,6 +256,19 @@ export async function isClearPath(bot, target) { return path.status === 'success'; } +export function shouldPlaceTorch(bot) { + if (!bot.modes.isOn('torch_placing') || bot.interrupt_code) return false; + const pos = getPosition(bot); + // TODO: check light level instead of nearby torches, block.light is broken + let nearest_torch = getNearestBlock(bot, 'torch', 6); + if (!nearest_torch) { + const block = bot.blockAt(pos); + let has_torch = bot.inventory.items().find(item => item.name === 'torch'); + return has_torch && block.name === 'air'; + } + return false; +} + export function getBiomeName(bot) { /** * Get the name of the biome the bot is in. diff --git a/src/agent/modes.js b/src/agent/modes.js index 4d5b83c..2d201d4 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -152,7 +152,7 @@ const modes = [ cooldown: 5, last_place: Date.now(), update: function (agent) { - if (skills.shouldPlaceTorch(agent.bot)) { + if (world.shouldPlaceTorch(agent.bot)) { if (Date.now() - this.last_place < this.cooldown * 1000) return; execute(this, agent, async () => { const pos = agent.bot.entity.position;