Merge pull request #596 from mindcraft-bots/remote-output

Write all bot output to mindserver
This commit is contained in:
Max Robinson 2025-08-25 16:12:36 -05:00 committed by GitHub
commit de03a8a25d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 50 additions and 13 deletions

View file

@ -29,6 +29,7 @@ const settings = {
"init_message": "Respond with hello world and your name", // sends to all on spawn "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 "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` "speak": false, // allows all bots to speak through system text-to-speech. works on windows, mac, on linux you need to `apt install espeak`
"chat_ingame": true, // bot responses are shown in minecraft chat
"language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages "language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages
"render_bot_view": 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...
@ -41,7 +42,7 @@ const settings = {
"max_messages": 15, // max number of messages to keep in context "max_messages": 15, // max number of messages to keep in context
"num_examples": 2, // number of examples to give to the model "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 "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 "show_command_syntax": "full", // "full", "shortened", or "none"
"narrate_behavior": true, // chat simple automatic actions ('Picking up item!') "narrate_behavior": true, // chat simple automatic actions ('Picking up item!')
"chat_bot_messages": true, // publicly chat messages to other bots "chat_bot_messages": true, // publicly chat messages to other bots

View file

@ -12,7 +12,7 @@ import { SelfPrompter } from './self_prompter.js';
import convoManager from './conversation.js'; import convoManager from './conversation.js';
import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js'; import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js';
import { addBrowserViewer } from './vision/browser_viewer.js'; import { addBrowserViewer } from './vision/browser_viewer.js';
import { serverProxy } from './mindserver_proxy.js'; import { serverProxy, sendOutputToServer } from './mindserver_proxy.js';
import settings from './settings.js'; import settings from './settings.js';
import { Task } from './tasks/tasks.js'; import { Task } from './tasks/tasks.js';
import { say } from './speak.js'; import { say } from './speak.js';
@ -304,16 +304,23 @@ export class Agent {
if (checkInterrupt()) break; if (checkInterrupt()) break;
this.self_prompter.handleUserPromptedCmd(self_prompt, isAction(command_name)); this.self_prompter.handleUserPromptedCmd(self_prompt, isAction(command_name));
if (settings.verbose_commands) { if (settings.show_command_syntax === "full") {
this.routeResponse(source, res); this.routeResponse(source, res);
} }
else { // only output command name else if (settings.show_command_syntax === "shortened") {
// show only "used !commandname"
let pre_message = res.substring(0, res.indexOf(command_name)).trim(); let pre_message = res.substring(0, res.indexOf(command_name)).trim();
let chat_message = `*used ${command_name.substring(1)}*`; let chat_message = `*used ${command_name.substring(1)}*`;
if (pre_message.length > 0) if (pre_message.length > 0)
chat_message = `${pre_message} ${chat_message}`; chat_message = `${pre_message} ${chat_message}`;
this.routeResponse(source, chat_message); this.routeResponse(source, chat_message);
} }
else {
// no command at all
let pre_message = res.substring(0, res.indexOf(command_name)).trim();
if (pre_message.trim().length > 0)
this.routeResponse(source, pre_message);
}
let execute_res = await executeCommand(this, res); let execute_res = await executeCommand(this, res);
@ -379,7 +386,8 @@ export class Agent {
if (settings.speak) { if (settings.speak) {
say(to_translate); say(to_translate);
} }
this.bot.chat(message); if (settings.chat_ingame) {this.bot.chat(message);}
sendOutputToServer(this.name, message);
} }
} }

View file

@ -2,7 +2,7 @@ import { io } from 'socket.io-client';
import convoManager from './conversation.js'; import convoManager from './conversation.js';
import { setSettings } from './settings.js'; import { setSettings } from './settings.js';
// agents connection to mindserver // agent's individual connection to the mindserver
// always connect to localhost // always connect to localhost
class MindServerProxy { class MindServerProxy {
@ -110,6 +110,12 @@ class MindServerProxy {
// Create and export a singleton instance // Create and export a singleton instance
export const serverProxy = new MindServerProxy(); export const serverProxy = new MindServerProxy();
// for chatting with other bots
export function sendBotChatToServer(agentName, json) { export function sendBotChatToServer(agentName, json) {
serverProxy.getSocket().emit('chat-message', agentName, json); serverProxy.getSocket().emit('chat-message', agentName, json);
} }
// for sending general output to server for display
export function sendOutputToServer(agentName, message) {
serverProxy.getSocket().emit('bot-output', agentName, message);
}

View file

@ -170,6 +170,10 @@ export function createMindServer(host_public = false, port = 8080) {
console.error('Error: ', error); console.error('Error: ', error);
} }
}); });
socket.on('bot-output', (agentName, message) => {
io.emit('bot-output', agentName, message);
});
}); });
let host = host_public ? '0.0.0.0' : 'localhost'; let host = host_public ? '0.0.0.0' : 'localhost';

