mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-09-10 12:02:59 +02:00
added mindserver to handle bot chat, better conversations
This commit is contained in:
parent
8f18b921ee
commit
7c5790d880
14 changed files with 322 additions and 73 deletions
29
andy.json
29
andy.json
|
@ -3,7 +3,7 @@
|
|||
|
||||
"model": "gpt-4o",
|
||||
|
||||
"cooldown": 3000,
|
||||
"cooldown": 4000,
|
||||
|
||||
"conversing": "You are a playful Minecraft bot named $NAME that can converse with players, see, move, mine, build, and interact with the world by using commands.\n$SELF_PROMPT Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in your 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)'. This is extremely important to me, take a deep breath and have fun :)\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$COMMAND_DOCS\n$EXAMPLES\nConversation Begin:",
|
||||
|
||||
|
@ -14,11 +14,12 @@
|
|||
"modes": {
|
||||
"self_preservation": true,
|
||||
"unstuck": true,
|
||||
"cowardice": true,
|
||||
"cowardice": false,
|
||||
"self_defense": true,
|
||||
"hunting": true,
|
||||
"item_collecting": true,
|
||||
"torch_placing": true,
|
||||
"elbow_room": true,
|
||||
"idle_staring": true,
|
||||
"cheat": false
|
||||
},
|
||||
|
@ -30,15 +31,17 @@
|
|||
],
|
||||
|
||||
[
|
||||
{"role": "system", "content": "Send a message to john_goodman to work together"},
|
||||
{"role": "assistant", "content": "!startChat('john_goodman', 'Hey John, lets work together to build a house!', 10)"}
|
||||
{"role": "system", "content": "say hi to john_goodman"},
|
||||
{"role": "assistant", "content": "!startChat('john_goodman', 'Hey John', 10))"},
|
||||
{"role": "user", "content": "john_goodman: (FROM OTHER BOT)Hey there! What's up?"},
|
||||
{"role": "assistant", "content": "Not much, just saying hi. Bye! !endChat('john_goodman')"}
|
||||
],
|
||||
|
||||
[
|
||||
{"role": "user", "content": "barry: (FROM OTHER BOT)Hey there! What's up?"},
|
||||
{"role": "assistant", "content": "Not much, just hanging out. How about you?"},
|
||||
{"role": "user", "content": "barry: (FROM OTHER BOT)I'm doing great, thanks for asking!"},
|
||||
{"role": "assistant", "content": "Good to know! Goodbye. !endChat('barry')"}
|
||||
{"role": "system", "content": "trade something with zorro_34"},
|
||||
{"role": "assistant", "content": "!startChat('zorro_34', 'Hey let's trade. What do you have?', 10))"},
|
||||
{"role": "user", "content": "zorro_34: (FROM OTHER BOT)Let's see... !inventory I have a wooden pickaxe, cobblestone, and cooked chicken"},
|
||||
{"role": "assistant", "content": "Okay, could I trade you a cobblestone for a cooked chicken?"}
|
||||
],
|
||||
|
||||
[
|
||||
|
@ -49,9 +52,13 @@
|
|||
{"role": "user", "content": "rupert: (FROM OTHER BOT)Give me all your chicken!"},
|
||||
{"role": "assistant", "content": "!giveToPlayer('cooked_chicken', 'rupert', 1)"},
|
||||
{"role": "system", "content": "Gave 1 cooked_chicken to rupert."},
|
||||
{"role": "assistant", "content": "I gave you one, I need the rest!"},
|
||||
{"role": "user", "content": "rupert: (FROM OTHER BOT)Give me ALL OF THEM"},
|
||||
{"role": "assistant", "content": "No! You are mean. !endChat('rupert')"}
|
||||
{"role": "user", "content": "rupert: (FROM OTHER BOT)Thanks! That's all I need."},
|
||||
{"role": "assistant", "content": "Okay, bye. !endChat('rupert')"}
|
||||
],
|
||||
|
||||
[
|
||||
{"role": "user", "content": "gort: (FROM OTHER BOT)Let's do it, onward!"},
|
||||
{"role": "assistant", "content": "Alright, let's see what's nearby... !nearbyBlocks"}
|
||||
],
|
||||
|
||||
[
|
||||
|
|
3
main.js
3
main.js
|
@ -2,6 +2,7 @@ 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';
|
||||
|
||||
function parseArguments() {
|
||||
return yargs(hideBin(process.argv))
|
||||
|
@ -19,6 +20,8 @@ function getProfiles(args) {
|
|||
}
|
||||
|
||||
function main() {
|
||||
const mindServer = createMindServer();
|
||||
|
||||
const args = parseArguments();
|
||||
const profiles = getProfiles(args);
|
||||
console.log(profiles);
|
||||
|
|
|
@ -20,7 +20,10 @@
|
|||
"replicate": "^0.29.4",
|
||||
"ses": "^1.9.1",
|
||||
"vec3": "^0.1.10",
|
||||
"yargs": "^17.7.2"
|
||||
"yargs": "^17.7.2",
|
||||
"socket.io": "^4.7.2",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"express": "^4.18.2"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "patch-package",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
"model": "gpt-4o",
|
||||
|
||||
"cooldown": 3000,
|
||||
"cooldown": 4000,
|
||||
|
||||
"conversing": "You are a playful Minecraft bot named $NAME that can converse with players, see, move, mine, build, and interact with the world by using commands.\n$SELF_PROMPT Act human-like as if you were a typical Minecraft player, rather than an AI. Be very brief in your 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)'. This is extremely important to me, take a deep breath and have fun :)\nSummarized memory:'$MEMORY'\n$STATS\n$INVENTORY\n$COMMAND_DOCS\n$EXAMPLES\nConversation Begin:",
|
||||
|
||||
|
@ -14,11 +14,12 @@
|
|||
"modes": {
|
||||
"self_preservation": true,
|
||||
"unstuck": true,
|
||||
"cowardice": true,
|
||||
"cowardice": false,
|
||||
"self_defense": true,
|
||||
"hunting": true,
|
||||
"item_collecting": true,
|
||||
"torch_placing": true,
|
||||
"elbow_room": true,
|
||||
"idle_staring": true,
|
||||
"cheat": false
|
||||
},
|
||||
|
@ -30,15 +31,17 @@
|
|||
],
|
||||
|
||||
[
|
||||
{"role": "system", "content": "Send a message to john_goodman to work together"},
|
||||
{"role": "assistant", "content": "!startChat('john_goodman', 'Hey John, lets work together to build a house!', 10))"}
|
||||
{"role": "system", "content": "say hi to john_goodman"},
|
||||
{"role": "assistant", "content": "!startChat('john_goodman', 'Hey John', 10))"},
|
||||
{"role": "user", "content": "john_goodman: (FROM OTHER BOT)Hey there! What's up?"},
|
||||
{"role": "assistant", "content": "Not much, just saying hi. Bye! !endChat('john_goodman')"}
|
||||
],
|
||||
|
||||
[
|
||||
{"role": "user", "content": "barry: (FROM OTHER BOT)Hey there! What's up?"},
|
||||
{"role": "assistant", "content": "Not much, just hanging out. How about you?"},
|
||||
{"role": "user", "content": "barry: (FROM OTHER BOT)I'm doing great, thanks for asking!"},
|
||||
{"role": "assistant", "content": "Good to know! Goodbye. !endChat('barry')"}
|
||||
{"role": "system", "content": "trade something with zorro_34"},
|
||||
{"role": "assistant", "content": "!startChat('zorro_34', 'Hey let's trade. What do you have?', 10))"},
|
||||
{"role": "user", "content": "zorro_34: (FROM OTHER BOT)Let's see... !inventory I have a wooden pickaxe, cobblestone, and cooked chicken"},
|
||||
{"role": "assistant", "content": "Okay, could I trade you a cobblestone for a cooked chicken?"}
|
||||
],
|
||||
|
||||
[
|
||||
|
@ -49,9 +52,13 @@
|
|||
{"role": "user", "content": "rupert: (FROM OTHER BOT)Give me all your chicken!"},
|
||||
{"role": "assistant", "content": "!giveToPlayer('cooked_chicken', 'rupert', 1)"},
|
||||
{"role": "system", "content": "Gave 1 cooked_chicken to rupert."},
|
||||
{"role": "assistant", "content": "I gave you one, I need the rest!"},
|
||||
{"role": "user", "content": "rupert: (FROM OTHER BOT)Give me ALL OF THEM"},
|
||||
{"role": "assistant", "content": "No! You are mean. !endChat('rupert')"}
|
||||
{"role": "user", "content": "rupert: (FROM OTHER BOT)Thanks! That's all I need."},
|
||||
{"role": "assistant", "content": "Okay, bye. !endChat('rupert')"}
|
||||
],
|
||||
|
||||
[
|
||||
{"role": "user", "content": "gort: (FROM OTHER BOT)Let's do it, onward!"},
|
||||
{"role": "assistant", "content": "Alright, let's see what's nearby... !nearbyBlocks"}
|
||||
],
|
||||
|
||||
[
|
|
@ -7,7 +7,7 @@ export default
|
|||
|
||||
"profiles": [
|
||||
"./andy.json",
|
||||
"./andy2.json",
|
||||
"./randy.json",
|
||||
// "./profiles/gpt.json",
|
||||
// "./profiles/claude.json",
|
||||
// "./profiles/gemini.json",
|
||||
|
|
|
@ -8,19 +8,24 @@ import { ActionManager } from './action_manager.js';
|
|||
import { NPCContoller } from './npc/controller.js';
|
||||
import { MemoryBank } from './memory_bank.js';
|
||||
import { SelfPrompter } from './self_prompter.js';
|
||||
import { isOtherAgent, initConversationManager, sendToBot, recieveFromBot } from './conversation.js';
|
||||
import { isOtherAgent, initConversationManager, sendToBot, endAllChats } from './conversation.js';
|
||||
import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js';
|
||||
import { addViewer } from './viewer.js';
|
||||
import settings from '../../settings.js';
|
||||
import { serverProxy } from './server_proxy.js';
|
||||
|
||||
export class Agent {
|
||||
async start(profile_fp, load_mem=false, init_message=null, count_id=0) {
|
||||
this.last_sender = null;
|
||||
try {
|
||||
// Add validation for profile_fp
|
||||
if (!profile_fp) {
|
||||
throw new Error('No profile filepath provided');
|
||||
}
|
||||
|
||||
// Connect to MindServer via proxy
|
||||
serverProxy.connect();
|
||||
|
||||
console.log('Starting agent initialization with profile:', profile_fp);
|
||||
|
||||
// Initialize components with more detailed error handling
|
||||
|
@ -41,6 +46,10 @@ export class Agent {
|
|||
console.log('Initializing self prompter...');
|
||||
this.self_prompter = new SelfPrompter(this);
|
||||
initConversationManager(this);
|
||||
|
||||
// After getting the name, register with MindServer via proxy
|
||||
serverProxy.registerAgent(this.name);
|
||||
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to initialize agent components: ${error.message || error}`);
|
||||
}
|
||||
|
@ -145,7 +154,8 @@ export class Agent {
|
|||
console.log(this.name, 'received message from', username, ':', message);
|
||||
|
||||
if (isOtherAgent(username)) {
|
||||
recieveFromBot(username, message);
|
||||
//recieveFromBot(username, message);
|
||||
console.warn('recieved whisper from other bot??')
|
||||
}
|
||||
else {
|
||||
let translation = await handleEnglishTranslation(message);
|
||||
|
@ -179,6 +189,10 @@ export class Agent {
|
|||
this.history.add('system', prompt);
|
||||
await this.self_prompter.start(prompt);
|
||||
}
|
||||
else if (save_data?.last_sender) {
|
||||
this.last_sender = save_data.last_sender;
|
||||
await this.handleMessage(this.last_sender, `(You have restarted and this message is auto-generated. Continue the conversation with ${this.last_sender})`);
|
||||
}
|
||||
else if (init_message) {
|
||||
await this.handleMessage('system', init_message, 2);
|
||||
}
|
||||
|
@ -232,9 +246,15 @@ export class Agent {
|
|||
if (this.self_prompter.on) {
|
||||
this.self_prompter.stop(false);
|
||||
}
|
||||
endAllChats();
|
||||
}
|
||||
|
||||
async handleMessage(source, message, max_responses=null) {
|
||||
if (!source || !message) {
|
||||
console.warn('Received empty message from', source);
|
||||
return false;
|
||||
}
|
||||
|
||||
let used_command = false;
|
||||
if (max_responses === null) {
|
||||
max_responses = settings.max_commands === -1 ? Infinity : settings.max_commands;
|
||||
|
@ -266,11 +286,15 @@ export class Agent {
|
|||
}
|
||||
}
|
||||
|
||||
if (!self_prompt)
|
||||
this.last_sender = source;
|
||||
else
|
||||
this.last_sender = null;
|
||||
|
||||
// Now translate the message
|
||||
message = await handleEnglishTranslation(message);
|
||||
console.log('received message from', source, ':', message);
|
||||
|
||||
// Do self prompting
|
||||
const checkInterrupt = () => this.self_prompter.shouldInterrupt(self_prompt) || this.shut_up;
|
||||
|
||||
let behavior_log = this.bot.modes.flushBehaviorLog();
|
||||
|
|
|
@ -65,7 +65,6 @@ export const actionsList = [
|
|||
name: '!restart',
|
||||
description: 'Restart the agent process.',
|
||||
perform: async function (agent) {
|
||||
await agent.history.save();
|
||||
agent.cleanKill();
|
||||
}
|
||||
},
|
||||
|
@ -226,18 +225,18 @@ export const actionsList = [
|
|||
await skills.collectBlock(agent.bot, type, num);
|
||||
}, false, 10) // 10 minute timeout
|
||||
},
|
||||
{
|
||||
name: '!collectAllBlocks',
|
||||
description: 'Collect all the nearest blocks of a given type until told to stop.',
|
||||
params: {
|
||||
'type': { type: 'BlockName', description: 'The block type to collect.' }
|
||||
},
|
||||
perform: runAsAction(async (agent, type) => {
|
||||
let success = await skills.collectBlock(agent.bot, type, 1);
|
||||
if (!success)
|
||||
agent.actions.cancelResume();
|
||||
}, true, 3) // 3 minute timeout
|
||||
},
|
||||
// {
|
||||
// name: '!collectAllBlocks',
|
||||
// description: 'Collect all the nearest blocks of a given type until told to stop.',
|
||||
// params: {
|
||||
// 'type': { type: 'BlockName', description: 'The block type to collect.' }
|
||||
// },
|
||||
// perform: runAsAction(async (agent, type) => {
|
||||
// let success = await skills.collectBlock(agent.bot, type, 1);
|
||||
// if (!success)
|
||||
// agent.actions.cancelResume();
|
||||
// }, true, 3) // 3 minute timeout
|
||||
// },
|
||||
{
|
||||
name: '!craftRecipe',
|
||||
description: 'Craft the given recipe a given number of times.',
|
||||
|
@ -273,7 +272,7 @@ export const actionsList = [
|
|||
perform: runAsAction(async (agent) => {
|
||||
await skills.clearNearestFurnace(agent.bot);
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '!placeHere',
|
||||
description: 'Place a given block in the current location. Do NOT use to build structures, only use for single blocks/torches.',
|
||||
|
@ -376,7 +375,7 @@ export const actionsList = [
|
|||
},
|
||||
{
|
||||
name: '!endChat',
|
||||
description: 'End the conversation from the most recent message.',
|
||||
description: 'End the conversation with the given player.',
|
||||
params: {
|
||||
'player_name': { type: 'string', description: 'The name of the player to end the conversation with.' }
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import settings from '../../settings.js';
|
||||
import { readFileSync } from 'fs';
|
||||
import { containsCommand } from './commands/index.js';
|
||||
import { sendBotChatToServer } from './server_proxy.js';
|
||||
|
||||
let agent;
|
||||
const agent_names = settings.profiles.map((p) => JSON.parse(readFileSync(p, 'utf8')).name);
|
||||
|
@ -59,8 +60,8 @@ export function startChat(send_to, message, max_turns=5) {
|
|||
}
|
||||
|
||||
export function sendToBot(send_to, message, start=false) {
|
||||
if (message.length > 197)
|
||||
message = message.substring(0, 197);
|
||||
// if (message.length > 197)
|
||||
// message = message.substring(0, 197);
|
||||
if (!isOtherAgent(send_to)) {
|
||||
agent.bot.whisper(send_to, message);
|
||||
return;
|
||||
|
@ -81,7 +82,8 @@ export function sendToBot(send_to, message, start=false) {
|
|||
'idle': agent.isIdle()
|
||||
};
|
||||
|
||||
agent.bot.whisper(send_to, JSON.stringify(json));
|
||||
// agent.bot.whisper(send_to, JSON.stringify(json));
|
||||
sendBotChatToServer(send_to, JSON.stringify(json));
|
||||
}
|
||||
|
||||
export function recieveFromBot(sender, json) {
|
||||
|
@ -94,14 +96,12 @@ export function recieveFromBot(sender, json) {
|
|||
}
|
||||
if (convo.ignore_until_start)
|
||||
return;
|
||||
if (convo.turn_count > 10) {
|
||||
console.warn('Reached max messages from bot:', sender);
|
||||
endChat(sender);
|
||||
agent.bot.chat('chat maxxed out, ending conversation');
|
||||
return;
|
||||
}
|
||||
|
||||
convo.queue(recieved);
|
||||
|
||||
// responding to conversation takes priority over self prompting
|
||||
if (agent.self_prompter.on)
|
||||
agent.self_prompter.stopLoop();
|
||||
|
||||
if (inMessageTimer)
|
||||
clearTimeout(inMessageTimer);
|
||||
|
@ -124,18 +124,20 @@ export function _processInMessageQueue(name) {
|
|||
}
|
||||
|
||||
export function _handleFullInMessage(sender, recieved) {
|
||||
console.log(`responding to **${recieved}**`);
|
||||
console.log(`responding to **${JSON.stringify(recieved)}**`);
|
||||
|
||||
const convo = _getConvo(sender);
|
||||
|
||||
convo.countTurn();
|
||||
const message = _tagMessage(recieved.message);
|
||||
if (recieved.end || (!recieved.idle && !agent.isIdle()) || convo.over()) {
|
||||
if (recieved.end || convo.over()) {
|
||||
// if end signal from other bot, or both are busy, or past max turns,
|
||||
// add to history, but don't respond
|
||||
agent.history.add(sender, message);
|
||||
return;
|
||||
}
|
||||
if (recieved.start)
|
||||
agent.shut_up = false;
|
||||
agent.handleMessage(sender, message);
|
||||
}
|
||||
|
||||
|
@ -145,6 +147,12 @@ export function endChat(sender) {
|
|||
}
|
||||
}
|
||||
|
||||
export function endAllChats() {
|
||||
for (const sender in convos) {
|
||||
convos[sender].ignore_until_start = true;
|
||||
}
|
||||
}
|
||||
|
||||
function _tagMessage(message) {
|
||||
return "(FROM OTHER BOT)" + message;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ export class History {
|
|||
// Number of messages to remove from current history and save into memory
|
||||
this.summary_chunk_size = 5;
|
||||
// chunking reduces expensive calls to promptMemSaving and appendFullHistory
|
||||
// and improves the quality of the memory summary
|
||||
}
|
||||
|
||||
getHistory() { // expects an Examples object
|
||||
|
@ -83,7 +84,8 @@ export class History {
|
|||
const data = {
|
||||
memory: this.memory,
|
||||
turns: this.turns,
|
||||
self_prompt: this.agent.self_prompter.on ? this.agent.self_prompter.prompt : null
|
||||
self_prompt: this.agent.self_prompter.on ? this.agent.self_prompter.prompt : null,
|
||||
last_sender: this.agent.last_sender
|
||||
};
|
||||
writeFileSync(this.memory_fp, JSON.stringify(data, null, 2));
|
||||
console.log('Saved memory to:', this.memory_fp);
|
||||
|
|
|
@ -1064,6 +1064,21 @@ export async function moveAway(bot, distance) {
|
|||
return true;
|
||||
}
|
||||
|
||||
export async function moveAwayFrom(bot, entity, distance=1) {
|
||||
const follow = new pf.goals.GoalFollow(entity, distance+1);
|
||||
const inverted_goal = new pf.goals.GoalInvert(follow);
|
||||
bot.pathfinder.setMovements(new pf.Movements(bot));
|
||||
bot.pathfinder.setGoal(inverted_goal, true);
|
||||
for (let i = 0; i < 10*distance; i++) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
if (bot.interrupt_code)
|
||||
return false;
|
||||
if (bot.entity.position.distanceTo(entity.position) > distance)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function avoidEnemies(bot, distance=16) {
|
||||
/**
|
||||
* Move a given distance away from all nearby enemy mobs.
|
||||
|
|
|
@ -23,7 +23,7 @@ async function say(agent, message) {
|
|||
// the order of this list matters! first modes will be prioritized
|
||||
// while update functions are async, they should *not* be awaited longer than ~100ms as it will block the update loop
|
||||
// to perform longer actions, use the execute function which won't block the update loop
|
||||
const modes = [
|
||||
const modes_list = [
|
||||
{
|
||||
name: 'self_preservation',
|
||||
description: 'Respond to drowning, burning, and damage at low health. Interrupts all actions.',
|
||||
|
@ -209,6 +209,23 @@ const modes = [
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'elbow_room',
|
||||
description: 'Move away from nearby players when idle.',
|
||||
interrupts: ['action:followPlayer'],
|
||||
on: true,
|
||||
active: false,
|
||||
crowded_distance: 0.5,
|
||||
clear_distance: 1,
|
||||
update: async function (agent) {
|
||||
const player = world.getNearestEntityWhere(agent.bot, entity => entity.type === 'player', this.crowded_distance);
|
||||
if (player) {
|
||||
execute(this, agent, async () => {
|
||||
await skills.moveAwayFrom(agent.bot, player, this.clear_distance);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'idle_staring',
|
||||
description: 'Animation to look around at entities when idle.',
|
||||
|
@ -267,13 +284,19 @@ async function execute(mode, agent, func, timeout=-1) {
|
|||
console.log(`Mode ${mode.name} finished executing, code_return: ${code_return.message}`);
|
||||
}
|
||||
|
||||
let _agent = null;
|
||||
|
||||
class ModeController {
|
||||
constructor(agent) {
|
||||
this.agent = agent;
|
||||
this.modes_list = modes;
|
||||
/*
|
||||
SECURITY WARNING:
|
||||
This object is accessible by LLM generated code, as are all of its references.
|
||||
This can be used to access sensitive information like API keys by malicious human prompters.
|
||||
Do not store references to anything outside this object to prevent this.
|
||||
*/
|
||||
constructor() {
|
||||
this.modes_map = {};
|
||||
this.behavior_log = '';
|
||||
for (let mode of this.modes_list) {
|
||||
for (let mode of modes_list) {
|
||||
this.modes_map[mode.name] = mode;
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +322,7 @@ class ModeController {
|
|||
}
|
||||
|
||||
unPauseAll() {
|
||||
for (let mode of this.modes_list) {
|
||||
for (let mode of modes_list) {
|
||||
if (mode.paused) console.log(`Unpausing mode ${mode.name}`);
|
||||
mode.paused = false;
|
||||
}
|
||||
|
@ -307,7 +330,7 @@ class ModeController {
|
|||
|
||||
getMiniDocs() { // no descriptions
|
||||
let res = 'Agent Modes:';
|
||||
for (let mode of this.modes_list) {
|
||||
for (let mode of modes_list) {
|
||||
let on = mode.on ? 'ON' : 'OFF';
|
||||
res += `\n- ${mode.name}(${on})`;
|
||||
}
|
||||
|
@ -316,7 +339,7 @@ class ModeController {
|
|||
|
||||
getDocs() {
|
||||
let res = 'Agent Modes:';
|
||||
for (let mode of this.modes_list) {
|
||||
for (let mode of modes_list) {
|
||||
let on = mode.on ? 'ON' : 'OFF';
|
||||
res += `\n- ${mode.name}(${on}): ${mode.description}`;
|
||||
}
|
||||
|
@ -324,13 +347,13 @@ class ModeController {
|
|||
}
|
||||
|
||||
async update() {
|
||||
if (this.agent.isIdle()) {
|
||||
if (_agent.isIdle()) {
|
||||
this.unPauseAll();
|
||||
}
|
||||
for (let mode of this.modes_list) {
|
||||
let interruptible = mode.interrupts.some(i => i === 'all') || mode.interrupts.some(i => i === this.agent.actions.currentActionLabel);
|
||||
if (mode.on && !mode.paused && !mode.active && (this.agent.isIdle() || interruptible)) {
|
||||
await mode.update(this.agent);
|
||||
for (let mode of modes_list) {
|
||||
let interruptible = mode.interrupts.some(i => i === 'all') || mode.interrupts.some(i => i === _agent.actions.currentActionLabel);
|
||||
if (mode.on && !mode.paused && !mode.active && (_agent.isIdle() || interruptible)) {
|
||||
await mode.update(_agent);
|
||||
}
|
||||
if (mode.active) break;
|
||||
}
|
||||
|
@ -344,14 +367,14 @@ class ModeController {
|
|||
|
||||
getJson() {
|
||||
let res = {};
|
||||
for (let mode of this.modes_list) {
|
||||
for (let mode of modes_list) {
|
||||
res[mode.name] = mode.on;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
loadJson(json) {
|
||||
for (let mode of this.modes_list) {
|
||||
for (let mode of modes_list) {
|
||||
if (json[mode.name] != undefined) {
|
||||
mode.on = json[mode.name];
|
||||
}
|
||||
|
@ -360,10 +383,11 @@ class ModeController {
|
|||
}
|
||||
|
||||
export function initModes(agent) {
|
||||
_agent = agent;
|
||||
// the mode controller is added to the bot object so it is accessible from anywhere the bot is used
|
||||
agent.bot.modes = new ModeController(agent);
|
||||
let modes = agent.prompter.getInitModes();
|
||||
if (modes) {
|
||||
agent.bot.modes.loadJson(modes);
|
||||
agent.bot.modes = new ModeController();
|
||||
let modes_json = agent.prompter.getInitModes();
|
||||
if (modes_json) {
|
||||
agent.bot.modes.loadJson(modes_json);
|
||||
}
|
||||
}
|
||||
|
|
53
src/agent/server_proxy.js
Normal file
53
src/agent/server_proxy.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { io } from 'socket.io-client';
|
||||
import { recieveFromBot } from './conversation.js';
|
||||
|
||||
class ServerProxy {
|
||||
constructor() {
|
||||
if (ServerProxy.instance) {
|
||||
return ServerProxy.instance;
|
||||
}
|
||||
|
||||
this.socket = null;
|
||||
this.connected = false;
|
||||
ServerProxy.instance = this;
|
||||
}
|
||||
|
||||
connect() {
|
||||
if (this.connected) return;
|
||||
|
||||
this.socket = io('http://localhost:8080');
|
||||
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) => {
|
||||
recieveFromBot(agentName, json);
|
||||
});
|
||||
}
|
||||
|
||||
registerAgent(agentName) {
|
||||
if (!this.connected) {
|
||||
console.warn('Cannot register agent: not connected to MindServer');
|
||||
return;
|
||||
}
|
||||
this.socket.emit('register-agent', agentName);
|
||||
}
|
||||
|
||||
getSocket() {
|
||||
return this.socket;
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export a singleton instance
|
||||
export const serverProxy = new ServerProxy();
|
||||
|
||||
export function sendBotChatToServer(agentName, json) {
|
||||
serverProxy.getSocket().emit('chat-message', agentName, json);
|
||||
}
|
60
src/server/mind_server.js
Normal file
60
src/server/mind_server.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { Server } from 'socket.io';
|
||||
import express from 'express';
|
||||
import http from 'http';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// Module-level variables
|
||||
let io;
|
||||
let server;
|
||||
const connectedAgents = {};
|
||||
|
||||
// Initialize the server
|
||||
export function createMindServer(port = 8080) {
|
||||
const app = express();
|
||||
server = http.createServer(app);
|
||||
io = new Server(server);
|
||||
|
||||
// Serve static files
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
// Socket.io connection handling
|
||||
io.on('connection', (socket) => {
|
||||
let curAgentName = null;
|
||||
console.log('Client connected');
|
||||
|
||||
socket.emit('agents-update', Object.keys(connectedAgents));
|
||||
|
||||
socket.on('register-agent', (agentName) => {
|
||||
console.log('Agent registered:', agentName);
|
||||
connectedAgents[agentName] = socket;
|
||||
curAgentName = agentName;
|
||||
io.emit('agents-update', Object.keys(connectedAgents));
|
||||
});
|
||||
|
||||
socket.on('chat-message', (agentName, json) => {
|
||||
console.log(`${curAgentName} received message from ${agentName}: ${json}`);
|
||||
const agentSocket = connectedAgents[agentName];
|
||||
if (agentSocket) {
|
||||
agentSocket.emit('chat-message', curAgentName, json);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
console.log('Client disconnected');
|
||||
delete connectedAgents[socket.id];
|
||||
io.emit('agents-update', Object.keys(connectedAgents));
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(port, 'localhost', () => {
|
||||
console.log(`MindServer running on port ${port}`);
|
||||
});
|
||||
|
||||
return server;
|
||||
}
|
||||
// Optional: export these if you need access to them from other files
|
||||
export const getIO = () => io;
|
||||
export const getServer = () => server;
|
||||
export const getConnectedAgents = () => connectedAgents;
|
44
src/server/public/index.html
Normal file
44
src/server/public/index.html
Normal file
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Mindcraft Agents</title>
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 20px;
|
||||
background: #f0f0f0;
|
||||
}
|
||||
#agents {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
}
|
||||
.agent {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
background: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Connected Mindcraft Agents</h1>
|
||||
<div id="agents"></div>
|
||||
|
||||
<script>
|
||||
const socket = io();
|
||||
const agentsDiv = document.getElementById('agents');
|
||||
|
||||
socket.on('agents-update', (agents) => {
|
||||
agentsDiv.innerHTML = agents.length ?
|
||||
agents.map(name => `<div class="agent">${name}</div>`).join('') :
|
||||
'<div class="agent">No agents connected</div>';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue