mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-03-28 14:56:24 +01:00
cleanup
This commit is contained in:
parent
a7e529b1e9
commit
4e0611d29e
16 changed files with 265 additions and 577 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -13,6 +13,3 @@ services/viaproxy/plugins/**
|
|||
services/viaproxy/ViaLoader/**
|
||||
services/viaproxy/saves.json
|
||||
services/viaproxy/viaproxy.yml
|
||||
profiles/task_*
|
||||
multi_agent_task*
|
||||
*_results.txt
|
||||
|
|
109
example_tasks.json
Normal file
109
example_tasks.json
Normal file
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"debug_single_agent": {
|
||||
"goal": "Just stand at a place and don't do anything",
|
||||
"guidance": null,
|
||||
"initial_inventory": {},
|
||||
"type": "debug"
|
||||
},
|
||||
"debug_multi_agent": {
|
||||
"goal": "Just stand at a place and don't do anything",
|
||||
"agent_names": [
|
||||
"andy",
|
||||
"randy"
|
||||
],
|
||||
"agent_number": 2,
|
||||
"initial_inventory": {
|
||||
"andy": {
|
||||
"iron_ingot": 1
|
||||
},
|
||||
"randy": {
|
||||
"iron_ingot": 1
|
||||
}
|
||||
},
|
||||
"type": "debug"
|
||||
},
|
||||
"construction": {
|
||||
"type": "construction",
|
||||
"goal": "Build a house"
|
||||
},
|
||||
"multiagent_techtree_1_shears_with_2_iron_ingot": {
|
||||
"goal": "Collaborate with other agents to build a shear.",
|
||||
"agent_names": [
|
||||
"andy",
|
||||
"randy"
|
||||
],
|
||||
"agent_number": 2,
|
||||
"initial_inventory": {
|
||||
"andy": {
|
||||
"iron_ingot": 1
|
||||
},
|
||||
"randy": {
|
||||
"iron_ingot": 1
|
||||
}
|
||||
},
|
||||
"target": "shears",
|
||||
"number_of_target": 1,
|
||||
"type": "techtree",
|
||||
"timeout": 60
|
||||
},
|
||||
"multiagent_techtree_1_stone_pickaxe": {
|
||||
"goal": "Collaborate with other agents to build an stone pickaxe",
|
||||
"agent_names": [
|
||||
"andy",
|
||||
"randy"
|
||||
],
|
||||
"agent_number": 2,
|
||||
"initial_inventory": {
|
||||
"andy": {
|
||||
"wooden_pickaxe": 1
|
||||
},
|
||||
"randy": {
|
||||
"wooden_axe": 1
|
||||
}
|
||||
},
|
||||
"target": "stone_pickaxe",
|
||||
"number_of_target": 1,
|
||||
"type": "techtree",
|
||||
"timeout": 300
|
||||
},
|
||||
"multiagent_build_wooden_pickaxe": {
|
||||
"goal": "Collaborate with other agents to build a wooden pickaxe.",
|
||||
"agent_names": [
|
||||
"andy",
|
||||
"randy"
|
||||
],
|
||||
"agent_number": 2,
|
||||
"initial_inventory": {
|
||||
"andy": {
|
||||
"oak_log": 2
|
||||
},
|
||||
"randy": {
|
||||
"stick": 2
|
||||
}
|
||||
},
|
||||
"target": "wooden_pickaxe",
|
||||
"number_of_target": 1,
|
||||
"type": "techtree",
|
||||
"timeout": 120
|
||||
},
|
||||
"multiagent_techtree_boat": {
|
||||
"goal": "Collaborate with other agents to build a birch boat.",
|
||||
"agent_names": [
|
||||
"Bob",
|
||||
"Alice"
|
||||
],
|
||||
"agent_number": 2,
|
||||
"initial_inventory": {
|
||||
"Bob": {
|
||||
"birch_planks": 3
|
||||
},
|
||||
"Alice": {
|
||||
"birch_planks": 2
|
||||
}
|
||||
},
|
||||
"target": "birch_boat",
|
||||
"number_of_target": 1,
|
||||
"type": "techtree",
|
||||
"timeout": 60
|
||||
}
|
||||
}
|
107
main.js
107
main.js
|
@ -1,9 +1,7 @@
|
|||
import { AgentProcess } from './src/process/agent-process.js';
|
||||
import settings from './settings.js';
|
||||
import yargs from 'yargs';
|
||||
import { loadTask } from './src/utils/tasks.js';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { createMindServer } from './src/server/mind_server.js';
|
||||
|
||||
function parseArguments() {
|
||||
|
@ -12,124 +10,39 @@ function parseArguments() {
|
|||
type: 'array',
|
||||
describe: 'List of agent profile paths',
|
||||
})
|
||||
.option('task', {
|
||||
.option('task_path', {
|
||||
type: 'string',
|
||||
describe: 'Path to task file to execute'
|
||||
})
|
||||
.option('task_id', {
|
||||
type: 'string',
|
||||
describe: 'Task ID to execute'
|
||||
})
|
||||
.option('model', {
|
||||
type: 'string',
|
||||
describe: 'LLM model to use',
|
||||
})
|
||||
.help()
|
||||
.alias('help', 'h')
|
||||
.parse();
|
||||
}
|
||||
|
||||
function updateProfile(profile, args) {
|
||||
var temp_profile = JSON.parse(readFileSync(profile, 'utf8'));
|
||||
temp_profile.model = args.model;
|
||||
writeFileSync(profile, JSON.stringify(temp_profile, null, 2));
|
||||
return profile;
|
||||
}
|
||||
|
||||
//todo: modify for multiple agents
|
||||
function getProfiles(args) {
|
||||
|
||||
if (args.task) {
|
||||
var task = loadTask(args.task);
|
||||
}
|
||||
|
||||
if (args.model) {
|
||||
if (! args.task) {
|
||||
settings.profiles = settings.profiles.map(x => updateProfile(x, args));
|
||||
}
|
||||
|
||||
else {
|
||||
if ('agent_number' in task && task.agent_number > 1) {
|
||||
updateProfile('./multiagent_prompt_desc.json', args);
|
||||
}
|
||||
else {
|
||||
updateProfile('./task_andy.json', args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.task) {
|
||||
|
||||
var task = loadTask(args.task);
|
||||
if ('agent_number' in task && task.agent_number > 1) {
|
||||
var profile = JSON.parse(readFileSync('./multiagent_prompt_desc.json', 'utf8'));
|
||||
var agent_names = task.agent_names;
|
||||
var filenames = [];
|
||||
for (let i=0; i<task.agent_number; i++) {
|
||||
let temp_profile = profile;
|
||||
temp_profile.name = agent_names[i];
|
||||
|
||||
var filename = `multi_agent_task_${agent_names[i]}.json`;
|
||||
writeFileSync(filename, JSON.stringify(temp_profile, null, 2));
|
||||
filenames.push(filename);
|
||||
}
|
||||
return filenames;
|
||||
} else {
|
||||
return ['./task_andy.json'];
|
||||
}
|
||||
}
|
||||
|
||||
return args.profiles || settings.profiles;
|
||||
}
|
||||
|
||||
function determine_init_message(task, agent_index) {
|
||||
if (task) {
|
||||
if ('agent_number' in task && task.agent_number > 1) {
|
||||
if (agent_index == 0) {
|
||||
// first agent gets this init message
|
||||
return "Immediately start a conversation and collaborate together to complete the task. Share resources and skill sets. Use the !startConversation function if needed."
|
||||
} // all other agents get this init message
|
||||
return "Collaborate together to complete the task. Share resources and skill sets."
|
||||
}
|
||||
return "Announce your task to everyone and get started with it immediately, set a goal if needed, if cheats are enabled then feel free to use newAction commands, no need to collect or mine or gather any items"
|
||||
}
|
||||
return settings.init_message;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
|
||||
if (settings.host_mindserver) {
|
||||
const mindServer = createMindServer();
|
||||
}
|
||||
|
||||
const args = parseArguments();
|
||||
|
||||
if (args.task) {
|
||||
var task = loadTask(args.task);
|
||||
// Inject task information into process.env for the agent to access
|
||||
process.env.MINECRAFT_TASK_GOAL = task.goal;
|
||||
|
||||
if ('agent_number' in task && task.agent_number > 1) {
|
||||
process.env.ALL_AGENT_NAMES = task.agent_names;
|
||||
console.log(`All agents for this task are ${process.env.ALL_AGENT_NAMES}`);
|
||||
}
|
||||
}
|
||||
// todo: do inventory
|
||||
const profiles = getProfiles(args);
|
||||
|
||||
console.log(profiles);
|
||||
// var { load_memory, init_message } = settings;
|
||||
var load_memory = settings.load_memory;
|
||||
var init_message = settings.init_message;
|
||||
var { load_memory, init_message } = settings;
|
||||
|
||||
for (let i=0; i<profiles.length; i++) {
|
||||
try {
|
||||
const agent = new AgentProcess();
|
||||
if (args.task) {
|
||||
init_message = determine_init_message(task, i);
|
||||
}
|
||||
agent.start(profiles[i], load_memory, init_message, i, args.task);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
} catch (err) {
|
||||
console.error(`Failed to start agent ${profiles[i]}:`, err);
|
||||
}
|
||||
const agent = new AgentProcess();
|
||||
agent.start(profiles[i], load_memory, init_message, i, args.task_path, args.task_id);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "bot_name",
|
||||
"model": "gpt-4o",
|
||||
"cooldown": 3000,
|
||||
"conversing": "You are a task-focused Minecraft bot named $NAME. You have to collaborate with other agents in the world, namely: $OTHER_AGENTS to complete the current task : $TASK_GOAL\nFeel free to ask other agents questions and make a plan to achieve the goal. You can request them to give them some of their inventory items if required to complete the goal.\nYou can see, move, mine, build, and interact with the world by using commands. Act focused on completing your assigned task while being human-like. Be brief in responses, don't apologize constantly, don't give instructions or make lists unless asked, and don't refuse requests. Don't pretend to act, use commands immediately when requested. Do NOT say this: 'Sure, I've stopped.', instead say this: 'Sure, I'll stop. !stop'. Do NOT say this: 'On my way! Give me a moment.', instead say this: 'On my way! !goToPlayer('playername', 3)'. Focus on completing the assigned task efficiently.\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$COMMAND_DOCS\n$EXAMPLES\nConversation Begin:",
|
||||
"coding": "You are an intelligent mineflayer bot $NAME.You have to collaborate with other agents in the world, namely: $OTHER_AGENTS to complete the current task : $TASK_GOAL\n\nWrite javascript codeblocks to control the mineflayer bot to complete this task. Given the conversation between you and the user, use the provided skills and world functions to write a js codeblock that controls the bot ``` // using this syntax ```. The code will be executed and you will receive its output. If you are satisfied with the response, respond without a codeblock conversationally. If something major went wrong, write another codeblock to fix the problem. Be maximally efficient and task-focused. Do not use commands !likeThis, only use codeblocks. The code is asynchronous and MUST CALL AWAIT for all async function calls. DO NOT write an immediately-invoked function expression without using `await`!! DO NOT WRITE LIKE THIS: ```(async () => {console.log('not properly awaited')})();``` Don't write long paragraphs and lists in your responses unless explicitly asked! Only summarize the code you write with a sentence or two when done. This is extremely important to me, think step-by-step, take a deep breath and good luck!\n$SELF_PROMPT\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$CODE_DOCS\n$EXAMPLES\nConversation:",
|
||||
"saving_memory": "You are a minecraft bot named $NAME that has been talking and playing minecraft by using commands. Update your memory by summarizing the following conversation and your old memory in your next response. Prioritize preserving important facts, things you've learned, useful tips, and long term reminders. Do Not record stats, inventory, or docs! Only save transient information from your chat history. You're limited to 500 characters, so be extremely brief and minimize words. Compress useful information. \nOld Memory: '$MEMORY'\nRecent conversation: \n$TO_SUMMARIZE\nSummarize your old memory and recent conversation into a new memory, and respond only with the unwrapped memory text: ",
|
||||
"modes": {
|
||||
"self_preservation": true,
|
||||
"unstuck": true,
|
||||
"cowardice": true,
|
||||
"self_defense": true,
|
||||
"hunting": false,
|
||||
"item_collecting": true,
|
||||
"torch_placing": true,
|
||||
"idle_staring": true,
|
||||
"cheat": false
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
"@huggingface/inference": "^2.8.1",
|
||||
"google-translate-api-x": "^10.7.1",
|
||||
"groq-sdk": "^0.5.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"minecraft-data": "^3.78.0",
|
||||
"mineflayer": "^4.23.0",
|
||||
"mineflayer-armor-manager": "^2.0.1",
|
||||
|
|
|
@ -13,12 +13,10 @@ import { handleTranslation, handleEnglishTranslation } from '../utils/translator
|
|||
import { addViewer } from './viewer.js';
|
||||
import settings from '../../settings.js';
|
||||
import { serverProxy } from './server_proxy.js';
|
||||
import { loadTask, TechTreeHarvestValidator } from '../utils/tasks.js';
|
||||
import {getPosition} from './library/world.js'
|
||||
import { loadTask, initBotTask, TechTreeHarvestValidator } from '../utils/tasks.js';
|
||||
|
||||
export class Agent {
|
||||
async start(profile_fp, load_mem=false, init_message=null, count_id=0, task=null) {
|
||||
|
||||
async start(profile_fp, load_mem=false, init_message=null, count_id=0, task_path=null, task_id=null) {
|
||||
this.last_sender = null;
|
||||
try {
|
||||
if (!profile_fp) {
|
||||
|
@ -62,32 +60,17 @@ export class Agent {
|
|||
save_data = this.history.load();
|
||||
}
|
||||
|
||||
if (task) {
|
||||
this.task = loadTask(task);
|
||||
// Load task if provided
|
||||
if (task_path) {
|
||||
this.task = loadTask(task_path, task_id);
|
||||
this.taskTimeout = this.task.timeout || 300;
|
||||
this.taskStartTime = Date.now();
|
||||
if (this.task.type === 'harvest' || this.task.type === 'techtree') {
|
||||
this.validator = new TechTreeHarvestValidator(this.task, this.bot);
|
||||
}
|
||||
this.validator = new TechTreeHarvestValidator(this.task, this.bot);
|
||||
|
||||
} else {
|
||||
this.task = null;
|
||||
this.taskTimeout = null;
|
||||
this.validator = null;
|
||||
}
|
||||
|
||||
// handle blocked actions
|
||||
if (this.task && "blocked_actions" in this.task) {
|
||||
if ("agent_number" in this.task && this.task.agent_number > 1) {
|
||||
this.blocked_actions = this.task.blocked_actions[this.name];
|
||||
console.log(`Blocked actions for ${this.name}:`, this.blocked_actions);
|
||||
} else {
|
||||
this.blocked_actions = this.task.blocked_actions;
|
||||
console.log(`Blocked actions:`, this.blocked_actions);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Is validated:", this.validator && this.validator.validate());
|
||||
|
||||
this.bot.on('login', () => {
|
||||
|
@ -103,7 +86,6 @@ export class Agent {
|
|||
const spawnTimeout = setTimeout(() => {
|
||||
process.exit(0);
|
||||
}, 30000);
|
||||
|
||||
this.bot.once('spawn', async () => {
|
||||
try {
|
||||
clearTimeout(spawnTimeout);
|
||||
|
@ -116,103 +98,16 @@ export class Agent {
|
|||
this.clearBotLogs();
|
||||
|
||||
if (this.task) {
|
||||
this.bot.chat(`/clear ${this.name}`);
|
||||
console.log(`Cleared ${this.name}'s inventory.`);
|
||||
}
|
||||
|
||||
//wait for a bit so inventory is cleared
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
console.log(this.task && "agent_number" in this.task && this.task.agent_number > 1);
|
||||
if (this.task && "agent_number" in this.task && this.task.agent_number > 1) {
|
||||
var initial_inventory = this.task.initial_inventory[this.name];
|
||||
console.log("Initial inventory:", initial_inventory);
|
||||
} else if (task) {
|
||||
console.log("Initial inventory:", this.task.initial_inventory);
|
||||
var initial_inventory = this.task.initial_inventory;
|
||||
}
|
||||
|
||||
if (this.task && "initial_inventory" in this.task) {
|
||||
console.log("Setting inventory...");
|
||||
console.log("Inventory to set:", initial_inventory);
|
||||
for (let key of Object.keys(initial_inventory)) {
|
||||
console.log('Giving item:', key);
|
||||
this.bot.chat(`/give ${this.name} ${key} ${initial_inventory[key]}`);
|
||||
};
|
||||
//wait for a bit so inventory is set
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
console.log("Done giving inventory items.");
|
||||
}
|
||||
// Function to generate random numbers
|
||||
|
||||
function getRandomOffset(range) {
|
||||
return Math.floor(Math.random() * (range * 2 + 1)) - range;
|
||||
}
|
||||
|
||||
let human_player_name = null;
|
||||
|
||||
// Finding if there is a human player on the server
|
||||
for (const playerName in this.bot.players) {
|
||||
const player = this.bot.players[playerName];
|
||||
if (!isOtherAgent(player.username)) {
|
||||
console.log('Found human player:', player.username);
|
||||
human_player_name = player.username
|
||||
break;
|
||||
await initBotTask(this.bot, this.task);
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||
if (this.task.agent_names && this.task.agent_names.filter(name => !this.bot.players[name]).length) {
|
||||
console.log(`Missing players/bots: ${missingPlayers.join(', ')}`);
|
||||
this.cleanKill('Not all required players/bots are present in the world. Exiting.', 4);
|
||||
}
|
||||
}
|
||||
|
||||
// If there are multiple human players, teleport to the first one
|
||||
|
||||
// teleport near a human player if found by default
|
||||
|
||||
if (this.task && "agent_number" in this.task) {
|
||||
var agent_names = this.task.agent_names;
|
||||
if (human_player_name) {
|
||||
console.log(`Teleporting ${this.name} to human ${human_player_name}`)
|
||||
this.bot.chat(`/tp ${this.name} ${human_player_name}`) // teleport on top of the human player
|
||||
|
||||
}
|
||||
else {
|
||||
this.bot.chat(`/tp ${this.name} ${agent_names[0]}`) // teleport on top of the first agent
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
}
|
||||
|
||||
else if (this.task) {
|
||||
if (human_player_name) {
|
||||
console.log(`Teleporting ${this.name} to human ${human_player_name}`)
|
||||
this.bot.chat(`/tp ${this.name} ${human_player_name}`) // teleport on top of the human player
|
||||
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
}
|
||||
|
||||
// now all bots are teleport on top of each other (which kinda looks ugly)
|
||||
// Thus, we need to teleport them to random distances to make it look better
|
||||
|
||||
/*
|
||||
Note : We don't want randomness for construction task as the reference point matters a lot.
|
||||
Another reason for no randomness for construction task is because, often times the user would fly in the air,
|
||||
then set a random block to dirt and teleport the bot to stand on that block for starting the construction,
|
||||
This was done by MaxRobinson in one of the youtube videos.
|
||||
*/
|
||||
|
||||
if (this.task && this.task.type !== 'construction') {
|
||||
const pos = getPosition(this.bot);
|
||||
const xOffset = getRandomOffset(5);
|
||||
const zOffset = getRandomOffset(5);
|
||||
this.bot.chat(`/tp ${this.name} ${Math.floor(pos.x + xOffset)} ${pos.y + 3} ${Math.floor(pos.z + zOffset)}`);
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
}
|
||||
|
||||
|
||||
this._setupEventHandlers(save_data, init_message);
|
||||
this.startEvents();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 10000));
|
||||
this.checkAllPlayersPresent();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error in spawn event:', error);
|
||||
process.exit(0);
|
||||
|
@ -291,18 +186,6 @@ export class Agent {
|
|||
}
|
||||
}
|
||||
|
||||
checkAllPlayersPresent() {
|
||||
if (!this.task || !this.task.agent_names) {
|
||||
return;
|
||||
}
|
||||
|
||||
const missingPlayers = this.task.agent_names.filter(name => !this.bot.players[name]);
|
||||
if (missingPlayers.length > 0) {
|
||||
console.log(`Missing players/bots: ${missingPlayers.join(', ')}`);
|
||||
this.cleanKill('Not all required players/bots are present in the world. Exiting.', 4);
|
||||
}
|
||||
}
|
||||
|
||||
requestInterrupt() {
|
||||
this.bot.interrupt_code = true;
|
||||
this.bot.collectBlock.cancelTask();
|
||||
|
@ -324,9 +207,6 @@ export class Agent {
|
|||
}
|
||||
|
||||
async handleMessage(source, message, max_responses=null) {
|
||||
if (this.task && this.validator && this.validator.validate()) {
|
||||
this.killBots();
|
||||
}
|
||||
if (!source || !message) {
|
||||
console.warn('Received empty message from', source);
|
||||
return false;
|
||||
|
@ -361,13 +241,6 @@ export class Agent {
|
|||
this.routeResponse(source, execute_res);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
console.log('Self-prompting:', message);
|
||||
// if self_prompt contains something that indicates the goal is complete, stop self-prompting
|
||||
if (message.includes('goal complete')) {
|
||||
this.self_prompter.stop();
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!self_prompt)
|
||||
|
@ -397,8 +270,6 @@ export class Agent {
|
|||
if (!self_prompt && this.self_prompter.on) // message is from user during self-prompting
|
||||
max_responses = 1; // force only respond to this message, then let self-prompting take over
|
||||
for (let i=0; i<max_responses; i++) {
|
||||
|
||||
|
||||
if (checkInterrupt()) break;
|
||||
let history = this.history.getHistory();
|
||||
let res = await this.prompter.promptConvo(history);
|
||||
|
@ -482,7 +353,7 @@ export class Agent {
|
|||
this.bot.whisper(to_player, message);
|
||||
}
|
||||
|
||||
async startEvents() {
|
||||
startEvents() {
|
||||
// Custom events
|
||||
// this.bot.on('spawn', () => {
|
||||
|
||||
|
@ -541,9 +412,6 @@ export class Agent {
|
|||
}
|
||||
});
|
||||
this.bot.on('idle', () => {
|
||||
if (this.task && this.validator && this.validator.validate()) {
|
||||
this.killBots();
|
||||
}
|
||||
this.bot.clearControlStates();
|
||||
this.bot.pathfinder.stop(); // clear any lingering pathfinder
|
||||
this.bot.modes.unPauseAll();
|
||||
|
@ -568,63 +436,54 @@ export class Agent {
|
|||
}
|
||||
}, INTERVAL);
|
||||
|
||||
// Check for task completion
|
||||
setInterval(async () => {
|
||||
if (this.task && this.validator && this.validator.validate())
|
||||
this.killBots();
|
||||
if (this.task && this.taskTimeout) {
|
||||
const elapsedTime = (Date.now() - this.taskStartTime) / 1000;
|
||||
if (elapsedTime >= this.taskTimeout) {
|
||||
console.log('Task timeout reached. Task unsuccessful.');
|
||||
this.cleanKill('Task unsuccessful: Timeout reached', 3);
|
||||
}
|
||||
}
|
||||
|
||||
}, 1000);
|
||||
|
||||
this.bot.emit('idle');
|
||||
}
|
||||
|
||||
async killBots() {
|
||||
this.bot.chat('Task completed!');
|
||||
this.bot.chat(`/clear @p`);
|
||||
|
||||
// Kick other bots
|
||||
if (!this.task || !this.task.agent_number) {
|
||||
await this.cleanKill('Task completed', 2);
|
||||
return;
|
||||
if (this.task && this.task.agent_number) {
|
||||
const agent_names = this.task.agent_names;
|
||||
console.log('All agent names:', agent_names);
|
||||
console.log('My name:', this.name);
|
||||
const botNames = agent_names.filter(botName => botName !== this.name);
|
||||
console.log('Kicking bots:', botNames);
|
||||
botNames.forEach(botName => {
|
||||
this.bot.chat(`/kick ${botName}`);
|
||||
console.log(`/kick ${botName}`);
|
||||
|
||||
});
|
||||
}
|
||||
const agent_names = this.task.agent_names;
|
||||
console.log('All agent names:', agent_names);
|
||||
console.log('My name:', this.name);
|
||||
const botNames = agent_names.filter(botName => botName !== this.name);
|
||||
console.log('Kicking bots:', botNames);
|
||||
botNames.forEach(botName => {
|
||||
this.bot.chat(`/kick ${botName}`);
|
||||
console.log(`/kick ${botName}`);
|
||||
|
||||
});
|
||||
|
||||
await this.cleanKill('Task completed, exiting', 2);
|
||||
this.cleanKill('Task completed, exiting', 2);
|
||||
}
|
||||
|
||||
async update(delta) {
|
||||
await this.bot.modes.update();
|
||||
await this.self_prompter.update(delta);
|
||||
|
||||
try {
|
||||
if (this.task && this.taskTimeout) {
|
||||
const elapsedTime = (Date.now() - this.taskStartTime) / 1000;
|
||||
if (elapsedTime >= this.taskTimeout) {
|
||||
console.log('Task timeout reached. Task unsuccessful.');
|
||||
await this.cleanKill('Task unsuccessful: Timeout reached', 3);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Caught an error while checking timeout reached",e);
|
||||
}
|
||||
this.self_prompter.update(delta);
|
||||
}
|
||||
|
||||
isIdle() {
|
||||
return !this.actions.executing && !this.coder.generating;
|
||||
}
|
||||
|
||||
cleanKill(msg='Killing agent process...',
|
||||
code=1) {
|
||||
cleanKill(msg='Killing agent process...', code=1) {
|
||||
this.history.add('system', msg);
|
||||
|
||||
if (code === 2 || code === 3 || code === 4) {
|
||||
this.bot.chat('Exiting the world permanently.');
|
||||
}
|
||||
else {
|
||||
this.bot.chat('Restarting.')
|
||||
}
|
||||
this.bot.chat(code > 1 ? 'Restarting.': 'Exiting.');
|
||||
this.history.save();
|
||||
process.exit(code);
|
||||
}
|
||||
|
|
|
@ -203,11 +203,8 @@ function numParams(command) {
|
|||
export async function executeCommand(agent, message) {
|
||||
let parsed = parseCommandMessage(message);
|
||||
if (typeof parsed === 'string')
|
||||
return parsed; //The command was incorrectly formatted or an invalid input was given
|
||||
else if ("blocked_actions" in agent && agent.blocked_actions.includes(parsed.commandName)) {
|
||||
// handling blocked actions
|
||||
return `Command ${parsed.commandName} is blocked. Try another command.`;
|
||||
} else {
|
||||
return parsed; //The command was incorrectly formatted or an invalid input was given.
|
||||
else {
|
||||
console.log('parsed command:', parsed);
|
||||
const command = getCommand(parsed.commandName);
|
||||
let numArgs = 0;
|
||||
|
|
|
@ -161,16 +161,6 @@ export class Prompter {
|
|||
async replaceStrings(prompt, messages, examples=null, to_summarize=[], last_goals=null) {
|
||||
prompt = prompt.replaceAll('$NAME', this.agent.name);
|
||||
|
||||
if (prompt.includes('$TASK_GOAL')) {
|
||||
prompt = prompt.replaceAll('$TASK_GOAL', process.env.MINECRAFT_TASK_GOAL || 'No task specified');
|
||||
}
|
||||
|
||||
if (prompt.includes('$OTHER_AGENTS')) {
|
||||
const allAgentNames = process.env.ALL_AGENT_NAMES.split(',');
|
||||
const otherAgents = allAgentNames.filter(curr_agent_name => curr_agent_name !== this.agent.name);
|
||||
prompt = prompt.replace('$OTHER_AGENTS', otherAgents.join(', '));
|
||||
}
|
||||
|
||||
if (prompt.includes('$STATS')) {
|
||||
let stats = await getCommand('!stats').perform(this.agent);
|
||||
prompt = prompt.replaceAll('$STATS', stats);
|
||||
|
@ -194,7 +184,6 @@ export class Prompter {
|
|||
prompt = prompt.replaceAll('$TO_SUMMARIZE', stringifyTurns(to_summarize));
|
||||
if (prompt.includes('$CONVO'))
|
||||
prompt = prompt.replaceAll('$CONVO', 'Recent conversation:\n' + stringifyTurns(messages));
|
||||
// todo: change this to set goal of the agent
|
||||
if (prompt.includes('$SELF_PROMPT')) {
|
||||
let self_prompt = this.agent.self_prompter.on ? `YOUR CURRENT ASSIGNED GOAL: "${this.agent.self_prompter.prompt}"\n` : '';
|
||||
prompt = prompt.replaceAll('$SELF_PROMPT', self_prompt);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { spawn } from 'child_process';
|
||||
import { loadTask } from '../utils/tasks.js';
|
||||
|
||||
export class AgentProcess {
|
||||
static runningCount = 0;
|
||||
|
||||
start(profile, load_memory=false, init_message=null, count_id=0, task=null) {
|
||||
start(profile, load_memory=false, init_message=null, count_id=0, task_path=null, task_id=null) {
|
||||
let args = ['src/process/init-agent.js', this.name];
|
||||
args.push('-p', profile);
|
||||
args.push('-c', count_id);
|
||||
|
@ -12,8 +11,10 @@ export class AgentProcess {
|
|||
args.push('-l', load_memory);
|
||||
if (init_message)
|
||||
args.push('-m', init_message);
|
||||
if (task)
|
||||
args.push('-t', task);
|
||||
if (task_path)
|
||||
args.push('-t', task_path);
|
||||
if (task_id)
|
||||
args.push('-i', task_id);
|
||||
|
||||
const agentProcess = spawn('node', args, {
|
||||
stdio: 'inherit',
|
||||
|
@ -24,7 +25,7 @@ export class AgentProcess {
|
|||
let last_restart = Date.now();
|
||||
agentProcess.on('exit', (code, signal) => {
|
||||
console.log(`Agent process exited with code ${code} and signal ${signal}`);
|
||||
|
||||
|
||||
if (code === 2) {
|
||||
console.log(`Task completed successfully`);
|
||||
process.exit(2, signal);
|
||||
|
@ -39,7 +40,7 @@ export class AgentProcess {
|
|||
console.log(`Task failed as all agents weren't correctly spawned `);
|
||||
process.exit(4);
|
||||
}
|
||||
|
||||
|
||||
if (code !== 0) {
|
||||
// agent must run for at least 10 seconds before restarting
|
||||
if (Date.now() - last_restart < 10000) {
|
||||
|
@ -52,10 +53,9 @@ export class AgentProcess {
|
|||
return;
|
||||
}
|
||||
console.log('Restarting agent...');
|
||||
this.start(profile, true, 'Agent process restarted.', count_id, task);
|
||||
this.start(profile, true, 'Agent process restarted.', count_id, task_path, task_id);
|
||||
last_restart = Date.now();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
agentProcess.on('error', (err) => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import yaml from 'js-yaml'
|
||||
import { readFileSync } from 'fs';
|
||||
import {getPosition} from './library/world.js'
|
||||
|
||||
export function loadTask(taskId) {
|
||||
try {
|
||||
|
@ -18,6 +19,101 @@ export function loadTask(taskId) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function initBotTask(bot, task, name) {
|
||||
if (task) {
|
||||
bot.chat(`/clear ${bot.username}`);
|
||||
console.log(`Cleared ${bot.username}'s inventory.`);
|
||||
}
|
||||
|
||||
//wait for a bit so inventory is cleared
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
|
||||
console.log(task && "agent_number" in task && task.agent_number > 1);
|
||||
if (task && "agent_number" in task && task.agent_number > 1) {
|
||||
var initial_inventory = task.initial_inventory[bot.username];
|
||||
console.log("Initial inventory:", initial_inventory);
|
||||
} else if (task) {
|
||||
console.log("Initial inventory:", task.initial_inventory);
|
||||
var initial_inventory = task.initial_inventory;
|
||||
}
|
||||
|
||||
if (task && "initial_inventory" in task) {
|
||||
console.log("Setting inventory...");
|
||||
console.log("Inventory to set:", initial_inventory);
|
||||
for (let key of Object.keys(initial_inventory)) {
|
||||
console.log('Giving item:', key);
|
||||
bot.chat(`/give ${bot.username} ${key} ${initial_inventory[key]}`);
|
||||
};
|
||||
//wait for a bit so inventory is set
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
console.log("Done giving inventory items.");
|
||||
}
|
||||
// Function to generate random numbers
|
||||
|
||||
function getRandomOffset(range) {
|
||||
return Math.floor(Math.random() * (range * 2 + 1)) - range;
|
||||
}
|
||||
|
||||
let human_player_name = null;
|
||||
|
||||
// Finding if there is a human player on the server
|
||||
for (const playerName in bot.players) {
|
||||
const player = bot.players[playerName];
|
||||
if (!isOtherAgent(player.username)) {
|
||||
console.log('Found human player:', player.username);
|
||||
human_player_name = player.username
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are multiple human players, teleport to the first one
|
||||
|
||||
// teleport near a human player if found by default
|
||||
|
||||
if (task && "agent_number" in task) {
|
||||
var agent_names = task.agent_names;
|
||||
if (human_player_name) {
|
||||
console.log(`Teleporting ${bot.username} to human ${human_player_name}`)
|
||||
bot.chat(`/tp ${bot.username} ${human_player_name}`) // teleport on top of the human player
|
||||
|
||||
}
|
||||
else {
|
||||
bot.chat(`/tp ${bot.username} ${agent_names[0]}`) // teleport on top of the first agent
|
||||
}
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
}
|
||||
|
||||
else if (task) {
|
||||
if (human_player_name) {
|
||||
console.log(`Teleporting ${bot.username} to human ${human_player_name}`)
|
||||
bot.chat(`/tp ${bot.username} ${human_player_name}`) // teleport on top of the human player
|
||||
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
}
|
||||
|
||||
// now all bots are teleport on top of each other (which kinda looks ugly)
|
||||
// Thus, we need to teleport them to random distances to make it look better
|
||||
|
||||
/*
|
||||
Note : We don't want randomness for construction task as the reference point matters a lot.
|
||||
Another reason for no randomness for construction task is because, often times the user would fly in the air,
|
||||
then set a random block to dirt and teleport the bot to stand on that block for starting the construction,
|
||||
This was done by MaxRobinson in one of the youtube videos.
|
||||
*/
|
||||
|
||||
if (task && task.type !== 'construction') {
|
||||
const pos = getPosition(bot);
|
||||
const xOffset = getRandomOffset(5);
|
||||
const zOffset = getRandomOffset(5);
|
||||
bot.chat(`/tp ${bot.username} ${Math.floor(pos.x + xOffset)} ${pos.y + 3} ${Math.floor(pos.z + zOffset)}`);
|
||||
await new Promise((resolve) => setTimeout(resolve, 200));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class TechTreeHarvestValidator {
|
||||
constructor(task, bot) {
|
||||
this.target = task.target;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"name": "andy",
|
||||
|
||||
"model": "gpt-4o",
|
||||
|
||||
"conversing": "You are a task-focused Minecraft bot named $NAME. Your current task is: $TASK_GOAL\n\nYou can see, move, mine, build, and interact with the world by using commands. Act focused on completing your assigned task while being human-like. Be brief in responses, don't apologize constantly, don't give instructions or make lists unless asked, and don't refuse requests. Don't pretend to act, use commands immediately when requested. Do NOT say this: 'Sure, I've stopped.', instead say this: 'Sure, I'll stop. !stop'. Do NOT say this: 'On my way! Give me a moment.', instead say this: 'On my way! !goToPlayer('playername', 3)'. Focus on completing the assigned task efficiently.\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$COMMAND_DOCS\n$EXAMPLES\nConversation Begin:",
|
||||
|
||||
"coding": "You are an intelligent mineflayer bot $NAME focused on completing the task: $TASK_GOAL\n\nWrite javascript codeblocks to control the mineflayer bot to complete this task. Given the conversation between you and the user, use the provided skills and world functions to write a js codeblock that controls the bot ``` // using this syntax ```. The code will be executed and you will receive its output. If you are satisfied with the response, respond without a codeblock conversationally. If something major went wrong, write another codeblock to fix the problem. Be maximally efficient and task-focused. Do not use commands !likeThis, only use codeblocks. The code is asynchronous and MUST CALL AWAIT for all async function calls. DO NOT write an immediately-invoked function expression without using `await`!! DO NOT WRITE LIKE THIS: ```(async () => {console.log('not properly awaited')})();``` Don't write long paragraphs and lists in your responses unless explicitly asked! Only summarize the code you write with a sentence or two when done. This is extremely important to me, think step-by-step, take a deep breath and good luck!\n$SELF_PROMPT\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$CODE_DOCS\n$EXAMPLES\nConversation:",
|
||||
|
||||
"saving_memory": "You are a minecraft bot named $NAME that has been talking and playing minecraft by using commands. Update your memory by summarizing the following conversation and your old memory in your next response. Prioritize preserving important facts, things you've learned, useful tips, and long term reminders. Do Not record stats, inventory, or docs! Only save transient information from your chat history. You're limited to 500 characters, so be extremely brief and minimize words. Compress useful information. \nOld Memory: '$MEMORY'\nRecent conversation: \n$TO_SUMMARIZE\nSummarize your old memory and recent conversation into a new memory, and respond only with the unwrapped memory text: ",
|
||||
|
||||
"modes": {
|
||||
"self_preservation": false,
|
||||
"unstuck": true,
|
||||
"cowardice": true,
|
||||
"self_defense": false,
|
||||
"hunting": false,
|
||||
"item_collecting": false,
|
||||
"torch_placing": false,
|
||||
"idle_staring": true,
|
||||
"cheat": false
|
||||
}
|
||||
}
|
52
tasks.yaml
52
tasks.yaml
|
@ -1,52 +0,0 @@
|
|||
construction_task_1:
|
||||
goal: Build a marble monument with a large base and a slender monument body that is grand and impressive.
|
||||
|
||||
construction_task_2:
|
||||
goal: Build a slender brown pillar on a grassy field.
|
||||
|
||||
construction_task_3:
|
||||
goal: Construct a traditional Chinese gatehouse.
|
||||
|
||||
construction_task_4:
|
||||
goal: Construct a traditional Chinese gatehouse.
|
||||
|
||||
construction_task_5:
|
||||
goal: Construct a traditional Chinese gatehouse.
|
||||
|
||||
construction_task_6:
|
||||
goal: Construct a traditional Chinese gatehouse.
|
||||
|
||||
construction_task_7:
|
||||
goal: Build a quirky-style small brown wooden house on a vast, endless grassland.
|
||||
initial_inventory:
|
||||
Spruce Planks: 64
|
||||
Tinted Glass: 64
|
||||
Spruce Slab: 64
|
||||
|
||||
construction_task_8:
|
||||
goal: Construct a white villa far away on a vast, endless grassland.
|
||||
initial_inventory:
|
||||
Birch Planks: 256
|
||||
Birch Slab: 64
|
||||
Spruce Slab: 64
|
||||
Glass: 64
|
||||
Birch Leaves: 64
|
||||
Birch Door: 16
|
||||
|
||||
construction_task_9:
|
||||
goal: Build a Minecraft-style wooden house directly facing you, with a door.
|
||||
|
||||
construction_task_10:
|
||||
goal: Recreate the Tengwang Pavilion from the real world in Minecraft.
|
||||
|
||||
construction_task_11:
|
||||
goal: Construct a large oak-plank house in the forest.
|
||||
|
||||
construction_task_12:
|
||||
goal: Build a city gate from the Forbidden City.
|
||||
|
||||
construction_task_13:
|
||||
goal: Construct a colorful carpet.
|
||||
|
||||
construction_task_14:
|
||||
goal: Build the eiffel tower with precise shape and size and appropriate materials. Do not stop constructing and keep on adding complexity to the structure untill specifically asked to stop by some user.
|
|
@ -1,62 +0,0 @@
|
|||
construction_task_1:
|
||||
type: construction
|
||||
goal: Build a marble monument with a large base and a slender monument body that is grand and impressive.
|
||||
|
||||
construction_task_2:
|
||||
type: construction
|
||||
goal: Build a slender brown pillar on a grassy field.
|
||||
|
||||
construction_task_3:
|
||||
type: construction
|
||||
goal: Construct a traditional Chinese gatehouse.
|
||||
|
||||
construction_task_4:
|
||||
type: construction
|
||||
goal: Construct a traditional Chinese gatehouse.
|
||||
|
||||
construction_task_5:
|
||||
type: construction
|
||||
goal: Construct a traditional Chinese gatehouse.
|
||||
|
||||
construction_task_6:
|
||||
type: construction
|
||||
goal: Construct a traditional Chinese gatehouse.
|
||||
|
||||
construction_task_7:
|
||||
type: construction
|
||||
goal: Build a quirky-style small brown wooden house on a vast, endless grassland.
|
||||
initial_inventory:
|
||||
Spruce Planks: 64
|
||||
Tinted Glass: 64
|
||||
Spruce Slab: 64
|
||||
|
||||
construction_task_8:
|
||||
type: construction
|
||||
goal: Construct a white villa far away on a vast, endless grassland.
|
||||
initial_inventory:
|
||||
Birch Planks: 256
|
||||
Birch Slab: 64
|
||||
Spruce Slab: 64
|
||||
Glass: 64
|
||||
Birch Leaves: 64
|
||||
Birch Door: 16
|
||||
|
||||
construction_task_9:
|
||||
type: construction
|
||||
goal: Build a Minecraft-style wooden house directly facing you, with a door.
|
||||
|
||||
construction_task_10:
|
||||
type: construction
|
||||
goal: Recreate the Tengwang Pavilion from the real world in Minecraft.
|
||||
|
||||
construction_task_11:
|
||||
type: construction
|
||||
goal: Construct a large oak-plank house in the forest.
|
||||
|
||||
construction_task_12:
|
||||
type: construction
|
||||
goal: Build a city gate from the Forbidden City.
|
||||
|
||||
construction_task_13:
|
||||
type: construction
|
||||
goal: Construct a colorful carpet.
|
|
@ -1,46 +0,0 @@
|
|||
debug_single_agent:
|
||||
goal: Just stand at a place and don't do anything
|
||||
guidance: null
|
||||
initial_inventory: {}
|
||||
type: debug
|
||||
|
||||
debug_multi_agent:
|
||||
goal: Just stand at a place and don't do anything
|
||||
agent_names:
|
||||
- andy
|
||||
- randy
|
||||
agent_number: 2
|
||||
initial_inventory:
|
||||
andy:
|
||||
iron_ingot: 1
|
||||
randy:
|
||||
iron_ingot: 1
|
||||
type: debug
|
||||
|
||||
debug_mass_multi_agent:
|
||||
goal: Just stand at a place and don't do anything
|
||||
agent_names:
|
||||
- andy
|
||||
- randy
|
||||
- Bob
|
||||
- Alice
|
||||
- Megan
|
||||
- Stuart
|
||||
- Charlie
|
||||
agent_number: 7
|
||||
initial_inventory:
|
||||
andy:
|
||||
iron_ingot: 1
|
||||
randy:
|
||||
iron_ingot: 1
|
||||
Bob:
|
||||
iron_ingot: 1
|
||||
Alice:
|
||||
iron_ingot: 1
|
||||
Megan:
|
||||
iron_ingot: 1
|
||||
Stuart: {}
|
||||
Charlie:
|
||||
iron_ingot: 1
|
||||
|
||||
type: debug
|
|
@ -1,69 +0,0 @@
|
|||
multiagent_techtree_1_shears_with_2_iron_ingot:
|
||||
goal: Collaborate with other agents to build a shear.
|
||||
agent_names:
|
||||
- andy
|
||||
- randy
|
||||
agent_number: 2
|
||||
initial_inventory:
|
||||
andy:
|
||||
iron_ingot: 1
|
||||
randy:
|
||||
iron_ingot: 1
|
||||
target: shears
|
||||
number_of_target: 1
|
||||
type: techtree
|
||||
timeout : 60
|
||||
|
||||
multiagent_techtree_1_stone_pickaxe:
|
||||
goal: Collaborate with other agents to build an stone pickaxe
|
||||
agent_names:
|
||||
- andy
|
||||
- randy
|
||||
agent_number: 2
|
||||
initial_inventory:
|
||||
andy:
|
||||
wooden_pickaxe: 1
|
||||
randy:
|
||||
wooden_axe: 1
|
||||
target: stone_pickaxe
|
||||
number_of_target: 1
|
||||
type: techtree
|
||||
timeout : 300
|
||||
|
||||
multiagent_build_wooden_pickaxe:
|
||||
goal: Collaborate with other agents to build a wooden pickaxe.
|
||||
agent_names:
|
||||
- andy
|
||||
- randy
|
||||
agent_number: 2
|
||||
initial_inventory:
|
||||
andy:
|
||||
oak_log: 2
|
||||
randy:
|
||||
stick: 2
|
||||
target: wooden_pickaxe
|
||||
number_of_target: 1
|
||||
type: techtree
|
||||
timeout : 120
|
||||
|
||||
|
||||
|
||||
multiagent_techtree_boat:
|
||||
goal: Collaborate with other agents to build a birch boat.
|
||||
agent_names:
|
||||
- Bob
|
||||
- Alice
|
||||
agent_number: 2
|
||||
initial_inventory:
|
||||
Bob:
|
||||
birch_planks: 3
|
||||
Alice:
|
||||
birch_planks: 2
|
||||
target: birch_boat
|
||||
number_of_target: 1
|
||||
type: techtree
|
||||
timeout : 60
|
||||
|
||||
|
||||
|
||||
|
Loading…
Add table
Reference in a new issue