View file

@ -25,8 +25,8 @@
background: #363636; background: #363636;
border-radius: 4px; border-radius: 4px;
display: flex; display: flex;
justify-content: space-between; flex-direction: column;
align-items: center; align-items: flex-start;
} }
.restart-btn, .start-btn, .stop-btn { .restart-btn, .start-btn, .stop-btn {
color: white; color: white;
@ -102,6 +102,13 @@
border: none; border: none;
margin-left: 10px; margin-left: 10px;
} }
.last-message {
font-style: italic;
color: #aaa;
margin-top: 5px;
white-space: pre-wrap;
word-break: break-word;
}
.start-btn:disabled { .start-btn:disabled {
opacity: 0.4; opacity: 0.4;
cursor: not-allowed; cursor: not-allowed;
@ -135,6 +142,7 @@
let settingsSpec = {}; let settingsSpec = {};
let profileData = null; let profileData = null;
const agentSettings = {}; const agentSettings = {};
const agentLastMessage = {};
fetch('/settings_spec.json') fetch('/settings_spec.json')
.then(r => r.json()) .then(r => r.json())
@ -229,6 +237,14 @@
}); });
}); });
socket.on('bot-output', (agentName, message) => {
agentLastMessage[agentName] = message;
const messageDiv = document.getElementById(`lastMessage-${agentName}`);
if (messageDiv) {
messageDiv.textContent = message;
}
});
function fetchAgentSettings(name) { function fetchAgentSettings(name) {
return new Promise((resolve) => { return new Promise((resolve) => {
if (agentSettings[name]) { resolve(agentSettings[name]); return; } if (agentSettings[name]) { resolve(agentSettings[name]); return; }
@ -250,9 +266,10 @@
const cfg = agentSettings[agent.name] || {}; const cfg = agentSettings[agent.name] || {};
const showViewer = cfg.render_bot_view === true; const showViewer = cfg.render_bot_view === true;
const viewerHTML = showViewer ? `<div class="agent-view-container"><iframe class="agent-viewer" src="http://localhost:${3000 + idx}"></iframe></div>` : ''; const viewerHTML = showViewer ? `<div class="agent-view-container"><iframe class="agent-viewer" src="http://localhost:${3000 + idx}"></iframe></div>` : '';
const lastMessage = agentLastMessage[agent.name] || '';
return ` return `
<div class="agent"> <div class="agent">
<div style="display:flex;justify-content:space-between;align-items:center;"> <div style="display:flex;justify-content:space-between;align-items:center;width:100%;">
<span><span class="status-icon ${agent.in_game ? 'online' : 'offline'}"></span>${agent.name}</span> <span><span class="status-icon ${agent.in_game ? 'online' : 'offline'}"></span>${agent.name}</span>
<div style="display:flex;align-items:center;"> <div style="display:flex;align-items:center;">
${agent.in_game ? ` ${agent.in_game ? `
@ -265,6 +282,7 @@
`} `}
</div> </div>
</div> </div>
<div id="lastMessage-${agent.name}" class="last-message">${lastMessage}</div>
${viewerHTML} ${viewerHTML}
</div>`; </div>`;
}).join('') + }).join('') +

View file

@ -94,10 +94,10 @@
"description": "Whether to log all prompts to file. Can be very verbose.", "description": "Whether to log all prompts to file. Can be very verbose.",
"default": false "default": false
}, },
"verbose_commands": { "show_command_syntax": {
"type": "boolean", "type": "string",
"description": "Whether to show full command syntax in bot responses. If false will use a shortened syntax.", "description": "Whether to show \"full\" command syntax, \"shortened\" command syntax, or \"none\"",
"default": true "default": "full"
}, },
"chat_bot_messages": { "chat_bot_messages": {
"type": "boolean", "type": "boolean",