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

+
+ + +
+