additional task features

This commit is contained in:
Kolby Nottingham 2024-12-09 17:06:22 -08:00
parent 4e0611d29e
commit a15ab15bcd
6 changed files with 68 additions and 96 deletions

View file

@ -11,7 +11,6 @@
"andy",
"randy"
],
"agent_number": 2,
"initial_inventory": {
"andy": {
"iron_ingot": 1
@ -26,13 +25,8 @@
"type": "construction",
"goal": "Build a house"
},
"multiagent_techtree_1_shears_with_2_iron_ingot": {
"goal": "Collaborate with other agents to build a shear.",
"agent_names": [
"andy",
"randy"
],
"agent_number": 2,
"techtree_1_shears_with_2_iron_ingot": {
"goal": "Build a shear.",
"initial_inventory": {
"andy": {
"iron_ingot": 1
@ -48,11 +42,11 @@
},
"multiagent_techtree_1_stone_pickaxe": {
"goal": "Collaborate with other agents to build an stone pickaxe",
"conversation": "Let's build a stone pickaxe",
"agent_names": [
"andy",
"randy"
],
"agent_number": 2,
"initial_inventory": {
"andy": {
"wooden_pickaxe": 1
@ -65,45 +59,5 @@
"number_of_target": 1,
"type": "techtree",
"timeout": 300
},
"multiagent_build_wooden_pickaxe": {
"goal": "Collaborate with other agents to build a wooden pickaxe.",
"agent_names": [
"andy",
"randy"
],
"agent_number": 2,
"initial_inventory": {
"andy": {
"oak_log": 2
},
"randy": {
"stick": 2
}
},
"target": "wooden_pickaxe",
"number_of_target": 1,
"type": "techtree",
"timeout": 120
},
"multiagent_techtree_boat": {
"goal": "Collaborate with other agents to build a birch boat.",
"agent_names": [
"Bob",
"Alice"
],
"agent_number": 2,
"initial_inventory": {
"Bob": {
"birch_planks": 3
},
"Alice": {
"birch_planks": 2
}
},
"target": "birch_boat",
"number_of_target": 1,
"type": "techtree",
"timeout": 60
}
}

View file

@ -8,7 +8,7 @@ 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, endAllChats, responseScheduledFor} from './conversation.js';
import { isOtherAgent, initConversationManager, sendToBot, endAllChats, responseScheduledFor, inConversation } from './conversation.js';
import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js';
import { addViewer } from './viewer.js';
import settings from '../../settings.js';
@ -18,6 +18,7 @@ import { loadTask, initBotTask, TechTreeHarvestValidator } from '../utils/tasks.
export class Agent {
async start(profile_fp, load_mem=false, init_message=null, count_id=0, task_path=null, task_id=null) {
this.last_sender = null;
this.count_id = count_id;
try {
if (!profile_fp) {
throw new Error('No profile filepath provided');
@ -66,10 +67,16 @@ export class Agent {
this.taskTimeout = this.task.timeout || 300;
this.taskStartTime = Date.now();
this.validator = new TechTreeHarvestValidator(this.task, this.bot);
this.blocked_actions = this.task.blocked_actions || [];
if (this.task.goal)
this.blocked_actions.push('!endGoal');
if (this.task.conversation)
this.blocked_actions.push('!endConversation');
} else {
this.task = null;
this.taskTimeout = null;
this.validator = null;
this.blocked_actions = [];
}
console.log("Is validated:", this.validator && this.validator.validate());
@ -96,18 +103,13 @@ export class Agent {
console.log(`${this.name} spawned.`);
this.clearBotLogs();
if (this.task) {
await initBotTask(this.bot, this.task);
await new Promise((resolve) => setTimeout(resolve, 10000));
if (this.task.agent_names && this.task.agent_names.filter(name => !this.bot.players[name]).length) {
console.log(`Missing players/bots: ${missingPlayers.join(', ')}`);
this.cleanKill('Not all required players/bots are present in the world. Exiting.', 4);
}
}
this._setupEventHandlers(save_data, init_message);
this.startEvents();
if (this.task)
await initBotTask(this);
} catch (error) {
console.error('Error in spawn event:', error);
process.exit(0);
@ -437,18 +439,24 @@ export class Agent {
}, INTERVAL);
// Check for task completion
setInterval(async () => {
if (this.task && this.validator && this.validator.validate())
this.killBots();
if (this.task && this.taskTimeout) {
const elapsedTime = (Date.now() - this.taskStartTime) / 1000;
if (elapsedTime >= this.taskTimeout) {
console.log('Task timeout reached. Task unsuccessful.');
this.cleanKill('Task unsuccessful: Timeout reached', 3);
if (this.task) {
setInterval(async () => {
if (this.validator && this.validator.validate())
this.killBots();
if (this.task.goal && !this.self_prompter.on)
this.cleanKill('Task unsuccessful: Agent ended goal', 3);
if (this.task.conversation && !inConversation())
this.cleanKill('Task unsuccessful: Agent ended conversation', 3);
if (this.taskTimeout) {
const elapsedTime = (Date.now() - this.taskStartTime) / 1000;
if (elapsedTime >= this.taskTimeout) {
console.log('Task timeout reached. Task unsuccessful.');
this.cleanKill('Task unsuccessful: Timeout reached', 3);
}
}
}
}, 1000);
}, 1000);
}
this.bot.emit('idle');
}

View file

@ -220,7 +220,7 @@ export async function executeCommand(agent, message) {
}
}
export function getCommandDocs() {
export function getCommandDocs(blacklist=null) {
const typeTranslations = {
//This was added to keep the prompt the same as before type checks were implemented.
//If the language model is giving invalid inputs changing this might help.
@ -234,6 +234,9 @@ export function getCommandDocs() {
Use the commands with the syntax: !commandName or !commandName("arg1", 1.2, ...) if the command takes arguments.\n
Do not use codeblocks. Use double quotes for strings. Only use one command in each response, trailing commands and comments will be ignored.\n`;
for (let command of commandList) {
if (blacklist && blacklist.includes(command.name)) {
continue;
}
docs += command.name + ': ' + command.description + '\n';
if (command.params) {
docs += 'Params:\n';

View file

@ -173,7 +173,7 @@ export class Prompter {
prompt = prompt.replaceAll('$ACTION', this.agent.actions.currentActionLabel);
}
if (prompt.includes('$COMMAND_DOCS'))
prompt = prompt.replaceAll('$COMMAND_DOCS', getCommandDocs());
prompt = prompt.replaceAll('$COMMAND_DOCS', getCommandDocs(this.agent.blocked_actions));
if (prompt.includes('$CODE_DOCS'))
prompt = prompt.replaceAll('$CODE_DOCS', getSkillDocs());
if (prompt.includes('$EXAMPLES') && examples !== null)

View file

@ -26,19 +26,9 @@ export class AgentProcess {
agentProcess.on('exit', (code, signal) => {
console.log(`Agent process exited with code ${code} and signal ${signal}`);
if (code === 2) {
console.log(`Task completed successfully`);
process.exit(2, signal);
}
if (code === 3) {
console.log(`Task failed due to reaching timeout`);
process.exit(3);
}
if (code === 4) {
console.log(`Task failed as all agents weren't correctly spawned `);
process.exit(4);
if (code > 1) {
console.log(`Ending task`);
process.exit(code);
}
if (code !== 0) {

View file

@ -1,6 +1,7 @@
import yaml from 'js-yaml'
import { readFileSync } from 'fs';
import {getPosition} from './library/world.js'
import { executeCommand } from './commands/index.js';
import { getPosition } from './library/world.js'
export function loadTask(taskId) {
try {
@ -19,17 +20,18 @@ export function loadTask(taskId) {
}
}
export async function initBotTask(bot, task, name) {
if (task) {
bot.chat(`/clear ${bot.username}`);
console.log(`Cleared ${bot.username}'s inventory.`);
}
export async function initBotTask(agent) {
let bot = agent.bot;
let task = agent.task;
bot.chat(`/clear ${bot.username}`);
console.log(`Cleared ${bot.username}'s inventory.`);
//wait for a bit so inventory is cleared
await new Promise((resolve) => setTimeout(resolve, 500));
console.log(task && "agent_number" in task && task.agent_number > 1);
if (task && "agent_number" in task && task.agent_number > 1) {
console.log("agent_number" in task.agent_number > 1);
if ("agent_number" in task.agent_number > 1) {
var initial_inventory = task.initial_inventory[bot.username];
console.log("Initial inventory:", initial_inventory);
} else if (task) {
@ -37,7 +39,7 @@ export async function initBotTask(bot, task, name) {
var initial_inventory = task.initial_inventory;
}
if (task && "initial_inventory" in task) {
if ("initial_inventory" in task) {
console.log("Setting inventory...");
console.log("Inventory to set:", initial_inventory);
for (let key of Object.keys(initial_inventory)) {
@ -70,7 +72,7 @@ export async function initBotTask(bot, task, name) {
// teleport near a human player if found by default
if (task && "agent_number" in task) {
if ("agent_number" in task) {
var agent_names = task.agent_names;
if (human_player_name) {
console.log(`Teleporting ${bot.username} to human ${human_player_name}`)
@ -103,7 +105,7 @@ export async function initBotTask(bot, task, name) {
This was done by MaxRobinson in one of the youtube videos.
*/
if (task && task.type !== 'construction') {
if (task.type !== 'construction') {
const pos = getPosition(bot);
const xOffset = getRandomOffset(5);
const zOffset = getRandomOffset(5);
@ -111,7 +113,22 @@ export async function initBotTask(bot, task, name) {
await new Promise((resolve) => setTimeout(resolve, 200));
}
if (task.goal) {
await executeCommand(agent, `!goal("${task.goal}")`);
}
if (task.agent_names) {
await new Promise((resolve) => setTimeout(resolve, 10000));
if (task.agent_names.filter(name => !bot.players[name]).length) {
console.log(`Missing players/bots: ${missingPlayers.join(', ')}`);
agent.cleanKill('Not all required players/bots are present in the world. Exiting.', 4);
}
}
if (task.conversation && agent.count_id === 0) {
let other_name = task.agent_names.filter(name => name !== bot.username)[0];
await executeCommand(agent, `!startConversation("${other_name}", "${task.conversation}")`);
}
}
export class TechTreeHarvestValidator {