mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-03 13:55:36 +02:00
refactored to SelfPrompter class and interruption logic
This commit is contained in:
parent
d1890a3a3b
commit
c7b115ebbf
4 changed files with 131 additions and 87 deletions
|
@ -6,6 +6,7 @@ import { initBot } from '../utils/mcdata.js';
|
|||
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction } from './commands/index.js';
|
||||
import { NPCContoller } from './npc/controller.js';
|
||||
import { MemoryBank } from './memory_bank.js';
|
||||
import { SelfPrompter } from './self_prompter.js';
|
||||
|
||||
|
||||
export class Agent {
|
||||
|
@ -16,6 +17,7 @@ export class Agent {
|
|||
this.coder = new Coder(this);
|
||||
this.npc = new NPCContoller(this);
|
||||
this.memory_bank = new MemoryBank();
|
||||
this.self_prompter = new SelfPrompter(this);
|
||||
|
||||
await this.prompter.initExamples();
|
||||
|
||||
|
@ -27,10 +29,6 @@ export class Agent {
|
|||
if (load_mem)
|
||||
this.history.load();
|
||||
|
||||
this.auto_prompting = false;
|
||||
this.last_user_prompted_action = 0;
|
||||
this.interrupt_auto_prompt = false;
|
||||
|
||||
this.bot.once('spawn', async () => {
|
||||
// wait for a bit so stats are not undefined
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
@ -103,19 +101,14 @@ export class Agent {
|
|||
}
|
||||
if (execute_res)
|
||||
this.cleanChat(execute_res);
|
||||
if (isAction(user_command_name)) {
|
||||
this.last_user_prompted_action = Date.now();
|
||||
if (this.auto_prompting) {
|
||||
this.interrupt_auto_prompt = true;
|
||||
this.bot.chat('User initiated action. Stopping auto-prompting.');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const MAX_ATTEMPTS = 5;
|
||||
let MAX_ATTEMPTS = 5;
|
||||
if (!self_prompt && this.self_prompter.on)
|
||||
MAX_ATTEMPTS = 1; // immediately respond to this message, then let self-prompting take over
|
||||
for (let i=0; i<MAX_ATTEMPTS; i++) {
|
||||
if (self_prompt && this.interrupt_auto_prompt) break;
|
||||
if (self_prompt && this.self_prompter.on && this.self_prompter.interrupt) break;
|
||||
let history = this.history.getHistory();
|
||||
let res = await this.prompter.promptConvo(history);
|
||||
|
||||
|
@ -127,24 +120,27 @@ export class Agent {
|
|||
this.history.add(this.name, res);
|
||||
if (!commandExists(command_name)) {
|
||||
this.history.add('system', `Command ${command_name} does not exist. Use !newAction to perform custom actions.`);
|
||||
console.log('Agent hallucinated command:', command_name)
|
||||
console.warn('Agent hallucinated command:', command_name)
|
||||
continue;
|
||||
}
|
||||
let pre_message = res.substring(0, res.indexOf(command_name)).trim();
|
||||
let chat_message = `*used ${command_name.substring(1)}*`;
|
||||
if (pre_message.length > 0)
|
||||
chat_message = `${pre_message} ${chat_message}`;
|
||||
this.cleanChat(chat_message);
|
||||
|
||||
if (!self_prompt && isAction(command_name)) {
|
||||
console.log('User initiated action.');
|
||||
this.last_user_prompted_action = Date.now();
|
||||
if (this.auto_prompting) {
|
||||
this.interrupt_auto_prompt = true;
|
||||
this.bot.chat('User initiated action. Stopping auto-prompting.');
|
||||
}
|
||||
if (command_name === '!stopSelfPrompt' && self_prompt) {
|
||||
this.history.add('system', `Cannot stopSelfPrompt unless requested by user.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// let pre_message = res.substring(0, res.indexOf(command_name)).trim();
|
||||
// let chat_message = `*used ${command_name.substring(1)}*`;
|
||||
// if (pre_message.length > 0)
|
||||
// chat_message = `${pre_message} ${chat_message}`;
|
||||
this.cleanChat(res);
|
||||
|
||||
if (self_prompt && this.self_prompter.on && this.self_prompter.interrupt) break;
|
||||
|
||||
if (isAction(command_name) && !self_prompt && this.self_prompter.on) {
|
||||
this.self_prompter.stopLoop(); // so agent doesn't respond from self-prompting loop
|
||||
// will be automatically restarted by self-prompter
|
||||
}
|
||||
if (self_prompt && this.interrupt_auto_prompt) break;
|
||||
|
||||
let execute_res = await executeCommand(this, res);
|
||||
|
||||
|
@ -169,51 +165,6 @@ export class Agent {
|
|||
return used_command;
|
||||
}
|
||||
|
||||
async autoPrompt(prompt) {
|
||||
if (this.auto_prompting) {
|
||||
return 'Agent is already auto-prompting. Ignoring request.';
|
||||
}
|
||||
this.auto_prompting = true;
|
||||
let first = true;
|
||||
let no_command_count = 0;
|
||||
const MAX_NO_COMMAND = 3;
|
||||
while (!this.interrupt_auto_prompt) {
|
||||
let msg;
|
||||
if (first) {
|
||||
msg = prompt;
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
msg = `You are self-prompting with the intial message: '${prompt}'. Your next response MUST contain a command !likeThis. Respond:`;
|
||||
}
|
||||
|
||||
let used_command = await this.handleMessage('system', msg, true);
|
||||
|
||||
if (!used_command) {
|
||||
no_command_count++;
|
||||
if (no_command_count >= MAX_NO_COMMAND) {
|
||||
let out = `Agent did not use command in the last ${MAX_NO_COMMAND} auto-prompts. Stopping auto-prompting.`;
|
||||
this.bot.chat(out);
|
||||
console.warn(out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
no_command_count = 0;
|
||||
}
|
||||
this.auto_prompting = false;
|
||||
this.interrupt_auto_prompt = false;
|
||||
return 'Auto-prompting finished.';
|
||||
}
|
||||
|
||||
async waitStopAutoPrompt() {
|
||||
while (this.auto_prompting) {
|
||||
this.interrupt_auto_prompt = true;
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
}
|
||||
this.interrupt_auto_prompt = false;
|
||||
}
|
||||
|
||||
startEvents() {
|
||||
// Custom events
|
||||
this.bot.on('time', () => {
|
||||
|
@ -270,20 +221,27 @@ export class Agent {
|
|||
|
||||
// This update loop ensures that each update() is called one at a time, even if it takes longer than the interval
|
||||
const INTERVAL = 300;
|
||||
let last = Date.now();
|
||||
setTimeout(async () => {
|
||||
while (true) {
|
||||
let start = Date.now();
|
||||
await this.bot.modes.update();
|
||||
await this.update(start - last);
|
||||
let remaining = INTERVAL - (Date.now() - start);
|
||||
if (remaining > 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, remaining));
|
||||
}
|
||||
last = start;
|
||||
}
|
||||
}, INTERVAL);
|
||||
|
||||
this.bot.emit('idle');
|
||||
}
|
||||
|
||||
async update(delta) {
|
||||
await this.bot.modes.update();
|
||||
await this.self_prompter.update(delta);
|
||||
}
|
||||
|
||||
isIdle() {
|
||||
return !this.coder.executing && !this.coder.generating;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,10 @@ export const actionsList = [
|
|||
agent.coder.clear();
|
||||
agent.coder.cancelResume();
|
||||
agent.bot.emit('idle');
|
||||
return 'Agent stopped.';
|
||||
let msg = 'Agent stopped.';
|
||||
if (agent.self_prompter.on)
|
||||
msg += ' Self-prompting still active.';
|
||||
return msg;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -241,7 +244,15 @@ export const actionsList = [
|
|||
'prompt': '(string) The starting prompt.',
|
||||
},
|
||||
perform: async function (agent, prompt) {
|
||||
agent.autoPrompt(prompt); // don't await, don't return
|
||||
agent.self_prompter.start(prompt); // don't await, don't return
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '!stopSelfPrompt',
|
||||
description: 'Stop current action and self-prompting.',
|
||||
perform: async function (agent) {
|
||||
agent.self_prompter.stop(); // will stop and
|
||||
return 'Self-prompting stopped.';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
@ -202,22 +202,13 @@ const modes = [
|
|||
];
|
||||
|
||||
async function execute(mode, agent, func, timeout=-1) {
|
||||
let was_auto_prompting = agent.auto_prompting;
|
||||
if (was_auto_prompting) agent.interrupt_auto_prompt = true;
|
||||
if (agent.self_prompter.on)
|
||||
agent.self_prompter.stopLoop();
|
||||
mode.active = true;
|
||||
let code_return = await agent.coder.execute(async () => {
|
||||
await func();
|
||||
}, timeout);
|
||||
mode.active = false;
|
||||
if (was_auto_prompting) {
|
||||
setTimeout(() => { // don't await
|
||||
if (agent.isIdle()) { // not gonna work if another action is triggered because auto_prompting will be false. need to fix
|
||||
let output = agent.bot.output;
|
||||
let reprompt = `Agent was interrupted by ${mode.name} mode. Code output: "${output}". Continue self prompting:`;
|
||||
agent.autoPrompt(reprompt);
|
||||
}
|
||||
}, 1000); // wait 1 second before re-enabling auto_prompt bc often another action will be triggered
|
||||
}
|
||||
console.log(`Mode ${mode.name} finished executing, code_return: ${code_return.message}`);
|
||||
}
|
||||
|
||||
|
|
84
src/agent/self_prompter.js
Normal file
84
src/agent/self_prompter.js
Normal file
|
@ -0,0 +1,84 @@
|
|||
export class SelfPrompter {
|
||||
constructor(agent) {
|
||||
this.agent = agent;
|
||||
this.on = false;
|
||||
this.loop_active = false;
|
||||
this.interrupt = false;
|
||||
this.prompt = '';
|
||||
this.idle_time = 0;
|
||||
this.restart_after = 1000;
|
||||
}
|
||||
|
||||
start(prompt) {
|
||||
console.log('Self-prompting started.');
|
||||
if (this.on) {
|
||||
return 'Agent is already self-prompting. Ignoring request.';
|
||||
}
|
||||
this.on = true;
|
||||
this.prompt = prompt;
|
||||
this.startLoop();
|
||||
}
|
||||
|
||||
async startLoop() {
|
||||
if (this.loop_active) {
|
||||
console.warn('Self-prompt loop is already active. Ignoring request.');
|
||||
return;
|
||||
}
|
||||
console.log('starting self-prompt loop')
|
||||
this.loop_active = true;
|
||||
let no_command_count = 0;
|
||||
const MAX_NO_COMMAND = 3;
|
||||
while (!this.interrupt) {
|
||||
let msg = `You are self-prompting with the prompt: '${this.prompt}'. Your next response MUST contain a command !withThisSyntax. Respond:`;
|
||||
|
||||
let used_command = await this.agent.handleMessage('system', msg, true);
|
||||
if (!used_command) {
|
||||
no_command_count++;
|
||||
if (no_command_count >= MAX_NO_COMMAND) {
|
||||
let out = `Agent did not use command in the last ${MAX_NO_COMMAND} auto-prompts. Stopping auto-prompting.`;
|
||||
this.agent.bot.chat(out);
|
||||
console.warn(out);
|
||||
this.on = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
no_command_count = 0;
|
||||
}
|
||||
console.log('self prompt loop stopped')
|
||||
this.loop_active = false;
|
||||
this.interrupt = false;
|
||||
}
|
||||
|
||||
update(delta) {
|
||||
// automatically restarts loop
|
||||
if (this.on && !this.loop_active && !this.interrupt) {
|
||||
if (this.agent.isIdle())
|
||||
this.idle_time += delta;
|
||||
else
|
||||
this.idle_time = 0;
|
||||
|
||||
if (this.idle_time >= this.restart_after) {
|
||||
this.agent.bot.chat('Restarting self-prompting...');
|
||||
this.startLoop();
|
||||
this.idle_time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async stopLoop() {
|
||||
// you can call this without await if you don't need to wait for it to finish
|
||||
this.interrupt = true;
|
||||
while (this.loop_active) {
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
}
|
||||
this.interrupt = false;
|
||||
}
|
||||
|
||||
async stop() {
|
||||
this.interrupt = true;
|
||||
await this.agent.coder.stop();
|
||||
await this.stopLoop();
|
||||
this.on = false;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue