From 87df69108fdb172a2a9d29fbf4f672f7defea961 Mon Sep 17 00:00:00 2001 From: mrelmida Date: Thu, 1 May 2025 17:57:47 +0300 Subject: [PATCH 01/23] fix groq bug fix the bug regarding the empty result --- src/models/groq.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/groq.js b/src/models/groq.js index e601137..e4e8f3b 100644 --- a/src/models/groq.js +++ b/src/models/groq.js @@ -55,7 +55,7 @@ export class GroqCloudAPI { ...(this.params || {}) }); - res = completion.choices[0].message; + res = completion.choices[0].message.content; res = res.replace(/[\s\S]*?<\/think>/g, '').trim(); } From 4343f7811810429e6bb110787c773710d2b8b486 Mon Sep 17 00:00:00 2001 From: uukelele Date: Sun, 25 May 2025 08:06:03 +0100 Subject: [PATCH 02/23] bump mineflayer version from 4.26.0 to 4.29.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bb3fd90..5fb95d7 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "google-translate-api-x": "^10.7.1", "groq-sdk": "^0.15.0", "minecraft-data": "^3.78.0", - "mineflayer": "^4.26.0", + "mineflayer": "^4.29.0", "mineflayer-armor-manager": "^2.0.1", "mineflayer-auto-eat": "^3.3.6", "mineflayer-collectblock": "^1.4.1", From 6b4b895cc1739b3271e7299d781482d93915d64d Mon Sep 17 00:00:00 2001 From: Isadora White Date: Mon, 26 May 2025 16:46:42 -0700 Subject: [PATCH 03/23] human ai tasks --- minecollab.md | 2 + ..._three_agents.pdf => church_blueprint.pdf} | Bin .../human_ai/1_agent_1_human.json | 34 + .../human_ai_tasks/1_agent_1_human.json | 762 ------------------ tasks/running_human_ai.md | 20 + 5 files changed, 56 insertions(+), 762 deletions(-) rename tasks/construction_tasks/{church_three_agents.pdf => church_blueprint.pdf} (100%) create mode 100644 tasks/cooking_tasks/human_ai/1_agent_1_human.json create mode 100644 tasks/running_human_ai.md diff --git a/minecollab.md b/minecollab.md index 019d8b1..551c3b7 100644 --- a/minecollab.md +++ b/minecollab.md @@ -41,6 +41,8 @@ You can view the crafting task in action [here](https://www.youtube.com/shorts/V ## Installation +You **DO NOT** need Linux to run this, you can run on Windows with the --no-launch-world flag and by installing git bash. + Please follow the installation docs in the README to install mindcraft. You can create a docker image using the Dockerfile. Download the relevant task files and server data files, you can find the link [here](https://drive.google.com/drive/folders/1XygbitBBTsNO6q_doEiZHmdETpnyRmCS). The tasks files are for specifying the tasks to run and the server data is for allowing the models to launch the task in the correct world automatically. **Unzip the server_data.zip in the base `tasks/` folder**. diff --git a/tasks/construction_tasks/church_three_agents.pdf b/tasks/construction_tasks/church_blueprint.pdf similarity index 100% rename from tasks/construction_tasks/church_three_agents.pdf rename to tasks/construction_tasks/church_blueprint.pdf diff --git a/tasks/cooking_tasks/human_ai/1_agent_1_human.json b/tasks/cooking_tasks/human_ai/1_agent_1_human.json new file mode 100644 index 0000000..0212f7a --- /dev/null +++ b/tasks/cooking_tasks/human_ai/1_agent_1_human.json @@ -0,0 +1,34 @@ +{ + "multiagent_cooking_bread_golden_apple": { + "type": "cooking", + "recipes": { + "bread": [ + "Step 1: Go to the farm and collect 3 wheat.", + "Step 2: Go to the crafting table and use the wheat to craft bread." + ], + "golden_apple": [ + "Step 1: Get 1 apple and 8 gold ingots from your inventory or other bots.", + "Step 2: Go to the crafting table and surround the apple with the gold ingots to create a golden apple." + ] + }, + "agent_count": 2, + "target": { + "bread": 1, + "golden_apple": 1 + }, + "initial_inventory": { + "0": { + "gold_ingot": 4, + "apple": 1 + }, + "1": { + "gold_ingot": 4 + } + }, + "goal": { + "0": "Collaborate with other agents around you to make bread, golden_apple, The recipes are as follows:\nRecipe for bread:\n['Step 1: Go to the farm and collect 3 wheat.', 'Step 2: Go to the crafting table and use the wheat to craft bread.']\nRecipe for golden_apple:\n['Step 1: Get 1 apple and 8 gold ingots from your inventory or other bots.', 'Step 2: Go to the crafting table and surround the apple with the gold ingots to create a golden apple.']\n", + "1": "Collaborate with other agents around you to make bread, golden_apple, The recipes are as follows:\nRecipe for bread:\n['Step 1: Go to the farm and collect 3 wheat.', 'Step 2: Go to the crafting table and use the wheat to craft bread.']\nRecipe for golden_apple:\n['Step 1: Get 1 apple and 8 gold ingots from your inventory or other bots.', 'Step 2: Go to the crafting table and surround the apple with the gold ingots to create a golden apple.']\n" + }, + "conversation": "Let's collaborate to make bread, golden_apple, " + } +} \ No newline at end of file diff --git a/tasks/crafting_tasks/human_ai_tasks/1_agent_1_human.json b/tasks/crafting_tasks/human_ai_tasks/1_agent_1_human.json index e539510..cf01d07 100644 --- a/tasks/crafting_tasks/human_ai_tasks/1_agent_1_human.json +++ b/tasks/crafting_tasks/human_ai_tasks/1_agent_1_human.json @@ -27,767 +27,5 @@ "izzycw" ], "requires_ctable": false - }, - "multiagent_crafting_lime_wool_partial_plan__depth_0": { - "goal": "Collaborate with other agents to craft an lime_wool", - "conversation": "Let's work together to craft an lime_wool.", - "initial_inventory": { - "0": { - "lime_dye": 1 - }, - "1": { - "black_wool": 1 - } - }, - "agent_count": 1, - "target": "lime_wool", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 0, - "timeout": 300, - "blocked_actions": { - "0": [], - "1": [ - "!getCraftingPlan" - ] - }, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "missing_items": [], - "requires_ctable": false - }, - "multiagent_crafting_purple_banner_full_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft an purple_banner", - "conversation": "Let's work together to craft an purple_banner.", - "initial_inventory": { - "0": { - "purple_wool": 4, - "stick": 1 - }, - "1": { - "purple_wool": 3, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "purple_banner", - "number_of_target": 1, - "type": "techtree", - "max_depth": 3, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_soul_campfire_partial_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft an soul_campfire", - "conversation": "Let's work together to craft an soul_campfire.", - "initial_inventory": { - "0": { - "oak_planks": 2, - "soul_sand": 1, - "dark_oak_log": 2 - }, - "1": { - "oak_planks": 1, - "dark_oak_log": 1, - "crafting_table": 1 - } - }, - "agent_count": 2, - "target": "soul_campfire", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_bookshelf_full_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft a bookshelf", - "conversation": "Let's work together to craft a bookshelf.", - "initial_inventory": { - "0": { - "oak_planks": 4, - "book": 2 - }, - "1": { - "oak_planks": 2, - "book": 1, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "bookshelf", - "number_of_target": 1, - "type": "techtree", - "max_depth": 1, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_compass_partial_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft a compass", - "conversation": "Let's work together to craft a compass.", - "initial_inventory": { - "0": { - "iron_ingot": 2 - }, - "1": { - "iron_ingot": 2, - "redstone": 1, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "compass", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_fishing_rod_full_plan_requires_ctable__depth_1": { - "goal": "Collaborate with other agents to craft a fishing_rod", - "conversation": "Let's work together to craft a fishing_rod.", - "initial_inventory": { - "0": { - "string": 1, - "oak_planks": 2 - }, - "1": { - "string": 1, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "fishing_rod", - "number_of_target": 1, - "type": "techtree", - "max_depth": 3, - "depth": 1, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_cake_partial_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft a cake", - "conversation": "Let's work together to craft a cake.", - "initial_inventory": { - "0": { - "wheat": 2, - "sugar": 1, - "egg": 1 - }, - "1": { - "wheat": 1, - "milk_bucket": 2, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "cake", - "number_of_target": 1, - "type": "techtree", - "max_depth": 3, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_golden_carrot_full_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft a golden_carrot", - "conversation": "Let's work together to craft a golden_carrot.", - "initial_inventory": { - "0": { - "gold_nugget": 5, - "carrot": 1 - }, - "1": { - "gold_nugget": 3, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "golden_carrot", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_map_partial_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft a map", - "conversation": "Let's work together to craft a map.", - "initial_inventory": { - "0": { - "paper": 5 - }, - "1": { - "paper": 3, - "compass": 1, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "map", - "number_of_target": 1, - "type": "techtree", - "max_depth": 1, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_blue_wool_full_plan__depth_0": { - "goal": "Collaborate with other agents to craft blue_wool", - "conversation": "Let's work together to craft blue_wool.", - "initial_inventory": { - "0": { - "blue_dye": 1 - }, - "1": { - "white_wool": 1 - } - }, - "agent_count": 2, - "target": "blue_wool", - "number_of_target": 1, - "type": "techtree", - "max_depth": 1, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": false - }, - "multiagent_crafting_lime_wool_partial_plan__depth_2": { - "goal": "Collaborate with other agents to craft lime_wool", - "conversation": "Let's work together to craft lime_wool.", - "initial_inventory": { - "0": { - "green_dye": 1 - }, - "1": { - "white_wool": 1, - "bone_meal": 1 - } - }, - "agent_count": 2, - "target": "lime_wool", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 1, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": false - }, - "multiagent_crafting_magenta_wool_full_plan__depth_2": { - "goal": "Collaborate with other agents to craft magenta_wool", - "conversation": "Let's work together to craft magenta_wool.", - "initial_inventory": { - "0": { - "rose_red": 1, - "lapis_lazuli": 1 - }, - "1": { - "white_wool": 1, - "bone_meal": 1 - } - }, - "agent_count": 1, - "target": "magenta_wool", - "number_of_target": 1, - "type": "techtree", - "max_depth": 3, - "depth": 2, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": false - }, - "multiagent_crafting_chest_full_plan_requires_ctable__depth_1": { - "goal": "Collaborate with other agents to craft a chest", - "conversation": "Let's work together to craft a chest.", - "initial_inventory": { - "0": { - "oak_log": 1 - }, - "1": { - "oak_planks": 4, - "crafting_table": 1 - } - }, - "agent_count": 2, - "target": "chest", - "number_of_target": 1, - "type": "techtree", - "max_depth": 1, - "depth": 1, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_barrel_partial_plan_requires_ctable__depth_1": { - "goal": "Collaborate with other agents to craft a barrel", - "conversation": "Let's work together to craft a barrel.", - "initial_inventory": { - "0": { - "spruce_planks": 3, - "crafting_table": 1 - }, - "1": { - "spruce_planks": 3, - "wooden_slab": 1 - } - }, - "agent_count": 2, - "target": "barrel", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 1, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_lectern_full_plan_requires_ctable__depth_2": { - "goal": "Collaborate with other agents to craft a lectern", - "conversation": "Let's work together to craft a lectern.", - "initial_inventory": { - "0": { - "birch_slab": 5, - "crafting_table": 1 - }, - "1": { - "birch_log": 2, - "book": 3 - } - }, - "agent_count": 1, - "target": "lectern", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 2, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_clock_partial_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft a clock", - "conversation": "Let's work together to craft a clock.", - "initial_inventory": { - "0": { - "gold_ingot": 2 - }, - "1": { - "gold_ingot": 2, - "redstone": 1, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "clock", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_firework_rocket_partial_plan__depth_0": { - "goal": "Collaborate with other agents to craft firework_rocket", - "conversation": "Let's work together to craft firework_rocket.", - "initial_inventory": { - "0": { - "paper": 1 - }, - "1": { - "gunpowder": 3 - } - }, - "agent_count": 2, - "target": "firework_rocket", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": false - }, - "multiagent_crafting_enchanting_table_partial_plan_requires_ctable__depth_0": { - "goal": "Collaborate with other agents to craft an enchanting_table", - "conversation": "Let's work together to craft an enchanting_table.", - "initial_inventory": { - "0": { - "diamond": 2, - "obsidian": 2, - "crafting_table": 1 - }, - "1": { - "obsidian": 2, - "book": 1 - } - }, - "agent_count": 2, - "target": "enchanting_table", - "number_of_target": 1, - "type": "techtree", - "max_depth": 0, - "depth": 0, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_jukebox_full_plan_requires_ctable__depth_1": { - "goal": "Collaborate with other agents to craft a jukebox", - "conversation": "Let's work together to craft a jukebox.", - "initial_inventory": { - "0": { - "diamond": 1 - }, - "1": { - "oak_log": 2, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "jukebox", - "number_of_target": 1, - "type": "techtree", - "max_depth": 1, - "depth": 1, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_light_gray_wool_full_plan__depth_1": { - "goal": "Collaborate with other agents to craft light_gray_wool", - "conversation": "Let's work together to craft light_gray_wool.", - "initial_inventory": { - "0": { - "black_dye": 1 - }, - "1": { - "white_wool": 1, - "white_dye": 2 - } - }, - "agent_count": 1, - "target": "light_gray_wool", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 1, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": false - }, - "multiagent_crafting_blast_furnace_full_plan_requires_ctable__depth_1": { - "goal": "Collaborate with other agents to craft a blast_furnace", - "conversation": "Let's work together to craft a blast_furnace.", - "initial_inventory": { - "0": { - "iron_ingot": 5, - "smooth_stone": 3 - }, - "1": { - "cobblestone": 8, - "crafting_table": 1 - } - }, - "agent_count": 2, - "target": "blast_furnace", - "number_of_target": 1, - "type": "techtree", - "max_depth": 2, - "depth": 1, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_activator_rail_full_plan_requires_ctable__depth_2": { - "goal": "Collaborate with other agents to craft activator_rail", - "conversation": "Let's work together to craft activator_rail.", - "initial_inventory": { - "0": { - "iron_ingot": 3, - "oak_planks": 6 - }, - "1": { - "redstone": 1, - "iron_ingot": 3, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "activator_rail", - "number_of_target": 1, - "type": "techtree", - "max_depth": 3, - "depth": 2, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_campfire_partial_plan_requires_ctable__depth_2": { - "goal": "Collaborate with other agents to craft campfire", - "conversation": "Let's work together to craft campfire.", - "initial_inventory": { - "0": { - "oak_log": 8 - }, - "1": { - "coal": 1, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "campfire", - "number_of_target": 1, - "type": "techtree", - "max_depth": 3, - "depth": 2, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [ - "!getCraftingPlan" - ], - "1": [] - }, - "missing_items": [], - "requires_ctable": true - }, - "multiagent_crafting_crossbow_full_plan_requires_ctable__depth_2": { - "goal": "Collaborate with other agents to craft a crossbow", - "conversation": "Let's work together to craft a crossbow.", - "initial_inventory": { - "0": { - "oak_planks": 8, - "iron_ingot": 2 - }, - "1": { - "string": 2, - "crafting_table": 1 - } - }, - "agent_count": 1, - "target": "crossbow", - "number_of_target": 1, - "type": "techtree", - "max_depth": 3, - "depth": 2, - "timeout": 300, - "human_count": 1, - "usernames": [ - "izzycw" - ], - "blocked_actions": { - "0": [], - "1": [] - }, - "missing_items": [], - "requires_ctable": true } } \ No newline at end of file diff --git a/tasks/running_human_ai.md b/tasks/running_human_ai.md new file mode 100644 index 0000000..d1d04d6 --- /dev/null +++ b/tasks/running_human_ai.md @@ -0,0 +1,20 @@ +# Human AI Instructions + +## Construction +Press F3 to view the coordinates of the game. And pull up the file tasks/construction_tasks/church_blueprint.pdf +Run +``` +python tasks/evaluation_script.py --no_launch_world --template_profile profiles/tasks/construction_profile.json --task_path tasks/construction_tasks/human_ai/1_agent_1_human.json --usernames YOUR_USERNAME --num_agents 1 --insecure_coding +``` + +## Crafting + +``` +python tasks/evaluation_script.py --no_launch_world --template_profile profiles/tasks/crafting_profile.json --task_path tasks/crafting_tasks/human_ai/1_agent_1_human.json --usernames YOUR_USERNAME --num_agents 1 +``` + +## Cooking + +``` +python tasks/evaluation_script.py --no_launch_world --template_profile profiles/tasks/cooking_profile.json --task_path tasks/cooking_tasks/human_ai/1_agent_1_human.json --usernames YOUR_USERNAME --num_agents 1 +``` \ No newline at end of file From ef9fb747573946fb4ecd12b03baba98f38f69289 Mon Sep 17 00:00:00 2001 From: Isadora White Date: Mon, 26 May 2025 21:25:10 -0700 Subject: [PATCH 04/23] cleaning up human ai tasks --- .../human_ai/1_agent_1_human.json | 2 +- tasks/cooking_tasks/human_ai/1_agent_1_human.json | 8 ++++++-- .../1_agent_1_human.json | 2 +- .../1_agent_1_human_copy.json | 0 .../3_agent_1_human.json | 0 tasks/running_human_ai.md | 14 ++++++++++++++ 6 files changed, 22 insertions(+), 4 deletions(-) rename tasks/crafting_tasks/{human_ai_tasks => human_ai}/1_agent_1_human.json (96%) rename tasks/crafting_tasks/{human_ai_tasks => human_ai}/1_agent_1_human_copy.json (100%) rename tasks/crafting_tasks/{human_ai_tasks => human_ai}/3_agent_1_human.json (100%) diff --git a/tasks/construction_tasks/human_ai/1_agent_1_human.json b/tasks/construction_tasks/human_ai/1_agent_1_human.json index 2dfaa22..23171da 100644 --- a/tasks/construction_tasks/human_ai/1_agent_1_human.json +++ b/tasks/construction_tasks/human_ai/1_agent_1_human.json @@ -2351,7 +2351,7 @@ } }, "usernames": [ - "izzycw" + "erringnine" ] } } \ No newline at end of file diff --git a/tasks/cooking_tasks/human_ai/1_agent_1_human.json b/tasks/cooking_tasks/human_ai/1_agent_1_human.json index 0212f7a..a6b34b7 100644 --- a/tasks/cooking_tasks/human_ai/1_agent_1_human.json +++ b/tasks/cooking_tasks/human_ai/1_agent_1_human.json @@ -11,7 +11,8 @@ "Step 2: Go to the crafting table and surround the apple with the gold ingots to create a golden apple." ] }, - "agent_count": 2, + "agent_count": 1, + "human_count": 1, "target": { "bread": 1, "golden_apple": 1 @@ -29,6 +30,9 @@ "0": "Collaborate with other agents around you to make bread, golden_apple, The recipes are as follows:\nRecipe for bread:\n['Step 1: Go to the farm and collect 3 wheat.', 'Step 2: Go to the crafting table and use the wheat to craft bread.']\nRecipe for golden_apple:\n['Step 1: Get 1 apple and 8 gold ingots from your inventory or other bots.', 'Step 2: Go to the crafting table and surround the apple with the gold ingots to create a golden apple.']\n", "1": "Collaborate with other agents around you to make bread, golden_apple, The recipes are as follows:\nRecipe for bread:\n['Step 1: Go to the farm and collect 3 wheat.', 'Step 2: Go to the crafting table and use the wheat to craft bread.']\nRecipe for golden_apple:\n['Step 1: Get 1 apple and 8 gold ingots from your inventory or other bots.', 'Step 2: Go to the crafting table and surround the apple with the gold ingots to create a golden apple.']\n" }, - "conversation": "Let's collaborate to make bread, golden_apple, " + "conversation": "Let's collaborate to make bread, golden_apple, ", + "usernames": [ + "erringnine" + ] } } \ No newline at end of file diff --git a/tasks/crafting_tasks/human_ai_tasks/1_agent_1_human.json b/tasks/crafting_tasks/human_ai/1_agent_1_human.json similarity index 96% rename from tasks/crafting_tasks/human_ai_tasks/1_agent_1_human.json rename to tasks/crafting_tasks/human_ai/1_agent_1_human.json index cf01d07..f47a540 100644 --- a/tasks/crafting_tasks/human_ai_tasks/1_agent_1_human.json +++ b/tasks/crafting_tasks/human_ai/1_agent_1_human.json @@ -24,7 +24,7 @@ "missing_items": [], "human_count": 1, "usernames": [ - "izzycw" + "erringnine" ], "requires_ctable": false } diff --git a/tasks/crafting_tasks/human_ai_tasks/1_agent_1_human_copy.json b/tasks/crafting_tasks/human_ai/1_agent_1_human_copy.json similarity index 100% rename from tasks/crafting_tasks/human_ai_tasks/1_agent_1_human_copy.json rename to tasks/crafting_tasks/human_ai/1_agent_1_human_copy.json diff --git a/tasks/crafting_tasks/human_ai_tasks/3_agent_1_human.json b/tasks/crafting_tasks/human_ai/3_agent_1_human.json similarity index 100% rename from tasks/crafting_tasks/human_ai_tasks/3_agent_1_human.json rename to tasks/crafting_tasks/human_ai/3_agent_1_human.json diff --git a/tasks/running_human_ai.md b/tasks/running_human_ai.md index d1d04d6..1dcb8ec 100644 --- a/tasks/running_human_ai.md +++ b/tasks/running_human_ai.md @@ -1,5 +1,19 @@ # Human AI Instructions +## Finishing Installation + +Install the conda environment for running the experiments by executing this in your command line: + +``` +conda create --name mindcraft python=3.11 +conda activate mindcraft +pip install -r requirements.txt +``` + +## Setting up the world + +Setting up the world! Make sure your world has cheats enabled! You can do this on creation of your Minecraft world in the Minecraft console, or you can type ```/op @a``` in the chat or in the console of the world launched from the jar file. + ## Construction Press F3 to view the coordinates of the game. And pull up the file tasks/construction_tasks/church_blueprint.pdf Run From 133123983065e0af598f5dedb2065a439f6e79e9 Mon Sep 17 00:00:00 2001 From: Isadora White Date: Fri, 30 May 2025 17:39:47 -0500 Subject: [PATCH 05/23] small changes to tasks --- tasks/cooking_tasks/human_ai/1_agent_1_human.json | 2 +- tasks/crafting_tasks/human_ai/1_agent_1_human.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/cooking_tasks/human_ai/1_agent_1_human.json b/tasks/cooking_tasks/human_ai/1_agent_1_human.json index a6b34b7..20a4973 100644 --- a/tasks/cooking_tasks/human_ai/1_agent_1_human.json +++ b/tasks/cooking_tasks/human_ai/1_agent_1_human.json @@ -32,7 +32,7 @@ }, "conversation": "Let's collaborate to make bread, golden_apple, ", "usernames": [ - "erringnine" + "izzycw" ] } } \ No newline at end of file diff --git a/tasks/crafting_tasks/human_ai/1_agent_1_human.json b/tasks/crafting_tasks/human_ai/1_agent_1_human.json index f47a540..cf01d07 100644 --- a/tasks/crafting_tasks/human_ai/1_agent_1_human.json +++ b/tasks/crafting_tasks/human_ai/1_agent_1_human.json @@ -24,7 +24,7 @@ "missing_items": [], "human_count": 1, "usernames": [ - "erringnine" + "izzycw" ], "requires_ctable": false } From 0bffe111b12b6d249d9677386ebd1db1570552dc Mon Sep 17 00:00:00 2001 From: Isadora White Date: Sun, 1 Jun 2025 18:43:28 -0500 Subject: [PATCH 06/23] fixing weird conversation thing maybe --- src/agent/tasks/tasks.js | 33 +++++++++++++++++---------------- src/models/vllm.js | 1 + 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/agent/tasks/tasks.js b/src/agent/tasks/tasks.js index 1c00d95..f2d0fb2 100644 --- a/src/agent/tasks/tasks.js +++ b/src/agent/tasks/tasks.js @@ -423,10 +423,24 @@ export class Task { console.log(`Setting goal for agent ${this.agent.count_id}: ${agentGoal}`); } await executeCommand(this.agent, `!goal("${agentGoal}")`); + + if (this.data.conversation && this.agent.count_id === 0) { + let other_name = this.available_agents.filter(n => n !== this.name)[0]; + let waitCount = 0; + while (other_name === undefined && waitCount < 20) { + other_name = this.available_agents.filter(n => n !== this.name)[0]; + await new Promise((resolve) => setTimeout(resolve, 1000)); + waitCount++; + } + if (other_name === undefined && this.data.agent_count > 1) { + console.log('No other agents found. Task unsuccessful.'); + this.agent.killAll(); + } + await executeCommand(this.agent, `!startConversation("${other_name}", "${this.data.conversation}")`); + } } async initBotTask() { - await this.setAgentGoal(); await this.agent.bot.chat(`/clear ${this.name}`); console.log(`Cleared ${this.name}'s inventory.`); @@ -511,21 +525,8 @@ export class Task { this.agent.killAll(); } } - - if (this.data.conversation && this.agent.count_id === 0) { - let other_name = this.available_agents.filter(n => n !== this.name)[0]; - let waitCount = 0; - while (other_name === undefined && waitCount < 20) { - other_name = this.available_agents.filter(n => n !== this.name)[0]; - await new Promise((resolve) => setTimeout(resolve, 1000)); - waitCount++; - } - if (other_name === undefined && this.data.agent_count > 1) { - console.log('No other agents found. Task unsuccessful.'); - this.agent.killAll(); - } - await executeCommand(this.agent, `!startConversation("${other_name}", "${this.data.conversation}")`); - } + await new Promise((resolve) => setTimeout(resolve, 500)); + await this.setAgentGoal(); } diff --git a/src/models/vllm.js b/src/models/vllm.js index 52e3e5b..e9116ef 100644 --- a/src/models/vllm.js +++ b/src/models/vllm.js @@ -38,6 +38,7 @@ export class VLLM { try { console.log('Awaiting openai api response...') // console.log('Messages:', messages); + // todo set max_tokens, temperature, top_p, etc. in pack let completion = await this.vllm.chat.completions.create(pack); if (completion.choices[0].finish_reason == 'length') throw new Error('Context length exceeded'); From 0503ee34097d80d965ef23091c6143a48597f736 Mon Sep 17 00:00:00 2001 From: Isadora White Date: Sun, 1 Jun 2025 18:46:50 -0500 Subject: [PATCH 07/23] remove the conversation thingie from the set agent goal command --- src/agent/tasks/tasks.js | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/agent/tasks/tasks.js b/src/agent/tasks/tasks.js index f2d0fb2..a7948f5 100644 --- a/src/agent/tasks/tasks.js +++ b/src/agent/tasks/tasks.js @@ -423,21 +423,6 @@ export class Task { console.log(`Setting goal for agent ${this.agent.count_id}: ${agentGoal}`); } await executeCommand(this.agent, `!goal("${agentGoal}")`); - - if (this.data.conversation && this.agent.count_id === 0) { - let other_name = this.available_agents.filter(n => n !== this.name)[0]; - let waitCount = 0; - while (other_name === undefined && waitCount < 20) { - other_name = this.available_agents.filter(n => n !== this.name)[0]; - await new Promise((resolve) => setTimeout(resolve, 1000)); - waitCount++; - } - if (other_name === undefined && this.data.agent_count > 1) { - console.log('No other agents found. Task unsuccessful.'); - this.agent.killAll(); - } - await executeCommand(this.agent, `!startConversation("${other_name}", "${this.data.conversation}")`); - } } async initBotTask() { @@ -526,9 +511,21 @@ export class Task { } } await new Promise((resolve) => setTimeout(resolve, 500)); + if (this.data.conversation && this.agent.count_id === 0) { + let other_name = this.available_agents.filter(n => n !== this.name)[0]; + let waitCount = 0; + while (other_name === undefined && waitCount < 20) { + other_name = this.available_agents.filter(n => n !== this.name)[0]; + await new Promise((resolve) => setTimeout(resolve, 1000)); + waitCount++; + } + if (other_name === undefined && this.data.agent_count > 1) { + console.log('No other agents found. Task unsuccessful.'); + this.agent.killAll(); + } + await executeCommand(this.agent, `!startConversation("${other_name}", "${this.data.conversation}")`); + } await this.setAgentGoal(); - - } async teleportBots() { From 6f2bf41e6e088cf176afd0a2f761a91213eab7e2 Mon Sep 17 00:00:00 2001 From: Maximus Date: Mon, 2 Jun 2025 13:47:07 -0600 Subject: [PATCH 08/23] initial refactor --- api.js | 43 +++++++++++++++++++ main.js | 8 ++-- mindcraft.js | 32 ++++++++++++++ settings.js | 25 ++++++----- src/agent/settings.js | 0 src/process/agent_process.js | 4 +- .../{main_proxy.js => mindserver_proxy.js} | 16 +++---- src/server/{mind_server.js => mindserver.js} | 5 +++ 8 files changed, 109 insertions(+), 24 deletions(-) create mode 100644 api.js create mode 100644 mindcraft.js create mode 100644 src/agent/settings.js rename src/process/{main_proxy.js => mindserver_proxy.js} (81%) rename src/server/{mind_server.js => mindserver.js} (96%) diff --git a/api.js b/api.js new file mode 100644 index 0000000..ddd3ea1 --- /dev/null +++ b/api.js @@ -0,0 +1,43 @@ +import * as Mindcraft from './mindcraft.js'; + +await Mindcraft.init('localhost', 8080); // starts server locally +await Mindcraft.connect('ip', 'port') // connects to remote server +// ^ must do one of these before calling anything else + +Mindcraft.addWorld( + { + name: 'test', + minecraft_version: "1.21.1", + host: 'localhost', + port: 55916, + auth: 'offline', + + render_bot_views: false, // show bot's view in browser at localhost:3000, 3001... + allow_insecure_coding: true, // allows newAction command and model can write/run code on server. enable at own risk + code_timeout_mins: -1, // minutes code is allowed to run. -1 for no timeout + verbose_commands: true, // show full command syntax + chat_bot_messages: true, // publicly chat bot-to-bot messages + } +) +// add world for easy reuse. not super necessary, easy for user to copy world def object around. remove? + + +Mindcraft.addAgent( + { + world: 'test', + world: { + minecraft_version: '', + host: '', + port: '', + auth: 'offline' + }, + profile: './profiles/test.json', + // profile: { + // name: 'test', + // prompt: 'test', + // }, + task: './tasks/test.json' + } +) + +Mindcraft.removeAgent() \ No newline at end of file diff --git a/main.js b/main.js index 521aadf..07d3808 100644 --- a/main.js +++ b/main.js @@ -2,8 +2,8 @@ import { AgentProcess } from './src/process/agent_process.js'; import settings from './settings.js'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; -import { createMindServer } from './src/server/mind_server.js'; -import { mainProxy } from './src/process/main_proxy.js'; +import { createMindServer } from './src/server/mindserver.js'; +import { mindserverProxy } from './src/process/mindserver_proxy.js.js'; import { readFileSync } from 'fs'; function parseArguments() { @@ -33,7 +33,7 @@ async function main() { if (settings.host_mindserver) { const mindServer = createMindServer(settings.mindserver_port); } - mainProxy.connect(); + mindserverProxy.connect(); const args = parseArguments(); const profiles = getProfiles(args); @@ -44,7 +44,7 @@ async function main() { const agent_process = new AgentProcess(); const profile = readFileSync(profiles[i], 'utf8'); const agent_json = JSON.parse(profile); - mainProxy.registerAgent(agent_json.name, agent_process); + mindserverProxy.registerAgent(agent_json.name, agent_process); agent_process.start(profiles[i], load_memory, init_message, i, args.task_path, args.task_id); await new Promise(resolve => setTimeout(resolve, 1000)); } diff --git a/mindcraft.js b/mindcraft.js new file mode 100644 index 0000000..e4fae5b --- /dev/null +++ b/mindcraft.js @@ -0,0 +1,32 @@ +import { AgentProcess } from './src/process/agent_process.js'; +import { createMindServer } from './src/server/mindserver.js'; +import { mindserverProxy } from './src/process/mindserver_proxy.js.js'; +import { readFileSync } from 'fs'; + +let mindserver; +let connected = false; + +export async function init(host='localhost', port=8080) { + if (connected) { + console.error('Already initiliazed!'); + return; + } + mindserver = createMindServer(host, port); + mindserverProxy.connect(host, port); + connected = true; +} + +export async function connect() { + if (connected) { + console.error('Already connected!'); + return; + } +} + +export function addWorld(settings) { + +} + +export async function addAgent(settings) { + +} \ No newline at end of file diff --git a/settings.js b/settings.js index b782097..1d6c5a2 100644 --- a/settings.js +++ b/settings.js @@ -10,11 +10,10 @@ const settings = { "mindserver_port": 8080, // the base profile is shared by all bots for default prompts/examples/modes - "base_profile": "./profiles/defaults/survival.json", // also see creative.json, god_mode.json "profiles": [ - "./andy.json", + // "./andy.json", // "./profiles/gpt.json", - // "./profiles/claude.json", + "./profiles/claude.json", // "./profiles/gemini.json", // "./profiles/llama.json", // "./profiles/qwen.json", @@ -25,26 +24,32 @@ const settings = { // using more than 1 profile requires you to /msg each bot indivually // individual profiles override values from the base profile ], + + // agent settings + "base_profile": "./profiles/defaults/god_mode.json", // also see creative.json, god_mode.json "load_memory": false, // load memory from previous session "init_message": "Respond with hello world and your name", // sends to all on spawn "only_chat_with": [], // users that the bots listen to and send general messages to. if empty it will chat publicly "speak": false, // allows all bots to speak through system text-to-speech. works on windows, mac, on linux you need to `apt install espeak` "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages - "show_bot_views": false, // show bot's view in browser at localhost:3000, 3001... - - "allow_insecure_coding": false, // allows newAction command and model can write/run code on your computer. enable at own risk "allow_vision": false, // allows vision model to interpret screenshots as inputs "blocked_actions" : ["!checkBlueprint", "!checkBlueprintLevel", "!getBlueprint", "!getBlueprintLevel"] , // commands to disable and remove from docs. Ex: ["!setMode"] - "code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout "relevant_docs_count": 5, // number of relevant code function docs to select for prompting. -1 for all - "max_messages": 15, // max number of messages to keep in context "num_examples": 2, // number of examples to give to the model "max_commands": -1, // max number of commands that can be used in consecutive responses. -1 for no limit - "verbose_commands": true, // show full command syntax "narrate_behavior": true, // chat simple automatic actions ('Picking up item!') - "chat_bot_messages": true, // publicly chat messages to other bots "log_all_prompts": false, // log ALL prompts to file + "task": {}, + "task_file": "", + "task_name": "", + "verbose_commands": true, // show full command syntax + "chat_bot_messages": true, // publicly chat bot-to-bot messages + + // mindserver settings + "render_bot_views": false, // show bot's view in browser at localhost:3000, 3001... + "allow_insecure_coding": true, // allows newAction command and model can write/run code on your computer. enable at own risk + "code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout } // these environment variables override certain settings diff --git a/src/agent/settings.js b/src/agent/settings.js new file mode 100644 index 0000000..e69de29 diff --git a/src/process/agent_process.js b/src/process/agent_process.js index 7418d31..603f348 100644 --- a/src/process/agent_process.js +++ b/src/process/agent_process.js @@ -1,5 +1,5 @@ import { spawn } from 'child_process'; -import { mainProxy } from './main_proxy.js'; +import { mindserverProxy } from './mindserver_proxy.js.js'; export class AgentProcess { start(profile, load_memory=false, init_message=null, count_id=0, task_path=null, task_id=null) { @@ -28,7 +28,7 @@ export class AgentProcess { agentProcess.on('exit', (code, signal) => { console.log(`Agent process exited with code ${code} and signal ${signal}`); this.running = false; - mainProxy.logoutAgent(this.name); + mindserverProxy.logoutAgent(this.name); if (code > 1) { console.log(`Ending task`); diff --git a/src/process/main_proxy.js b/src/process/mindserver_proxy.js similarity index 81% rename from src/process/main_proxy.js rename to src/process/mindserver_proxy.js index 8336458..d2d4a97 100644 --- a/src/process/main_proxy.js +++ b/src/process/mindserver_proxy.js @@ -1,23 +1,23 @@ import { io } from 'socket.io-client'; -import settings from '../../settings.js'; // Singleton mindserver proxy for the main process -class MainProxy { +// recieves commands from mindserver +class MindserverProxy { constructor() { - if (MainProxy.instance) { - return MainProxy.instance; + if (MindserverProxy.instance) { + return MindserverProxy.instance; } this.socket = null; this.connected = false; this.agent_processes = {}; - MainProxy.instance = this; + MindserverProxy.instance = this; } - connect() { + connect(host, port) { if (this.connected) return; - this.socket = io(`http://${settings.mindserver_host}:${settings.mindserver_port}`); + this.socket = io(`http://${host}:${port}`); this.connected = true; this.socket.on('stop-agent', (agentName) => { @@ -61,4 +61,4 @@ class MainProxy { } } -export const mainProxy = new MainProxy(); \ No newline at end of file +export const mindserverProxy = new MindserverProxy(); \ No newline at end of file diff --git a/src/server/mind_server.js b/src/server/mindserver.js similarity index 96% rename from src/server/mind_server.js rename to src/server/mindserver.js index eed71d7..ac54db3 100644 --- a/src/server/mind_server.js +++ b/src/server/mindserver.js @@ -4,6 +4,11 @@ import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; +// Mindserver purposes: +// - central hub for inter-process communication between all agent processes +// - api to control from other languages and remote users +// - host for webapp + // Module-level variables let io; let server; From 088b71a99aa8a7fd7b2258a2c9f97346ff1f4ca7 Mon Sep 17 00:00:00 2001 From: Isadora White Date: Mon, 9 Jun 2025 01:35:18 -0500 Subject: [PATCH 09/23] more friendly messages in the python evaluation script to make it more easy for the users to understand what is happening --- tasks/evaluation_script.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tasks/evaluation_script.py b/tasks/evaluation_script.py index cb380f5..4b5cb3c 100644 --- a/tasks/evaluation_script.py +++ b/tasks/evaluation_script.py @@ -179,7 +179,8 @@ def check_folder_results(folder_path): # Print summary print("\n=== Evaluation Results ===") - print(f"Total tasks evaluated: {results['total']}") + print("\nEvaluating Tasks!") + print(f"Results so far: {results['total']}") if "construction" not in folder_path: print(f"Successful tasks: {results['successful']}") @@ -517,7 +518,7 @@ def make_ops(agent_names, session_name): if agents_op: print("Agents are operators! You are good to go :D") else: - print("Agents are not operators! Something went wrong :(") + print("Agents are not operators! We will need to try making them operators again!") make_ops(agent_names, session_name) def check_agent_ops(agent_names, ops_file="ops.json"): From 6748b65fcbad0f3f5fcfe538977da6c7173ccff6 Mon Sep 17 00:00:00 2001 From: Isadora White <54700097+icwhite@users.noreply.github.com> Date: Mon, 9 Jun 2025 01:45:28 -0500 Subject: [PATCH 10/23] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 95f90ef..4c5b20d 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,15 @@ Do not connect this bot to public servers with coding enabled. This project allo ## Requirements -- [Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc) (up to v1.21.1, recommend v1.20.4) -- [Node.js Installed](https://nodejs.org/) (at least v14) +- [Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc) (up to v1.21.1, recommend v1.21.1) +- [Node.js Installed](https://nodejs.org/) (at least v14, recommended v20.9.0) - One of these: [OpenAI API Key](https://openai.com/blog/openai-api) | [Gemini API Key](https://aistudio.google.com/app/apikey) | [Anthropic API Key](https://docs.anthropic.com/claude/docs/getting-access-to-claude) | [Replicate API Key](https://replicate.com/) | [Hugging Face API Key](https://huggingface.co/) | [Groq API Key](https://console.groq.com/keys) | [Ollama Installed](https://ollama.com/download). | [Mistral API Key](https://docs.mistral.ai/getting-started/models/models_overview/) | [Qwen API Key [Intl.]](https://www.alibabacloud.com/help/en/model-studio/developer-reference/get-api-key)/[[cn]](https://help.aliyun.com/zh/model-studio/getting-started/first-api-call-to-qwen?) | [Novita AI API Key](https://novita.ai/settings?utm_source=github_mindcraft&utm_medium=github_readme&utm_campaign=link#key-management) | ## Install and Run 1. Make sure you have the requirements above. -2. Clone or download this repository (big green button) +2. Clone or download this repository (big green button) 'git clone https://github.com/kolbytn/mindcraft.git' 3. Rename `keys.example.json` to `keys.json` and fill in your API keys (you only need one). The desired model is set in `andy.json` or other profiles. For other models refer to the table below. @@ -28,7 +28,7 @@ Do not connect this bot to public servers with coding enabled. This project allo 6. Run `node main.js` from the installed directory -If you encounter issues, check the [FAQ](https://github.com/kolbytn/mindcraft/blob/main/FAQ.md) or find support on [discord](https://discord.gg/mp73p35dzC). We are currently not very responsive to github issues. +If you encounter issues, check the [FAQ](https://github.com/kolbytn/mindcraft/blob/main/FAQ.md) or find support on [discord](https://discord.gg/mp73p35dzC). We are currently not very responsive to github issues. To run tasks please refer to [Minecollab Instructions]((minecollab.md#installation) ## Tasks From 3a43b3c03c986d6c24b6465dc1e2625ec60702f7 Mon Sep 17 00:00:00 2001 From: Isadora White <54700097+icwhite@users.noreply.github.com> Date: Mon, 9 Jun 2025 01:46:08 -0500 Subject: [PATCH 11/23] Fix a small typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c5b20d..0835b4c 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Do not connect this bot to public servers with coding enabled. This project allo 6. Run `node main.js` from the installed directory -If you encounter issues, check the [FAQ](https://github.com/kolbytn/mindcraft/blob/main/FAQ.md) or find support on [discord](https://discord.gg/mp73p35dzC). We are currently not very responsive to github issues. To run tasks please refer to [Minecollab Instructions]((minecollab.md#installation) +If you encounter issues, check the [FAQ](https://github.com/kolbytn/mindcraft/blob/main/FAQ.md) or find support on [discord](https://discord.gg/mp73p35dzC). We are currently not very responsive to github issues. To run tasks please refer to [Minecollab Instructions](minecollab.md#installation) ## Tasks From 3661114321343df44e62aa4651ce3a3fd312b9bd Mon Sep 17 00:00:00 2001 From: Isadora White <54700097+icwhite@users.noreply.github.com> Date: Mon, 9 Jun 2025 01:55:47 -0500 Subject: [PATCH 12/23] Clarifying instructions for installing tmux --- minecollab.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/minecollab.md b/minecollab.md index 019d8b1..fa2fca3 100644 --- a/minecollab.md +++ b/minecollab.md @@ -55,9 +55,20 @@ pip install -r requirements.txt Then, you can run the evaluation_script **from the project root** using `python tasks/evaluation_script.py --task_path {your-task-path} --model {model you want to use}`. +### Tmux Installation +**MacOS**: +1. If brew isn't already installed run `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` +2. `brew install tmux` +**Linux**: `apt-get -y install tmux` +**Windows**: You can not use tmux on Windows, but you can run tasks with the --no-launch-world flag. Run +``` +cd /tasks/server_data/ +java -jar server.jar +``` + If you want to run with vllm be sure to run with `--api vllm --url {your_url_for_vllm} --model {model_name}`, by default vllm will use http://127.0.0.1:8000/v1 as the url for quering the model! -When running with construction tasks, make sure to set the flag `--insecure_coding` so that the agents can be allowed to write freeform javascript code to complete the tasks. However, when using insecure coding it is highly recommended to use a docker container to avoid damage to your computer. +When running with construction tasks, make sure to set the flag `--insecure_coding` so that the agents can be allowed to write freeform javascript code to complete the tasks. However, when using insecure coding it is **highly recommended** to use a docker container to avoid damage to your computer. When running an experiment that requires more than 2 agents, use the `--num_agents` flag to match the number of agents in your task file. For example, if you are running a task file with 3 agents, use `--num_agents 3`. @@ -81,7 +92,7 @@ python tasks/evaluation_script.py --task_path {path_to_two_agent_construction_ta When you launch the evaluation script, you will see the minecraft server being launched. If you want to join this world, you can connect to it on the port localhost:55916 the way you would a standard Minecraft world (go to single player -> direct connection -> type in localhost:55916) It may take a few minutes for everything to be properly loaded - as first the agents need to be added to the world and given the correct permissions to use cheats and add inventory. After about 5 minutes everything should be loaded and working. If you wish to kill the experiment run `tmux kill-server`. Sometimes there will be issues copying the files, if this happens you can run the python file twice. -## Installation (without tmux) +## Windows Installation (without tmux) If you are on a machine that can't run tmux (like a Windows PC without WSL) or you don't care about doing evaluations only running tasks you can run the following script @@ -99,7 +110,7 @@ As you run, the evalaution script will evaluate the performance so far. It will ### Running multiple worlds in parallel -You can use `--num_parallel` to run multiple Minecraft worlds in parallel. This will launch `n` tmux shells, claled `server_i` and shell `i`, where `i` corresponds to ith parallel world. It will also copy worlds into `server_data_i` as well. On an M3 Mac with 34 GB of RAM, we can normally support up to 4 parallel worlds. When running an open source model, it is more likely you will be constrained by the throughput and size of your GPU RAM. On a cluster of 8 H100s you can expect to run 4 experiments in parallel. However, for best performance it is advisable to only use one parallel world. +You can use `--num_parallel` to run multiple Minecraft worlds in parallel. This will launch `n` tmux shells, called `server_i` and shell `i`, where `i` corresponds to ith parallel world. It will also copy worlds into `server_data_i` as well. On an M3 Mac with 34 GB of RAM, we can normally support up to 4 parallel worlds. When running an open source model, it is more likely you will be constrained by the throughput and size of your GPU RAM. On a cluster of 8 H100s you can expect to run 4 experiments in parallel. However, for best performance it is advisable to only use one parallel world. ### Using an S3 Bucket to store files To use S3 set the --s3 flag and the --bucket_name to use an s3 bucket to log all the files collected. It will also copy the /bots folder in this case with all of the files in there. From 00aa14ab5fbfcf2b53a6857dd76a4307b3e2f702 Mon Sep 17 00:00:00 2001 From: Isadora White <54700097+icwhite@users.noreply.github.com> Date: Mon, 9 Jun 2025 02:00:04 -0500 Subject: [PATCH 13/23] Update minecollab.md --- minecollab.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/minecollab.md b/minecollab.md index fa2fca3..5e3fb00 100644 --- a/minecollab.md +++ b/minecollab.md @@ -43,6 +43,12 @@ You can view the crafting task in action [here](https://www.youtube.com/shorts/V Please follow the installation docs in the README to install mindcraft. You can create a docker image using the Dockerfile. +If you don't own Minecraft you can run a limited version solely for offline games using these instructions: +1. Download the TLauncher https://tlauncher.org/en/ +2. Enter a username and select version 1.21.1 +3. Click "Multiplayer" and then "Direct Connection" +4. Then enter "localhost:55916" and hit `Join Server` + Download the relevant task files and server data files, you can find the link [here](https://drive.google.com/drive/folders/1XygbitBBTsNO6q_doEiZHmdETpnyRmCS). The tasks files are for specifying the tasks to run and the server data is for allowing the models to launch the task in the correct world automatically. **Unzip the server_data.zip in the base `tasks/` folder**. Then, set up your conda environment: From 5a403951d1fef71cf3109c554a22d81a05a719a8 Mon Sep 17 00:00:00 2001 From: Isadora White <54700097+icwhite@users.noreply.github.com> Date: Mon, 9 Jun 2025 02:00:36 -0500 Subject: [PATCH 14/23] Update minecollab.md --- minecollab.md | 1 + 1 file changed, 1 insertion(+) diff --git a/minecollab.md b/minecollab.md index 5e3fb00..945a4db 100644 --- a/minecollab.md +++ b/minecollab.md @@ -65,6 +65,7 @@ Then, you can run the evaluation_script **from the project root** using `python **MacOS**: 1. If brew isn't already installed run `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"` 2. `brew install tmux` + **Linux**: `apt-get -y install tmux` **Windows**: You can not use tmux on Windows, but you can run tasks with the --no-launch-world flag. Run ``` From 447b906ce314a7b89246df86c18686bc40c59245 Mon Sep 17 00:00:00 2001 From: Isadora White <54700097+icwhite@users.noreply.github.com> Date: Mon, 9 Jun 2025 02:00:57 -0500 Subject: [PATCH 15/23] Update minecollab.md --- minecollab.md | 1 + 1 file changed, 1 insertion(+) diff --git a/minecollab.md b/minecollab.md index 945a4db..29206d4 100644 --- a/minecollab.md +++ b/minecollab.md @@ -67,6 +67,7 @@ Then, you can run the evaluation_script **from the project root** using `python 2. `brew install tmux` **Linux**: `apt-get -y install tmux` + **Windows**: You can not use tmux on Windows, but you can run tasks with the --no-launch-world flag. Run ``` cd /tasks/server_data/ From a33465ce032cbd2d620a177e3e703a255b33db89 Mon Sep 17 00:00:00 2001 From: Max Robinson Date: Tue, 10 Jun 2025 13:56:56 -0500 Subject: [PATCH 16/23] better node version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0835b4c..07ce15a 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Do not connect this bot to public servers with coding enabled. This project allo ## Requirements - [Minecraft Java Edition](https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc) (up to v1.21.1, recommend v1.21.1) -- [Node.js Installed](https://nodejs.org/) (at least v14, recommended v20.9.0) +- [Node.js Installed](https://nodejs.org/) (at least v18) - One of these: [OpenAI API Key](https://openai.com/blog/openai-api) | [Gemini API Key](https://aistudio.google.com/app/apikey) | [Anthropic API Key](https://docs.anthropic.com/claude/docs/getting-access-to-claude) | [Replicate API Key](https://replicate.com/) | [Hugging Face API Key](https://huggingface.co/) | [Groq API Key](https://console.groq.com/keys) | [Ollama Installed](https://ollama.com/download). | [Mistral API Key](https://docs.mistral.ai/getting-started/models/models_overview/) | [Qwen API Key [Intl.]](https://www.alibabacloud.com/help/en/model-studio/developer-reference/get-api-key)/[[cn]](https://help.aliyun.com/zh/model-studio/getting-started/first-api-call-to-qwen?) | [Novita AI API Key](https://novita.ai/settings?utm_source=github_mindcraft&utm_medium=github_readme&utm_campaign=link#key-management) | ## Install and Run From 8162fc1ab183c9756f7d142e0d9b32630d8280aa Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Tue, 10 Jun 2025 17:52:30 -0500 Subject: [PATCH 17/23] major refactor: use mindserver to init settings --- api.js | 120 ++++++++++++++++++++-------- main.js | 2 +- mindcraft.js | 56 ++++++++++--- src/agent/agent.js | 36 ++++----- src/agent/agent_proxy.js | 73 ----------------- src/agent/coder.js | 4 +- src/agent/commands/actions.js | 4 +- src/agent/conversation.js | 7 +- src/agent/history.js | 2 +- src/agent/library/lockdown.js | 26 +++--- src/agent/mindserver_proxy.js | 107 +++++++++++++++++++++++++ src/agent/modes.js | 2 +- src/agent/settings.js | 7 ++ src/agent/tasks/tasks.js | 46 +++-------- src/agent/vision/browser_viewer.js | 4 +- src/models/prompter.js | 15 +++- src/process/agent_process.js | 25 +++--- src/process/init_agent.js | 48 +++++------ src/process/mindserver_proxy.js | 64 --------------- src/server/mindserver.js | 123 +++++++++++++---------------- src/utils/mcdata.js | 18 +++-- src/utils/translator.js | 10 ++- 22 files changed, 423 insertions(+), 376 deletions(-) delete mode 100644 src/agent/agent_proxy.js create mode 100644 src/agent/mindserver_proxy.js delete mode 100644 src/process/mindserver_proxy.js diff --git a/api.js b/api.js index ddd3ea1..25322c4 100644 --- a/api.js +++ b/api.js @@ -1,43 +1,101 @@ import * as Mindcraft from './mindcraft.js'; +import { readFileSync } from 'fs'; + await Mindcraft.init('localhost', 8080); // starts server locally -await Mindcraft.connect('ip', 'port') // connects to remote server +// await Mindcraft.connect('ip', 'port') // connects to remote server // ^ must do one of these before calling anything else -Mindcraft.addWorld( +let profile = JSON.parse(readFileSync('./profiles/gemini.json', 'utf8')); + +Mindcraft.createAgent( { - name: 'test', - minecraft_version: "1.21.1", - host: 'localhost', - port: 55916, - auth: 'offline', - - render_bot_views: false, // show bot's view in browser at localhost:3000, 3001... - allow_insecure_coding: true, // allows newAction command and model can write/run code on server. enable at own risk - code_timeout_mins: -1, // minutes code is allowed to run. -1 for no timeout - verbose_commands: true, // show full command syntax - chat_bot_messages: true, // publicly chat bot-to-bot messages - } -) -// add world for easy reuse. not super necessary, easy for user to copy world def object around. remove? - - -Mindcraft.addAgent( - { - world: 'test', world: { - minecraft_version: '', - host: '', - port: '', - auth: 'offline' + "minecraft_version": "1.21.1", // supports up to 1.21.1 + "host": "127.0.0.1", // or "localhost", "your.ip.address.here" + "port": 55916, + "auth": "offline", // or "microsoft" }, - profile: './profiles/test.json', - // profile: { - // name: 'test', - // prompt: 'test', + profile, + "base_profile": "survival", // survival | creative | god_mode + "load_memory": false, // load memory from previous session + "init_message": "Respond with hello world and your name", // sends to all on spawn + "only_chat_with": [], // users that the bots listen to and send general messages to. if empty it will chat publicly + "speak": false, // allows all bots to speak through system text-to-speech. works on windows, mac, on linux you need to `apt install espeak` + "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages + "allow_vision": false, // allows vision model to interpret screenshots as inputs + "blocked_actions" : ["!checkBlueprint", "!checkBlueprintLevel", "!getBlueprint", "!getBlueprintLevel"] , // commands to disable and remove from docs. Ex: ["!setMode"] + "relevant_docs_count": 5, // number of relevant code function docs to select for prompting. -1 for all + "max_messages": 15, // max number of messages to keep in context + "num_examples": 2, // number of examples to give to the model + "max_commands": -1, // max number of commands that can be used in consecutive responses. -1 for no limit + "narrate_behavior": true, // chat simple automatic actions ('Picking up item!') + "log_all_prompts": false, // log ALL prompts to file + // "task": { + // "task_id": "multiagent_crafting_pink_wool_full_plan__depth_0", + // "goal": "Collaborate with other agents to craft an pink_wool", + // "conversation": "Let's work together to craft an pink_wool.", + // "initial_inventory": { + // "0": { + // "pink_dye": 1 + // } + // }, + // "agent_count": 1, + // "target": "pink_wool", + // "number_of_target": 1, + // "type": "techtree", + // "max_depth": 1, + // "depth": 0, + // "timeout": 300, + // "blocked_actions": { + // "0": [], + // }, + // "missing_items": [], + // "requires_ctable": false // }, - task: './tasks/test.json' + "verbose_commands": true, // show full command syntax + "chat_bot_messages": true, // publicly chat bot-to-bot messages + + // mindserver settings + "render_bot_view": false, // show bot's view in browser at localhost:3000, 3001... + "allow_insecure_coding": true, // allows newAction command and model can write/run code on your computer. enable at own risk + "code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout } ) -Mindcraft.removeAgent() \ No newline at end of file +// profile = JSON.parse(readFileSync('./andy.json', 'utf8')); + +// Mindcraft.createAgent( +// { +// world: { +// "minecraft_version": "1.21.1", // supports up to 1.21.1 +// "host": "127.0.0.1", // or "localhost", "your.ip.address.here" +// "port": 55916, +// "auth": "offline", // or "microsoft" +// }, +// profile, +// "base_profile": "survival", // also see creative.json, god_mode.json +// "load_memory": false, // load memory from previous session +// "init_message": "Respond with hello world and your name", // sends to all on spawn +// "only_chat_with": [], // users that the bots listen to and send general messages to. if empty it will chat publicly +// "speak": false, // allows all bots to speak through system text-to-speech. works on windows, mac, on linux you need to `apt install espeak` +// "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages +// "allow_vision": false, // allows vision model to interpret screenshots as inputs +// "blocked_actions" : ["!checkBlueprint", "!checkBlueprintLevel", "!getBlueprint", "!getBlueprintLevel"] , // commands to disable and remove from docs. Ex: ["!setMode"] +// "relevant_docs_count": 5, // number of relevant code function docs to select for prompting. -1 for all +// "max_messages": 15, // max number of messages to keep in context +// "num_examples": 2, // number of examples to give to the model +// "max_commands": -1, // max number of commands that can be used in consecutive responses. -1 for no limit +// "narrate_behavior": true, // chat simple automatic actions ('Picking up item!') +// "log_all_prompts": false, // log ALL prompts to file +// "task_file": "", +// "task_name": "", +// "verbose_commands": true, // show full command syntax +// "chat_bot_messages": true, // publicly chat bot-to-bot messages + +// // mindserver settings +// "render_bot_view": false, // show bot's view in browser at localhost:3000, 3001... +// "allow_insecure_coding": true, // allows newAction command and model can write/run code on your computer. enable at own risk +// "code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout +// } +// ) \ No newline at end of file diff --git a/main.js b/main.js index 07d3808..c5e3685 100644 --- a/main.js +++ b/main.js @@ -3,7 +3,7 @@ import settings from './settings.js'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { createMindServer } from './src/server/mindserver.js'; -import { mindserverProxy } from './src/process/mindserver_proxy.js.js'; +import { mindserverProxy } from './src/process/mindserver_proxy.js'; import { readFileSync } from 'fs'; function parseArguments() { diff --git a/mindcraft.js b/mindcraft.js index e4fae5b..6ec88aa 100644 --- a/mindcraft.js +++ b/mindcraft.js @@ -1,10 +1,12 @@ +import { createMindServer, registerAgent } from './src/server/mindserver.js'; import { AgentProcess } from './src/process/agent_process.js'; -import { createMindServer } from './src/server/mindserver.js'; -import { mindserverProxy } from './src/process/mindserver_proxy.js.js'; -import { readFileSync } from 'fs'; let mindserver; let connected = false; +let agent_processes = {}; +let agent_count = 0; +let host = 'localhost'; +let port = 8080; export async function init(host='localhost', port=8080) { if (connected) { @@ -12,21 +14,55 @@ export async function init(host='localhost', port=8080) { return; } mindserver = createMindServer(host, port); - mindserverProxy.connect(host, port); + host = host; + port = port; connected = true; } -export async function connect() { - if (connected) { - console.error('Already connected!'); +export async function createAgent(settings) { + if (!settings.profile.name) { + console.error('Agent name is required in profile'); return; } + let agent_name = settings.profile.name; + registerAgent(settings); + let load_memory = settings.load_memory || false; + let init_message = settings.init_message || null; + const agentProcess = new AgentProcess(agent_name); + agentProcess.start(load_memory, init_message, agent_count, host, port); + agent_count++; + agent_processes[settings.profile.name] = agentProcess; +} + +export function getAgentProcess(agentName) { + return agent_processes[agentName]; +} + +export function startAgent(agentName) { + if (this.agent_processes[agentName]) { + this.agent_processes[agentName].continue(); + } + else { + console.error(`Cannot start agent ${agentName}; not found`); + } } -export function addWorld(settings) { - +export function stopAgent(agentName) { + if (this.agent_processes[agentName]) { + this.agent_processes[agentName].stop(); + } } -export async function addAgent(settings) { +export function shutdown() { + console.log('Shutting down'); + for (let agentName in this.agent_processes) { + this.agent_processes[agentName].stop(); + } + setTimeout(() => { + process.exit(0); + }, 2000); +} +export function logoutAgent(agentName) { + this.socket.emit('logout-agent', agentName); } \ No newline at end of file diff --git a/src/agent/agent.js b/src/agent/agent.js index 3cd671b..d490557 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -12,26 +12,21 @@ import { SelfPrompter } from './self_prompter.js'; import convoManager from './conversation.js'; import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js'; import { addBrowserViewer } from './vision/browser_viewer.js'; -import settings from '../../settings.js'; -import { serverProxy } from './agent_proxy.js'; +import { serverProxy } from './mindserver_proxy.js'; +import settings from './settings.js'; import { Task } from './tasks/tasks.js'; import { say } from './speak.js'; export class Agent { - async start(profile_fp, load_mem=false, init_message=null, count_id=0, task_path=null, task_id=null) { + async start(load_mem=false, init_message=null, count_id=0) { this.last_sender = null; this.count_id = count_id; - if (!profile_fp) { - throw new Error('No profile filepath provided'); - } - - console.log('Starting agent initialization with profile:', profile_fp); // Initialize components with more detailed error handling console.log('Initializing action manager...'); this.actions = new ActionManager(this); console.log('Initializing prompter...'); - this.prompter = new Prompter(this, profile_fp); + this.prompter = new Prompter(this, settings.profile); this.name = this.prompter.getName(); console.log('Initializing history...'); this.history = new History(this); @@ -59,19 +54,15 @@ export class Agent { } else { taskStart = Date.now(); } - this.task = new Task(this, task_path, task_id, taskStart); + this.task = new Task(this, settings.task, taskStart); this.blocked_actions = settings.blocked_actions.concat(this.task.blocked_actions || []); blacklistCommands(this.blocked_actions); - serverProxy.connect(this); - console.log(this.name, 'logging into minecraft...'); this.bot = initBot(this.name); initModes(this); - - this.bot.on('login', () => { console.log(this.name, 'logged in!'); serverProxy.login(); @@ -90,6 +81,8 @@ export class Agent { try { clearTimeout(spawnTimeout); addBrowserViewer(this.bot, count_id); + console.log('Initializing vision intepreter...'); + this.vision_interpreter = new VisionInterpreter(this, settings.allow_vision); // wait for a bit so stats are not undefined await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -101,22 +94,19 @@ export class Agent { this.startEvents(); if (!load_mem) { - if (task_path !== null) { + if (settings.task) { this.task.initBotTask(); this.task.setAgentGoal(); } } else { // set the goal without initializing the rest of the task - if (task_path !== null) { + if (settings.task) { this.task.setAgentGoal(); } } await new Promise((resolve) => setTimeout(resolve, 10000)); this.checkAllPlayersPresent(); - - console.log('Initializing vision intepreter...'); - this.vision_interpreter = new VisionInterpreter(this, settings.allow_vision); } catch (error) { console.error('Error in spawn event:', error); @@ -160,8 +150,12 @@ export class Agent { this.respondFunc = respondFunc; this.bot.on('whisper', respondFunc); - if (settings.profiles.length === 1) - this.bot.on('chat', respondFunc); + + this.bot.on('chat', (username, message) => { + if (serverProxy.getNumOtherAgents() > 0) return; + // only respond to open chat messages when there are no other agents + respondFunc(username, message); + }); // Set up auto-eat this.bot.autoEat.options = { diff --git a/src/agent/agent_proxy.js b/src/agent/agent_proxy.js deleted file mode 100644 index b0333b1..0000000 --- a/src/agent/agent_proxy.js +++ /dev/null @@ -1,73 +0,0 @@ -import { io } from 'socket.io-client'; -import convoManager from './conversation.js'; -import settings from '../../settings.js'; - -class AgentServerProxy { - constructor() { - if (AgentServerProxy.instance) { - return AgentServerProxy.instance; - } - - this.socket = null; - this.connected = false; - AgentServerProxy.instance = this; - } - - connect(agent) { - if (this.connected) return; - - this.agent = agent; - - this.socket = io(`http://${settings.mindserver_host}:${settings.mindserver_port}`); - this.connected = true; - - this.socket.on('connect', () => { - console.log('Connected to MindServer'); - }); - - this.socket.on('disconnect', () => { - console.log('Disconnected from MindServer'); - this.connected = false; - }); - - this.socket.on('chat-message', (agentName, json) => { - convoManager.receiveFromBot(agentName, json); - }); - - this.socket.on('agents-update', (agents) => { - convoManager.updateAgents(agents); - }); - - this.socket.on('restart-agent', (agentName) => { - console.log(`Restarting agent: ${agentName}`); - this.agent.cleanKill(); - }); - - this.socket.on('send-message', (agentName, message) => { - try { - this.agent.respondFunc("NO USERNAME", message); - } catch (error) { - console.error('Error: ', JSON.stringify(error, Object.getOwnPropertyNames(error))); - } - }); - } - - login() { - this.socket.emit('login-agent', this.agent.name); - } - - shutdown() { - this.socket.emit('shutdown'); - } - - getSocket() { - return this.socket; - } -} - -// Create and export a singleton instance -export const serverProxy = new AgentServerProxy(); - -export function sendBotChatToServer(agentName, json) { - serverProxy.getSocket().emit('chat-message', agentName, json); -} diff --git a/src/agent/coder.js b/src/agent/coder.js index 956c8fe..18a5f26 100644 --- a/src/agent/coder.js +++ b/src/agent/coder.js @@ -1,6 +1,5 @@ import { writeFile, readFile, mkdirSync } from 'fs'; -import settings from '../../settings.js'; -import { makeCompartment } from './library/lockdown.js'; +import { makeCompartment, lockdown } from './library/lockdown.js'; import * as skills from './library/skills.js'; import * as world from './library/world.js'; import { Vec3 } from 'vec3'; @@ -27,6 +26,7 @@ export class Coder { async generateCode(agent_history) { this.agent.bot.modes.pause('unstuck'); + lockdown(); // this message history is transient and only maintained in this function let messages = agent_history.getHistory(); messages.push({role: 'system', content: 'Code generation started. Write code in codeblock in your response:'}); diff --git a/src/agent/commands/actions.js b/src/agent/commands/actions.js index b2b3ccb..e321764 100644 --- a/src/agent/commands/actions.js +++ b/src/agent/commands/actions.js @@ -1,5 +1,5 @@ import * as skills from '../library/skills.js'; -import settings from '../../../settings.js'; +import settings from '../settings.js'; import convoManager from '../conversation.js'; @@ -46,7 +46,7 @@ export const actionsList = [ result = 'Error generating code: ' + e.toString(); } }; - await agent.actions.runAction('action:newAction', actionFn); + await agent.actions.runAction('action:newAction', actionFn, {timeout: settings.code_timeout_mins}); return result; } }, diff --git a/src/agent/conversation.js b/src/agent/conversation.js index 41c6888..1cd781e 100644 --- a/src/agent/conversation.js +++ b/src/agent/conversation.js @@ -1,10 +1,9 @@ -import settings from '../../settings.js'; -import { readFileSync } from 'fs'; +import settings from './settings.js'; import { containsCommand } from './commands/index.js'; -import { sendBotChatToServer } from './agent_proxy.js'; +import { sendBotChatToServer } from './mindserver_proxy.js'; let agent; -let agent_names = settings.profiles.map((p) => JSON.parse(readFileSync(p, 'utf8')).name); +let agent_names = []; let agents_in_game = []; class Conversation { diff --git a/src/agent/history.js b/src/agent/history.js index 13b9c79..04a72f7 100644 --- a/src/agent/history.js +++ b/src/agent/history.js @@ -1,6 +1,6 @@ import { writeFileSync, readFileSync, mkdirSync, existsSync } from 'fs'; import { NPCData } from './npc/data.js'; -import settings from '../../settings.js'; +import settings from './settings.js'; export class History { diff --git a/src/agent/library/lockdown.js b/src/agent/library/lockdown.js index 2d8f79d..2db7e3f 100644 --- a/src/agent/library/lockdown.js +++ b/src/agent/library/lockdown.js @@ -4,16 +4,22 @@ import 'ses'; // We disable some of the taming to allow for more flexibility // For configuration, see https://github.com/endojs/endo/blob/master/packages/ses/docs/lockdown.md -lockdown({ - // basic devex and quality of life improvements - localeTaming: 'unsafe', - consoleTaming: 'unsafe', - errorTaming: 'unsafe', - stackFiltering: 'verbose', - // allow eval outside of created compartments - // (mineflayer dep "protodef" uses eval) - evalTaming: 'unsafeEval', -}); + +let lockeddown = false; +export function lockdown() { + if (lockeddown) return; + lockeddown = true; + lockdown({ + // basic devex and quality of life improvements + localeTaming: 'unsafe', + consoleTaming: 'unsafe', + errorTaming: 'unsafe', + stackFiltering: 'verbose', + // allow eval outside of created compartments + // (mineflayer dep "protodef" uses eval) + evalTaming: 'unsafeEval', + }); +} export const makeCompartment = (endowments = {}) => { return new Compartment({ diff --git a/src/agent/mindserver_proxy.js b/src/agent/mindserver_proxy.js new file mode 100644 index 0000000..59f043a --- /dev/null +++ b/src/agent/mindserver_proxy.js @@ -0,0 +1,107 @@ +import { io } from 'socket.io-client'; +import convoManager from './conversation.js'; +import { setSettings } from './settings.js'; + +class MindServerProxy { + constructor() { + if (MindServerProxy.instance) { + return MindServerProxy.instance; + } + + this.socket = null; + this.connected = false; + this.agents = []; + MindServerProxy.instance = this; + } + + async connect(name, host, port) { + if (this.connected) return; + + this.name = name; + + this.socket = io(`http://${host}:${port}`); + this.connected = true; + + this.socket.on('connect', () => { + console.log(name, 'connected to MindServer'); + }); + + this.socket.on('disconnect', () => { + console.log('Disconnected from MindServer'); + this.connected = false; + }); + + this.socket.on('chat-message', (agentName, json) => { + convoManager.receiveFromBot(agentName, json); + }); + + this.socket.on('agents-update', (agents) => { + this.agents = agents; + convoManager.updateAgents(agents); + if (this.agent?.task) { + console.log(this.agent.name, 'updating available agents'); + this.agent.task.updateAvailableAgents(agents); + } + }); + + this.socket.on('restart-agent', (agentName) => { + console.log(`Restarting agent: ${agentName}`); + this.agent.cleanKill(); + }); + + this.socket.on('send-message', (agentName, message) => { + try { + this.agent.respondFunc("NO USERNAME", message); + } catch (error) { + console.error('Error: ', JSON.stringify(error, Object.getOwnPropertyNames(error))); + } + }); + + // Request settings and wait for response + await new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + reject(new Error('Settings request timed out after 10 seconds')); + }, 10000); + + this.socket.emit('get-settings', name, (response) => { + clearTimeout(timeout); + if (response.error) { + return reject(new Error(response.error)); + } + setSettings(response.settings); + resolve(); + }); + }); + } + + setAgent(agent) { + this.agent = agent; + } + + getAgents() { + return this.agents; + } + + getNumOtherAgents() { + return this.agents.length - 1; + } + + login() { + this.socket.emit('login-agent', this.agent.name); + } + + shutdown() { + this.socket.emit('shutdown'); + } + + getSocket() { + return this.socket; + } +} + +// Create and export a singleton instance +export const serverProxy = new MindServerProxy(); + +export function sendBotChatToServer(agentName, json) { + serverProxy.getSocket().emit('chat-message', agentName, json); +} diff --git a/src/agent/modes.js b/src/agent/modes.js index 69b2f06..dc2b925 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -1,7 +1,7 @@ import * as skills from './library/skills.js'; import * as world from './library/world.js'; import * as mc from '../utils/mcdata.js'; -import settings from '../../settings.js' +import settings from './settings.js' import convoManager from './conversation.js'; async function say(agent, message) { diff --git a/src/agent/settings.js b/src/agent/settings.js index e69de29..e9fd133 100644 --- a/src/agent/settings.js +++ b/src/agent/settings.js @@ -0,0 +1,7 @@ +// extremely lightweight obj that can be imported/modified by any file +let settings = {}; +export default settings; +export function setSettings(new_settings) { + Object.keys(settings).forEach(key => delete settings[key]); + Object.assign(settings, new_settings); +} diff --git a/src/agent/tasks/tasks.js b/src/agent/tasks/tasks.js index 1c00d95..2234d59 100644 --- a/src/agent/tasks/tasks.js +++ b/src/agent/tasks/tasks.js @@ -1,7 +1,6 @@ import { readFileSync , writeFileSync, existsSync} from 'fs'; import { executeCommand } from '../commands/index.js'; import { getPosition } from '../library/world.js'; -import settings from '../../../settings.js'; import { ConstructionTaskValidator, Blueprint } from './construction_tasks.js'; import { CookingTaskInitiator } from './cooking_tasks.js'; @@ -233,27 +232,26 @@ class CookingCraftingTaskValidator { } export class Task { - constructor(agent, task_path, task_id, taskStartTime = null) { + constructor(agent, task_data, taskStartTime = null) { this.agent = agent; this.data = null; if (taskStartTime !== null) this.taskStartTime = taskStartTime; else this.taskStartTime = Date.now(); - console.log("Task start time set to", this.taskStartTime); this.validator = null; this.reset_function = null; this.blocked_actions = []; - this.task_id = task_id; - - if (task_path && task_id) { - console.log('Starting task', task_id); - if (task_id.endsWith('hells_kitchen')) { + this.task_data = task_data; + if (task_data) { + console.log('Starting task', task_data.task_id); + console.log("Task start time set to", this.taskStartTime); + if (task_data.task_id.endsWith('hells_kitchen')) { // Reset hells_kitchen progress when a new task starts - hellsKitchenProgressManager.resetTask(task_id); + hellsKitchenProgressManager.resetTask(task_data.task_id); console.log('Reset Hells Kitchen progress for new task'); } - this.data = this.loadTask(task_path, task_id); + this.data = task_data; this.task_type = this.data.type; if (this.task_type === 'construction' && this.data.blueprint) { this.blueprint = new Blueprint(this.data.blueprint); @@ -300,7 +298,11 @@ export class Task { } this.name = this.agent.name; - this.available_agents = settings.profiles.map((p) => JSON.parse(readFileSync(p, 'utf8')).name); + this.available_agents = [] + } + + updateAvailableAgents(agents) { + this.available_agents = agents } // Add this method if you want to manually reset the hells_kitchen progress @@ -360,28 +362,6 @@ export class Task { return null; } - loadTask(task_path, task_id) { - try { - const tasksFile = readFileSync(task_path, 'utf8'); - const tasks = JSON.parse(tasksFile); - let task = tasks[task_id]; - task['task_id'] = task_id; - console.log(task); - console.log(this.agent.count_id); - if (!task) { - throw new Error(`Task ${task_id} not found`); - } - // if ((!task.agent_count || task.agent_count <= 1) && this.agent.count_id > 0) { - // task = null; - // } - - return task; - } catch (error) { - console.error('Error loading task:', error); - process.exit(1); - } - } - isDone() { let res = null; if (this.validator) diff --git a/src/agent/vision/browser_viewer.js b/src/agent/vision/browser_viewer.js index 9ae7c7b..6cce3ed 100644 --- a/src/agent/vision/browser_viewer.js +++ b/src/agent/vision/browser_viewer.js @@ -1,8 +1,8 @@ -import settings from '../../../settings.js'; +import settings from '../settings.js'; import prismarineViewer from 'prismarine-viewer'; const mineflayerViewer = prismarineViewer.mineflayer; export function addBrowserViewer(bot, count_id) { - if (settings.show_bot_views) + if (settings.render_bot_view) mineflayerViewer(bot, { port: 3000+count_id, firstPerson: true, }); } \ No newline at end of file diff --git a/src/models/prompter.js b/src/models/prompter.js index e05f5a8..ebdf521 100644 --- a/src/models/prompter.js +++ b/src/models/prompter.js @@ -4,7 +4,7 @@ import { getCommandDocs } from '../agent/commands/index.js'; import { SkillLibrary } from "../agent/library/skill_library.js"; import { stringifyTurns } from '../utils/text.js'; import { getCommand } from '../agent/commands/index.js'; -import settings from '../../settings.js'; +import settings from '../agent/settings.js'; import { Gemini } from './gemini.js'; import { GPT } from './gpt.js'; @@ -30,11 +30,18 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export class Prompter { - constructor(agent, fp) { + constructor(agent, profile) { this.agent = agent; - this.profile = JSON.parse(readFileSync(fp, 'utf8')); + this.profile = profile; let default_profile = JSON.parse(readFileSync('./profiles/defaults/_default.json', 'utf8')); - let base_fp = settings.base_profile; + let base_fp = ''; + if (settings.base_profile === 'survival') { + base_fp = './profiles/defaults/survival.json'; + } else if (settings.base_profile === 'creative') { + base_fp = './profiles/defaults/creative.json'; + } else if (settings.base_profile === 'god_mode') { + base_fp = './profiles/defaults/god_mode.json'; + } let base_profile = JSON.parse(readFileSync(base_fp, 'utf8')); // first use defaults to fill in missing values in the base profile diff --git a/src/process/agent_process.js b/src/process/agent_process.js index 603f348..7dc7e05 100644 --- a/src/process/agent_process.js +++ b/src/process/agent_process.js @@ -1,23 +1,24 @@ import { spawn } from 'child_process'; -import { mindserverProxy } from './mindserver_proxy.js.js'; +import { logoutAgent } from '../server/mindserver.js'; export class AgentProcess { - start(profile, load_memory=false, init_message=null, count_id=0, task_path=null, task_id=null) { - this.profile = profile; + constructor(name) { + this.name = name; + } + + start(load_memory=false, init_message=null, count_id=0, host, port) { this.count_id = count_id; this.running = true; let args = ['src/process/init_agent.js', this.name]; - args.push('-p', profile); + args.push('-n', this.name); args.push('-c', count_id); if (load_memory) args.push('-l', load_memory); if (init_message) args.push('-m', init_message); - if (task_path) - args.push('-t', task_path); - if (task_id) - args.push('-i', task_id); + args.push('-h', host); + args.push('-p', port); const agentProcess = spawn('node', args, { stdio: 'inherit', @@ -28,7 +29,7 @@ export class AgentProcess { agentProcess.on('exit', (code, signal) => { console.log(`Agent process exited with code ${code} and signal ${signal}`); this.running = false; - mindserverProxy.logoutAgent(this.name); + logoutAgent(this.name); if (code > 1) { console.log(`Ending task`); @@ -38,11 +39,11 @@ export class AgentProcess { if (code !== 0 && signal !== 'SIGINT') { // agent must run for at least 10 seconds before restarting if (Date.now() - last_restart < 10000) { - console.error(`Agent process ${profile} exited too quickly and will not be restarted.`); + console.error(`Agent process exited too quickly and will not be restarted.`); return; } console.log('Restarting agent...'); - this.start(profile, true, 'Agent process restarted.', count_id, task_path, task_id); + this.start(true, 'Agent process restarted.', count_id, host, port); last_restart = Date.now(); } }); @@ -61,7 +62,7 @@ export class AgentProcess { continue() { if (!this.running) { - this.start(this.profile, true, 'Agent process restarted.', this.count_id); + this.start(true, 'Agent process restarted.', this.count_id); } } } \ No newline at end of file diff --git a/src/process/init_agent.js b/src/process/init_agent.js index da79189..1af31b1 100644 --- a/src/process/init_agent.js +++ b/src/process/init_agent.js @@ -1,16 +1,7 @@ import { Agent } from '../agent/agent.js'; +import { serverProxy } from '../agent/mindserver_proxy.js'; import yargs from 'yargs'; -// Add global unhandled rejection handler -process.on('unhandledRejection', (reason, promise) => { - console.error('Unhandled Rejection at:', { - promise: promise, - reason: reason, - stack: reason?.stack || 'No stack trace' - }); - process.exit(1); -}); - const args = process.argv.slice(2); if (args.length < 1) { console.log('Usage: node init_agent.js [profile] [load_memory] [init_message]'); @@ -18,10 +9,10 @@ if (args.length < 1) { } const argv = yargs(args) - .option('profile', { - alias: 'p', + .option('name', { + alias: 'n', type: 'string', - description: 'profile filepath to use for agent' + description: 'name of agent' }) .option('load_memory', { alias: 'l', @@ -33,29 +24,32 @@ const argv = yargs(args) type: 'string', description: 'automatically prompt the agent on startup' }) - .option('task_path', { - alias: 't', - type: 'string', - description: 'task filepath to use for agent' - }) - .option('task_id', { - alias: 'i', - type: 'string', - description: 'task ID to execute' - }) .option('count_id', { alias: 'c', type: 'number', default: 0, description: 'identifying count for multi-agent scenarios', - }).argv; + }) + .option('host', { + alias: 'h', + type: 'string', + description: 'host of mindserver' + }) + .option('port', { + alias: 'p', + type: 'number', + description: 'port of mindserver' + }) + .argv; -// Wrap agent start in async IIFE with proper error handling (async () => { try { - console.log('Starting agent with profile:', argv.profile); + console.log('Connecting to MindServer'); + await serverProxy.connect(argv.name, argv.host, argv.port); + console.log('Starting agent'); const agent = new Agent(); - await agent.start(argv.profile, argv.load_memory, argv.init_message, argv.count_id, argv.task_path, argv.task_id); + serverProxy.setAgent(agent); + await agent.start(argv.load_memory, argv.init_message, argv.count_id); } catch (error) { console.error('Failed to start agent process:'); console.error(error.message); diff --git a/src/process/mindserver_proxy.js b/src/process/mindserver_proxy.js deleted file mode 100644 index d2d4a97..0000000 --- a/src/process/mindserver_proxy.js +++ /dev/null @@ -1,64 +0,0 @@ -import { io } from 'socket.io-client'; - -// Singleton mindserver proxy for the main process -// recieves commands from mindserver -class MindserverProxy { - constructor() { - if (MindserverProxy.instance) { - return MindserverProxy.instance; - } - - this.socket = null; - this.connected = false; - this.agent_processes = {}; - MindserverProxy.instance = this; - } - - connect(host, port) { - if (this.connected) return; - - this.socket = io(`http://${host}:${port}`); - this.connected = true; - - this.socket.on('stop-agent', (agentName) => { - if (this.agent_processes[agentName]) { - this.agent_processes[agentName].stop(); - } - }); - - this.socket.on('start-agent', (agentName) => { - if (this.agent_processes[agentName]) { - this.agent_processes[agentName].continue(); - } - }); - - this.socket.on('register-agents-success', () => { - console.log('Agents registered'); - }); - - this.socket.on('shutdown', () => { - console.log('Shutting down'); - for (let agentName in this.agent_processes) { - this.agent_processes[agentName].stop(); - } - setTimeout(() => { - process.exit(0); - }, 2000); - }); - } - - addAgent(agent) { - this.agent_processes.push(agent); - } - - logoutAgent(agentName) { - this.socket.emit('logout-agent', agentName); - } - - registerAgent(name, process) { - this.socket.emit('register-agents', [name]); - this.agent_processes[name] = process; - } -} - -export const mindserverProxy = new MindserverProxy(); \ No newline at end of file diff --git a/src/server/mindserver.js b/src/server/mindserver.js index ac54db3..035354e 100644 --- a/src/server/mindserver.js +++ b/src/server/mindserver.js @@ -3,21 +3,41 @@ import express from 'express'; import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; +import * as mindcraft from '../../mindcraft.js'; -// Mindserver purposes: -// - central hub for inter-process communication between all agent processes +// Mindserver is: +// - central hub for communication between all agent processes // - api to control from other languages and remote users // - host for webapp // Module-level variables let io; let server; -const registeredAgents = new Set(); -const inGameAgents = {}; -const agentManagers = {}; // socket for main process that registers/controls agents +const agent_connections = {}; + +class AgentConnection { + constructor(settings) { + this.socket = null; + this.settings = settings; + this.in_game = false; + } + +} + +export function registerAgent(settings) { + let agentConnection = new AgentConnection(settings); + agent_connections[settings.profile.name] = agentConnection; +} + +export function logoutAgent(agentName) { + if (agent_connections[agentName]) { + agent_connections[agentName].in_game = false; + agentsUpdate(); + } +} // Initialize the server -export function createMindServer(port = 8080) { +export function createMindServer(host = 'localhost', port = 8080) { const app = express(); server = http.createServer(app); io = new Server(server); @@ -33,109 +53,90 @@ export function createMindServer(port = 8080) { agentsUpdate(socket); - socket.on('register-agents', (agentNames) => { - console.log(`Registering agents: ${agentNames}`); - agentNames.forEach(name => registeredAgents.add(name)); - for (let name of agentNames) { - agentManagers[name] = socket; + socket.on('get-settings', (agentName, callback) => { + if (agent_connections[agentName]) { + callback({ settings: agent_connections[agentName].settings }); + } else { + callback({ error: `Agent '${agentName}' not found.` }); } - socket.emit('register-agents-success'); - agentsUpdate(); }); socket.on('login-agent', (agentName) => { - if (curAgentName && curAgentName !== agentName) { - console.warn(`Agent ${agentName} already logged in as ${curAgentName}`); - return; - } - if (registeredAgents.has(agentName)) { + if (agent_connections[agentName]) { + agent_connections[agentName].socket = socket; + agent_connections[agentName].in_game = true; curAgentName = agentName; - inGameAgents[agentName] = socket; agentsUpdate(); - } else { - console.warn(`Agent ${agentName} not registered`); + } + else { + console.warn(`Unregistered agent ${agentName} tried to login`); } }); socket.on('logout-agent', (agentName) => { - if (inGameAgents[agentName]) { - delete inGameAgents[agentName]; + if (agent_connections[agentName]) { + agent_connections[agentName].in_game = false; agentsUpdate(); } }); socket.on('disconnect', () => { console.log('Client disconnected'); - if (inGameAgents[curAgentName]) { - delete inGameAgents[curAgentName]; + if (agent_connections[curAgentName]) { + agent_connections[curAgentName].in_game = false; agentsUpdate(); } }); socket.on('chat-message', (agentName, json) => { - if (!inGameAgents[agentName]) { + if (!agent_connections[agentName]) { console.warn(`Agent ${agentName} tried to send a message but is not logged in`); return; } console.log(`${curAgentName} sending message to ${agentName}: ${json.message}`); - inGameAgents[agentName].emit('chat-message', curAgentName, json); + agent_connections[agentName].socket.emit('chat-message', curAgentName, json); }); socket.on('restart-agent', (agentName) => { console.log(`Restarting agent: ${agentName}`); - inGameAgents[agentName].emit('restart-agent'); + agent_connections[agentName].socket.emit('restart-agent'); }); socket.on('stop-agent', (agentName) => { - let manager = agentManagers[agentName]; - if (manager) { - manager.emit('stop-agent', agentName); - } - else { - console.warn(`Stopping unregisterd agent ${agentName}`); - } + mindcraft.stopAgent(agentName); }); socket.on('start-agent', (agentName) => { - let manager = agentManagers[agentName]; - if (manager) { - manager.emit('start-agent', agentName); - } - else { - console.warn(`Starting unregisterd agent ${agentName}`); - } + mindcraft.startAgent(agentName); }); socket.on('stop-all-agents', () => { console.log('Killing all agents'); - stopAllAgents(); + for (let agentName in agent_connections) { + mindcraft.stopAgent(agentName); + } }); socket.on('shutdown', () => { console.log('Shutting down'); - for (let manager of Object.values(agentManagers)) { - manager.emit('shutdown'); - } - setTimeout(() => { - process.exit(0); - }, 2000); + process.exit(0); }); socket.on('send-message', (agentName, message) => { - if (!inGameAgents[agentName]) { - console.warn(`Agent ${agentName} not logged in, cannot send message via MindServer.`); + if (!agent_connections[agentName]) { + console.warn(`Agent ${agentName} not in game, cannot send message via MindServer.`); return } try { console.log(`Sending message to agent ${agentName}: ${message}`); - inGameAgents[agentName].emit('send-message', agentName, message) + agent_connections[agentName].socket.emit('send-message', agentName, message) } catch (error) { console.error('Error: ', error); } }); }); - server.listen(port, 'localhost', () => { + server.listen(port, host, () => { console.log(`MindServer running on port ${port}`); }); @@ -147,22 +148,12 @@ function agentsUpdate(socket) { socket = io; } let agents = []; - registeredAgents.forEach(name => { - agents.push({name, in_game: !!inGameAgents[name]}); - }); + for (let agentName in agent_connections) { + agents.push({name: agentName, in_game: agent_connections[agentName].in_game}); + }; socket.emit('agents-update', agents); } -function stopAllAgents() { - for (const agentName in inGameAgents) { - let manager = agentManagers[agentName]; - if (manager) { - manager.emit('stop-agent', agentName); - } - } -} - // Optional: export these if you need access to them from other files export const getIO = () => io; export const getServer = () => server; -export const getConnectedAgents = () => connectedAgents; diff --git a/src/utils/mcdata.js b/src/utils/mcdata.js index 1b79d3e..f79092c 100644 --- a/src/utils/mcdata.js +++ b/src/utils/mcdata.js @@ -1,5 +1,5 @@ import minecraftData from 'minecraft-data'; -import settings from '../../settings.js'; +import settings from '../agent/settings.js'; import { createBot } from 'mineflayer'; import prismarine_items from 'prismarine-item'; import { pathfinder } from 'mineflayer-pathfinder'; @@ -8,10 +8,9 @@ import { plugin as collectblock } from 'mineflayer-collectblock'; import { plugin as autoEat } from 'mineflayer-auto-eat'; import plugin from 'mineflayer-armor-manager'; const armorManager = plugin; - -const mc_version = settings.minecraft_version; -const mcdata = minecraftData(mc_version); -const Item = prismarine_items(mc_version); +let mc_version = null; +let mcdata = null; +let Item = null; /** * @typedef {string} ItemName @@ -54,12 +53,15 @@ export const WOOL_COLORS = [ export function initBot(username) { + mc_version = settings.world.minecraft_version; + mcdata = minecraftData(mc_version); + Item = prismarine_items(mc_version); let bot = createBot({ username: username, - host: settings.host, - port: settings.port, - auth: settings.auth, + host: settings.world.host, + port: settings.world.port, + auth: settings.world.auth, version: mc_version, }); diff --git a/src/utils/translator.js b/src/utils/translator.js index bc9cc77..3ebd566 100644 --- a/src/utils/translator.js +++ b/src/utils/translator.js @@ -1,10 +1,11 @@ import translate from 'google-translate-api-x'; -import settings from '../../settings.js'; +import settings from '../agent/settings.js'; + -const preferred_lang = String(settings.language).toLowerCase(); export async function handleTranslation(message) { - if (preferred_lang === 'en' || preferred_lang === 'english') + let preferred_lang = String(settings.language).toLowerCase(); + if (!preferred_lang || preferred_lang === 'en' || preferred_lang === 'english') return message; try { const translation = await translate(message, { to: preferred_lang }); @@ -16,7 +17,8 @@ export async function handleTranslation(message) { } export async function handleEnglishTranslation(message) { - if (preferred_lang === 'en' || preferred_lang === 'english') + let preferred_lang = String(settings.language).toLowerCase(); + if (!preferred_lang || preferred_lang === 'en' || preferred_lang === 'english') return message; try { const translation = await translate(message, { to: 'english' }); From 0f5dd0cb07c745ab864cc62cd83dcd5284dcae32 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Wed, 11 Jun 2025 16:41:54 -0500 Subject: [PATCH 18/23] create-agent endpoint from ui --- api.js | 101 -------------------- main.js | 72 ++++++++------ settings.js | 37 +------ src/agent/agent.js | 10 +- src/agent/mindserver_proxy.js | 17 ++-- src/mindcraft/default_settings.json | 25 +++++ mindcraft.js => src/mindcraft/mindcraft.js | 25 +++-- src/{server => mindcraft}/mindserver.js | 33 +++++-- src/{server => mindcraft}/public/index.html | 34 +++++++ src/models/prompter.js | 6 +- src/process/agent_process.js | 14 +-- src/utils/mcdata.js | 8 +- 12 files changed, 167 insertions(+), 215 deletions(-) delete mode 100644 api.js create mode 100644 src/mindcraft/default_settings.json rename mindcraft.js => src/mindcraft/mindcraft.js (65%) rename src/{server => mindcraft}/mindserver.js (81%) rename src/{server => mindcraft}/public/index.html (72%) diff --git a/api.js b/api.js deleted file mode 100644 index 25322c4..0000000 --- a/api.js +++ /dev/null @@ -1,101 +0,0 @@ -import * as Mindcraft from './mindcraft.js'; -import { readFileSync } from 'fs'; - - -await Mindcraft.init('localhost', 8080); // starts server locally -// await Mindcraft.connect('ip', 'port') // connects to remote server -// ^ must do one of these before calling anything else - -let profile = JSON.parse(readFileSync('./profiles/gemini.json', 'utf8')); - -Mindcraft.createAgent( - { - world: { - "minecraft_version": "1.21.1", // supports up to 1.21.1 - "host": "127.0.0.1", // or "localhost", "your.ip.address.here" - "port": 55916, - "auth": "offline", // or "microsoft" - }, - profile, - "base_profile": "survival", // survival | creative | god_mode - "load_memory": false, // load memory from previous session - "init_message": "Respond with hello world and your name", // sends to all on spawn - "only_chat_with": [], // users that the bots listen to and send general messages to. if empty it will chat publicly - "speak": false, // allows all bots to speak through system text-to-speech. works on windows, mac, on linux you need to `apt install espeak` - "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages - "allow_vision": false, // allows vision model to interpret screenshots as inputs - "blocked_actions" : ["!checkBlueprint", "!checkBlueprintLevel", "!getBlueprint", "!getBlueprintLevel"] , // commands to disable and remove from docs. Ex: ["!setMode"] - "relevant_docs_count": 5, // number of relevant code function docs to select for prompting. -1 for all - "max_messages": 15, // max number of messages to keep in context - "num_examples": 2, // number of examples to give to the model - "max_commands": -1, // max number of commands that can be used in consecutive responses. -1 for no limit - "narrate_behavior": true, // chat simple automatic actions ('Picking up item!') - "log_all_prompts": false, // log ALL prompts to file - // "task": { - // "task_id": "multiagent_crafting_pink_wool_full_plan__depth_0", - // "goal": "Collaborate with other agents to craft an pink_wool", - // "conversation": "Let's work together to craft an pink_wool.", - // "initial_inventory": { - // "0": { - // "pink_dye": 1 - // } - // }, - // "agent_count": 1, - // "target": "pink_wool", - // "number_of_target": 1, - // "type": "techtree", - // "max_depth": 1, - // "depth": 0, - // "timeout": 300, - // "blocked_actions": { - // "0": [], - // }, - // "missing_items": [], - // "requires_ctable": false - // }, - "verbose_commands": true, // show full command syntax - "chat_bot_messages": true, // publicly chat bot-to-bot messages - - // mindserver settings - "render_bot_view": false, // show bot's view in browser at localhost:3000, 3001... - "allow_insecure_coding": true, // allows newAction command and model can write/run code on your computer. enable at own risk - "code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout - } -) - -// profile = JSON.parse(readFileSync('./andy.json', 'utf8')); - -// Mindcraft.createAgent( -// { -// world: { -// "minecraft_version": "1.21.1", // supports up to 1.21.1 -// "host": "127.0.0.1", // or "localhost", "your.ip.address.here" -// "port": 55916, -// "auth": "offline", // or "microsoft" -// }, -// profile, -// "base_profile": "survival", // also see creative.json, god_mode.json -// "load_memory": false, // load memory from previous session -// "init_message": "Respond with hello world and your name", // sends to all on spawn -// "only_chat_with": [], // users that the bots listen to and send general messages to. if empty it will chat publicly -// "speak": false, // allows all bots to speak through system text-to-speech. works on windows, mac, on linux you need to `apt install espeak` -// "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages -// "allow_vision": false, // allows vision model to interpret screenshots as inputs -// "blocked_actions" : ["!checkBlueprint", "!checkBlueprintLevel", "!getBlueprint", "!getBlueprintLevel"] , // commands to disable and remove from docs. Ex: ["!setMode"] -// "relevant_docs_count": 5, // number of relevant code function docs to select for prompting. -1 for all -// "max_messages": 15, // max number of messages to keep in context -// "num_examples": 2, // number of examples to give to the model -// "max_commands": -1, // max number of commands that can be used in consecutive responses. -1 for no limit -// "narrate_behavior": true, // chat simple automatic actions ('Picking up item!') -// "log_all_prompts": false, // log ALL prompts to file -// "task_file": "", -// "task_name": "", -// "verbose_commands": true, // show full command syntax -// "chat_bot_messages": true, // publicly chat bot-to-bot messages - -// // mindserver settings -// "render_bot_view": false, // show bot's view in browser at localhost:3000, 3001... -// "allow_insecure_coding": true, // allows newAction command and model can write/run code on your computer. enable at own risk -// "code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout -// } -// ) \ No newline at end of file diff --git a/main.js b/main.js index c5e3685..10b3e28 100644 --- a/main.js +++ b/main.js @@ -1,9 +1,7 @@ -import { AgentProcess } from './src/process/agent_process.js'; +import * as Mindcraft from './src/mindcraft/mindcraft.js'; import settings from './settings.js'; import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; -import { createMindServer } from './src/server/mindserver.js'; -import { mindserverProxy } from './src/process/mindserver_proxy.js'; import { readFileSync } from 'fs'; function parseArguments() { @@ -24,35 +22,51 @@ function parseArguments() { .alias('help', 'h') .parse(); } - -function getProfiles(args) { - return args.profiles || settings.profiles; +const args = parseArguments(); +if (args.profiles) { + settings.profiles = args.profiles; } - -async function main() { - if (settings.host_mindserver) { - const mindServer = createMindServer(settings.mindserver_port); +if (args.task_path) { + let tasks = JSON.parse(readFileSync(args.task_path, 'utf8')); + if (args.task_id) { + settings.task = tasks[args.task_id]; + settings.task.task_id = args.task_id; } - mindserverProxy.connect(); - - const args = parseArguments(); - const profiles = getProfiles(args); - console.log(profiles); - const { load_memory, init_message } = settings; - - for (let i=0; i setTimeout(resolve, 1000)); + else { + throw new Error('task_id is required when task_path is provided'); } } -try { - main(); -} catch (error) { - console.error('An error occurred:', error); - process.exit(1); +// these environment variables override certain settings +if (process.env.MINECRAFT_PORT) { + settings.port = process.env.MINECRAFT_PORT; } +if (process.env.MINDSERVER_PORT) { + settings.mindserver_port = process.env.MINDSERVER_PORT; +} +if (process.env.PROFILES && JSON.parse(process.env.PROFILES).length > 0) { + settings.profiles = JSON.parse(process.env.PROFILES); +} +if (process.env.INSECURE_CODING) { + settings.allow_insecure_coding = true; +} +if (process.env.BLOCKED_ACTIONS) { + settings.blocked_actions = JSON.parse(process.env.BLOCKED_ACTIONS); +} +if (process.env.MAX_MESSAGES) { + settings.max_messages = process.env.MAX_MESSAGES; +} +if (process.env.NUM_EXAMPLES) { + settings.num_examples = process.env.NUM_EXAMPLES; +} +if (process.env.LOG_ALL) { + settings.log_all_prompts = process.env.LOG_ALL; +} + +Mindcraft.init(settings.mindserver_host, settings.mindserver_port); + +for (let profile of settings.profiles) { + const profile_json = JSON.parse(readFileSync(profile, 'utf8')); + settings.profile = profile_json; + Mindcraft.createAgent(settings); +} \ No newline at end of file diff --git a/settings.js b/settings.js index 1d6c5a2..16f8b0f 100644 --- a/settings.js +++ b/settings.js @@ -11,9 +11,9 @@ const settings = { // the base profile is shared by all bots for default prompts/examples/modes "profiles": [ - // "./andy.json", + "./andy.json", // "./profiles/gpt.json", - "./profiles/claude.json", + // "./profiles/claude.json", // "./profiles/gemini.json", // "./profiles/llama.json", // "./profiles/qwen.json", @@ -26,7 +26,7 @@ const settings = { ], // agent settings - "base_profile": "./profiles/defaults/god_mode.json", // also see creative.json, god_mode.json + "base_profile": "survival", // survival, creative, or god_mode "load_memory": false, // load memory from previous session "init_message": "Respond with hello world and your name", // sends to all on spawn "only_chat_with": [], // users that the bots listen to and send general messages to. if empty it will chat publicly @@ -40,42 +40,13 @@ const settings = { "max_commands": -1, // max number of commands that can be used in consecutive responses. -1 for no limit "narrate_behavior": true, // chat simple automatic actions ('Picking up item!') "log_all_prompts": false, // log ALL prompts to file - "task": {}, - "task_file": "", - "task_name": "", "verbose_commands": true, // show full command syntax "chat_bot_messages": true, // publicly chat bot-to-bot messages // mindserver settings - "render_bot_views": false, // show bot's view in browser at localhost:3000, 3001... + "render_bot_view": false, // show bot's view in browser at localhost:3000, 3001... "allow_insecure_coding": true, // allows newAction command and model can write/run code on your computer. enable at own risk "code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout } -// these environment variables override certain settings -if (process.env.MINECRAFT_PORT) { - settings.port = process.env.MINECRAFT_PORT; -} -if (process.env.MINDSERVER_PORT) { - settings.mindserver_port = process.env.MINDSERVER_PORT; -} -if (process.env.PROFILES && JSON.parse(process.env.PROFILES).length > 0) { - settings.profiles = JSON.parse(process.env.PROFILES); -} -if (process.env.INSECURE_CODING) { - settings.allow_insecure_coding = true; -} -if (process.env.BLOCKED_ACTIONS) { - settings.blocked_actions = JSON.parse(process.env.BLOCKED_ACTIONS); -} -if (process.env.MAX_MESSAGES) { - settings.max_messages = process.env.MAX_MESSAGES; -} -if (process.env.NUM_EXAMPLES) { - settings.num_examples = process.env.NUM_EXAMPLES; -} -if (process.env.LOG_ALL) { - settings.log_all_prompts = process.env.LOG_ALL; -} - export default settings; diff --git a/src/agent/agent.js b/src/agent/agent.js index d490557..7bc692d 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -23,25 +23,17 @@ export class Agent { this.count_id = count_id; // Initialize components with more detailed error handling - console.log('Initializing action manager...'); + console.log(`Initializing agent ${this.name}...`); this.actions = new ActionManager(this); - console.log('Initializing prompter...'); this.prompter = new Prompter(this, settings.profile); this.name = this.prompter.getName(); - console.log('Initializing history...'); this.history = new History(this); - console.log('Initializing coder...'); this.coder = new Coder(this); - console.log('Initializing npc controller...'); this.npc = new NPCContoller(this); - console.log('Initializing memory bank...'); this.memory_bank = new MemoryBank(); - console.log('Initializing self prompter...'); this.self_prompter = new SelfPrompter(this); convoManager.initAgent(this); - console.log('Initializing examples...'); await this.prompter.initExamples(); - console.log('Initializing task...'); // load mem first before doing task let save_data = null; diff --git a/src/agent/mindserver_proxy.js b/src/agent/mindserver_proxy.js index 59f043a..da098e1 100644 --- a/src/agent/mindserver_proxy.js +++ b/src/agent/mindserver_proxy.js @@ -18,14 +18,19 @@ class MindServerProxy { if (this.connected) return; this.name = name; - this.socket = io(`http://${host}:${port}`); - this.connected = true; - this.socket.on('connect', () => { - console.log(name, 'connected to MindServer'); + await new Promise((resolve, reject) => { + this.socket.on('connect', resolve); + this.socket.on('connect_error', (err) => { + console.error('Connection failed:', err); + reject(err); + }); }); + this.connected = true; + console.log(name, 'connected to MindServer'); + this.socket.on('disconnect', () => { console.log('Disconnected from MindServer'); this.connected = false; @@ -60,8 +65,8 @@ class MindServerProxy { // Request settings and wait for response await new Promise((resolve, reject) => { const timeout = setTimeout(() => { - reject(new Error('Settings request timed out after 10 seconds')); - }, 10000); + reject(new Error('Settings request timed out after 5 seconds')); + }, 5000); this.socket.emit('get-settings', name, (response) => { clearTimeout(timeout); diff --git a/src/mindcraft/default_settings.json b/src/mindcraft/default_settings.json new file mode 100644 index 0000000..3ec448b --- /dev/null +++ b/src/mindcraft/default_settings.json @@ -0,0 +1,25 @@ +{ + "minecraft_version": "1.21.1", + "host": "127.0.0.1", + "port": 55916, + "auth": "offline", + "base_profile": "survival", + "load_memory": false, + "init_message": "Respond with hello world and your name", + "only_chat_with": [], + "speak": false, + "language": "en", + "allow_vision": false, + "blocked_actions" : ["!checkBlueprint", "!checkBlueprintLevel", "!getBlueprint", "!getBlueprintLevel"] , + "relevant_docs_count": 5, + "max_messages": 15, + "num_examples": 2, + "max_commands": -1, + "narrate_behavior": true, + "log_all_prompts": false, + "verbose_commands": true, + "chat_bot_messages": true, + "render_bot_view": false, + "allow_insecure_coding": false, + "code_timeout_mins": -1 +} \ No newline at end of file diff --git a/mindcraft.js b/src/mindcraft/mindcraft.js similarity index 65% rename from mindcraft.js rename to src/mindcraft/mindcraft.js index 6ec88aa..a36bbdc 100644 --- a/mindcraft.js +++ b/src/mindcraft/mindcraft.js @@ -1,5 +1,5 @@ -import { createMindServer, registerAgent } from './src/server/mindserver.js'; -import { AgentProcess } from './src/process/agent_process.js'; +import { createMindServer, registerAgent } from './mindserver.js'; +import { AgentProcess } from '../process/agent_process.js'; let mindserver; let connected = false; @@ -24,12 +24,13 @@ export async function createAgent(settings) { console.error('Agent name is required in profile'); return; } + settings = JSON.parse(JSON.stringify(settings)); let agent_name = settings.profile.name; registerAgent(settings); let load_memory = settings.load_memory || false; let init_message = settings.init_message || null; - const agentProcess = new AgentProcess(agent_name); - agentProcess.start(load_memory, init_message, agent_count, host, port); + const agentProcess = new AgentProcess(agent_name, host, port); + agentProcess.start(load_memory, init_message, agent_count); agent_count++; agent_processes[settings.profile.name] = agentProcess; } @@ -39,8 +40,8 @@ export function getAgentProcess(agentName) { } export function startAgent(agentName) { - if (this.agent_processes[agentName]) { - this.agent_processes[agentName].continue(); + if (agent_processes[agentName]) { + agent_processes[agentName].continue(); } else { console.error(`Cannot start agent ${agentName}; not found`); @@ -48,21 +49,17 @@ export function startAgent(agentName) { } export function stopAgent(agentName) { - if (this.agent_processes[agentName]) { - this.agent_processes[agentName].stop(); + if (agent_processes[agentName]) { + agent_processes[agentName].stop(); } } export function shutdown() { console.log('Shutting down'); - for (let agentName in this.agent_processes) { - this.agent_processes[agentName].stop(); + for (let agentName in agent_processes) { + agent_processes[agentName].stop(); } setTimeout(() => { process.exit(0); }, 2000); } - -export function logoutAgent(agentName) { - this.socket.emit('logout-agent', agentName); -} \ No newline at end of file diff --git a/src/server/mindserver.js b/src/mindcraft/mindserver.js similarity index 81% rename from src/server/mindserver.js rename to src/mindcraft/mindserver.js index 035354e..1d51854 100644 --- a/src/server/mindserver.js +++ b/src/mindcraft/mindserver.js @@ -3,18 +3,21 @@ import express from 'express'; import http from 'http'; import path from 'path'; import { fileURLToPath } from 'url'; -import * as mindcraft from '../../mindcraft.js'; +import * as mindcraft from './mindcraft.js'; +import { readFileSync } from 'fs'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); // Mindserver is: // - central hub for communication between all agent processes // - api to control from other languages and remote users // - host for webapp -// Module-level variables let io; let server; const agent_connections = {}; +const default_settings = JSON.parse(readFileSync(path.join(__dirname, 'default_settings.json'), 'utf8')); + class AgentConnection { constructor(settings) { this.socket = null; @@ -53,6 +56,23 @@ export function createMindServer(host = 'localhost', port = 8080) { agentsUpdate(socket); + socket.on('create-agent', (settings, callback) => { + console.log('API create agent...'); + settings = { ...default_settings, ...settings }; + if (settings.profile?.name) { + if (settings.profile.name in agent_connections) { + callback({ success: false, error: 'Agent already exists' }); + return; + } + mindcraft.createAgent(settings); + callback({ success: true }); + } + else { + console.error('Agent name is required in profile'); + callback({ success: false, error: 'Agent name is required in profile' }); + } + }); + socket.on('get-settings', (agentName, callback) => { if (agent_connections[agentName]) { callback({ settings: agent_connections[agentName].settings }); @@ -73,16 +93,9 @@ export function createMindServer(host = 'localhost', port = 8080) { } }); - socket.on('logout-agent', (agentName) => { - if (agent_connections[agentName]) { - agent_connections[agentName].in_game = false; - agentsUpdate(); - } - }); - socket.on('disconnect', () => { - console.log('Client disconnected'); if (agent_connections[curAgentName]) { + console.log(`Agent ${curAgentName} disconnected`); agent_connections[curAgentName].in_game = false; agentsUpdate(); } diff --git a/src/server/public/index.html b/src/mindcraft/public/index.html similarity index 72% rename from src/server/public/index.html rename to src/mindcraft/public/index.html index c66a986..f16105c 100644 --- a/src/server/public/index.html +++ b/src/mindcraft/public/index.html @@ -64,10 +64,44 @@

Mindcraft

+
+ + +
+ diff --git a/src/mindcraft/public/settings_spec.json b/src/mindcraft/public/settings_spec.json new file mode 100644 index 0000000..9023af6 --- /dev/null +++ b/src/mindcraft/public/settings_spec.json @@ -0,0 +1,127 @@ +{ + "profile": { + "type": "object", + "required": true, + "description": "The profile object to use, including name, prompts, and examples" + }, + "minecraft_version": { + "type": "string", + "description": "The version of Minecraft to use", + "default": "1.21.1" + }, + "host": { + "type": "string", + "description": "The minecraft server host address to connect to", + "default": "127.0.0.1" + }, + "port": { + "type": "number", + "description": "The minecraft server port to connect to", + "default": 55916 + }, + "auth": { + "type": "string", + "description": "The authentication method to use", + "default": "offline" + }, + "base_profile": { + "type": "string", + "description": "Allowed values: survival, creative, god_mode. Each has fine tuned settings for different game modes.", + "default": "survival" + }, + "load_memory": { + "type": "boolean", + "description": "Whether to load bot's previous memory", + "default": false + }, + "init_message": { + "type": "string", + "description": "The initial message to send to the bot", + "default": "Respond with hello world and your name" + }, + "only_chat_with": { + "type": "array", + "description": "List of agents to only chat with. If empty, the bot will chat publicly", + "default": [] + }, + "speak": { + "type": "boolean", + "description": "Whether to enable text-to-speech reading on the host machine", + "default": false + }, + "language": { + "type": "string", + "description": "The language to automatically translate to and from using google translate", + "default": "en" + }, + "allow_vision": { + "type": "boolean", + "description": "Whether to allow vision capabilities", + "default": false + }, + "blocked_actions": { + "type": "array", + "description": "List of actions that are blocked", + "default": ["!checkBlueprint", "!checkBlueprintLevel", "!getBlueprint", "!getBlueprintLevel"] + }, + "relevant_docs_count": { + "type": "number", + "description": "Number of relevant function documents to include in the prompt for LLM code writing", + "default": 5 + }, + "max_messages": { + "type": "number", + "description": "Maximum number of recent messages to keep in context for LLM", + "default": 15 + }, + "num_examples": { + "type": "number", + "description": "Number of examples to select to help prompt better LLM responses", + "default": 2 + }, + "max_commands": { + "type": "number", + "description": "Maximum number of commands allowed in consecutive responses. -1 for no limit", + "default": -1 + }, + "narrate_behavior": { + "type": "boolean", + "description": "Whether to openly chat automatic behavior like 'Picking up item!'", + "default": true + }, + "log_all_prompts": { + "type": "boolean", + "description": "Whether to log all prompts to file. Can be very verbose.", + "default": false + }, + "verbose_commands": { + "type": "boolean", + "description": "Whether to show full command syntax in bot responses. If false will use a shortened syntax.", + "default": true + }, + "chat_bot_messages": { + "type": "boolean", + "description": "Whether to publicly chat messages to and from other bots", + "default": true + }, + "render_bot_view": { + "type": "boolean", + "description": "Whether to render bot view for user observation. Does not give bot vision.", + "default": false + }, + "allow_insecure_coding": { + "type": "boolean", + "description": "Whether to allow newAction command that let's LLM write/run code on host computer. Despite sandboxxing, it is potentially insecure.", + "default": false + }, + "code_timeout_mins": { + "type": "number", + "description": "Number of minutes to allow code execution. -1 for no timeout", + "default": -1 + }, + "task": { + "type": "object", + "description": "The task object to give the agent on start. If null, the agent will not have a task.", + "default": null + } +} \ No newline at end of file diff --git a/viewer.html b/viewer.html deleted file mode 100644 index bdf2047..0000000 --- a/viewer.html +++ /dev/null @@ -1,69 +0,0 @@ - - - Viewer - - - -
- - - - -
- - -