better conversation monitor timers, examples, bot detection

This commit is contained in:
MaxRobinsonTheGreat 2024-12-08 20:58:59 -06:00
parent 5649cec439
commit c2dab95061
8 changed files with 106 additions and 39 deletions

View file

@ -12,7 +12,7 @@
"modes": { "modes": {
"self_preservation": true, "self_preservation": true,
"unstuck": true, "unstuck": true,
"cowardice": true, "cowardice": false,
"self_defense": true, "self_defense": true,
"hunting": true, "hunting": true,
"item_collecting": true, "item_collecting": true,
@ -35,6 +35,13 @@
{"role": "assistant", "content": "Hey John, not much. Just saying hi. Bye! !endConversation('john_goodman')"} {"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": "system", "content": "trade something with zorro_34"},
{"role": "assistant", "content": "!startConversation(\"zorro_34\", \"Hey let's trade. What do you have?\")"}, {"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": "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": "user", "content": "trixy88: craft some sticks"},
{"role": "assistant", "content": "!craftRecipe(\"stick\", 4)"}, {"role": "assistant", "content": "!craftRecipe(\"stick\", 4)"},

View file

@ -108,10 +108,16 @@ export class ActionManager {
this.currentActionFn = null; this.currentActionFn = null;
clearTimeout(TIMEOUT); clearTimeout(TIMEOUT);
this.cancelResume(); 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(); 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; let interrupted = this.agent.bot.interrupt_code;
this.agent.clearBotLogs(); this.agent.clearBotLogs();
if (!interrupted && !this.agent.coder.generating) { if (!interrupted && !this.agent.coder.generating) {

View file

@ -224,7 +224,7 @@ export class Agent {
} }
} }
if (!self_prompt) if (from_other_bot)
this.last_sender = source; this.last_sender = source;
// Now translate the message // Now translate the message
@ -311,7 +311,7 @@ export class Agent {
async routeResponse(to_player, message) { async routeResponse(to_player, message) {
let self_prompt = to_player === 'system' || to_player === this.name; 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 // 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 // so it can respond to events like death but be routed back to the last sender
to_player = this.last_sender; to_player = this.last_sender;
@ -320,7 +320,6 @@ export class Agent {
if (convoManager.isOtherAgent(to_player) && convoManager.inConversation(to_player)) { 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 // if we're in an ongoing conversation with the other bot, send the response to it
convoManager.sendToBot(to_player, message); convoManager.sendToBot(to_player, message);
} }
else { else {
// otherwise, use open chat // otherwise, use open chat

View file

@ -116,7 +116,7 @@ export const actionsList = [
description: 'Find and go to the nearest block of a given type in a given range.', description: 'Find and go to the nearest block of a given type in a given range.',
params: { params: {
'type': { type: 'BlockName', description: 'The block type to go to.' }, '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) => { perform: runAsAction(async (agent, block_type, range) => {
await skills.goToNearestBlock(agent.bot, block_type, 4, 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.', description: 'Find and go to the nearest entity of a given type in a given range.',
params: { params: {
'type': { type: 'string', description: 'The type of entity to go to.' }, '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) => { perform: runAsAction(async (agent, entity_type, range) => {
await skills.goToNearestEntity(agent.bot, entity_type, 4, 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.' }, 'message': { type: 'string', description: 'The message to send.' },
}, },
perform: async function (agent, player_name, message) { perform: async function (agent, player_name, message) {
if (convoManager.inConversation()) if (convoManager.inConversation() && !convoManager.inConversation(player_name))
return 'You are already in conversation'; return 'You are already in conversation with other bot.';
if (!convoManager.isOtherAgent(player_name)) if (!convoManager.isOtherAgent(player_name))
return player_name + ' is not a bot, cannot start conversation.'; 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); convoManager.startConversation(player_name, message);
} }
}, },

View file

@ -48,15 +48,11 @@ export const queryList = [
let players = world.getNearbyPlayerNames(bot); let players = world.getNearbyPlayerNames(bot);
let bots = []; let bots = convoManager.getInGameAgents().filter(b => b !== agent.name);
for (const player of players) { players = players.filter(p => !bots.includes(p));
if (convoManager.isOtherAgent(player))
bots.push(player);
}
players = players.filter(p => !convoManager.isOtherAgent(p));
res += '\n- Nearby Human Players: ' + players.join(', '); res += '\n- Nearby Human Players: ' + (players.length > 0 ? players.join(', ') : 'None.');
res += '\n- Nearby Bot Players: ' + bots.join(', '); res += '\n- Nearby Bot Players: ' + (bots.length > 0 ? bots.join(', ') : 'None.');
res += '\n' + agent.bot.modes.getMiniDocs() + '\n'; res += '\n' + agent.bot.modes.getMiniDocs() + '\n';
return pad(res); return pad(res);
@ -137,12 +133,8 @@ export const queryList = [
let bot = agent.bot; let bot = agent.bot;
let res = 'NEARBY_ENTITIES'; let res = 'NEARBY_ENTITIES';
let players = world.getNearbyPlayerNames(bot); let players = world.getNearbyPlayerNames(bot);
let bots = []; let bots = convoManager.getInGameAgents().filter(b => b !== agent.name);
for (const player of players) { players = players.filter(p => !bots.includes(p));
if (convoManager.isOtherAgent(player))
bots.push(player);
}
players = players.filter(p => !convoManager.isOtherAgent(p));
for (const player of players) { for (const player of players) {
res += `\n- Human player: ${player}`; res += `\n- Human player: ${player}`;

View file

@ -44,11 +44,14 @@ class Conversation {
} }
} }
const WAIT_TIME_START = 30000;
class ConversationManager { class ConversationManager {
constructor() { constructor() {
this.convos = {}; this.convos = {};
this.activeConversation = null; this.activeConversation = null;
this.awaiting_response = false;
this.connection_timeout = null;
this.wait_time_limit = WAIT_TIME_START;
} }
initAgent(a) { initAgent(a) {
@ -63,23 +66,60 @@ class ConversationManager {
_startMonitor() { _startMonitor() {
clearInterval(this.connection_monitor); clearInterval(this.connection_monitor);
let wait_time = 0;
let last_time = Date.now();
this.connection_monitor = setInterval(() => { this.connection_monitor = setInterval(() => {
if (!this.activeConversation) { if (!this.activeConversation) {
clearInterval(this.connection_monitor); this._stopMonitor();
return; // will clean itself up return; // will clean itself up
} }
let cur_name = this.activeConversation.name;
if (!this.otherAgentInGame(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;
}
}
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) { if (!self_prompter_paused) {
this.endConversation(cur_name); this.endConversation(convo_partner);
agent.handleMessage('system', `${cur_name} disconnected, conversation has ended.`); agent.handleMessage('system', `${convo_partner} disconnected, conversation has ended.`);
} }
else { else {
this.endConversation(cur_name); this.endConversation(convo_partner);
}
} }
}, 10000); }, 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) { async startConversation(send_to, message) {
const convo = this._getConvo(send_to); const convo = this._getConvo(send_to);
@ -97,6 +137,13 @@ class ConversationManager {
this.sendToBot(send_to, message, true); 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) { sendToBot(send_to, message, start=false) {
if (!this.isOtherAgent(send_to)) { if (!this.isOtherAgent(send_to)) {
agent.bot.whisper(send_to, message); agent.bot.whisper(send_to, message);
@ -118,6 +165,7 @@ class ConversationManager {
end, end,
}; };
this.awaiting_response = true;
sendBotChatToServer(send_to, json); sendBotChatToServer(send_to, json);
} }
@ -132,10 +180,12 @@ class ConversationManager {
if (recieved.start) { if (recieved.start) {
convo.reset(); convo.reset();
this.startConversationFromOtherBot(sender);
} }
if (convo.ignore_until_start) if (convo.ignore_until_start)
return; return;
this._clearMonitorTimeouts();
convo.queue(recieved); convo.queue(recieved);
// responding to conversation takes priority over self prompting // responding to conversation takes priority over self prompting
@ -167,6 +217,10 @@ class ConversationManager {
agents_in_game = agents.filter(a => a.in_game).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) { inConversation(other_agent=null) {
if (other_agent) if (other_agent)
return this.convos[other_agent]?.active; return this.convos[other_agent]?.active;
@ -176,6 +230,7 @@ class ConversationManager {
endConversation(sender) { endConversation(sender) {
if (this.convos[sender]) { if (this.convos[sender]) {
this.convos[sender].end(); this.convos[sender].end();
this._stopMonitor();
this.activeConversation = null; this.activeConversation = null;
if (self_prompter_paused && !this.inConversation()) { if (self_prompter_paused && !this.inConversation()) {
_resumeSelfPrompter(); _resumeSelfPrompter();
@ -276,7 +331,7 @@ function _handleFullInMessage(sender, recieved) {
let message = _tagMessage(recieved.message); let message = _tagMessage(recieved.message);
if (recieved.end) { if (recieved.end) {
convo.end(); convoManager.endConversation(sender);
sender = 'system'; // bot will respond to system instead of the other bot sender = 'system'; // bot will respond to system instead of the other bot
message = `Conversation with ${sender} ended with message: "${message}"`; message = `Conversation with ${sender} ended with message: "${message}"`;
} }

View file

@ -238,7 +238,7 @@ export function getNearbyPlayerNames(bot) {
* @example * @example
* let players = world.getNearbyPlayerNames(bot); * let players = world.getNearbyPlayerNames(bot);
**/ **/
let players = getNearbyPlayers(bot, 16); let players = getNearbyPlayers(bot, 64);
let found = []; let found = [];
for (let i = 0; i < players.length; i++) { for (let i = 0; i < players.length; i++) {
if (!found.includes(players[i].username) && players[i].username != bot.username) { if (!found.includes(players[i].username) && players[i].username != bot.username) {

View file

@ -2,6 +2,7 @@ import * as skills from './library/skills.js';
import * as world from './library/world.js'; import * as world from './library/world.js';
import * as mc from '../utils/mcdata.js'; import * as mc from '../utils/mcdata.js';
import settings from '../../settings.js' import settings from '../../settings.js'
import convoManager from './conversation.js';
async function say(agent, message) { async function say(agent, message) {
agent.bot.modes.behavior_log += message + '\n'; agent.bot.modes.behavior_log += message + '\n';
@ -294,7 +295,7 @@ async function execute(mode, agent, func, timeout=-1) {
if (should_reprompt) { if (should_reprompt) {
// auto prompt to respond to the interruption // 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(); let logs = agent.bot.modes.flushBehaviorLog();
agent.handleMessage(role, `(AUTO MESSAGE)Your previous action '${interrupted_action}' was interrupted by ${mode.name}. agent.handleMessage(role, `(AUTO MESSAGE)Your previous action '${interrupted_action}' was interrupted by ${mode.name}.
Your behavior log: ${logs}\nRespond accordingly.`); Your behavior log: ${logs}\nRespond accordingly.`);