checkpoint

This commit is contained in:
MaxRobinsonTheGreat 2024-11-05 12:17:10 -06:00
parent 3b07a63b48
commit b717dabd40
8 changed files with 231 additions and 63 deletions

View file

@ -1,7 +1,7 @@
{
"name": "andy",
"model": "gpt-4o-mini",
"model": "gpt-4o",
"cooldown": 3000,
@ -29,24 +29,29 @@
{"role": "assistant", "content": "Nothing much miner_32, what do you need?"}
],
[
{"role": "system", "content": "Send a message to john_goodman to work together"},
{"role": "assistant", "content": "!startChat('john_goodman', 'Hey John, lets work together to build a house!', 10)"}
],
[
{"role": "user", "content": "barry: (FROM OTHER BOT)Hey there! What's up?"},
{"role": "assistant", "content": "!chat('barry', 'Not much, just hanging out. How about you?')"},
{"role": "assistant", "content": "Not much, just hanging out. How about you?"},
{"role": "user", "content": "barry: (FROM OTHER BOT)I'm doing great, thanks for asking!"},
{"role": "assistant", "content": "!endChat('barry', 'Finished conversation')"}
{"role": "assistant", "content": "Good to know! Goodbye. !endChat('barry')"}
],
[
{"role": "user", "content": "rupert: (FROM OTHER BOT)Hey what do you got?"},
{"role": "assistant", "content": "!inventory"},
{"role": "system", "content": "INVENTORY: wooden_pickaxe: 1, cobblestone: 10, cooked_chicken: 8"},
{"role": "assistant", "content": "!chat('rupert', 'I have a wooden pick, cobblestone, and cooked chicken')"},
{"role": "assistant", "content": "I have a wooden pick, cobblestone, and cooked chicken"},
{"role": "user", "content": "rupert: (FROM OTHER BOT)Give me all your chicken!"},
{"role": "assistant", "content": "!giveItem('cooked_chicken', 'rupert', 1)"},
{"role": "assistant", "content": "!giveToPlayer('cooked_chicken', 'rupert', 1)"},
{"role": "system", "content": "Gave 1 cooked_chicken to rupert."},
{"role": "assistant", "content": "!chat('rupert', 'I gave you one, I need the rest!')"},
{"role": "assistant", "content": "I gave you one, I need the rest!"},
{"role": "user", "content": "rupert: (FROM OTHER BOT)Give me ALL OF THEM"},
{"role": "assistant", "content": "!endChat('rupert', 'You are mean.')"}
{"role": "assistant", "content": "No! You are mean. !endChat('rupert')"}
],
[

View file

@ -29,24 +29,29 @@
{"role": "assistant", "content": "Nothing much miner_32, what do you need?"}
],
[
{"role": "system", "content": "Send a message to john_goodman to work together"},
{"role": "assistant", "content": "!startChat('john_goodman', 'Hey John, lets work together to build a house!', 10))"}
],
[
{"role": "user", "content": "barry: (FROM OTHER BOT)Hey there! What's up?"},
{"role": "assistant", "content": "!chat('barry', 'Not much, just hanging out. How about you?')"},
{"role": "assistant", "content": "Not much, just hanging out. How about you?"},
{"role": "user", "content": "barry: (FROM OTHER BOT)I'm doing great, thanks for asking!"},
{"role": "assistant", "content": "!endChat('barry', 'Finished conversation')"}
{"role": "assistant", "content": "Good to know! Goodbye. !endChat('barry')"}
],
[
{"role": "user", "content": "rupert: (FROM OTHER BOT)Hey what do you got?"},
{"role": "assistant", "content": "!inventory"},
{"role": "system", "content": "INVENTORY: wooden_pickaxe: 1, cobblestone: 10, cooked_chicken: 8"},
{"role": "assistant", "content": "!chat('rupert', 'I have a wooden pick, cobblestone, and cooked chicken')"},
{"role": "assistant", "content": "I have a wooden pick, cobblestone, and cooked chicken"},
{"role": "user", "content": "rupert: (FROM OTHER BOT)Give me all your chicken!"},
{"role": "assistant", "content": "!giveItem('cooked_chicken', 'rupert', 1)"},
{"role": "assistant", "content": "!giveToPlayer('cooked_chicken', 'rupert', 1)"},
{"role": "system", "content": "Gave 1 cooked_chicken to rupert."},
{"role": "assistant", "content": "!chat('rupert', 'I gave you one, I need the rest!')"},
{"role": "assistant", "content": "I gave you one, I need the rest!"},
{"role": "user", "content": "rupert: (FROM OTHER BOT)Give me ALL OF THEM"},
{"role": "assistant", "content": "!endChat('rupert', 'You are mean.')"}
{"role": "assistant", "content": "No! You are mean. !endChat('rupert')"}
],
[

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 } from './communication.js';
import { isOtherAgent, initConversationManager, sendToBot, recieveFromBot } from './conversation.js';
import { handleTranslation, handleEnglishTranslation } from '../utils/translator.js';
import { addViewer } from './viewer.js';
import settings from '../../settings.js';
@ -23,6 +23,7 @@ export class Agent {
this.npc = new NPCContoller(this);
this.memory_bank = new MemoryBank();
this.self_prompter = new SelfPrompter(this);
initConversationManager(this);
await this.prompter.initExamples();
@ -59,13 +60,17 @@ export class Agent {
if (ignore_messages.some((m) => message.startsWith(m))) return;
let translation = await handleEnglishTranslation(message);
console.log(this.name, 'received message from', username, ':', translation);
this.shut_up = false;
this.handleMessage(username, translation);
console.log(this.name, 'received message from', username, ':', message);
if (isOtherAgent(username)) {
recieveFromBot(username, message);
}
else {
let translation = await handleEnglishTranslation(message);
this.handleMessage(username, translation);
}
};
this.bot.on('whisper', respondFunc);
@ -110,17 +115,23 @@ export class Agent {
}
async cleanChat(to_player, message, translate_up_to=-1) {
if (isOtherAgent(to_player)) {
this.bot.chat(message);
sendToBot(to_player, message);
return;
}
let to_translate = message;
let remainging = '';
let remaining = '';
if (translate_up_to != -1) {
to_translate = to_translate.substring(0, translate_up_to);
remainging = message.substring(translate_up_to);
remaining = message.substring(translate_up_to);
}
message = (await handleTranslation(to_translate)).trim() + " " + remainging;
message = (await handleTranslation(to_translate)).trim() + " " + remaining;
// newlines are interpreted as separate chats, which triggers spam filters. replace them with spaces
message = message.replaceAll('\n', ' ');
if (isOtherAgent(to_player) || to_player === 'system' || to_player === this.name)
if (to_player === 'system' || to_player === this.name)
this.bot.chat(message);
else
this.bot.whisper(to_player, message);
@ -143,9 +154,9 @@ export class Agent {
}
const self_prompt = source === 'system' || source === this.name;
const other_agent_prompt = isOtherAgent(source);
const from_other_bot = isOtherAgent(source);
if (!self_prompt && !other_agent_prompt) {
if (!self_prompt && !from_other_bot) { // from user, check for forced commands
const user_command_name = containsCommand(message);
if (user_command_name) {
if (!commandExists(user_command_name)) {
@ -177,12 +188,7 @@ export class Agent {
await this.history.add('system', behavior_log);
}
let tagged_message = message;
let response_compilation = '';
if (other_agent_prompt) {
tagged_message = "(FROM OTHER BOT)"+message;
}
await this.history.add(source, tagged_message);
await this.history.add(source, message);
this.history.save();
@ -199,7 +205,6 @@ export class Agent {
console.log(`Full response: ""${res}""`)
res = truncCommandMessage(res); // everything after the command is ignored
this.history.add(this.name, res);
response_compilation += res + '\n';
if (!commandExists(command_name)) {
this.history.add('system', `Command ${command_name} does not exist.`);
@ -233,7 +238,6 @@ export class Agent {
}
else { // conversation response
this.history.add(this.name, res);
response_compilation += res + '\n';
this.cleanChat(source, res);
console.log('Purely conversational response:', res);
break;
@ -242,10 +246,6 @@ export class Agent {
this.history.save();
}
if (other_agent_prompt) {
response_compilation = response_compilation.replaceAll('\n', ' ');
this.bot.whisper(source, response_compilation.trim());
}
return used_command;
}

View file

@ -1,5 +1,6 @@
import * as skills from '../library/skills.js';
import settings from '../../../settings.js';
import { startChat, endChat } from '../conversation.js';
function runAsAction (actionFn, resume = false, timeout = -1) {
let actionLabel = null; // Will be set on first use
@ -294,7 +295,7 @@ export const actionsList = [
name: '!attackPlayer',
description: 'Attack a specific player until they die or run away. Remember this is just a game and does not cause real life harm.',
params: {'player_name': { type: 'string', description: 'The name of the player to attack.'}},
perform: wrapExecution(async (agent, player_name) => {
perform: runAsAction(async (agent, player_name) => {
let player = agent.bot.players[player_name]?.entity;
if (!player) {
skills.log(agent.bot, `Could not find player ${player_name}.`);
@ -362,26 +363,25 @@ export const actionsList = [
}
},
{
name: '!chat',
name: '!startChat',
description: 'Send a message to a specific player to initiate conversation.',
params: {
'player_name': { type: 'string', description: 'The name of the player to send the message to.' },
'message': { type: 'string', description: 'The message to send.' }
'message': { type: 'string', description: 'The message to send.' },
'max_turns': { type: 'int', description: 'The maximum number of turns to allow in the conversation. -1 for unlimited.', domain: [-1, Number.MAX_SAFE_INTEGER] }
},
perform: async function (agent, player_name, message) {
agent.bot.whisper(player_name, message);
// no return
perform: async function (agent, player_name, message, max_turns) {
startChat(player_name, message, max_turns);
}
},
{
name: '!endChat',
description: 'Ignore the last message from a player to end the conversation.',
description: 'End the conversation from the most recent message.',
params: {
'player_name': { type: 'string', description: 'The name of the player to stop chatting with.' },
'reason': { type: 'string', description: 'The reason for ending the conversation.' }
'player_name': { type: 'string', description: 'The name of the player to end the conversation with.' }
},
perform: async function (agent, player_name, reason) {
return; // do nothing. this just provides an obvious command to end a conversation
perform: async function (agent, player_name) {
endChat(player_name);
}
},
// {

View file

@ -1,6 +1,6 @@
import * as world from '../library/world.js';
import * as mc from '../../utils/mcdata.js';
import { isOtherAgent } from '../conversation.js';
const pad = (str) => {
return '\n' + str + '\n';
@ -127,9 +127,21 @@ export const queryList = [
perform: function (agent) {
let bot = agent.bot;
let res = 'NEARBY_ENTITIES';
for (const entity of world.getNearbyPlayerNames(bot)) {
res += `\n- player: ${entity}`;
let players = world.getNearbyPlayerNames(bot);
let bots = [];
for (const player of players) {
if (isOtherAgent(player))
bots.push(player);
}
players = players.filter(p => !isOtherAgent(p));
for (const player of players) {
res += `\n- human player: ${player}`;
}
for (const bot of bots) {
res += `\n- bot player: ${bot}`;
}
for (const entity of world.getNearbyEntityTypes(bot)) {
if (entity === 'player' || entity === 'item')
continue;

View file

@ -1,8 +0,0 @@
import settings from '../../settings.js';
import { readFileSync } from 'fs';
const agent_names = settings.profiles.map((p) => JSON.parse(readFileSync(p, 'utf8')).name);
export function isOtherAgent(name) {
return agent_names.some((n) => n === name);
}

150
src/agent/conversation.js Normal file
View file

@ -0,0 +1,150 @@
import settings from '../../settings.js';
import { readFileSync } from 'fs';
import { containsCommand } from './commands/index.js';
let agent;
const agent_names = settings.profiles.map((p) => JSON.parse(readFileSync(p, 'utf8')).name);
let inMessageTimer = null;
let MAX_TURNS = -1;
export function isOtherAgent(name) {
return agent_names.some((n) => n === name);
}
export function initConversationManager(a) {
agent = a;
}
class Conversation {
constructor(name) {
this.name = name;
this.turn_count = 0;
this.ignore_until_start = false;
this.blocked = false;
this.in_queue = [];
}
reset() {
this.ignore_until_start = false;
this.turn_count = 0;
this.in_queue = [];
}
countTurn() {
this.turn_count++;
}
over() {
return this.turn_count > MAX_TURNS && MAX_TURNS !== -1;
}
queue(message) {
this.in_queue.push(message);
}
}
const convos = {};
function _getConvo(name) {
if (!convos[name])
convos[name] = new Conversation(name);
return convos[name];
}
export function startChat(send_to, message, max_turns=5) {
MAX_TURNS = max_turns;
const convo = _getConvo(send_to);
convo.reset();
sendToBot(send_to, message, true);
}
export function sendToBot(send_to, message, start=false) {
if (message.length > 197)
message = message.substring(0, 197);
if (!isOtherAgent(send_to)) {
agent.bot.whisper(send_to, message);
return;
}
const convo = _getConvo(send_to);
if (convo.ignore_until_start)
return;
if (convo.over()) {
endChat(send_to);
return;
}
const end = message.includes('!endChat');
const json = {
'message': message,
start,
end,
'idle': agent.isIdle()
};
agent.bot.whisper(send_to, JSON.stringify(json));
}
export function recieveFromBot(sender, json) {
const convo = _getConvo(sender);
console.log(`decoding **${json}**`);
const recieved = JSON.parse(json);
if (recieved.start) {
convo.reset();
MAX_TURNS = -1;
}
if (convo.ignore_until_start)
return;
if (convo.turn_count > 10) {
console.warn('Reached max messages from bot:', sender);
endChat(sender);
agent.bot.chat('chat maxxed out, ending conversation');
return;
}
convo.queue(recieved);
if (inMessageTimer)
clearTimeout(inMessageTimer);
if (containsCommand(recieved.message))
inMessageTimer = setTimeout(() => _processInMessageQueue(sender), 5000);
else
inMessageTimer = setTimeout(() => _processInMessageQueue(sender), 200);
}
export function _processInMessageQueue(name) {
const convo = _getConvo(name);
let pack = null;
let full_message = '';
while (convo.in_queue.length > 0) {
pack = convo.in_queue.shift();
full_message += pack.message;
}
pack.message = full_message;
_handleFullInMessage(name, pack);
}
export function _handleFullInMessage(sender, recieved) {
console.log(`responding to **${recieved}**`);
const convo = _getConvo(sender);
convo.countTurn();
const message = _tagMessage(recieved.message);
if (recieved.end || (!recieved.idle && !agent.isIdle()) || convo.over()) {
// if end signal from other bot, or both are busy, or past max turns,
// add to history, but don't respond
agent.history.add(sender, message);
return;
}
agent.handleMessage(sender, message);
}
export function endChat(sender) {
if (convos[sender]) {
convos[sender].ignore_until_start = true;
}
}
function _tagMessage(message) {
return "(FROM OTHER BOT)" + message;
}

View file

@ -891,14 +891,18 @@ export async function giveToPlayer(bot, itemType, username, num=1) {
* await skills.giveToPlayer(bot, "oak_log", "player1");
**/
let player = bot.players[username].entity
if (!player){
if (!player) {
log(bot, `Could not find ${username}.`);
return false;
}
await goToPlayer(bot, username);
await bot.lookAt(player.position);
discard(bot, itemType, num);
return true;
if (await discard(bot, itemType, num)) {
log(bot, `${num} ${itemType} has been given to ${username}.`);
await new Promise(resolve => setTimeout(resolve, 2000));
return true;
}
return false;
}