From c2dab950612f56dc959f63484045dd4744a75d79 Mon Sep 17 00:00:00 2001 From: MaxRobinsonTheGreat Date: Sun, 8 Dec 2024 20:58:59 -0600 Subject: [PATCH] better conversation monitor timers, examples, bot detection --- profiles/_default.json | 14 +++++- src/agent/action_manager.js | 10 ++++- src/agent/agent.js | 5 +-- src/agent/commands/actions.js | 10 +++-- src/agent/commands/queries.js | 20 +++------ src/agent/conversation.js | 81 +++++++++++++++++++++++++++++------ src/agent/library/world.js | 2 +- src/agent/modes.js | 3 +- 8 files changed, 106 insertions(+), 39 deletions(-) diff --git a/profiles/_default.json b/profiles/_default.json index 7908611..8b9fffe 100644 --- a/profiles/_default.json +++ b/profiles/_default.json @@ -12,7 +12,7 @@ "modes": { "self_preservation": true, "unstuck": true, - "cowardice": true, + "cowardice": false, "self_defense": true, "hunting": true, "item_collecting": true, @@ -35,6 +35,13 @@ {"role": "assistant", "content": "Hey John, not much. Just saying hi. Bye! !endConversation('john_goodman')"} ], + [ + {"role": "system", "content": "work together with the other bot"}, + {"role": "assistant", "content": "!startConversation(\"terrance\", \"Hey gpt! Let's work together on this.\"))"}, + {"role": "user", "content": "terrance: (FROM OTHER BOT)Sounds good, what should we do first?"}, + {"role": "assistant", "content": "I'll start by collecting some resources. !collectBlocks('stone', 10)"} + ], + [ {"role": "system", "content": "trade something with zorro_34"}, {"role": "assistant", "content": "!startConversation(\"zorro_34\", \"Hey let's trade. What do you have?\")"}, @@ -126,6 +133,11 @@ {"role": "assistant", "content": "I'll have to search a wider area. !searchForBlock(\"oak_log\", 100)"} ], + [ + {"role": "system", "content": "Code output: Placed oak_planks at (-154, 96, -228)."}, + {"role": "assistant", "content": "I've placed an oak plank for the house. Wait, I should use newAction to build complicated structures! !newAction(\"Build a house with oak planks\")"} + ], + [ {"role": "user", "content": "trixy88: craft some sticks"}, {"role": "assistant", "content": "!craftRecipe(\"stick\", 4)"}, diff --git a/src/agent/action_manager.js b/src/agent/action_manager.js index bac2a04..219856b 100644 --- a/src/agent/action_manager.js +++ b/src/agent/action_manager.js @@ -108,10 +108,16 @@ export class ActionManager { this.currentActionFn = null; clearTimeout(TIMEOUT); this.cancelResume(); - console.error("Code execution triggered catch: " + err); + console.error("Code execution triggered catch:", err); + // Log the full stack trace + console.error(err.stack); await this.stop(); - let message = this._getBotOutputSummary() + '!!Code threw exception!! Error: ' + err; + let message = this._getBotOutputSummary() + + '!!Code threw exception!!\n' + + 'Error: ' + err + '\n' + + 'Stack trace:\n' + err.stack; + let interrupted = this.agent.bot.interrupt_code; this.agent.clearBotLogs(); if (!interrupted && !this.agent.coder.generating) { diff --git a/src/agent/agent.js b/src/agent/agent.js index 479fa2a..7c514f0 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -224,7 +224,7 @@ export class Agent { } } - if (!self_prompt) + if (from_other_bot) this.last_sender = source; // Now translate the message @@ -311,7 +311,7 @@ export class Agent { async routeResponse(to_player, message) { let self_prompt = to_player === 'system' || to_player === this.name; - if (self_prompt && this.last_sender && !this.self_prompter.on) { + if (self_prompt && this.last_sender) { // this is for when the agent is prompted by system while still in conversation // so it can respond to events like death but be routed back to the last sender to_player = this.last_sender; @@ -320,7 +320,6 @@ export class Agent { if (convoManager.isOtherAgent(to_player) && convoManager.inConversation(to_player)) { // if we're in an ongoing conversation with the other bot, send the response to it convoManager.sendToBot(to_player, message); - } else { // otherwise, use open chat diff --git a/src/agent/commands/actions.js b/src/agent/commands/actions.js index 5856443..2a303d3 100644 --- a/src/agent/commands/actions.js +++ b/src/agent/commands/actions.js @@ -116,7 +116,7 @@ export const actionsList = [ description: 'Find and go to the nearest block of a given type in a given range.', params: { 'type': { type: 'BlockName', description: 'The block type to go to.' }, - 'search_range': { type: 'float', description: 'The range to search for the block.', domain: [0, 512] } + 'search_range': { type: 'float', description: 'The range to search for the block.', domain: [32, 512] } }, perform: runAsAction(async (agent, block_type, range) => { await skills.goToNearestBlock(agent.bot, block_type, 4, range); @@ -127,7 +127,7 @@ export const actionsList = [ description: 'Find and go to the nearest entity of a given type in a given range.', params: { 'type': { type: 'string', description: 'The type of entity to go to.' }, - 'search_range': { type: 'float', description: 'The range to search for the entity.', domain: [0, 512] } + 'search_range': { type: 'float', description: 'The range to search for the entity.', domain: [32, 512] } }, perform: runAsAction(async (agent, entity_type, range) => { await skills.goToNearestEntity(agent.bot, entity_type, 4, range); @@ -386,10 +386,12 @@ export const actionsList = [ 'message': { type: 'string', description: 'The message to send.' }, }, perform: async function (agent, player_name, message) { - if (convoManager.inConversation()) - return 'You are already in conversation'; + if (convoManager.inConversation() && !convoManager.inConversation(player_name)) + return 'You are already in conversation with other bot.'; if (!convoManager.isOtherAgent(player_name)) return player_name + ' is not a bot, cannot start conversation.'; + if (convoManager.inConversation(player_name)) + agent.history.add('system', 'You are already in conversation with ' + player_name + ' Don\'t use this command to talk to them.'); convoManager.startConversation(player_name, message); } }, diff --git a/src/agent/commands/queries.js b/src/agent/commands/queries.js index a65d0a9..3419d0f 100644 --- a/src/agent/commands/queries.js +++ b/src/agent/commands/queries.js @@ -48,15 +48,11 @@ export const queryList = [ let players = world.getNearbyPlayerNames(bot); - let bots = []; - for (const player of players) { - if (convoManager.isOtherAgent(player)) - bots.push(player); - } - players = players.filter(p => !convoManager.isOtherAgent(p)); + let bots = convoManager.getInGameAgents().filter(b => b !== agent.name); + players = players.filter(p => !bots.includes(p)); - res += '\n- Nearby Human Players: ' + players.join(', '); - res += '\n- Nearby Bot Players: ' + bots.join(', '); + res += '\n- Nearby Human Players: ' + (players.length > 0 ? players.join(', ') : 'None.'); + res += '\n- Nearby Bot Players: ' + (bots.length > 0 ? bots.join(', ') : 'None.'); res += '\n' + agent.bot.modes.getMiniDocs() + '\n'; return pad(res); @@ -137,12 +133,8 @@ export const queryList = [ let bot = agent.bot; let res = 'NEARBY_ENTITIES'; let players = world.getNearbyPlayerNames(bot); - let bots = []; - for (const player of players) { - if (convoManager.isOtherAgent(player)) - bots.push(player); - } - players = players.filter(p => !convoManager.isOtherAgent(p)); + let bots = convoManager.getInGameAgents().filter(b => b !== agent.name); + players = players.filter(p => !bots.includes(p)); for (const player of players) { res += `\n- Human player: ${player}`; diff --git a/src/agent/conversation.js b/src/agent/conversation.js index c68d412..1b09441 100644 --- a/src/agent/conversation.js +++ b/src/agent/conversation.js @@ -44,11 +44,14 @@ class Conversation { } } - +const WAIT_TIME_START = 30000; class ConversationManager { constructor() { this.convos = {}; this.activeConversation = null; + this.awaiting_response = false; + this.connection_timeout = null; + this.wait_time_limit = WAIT_TIME_START; } initAgent(a) { @@ -63,22 +66,59 @@ class ConversationManager { _startMonitor() { clearInterval(this.connection_monitor); + let wait_time = 0; + let last_time = Date.now(); this.connection_monitor = setInterval(() => { if (!this.activeConversation) { - clearInterval(this.connection_monitor); + this._stopMonitor(); return; // will clean itself up } - let cur_name = this.activeConversation.name; - if (!this.otherAgentInGame(cur_name)) { - if (!self_prompter_paused) { - this.endConversation(cur_name); - agent.handleMessage('system', `${cur_name} disconnected, conversation has ended.`); - } - else { - this.endConversation(cur_name); + + let delta = Date.now() - last_time; + last_time = Date.now(); + let convo_partner = this.activeConversation.name; + + if (this.awaiting_response && agent.isIdle()) { + wait_time += delta; + if (wait_time > this.wait_time_limit) { + agent.handleMessage('system', `${convo_partner} hasn't responded in ${this.wait_time_limit/1000} seconds, respond with a message to them or your own action.`); + wait_time = 0; + this.wait_time_limit*=2; } } - }, 10000); + else if (!this.awaiting_response){ + this.wait_time_limit = WAIT_TIME_START; + wait_time = 0; + } + + if (!this.otherAgentInGame(convo_partner) && !this.connection_timeout) { + this.connection_timeout = setTimeout(() => { + if (this.otherAgentInGame(convo_partner)){ + this._clearMonitorTimeouts(); + return; + } + if (!self_prompter_paused) { + this.endConversation(convo_partner); + agent.handleMessage('system', `${convo_partner} disconnected, conversation has ended.`); + } + else { + this.endConversation(convo_partner); + } + }, 10000); + } + }, 1000); + } + + _stopMonitor() { + clearInterval(this.connection_monitor); + this.connection_monitor = null; + this._clearMonitorTimeouts(); + } + + _clearMonitorTimeouts() { + this.awaiting_response = false; + clearTimeout(this.connection_timeout); + this.connection_timeout = null; } async startConversation(send_to, message) { @@ -97,6 +137,13 @@ class ConversationManager { this.sendToBot(send_to, message, true); } + startConversationFromOtherBot(name) { + const convo = this._getConvo(name); + convo.active = true; + this.activeConversation = convo; + this._startMonitor(); + } + sendToBot(send_to, message, start=false) { if (!this.isOtherAgent(send_to)) { agent.bot.whisper(send_to, message); @@ -118,6 +165,7 @@ class ConversationManager { end, }; + this.awaiting_response = true; sendBotChatToServer(send_to, json); } @@ -132,10 +180,12 @@ class ConversationManager { if (recieved.start) { convo.reset(); + this.startConversationFromOtherBot(sender); } if (convo.ignore_until_start) return; - + + this._clearMonitorTimeouts(); convo.queue(recieved); // responding to conversation takes priority over self prompting @@ -166,6 +216,10 @@ class ConversationManager { agent_names = agents.map(a => a.name); agents_in_game = agents.filter(a => a.in_game).map(a => a.name); } + + getInGameAgents() { + return agents_in_game; + } inConversation(other_agent=null) { if (other_agent) @@ -176,6 +230,7 @@ class ConversationManager { endConversation(sender) { if (this.convos[sender]) { this.convos[sender].end(); + this._stopMonitor(); this.activeConversation = null; if (self_prompter_paused && !this.inConversation()) { _resumeSelfPrompter(); @@ -276,7 +331,7 @@ function _handleFullInMessage(sender, recieved) { let message = _tagMessage(recieved.message); if (recieved.end) { - convo.end(); + convoManager.endConversation(sender); sender = 'system'; // bot will respond to system instead of the other bot message = `Conversation with ${sender} ended with message: "${message}"`; } diff --git a/src/agent/library/world.js b/src/agent/library/world.js index 1373f6f..6fc9460 100644 --- a/src/agent/library/world.js +++ b/src/agent/library/world.js @@ -238,7 +238,7 @@ export function getNearbyPlayerNames(bot) { * @example * let players = world.getNearbyPlayerNames(bot); **/ - let players = getNearbyPlayers(bot, 16); + let players = getNearbyPlayers(bot, 64); let found = []; for (let i = 0; i < players.length; i++) { if (!found.includes(players[i].username) && players[i].username != bot.username) { diff --git a/src/agent/modes.js b/src/agent/modes.js index c377431..d9ec75f 100644 --- a/src/agent/modes.js +++ b/src/agent/modes.js @@ -2,6 +2,7 @@ import * as skills from './library/skills.js'; import * as world from './library/world.js'; import * as mc from '../utils/mcdata.js'; import settings from '../../settings.js' +import convoManager from './conversation.js'; async function say(agent, message) { agent.bot.modes.behavior_log += message + '\n'; @@ -294,7 +295,7 @@ async function execute(mode, agent, func, timeout=-1) { if (should_reprompt) { // auto prompt to respond to the interruption - let role = agent.last_sender ? agent.last_sender : 'system'; + let role = convoManager.inConversation() ? agent.last_sender : 'system'; let logs = agent.bot.modes.flushBehaviorLog(); agent.handleMessage(role, `(AUTO MESSAGE)Your previous action '${interrupted_action}' was interrupted by ${mode.name}. Your behavior log: ${logs}\nRespond accordingly.`);