mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-03 13:55:36 +02:00
checkpoint
This commit is contained in:
parent
3b07a63b48
commit
b717dabd40
8 changed files with 231 additions and 63 deletions
19
andy.json
19
andy.json
|
@ -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')"}
|
||||
],
|
||||
|
||||
[
|
||||
|
|
17
andy2.json
17
andy2.json
|
@ -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')"}
|
||||
],
|
||||
|
||||
[
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
// {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
150
src/agent/conversation.js
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue