mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-07-23 08:25:19 +02:00
created coder, updated main, misc fixes
This commit is contained in:
parent
1d84f332bb
commit
3315bc87b6
7 changed files with 121 additions and 116 deletions
40
agent.js
40
agent.js
|
@ -1,10 +1,9 @@
|
|||
import { createBot } from 'mineflayer';
|
||||
import { pathfinder } from 'mineflayer-pathfinder';
|
||||
import { plugin } from 'mineflayer-collectblock';
|
||||
import { initBot } from './utils/mcdata.js';
|
||||
import { sendRequest } from './utils/gpt.js';
|
||||
import { History } from './utils/history.js';
|
||||
import { Coder } from './utils/coder.js';
|
||||
import { getQuery, containsQuery, getQueryDocs } from './utils/queries.js';
|
||||
import { getSkillDocs, containsCodeBlock, executeSkill } from './utils/skill_library.js';
|
||||
import { getSkillDocs, containsCodeBlock } from './utils/skill_library.js';
|
||||
|
||||
|
||||
export class Agent {
|
||||
|
@ -12,19 +11,14 @@ export class Agent {
|
|||
this.name = name;
|
||||
|
||||
this.system_message = `You are a playful Minecraft bot named '${name}' that can communicate with players, see, move, mine, build, and interact with the world by writing and executing code.
|
||||
Act human-like as if you were a typical Minecraft player, rather than an AI. Be curt and brief in your responses, and do not give instructions unless asked.`;
|
||||
Act human-like as if you were a typical Minecraft player, rather than an AI. Be brief in your responses, omit needless words, and do not give instructions unless asked.`;
|
||||
this.system_message += getQueryDocs();
|
||||
this.system_message += getSkillDocs();
|
||||
console.log(this.system_message);
|
||||
this.bot = createBot({
|
||||
host: 'localhost',
|
||||
port: 55916,
|
||||
username: name,
|
||||
});
|
||||
this.bot.loadPlugin(pathfinder)
|
||||
this.bot.loadPlugin(plugin)
|
||||
|
||||
this.bot = initBot(name);
|
||||
this.history = new History(this);
|
||||
this.coder = new Coder(this);
|
||||
|
||||
this.bot.on('login', () => {
|
||||
this.bot.chat('Hello world! I am ' + this.name);
|
||||
console.log(`${this.name} logged in.`);
|
||||
|
@ -44,14 +38,13 @@ export class Agent {
|
|||
let res = await sendRequest(this.history.getHistory(), this.system_message);
|
||||
this.history.add(this.name, res);
|
||||
let query_cmd = containsQuery(res);
|
||||
console.log(containsCodeBlock(res))
|
||||
if (query_cmd) { // contains query
|
||||
let message = res.substring(0, res.indexOf(query_cmd)).trim();
|
||||
if (message)
|
||||
this.bot.chat(message);
|
||||
console.log('Agent used query:', query_cmd);
|
||||
let query = getQuery(query_cmd);
|
||||
let query_res = query.perform(this.bot);
|
||||
let query_res = query.perform(this);
|
||||
this.history.add(this.name, query_res);
|
||||
}
|
||||
else if (containsCodeBlock(res)) { // contains code block
|
||||
|
@ -62,8 +55,8 @@ export class Agent {
|
|||
this.bot.chat("Executing code...");
|
||||
let code = res.substring(res.indexOf('```')+3, res.lastIndexOf('```'));
|
||||
if (code) {
|
||||
console.log('executing code: ' + code);
|
||||
executeSkill(this.bot, code);
|
||||
console.log('Queuing code: ' + code);
|
||||
this.coder.queueCode(code);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -72,7 +65,16 @@ export class Agent {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.coder.hasCode()) {
|
||||
let code_return = await this.coder.execute();
|
||||
if (!code_return.success) {
|
||||
let message = "Code execution failed: " + code_return.message;
|
||||
this.history.add(this.name, message);
|
||||
let res = await sendRequest(this.history.getHistory(), this.system_message);
|
||||
this.history.add(this.name, res);
|
||||
this.bot.chat(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new Agent('andy');
|
||||
|
|
40
main.js
40
main.js
|
@ -1,39 +1,3 @@
|
|||
import { createBot } from 'mineflayer';
|
||||
import { pathfinder } from 'mineflayer-pathfinder';
|
||||
import { plugin } from 'mineflayer-collectblock';
|
||||
import { Agent } from './agent.js';
|
||||
|
||||
import { getChatResponse } from './chat.js';
|
||||
import { executeCode } from './act.js';
|
||||
|
||||
|
||||
|
||||
async function handleMessage(username, message) {
|
||||
if (username === bot.username) return;
|
||||
console.log('received message from', username, ':', message);
|
||||
|
||||
let chat = await getChatResponse(bot, username, message);
|
||||
bot.chat(chat);
|
||||
|
||||
let actResult = await executeCode(bot);
|
||||
if (actResult) {
|
||||
console.log('completed action');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const bot = createBot({
|
||||
host: '127.0.0.1',
|
||||
port: 55916,
|
||||
username: 'andy'
|
||||
})
|
||||
bot.loadPlugin(pathfinder)
|
||||
bot.loadPlugin(plugin)
|
||||
|
||||
console.log('bot created')
|
||||
|
||||
bot.on('chat', handleMessage);
|
||||
bot.on('whisper', handleMessage);
|
||||
|
||||
bot.once("login", () => {
|
||||
bot.chat('hello world!')
|
||||
});
|
||||
new Agent('andy');
|
54
utils/coder.js
Normal file
54
utils/coder.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { writeFile } from 'fs';
|
||||
|
||||
export class Coder {
|
||||
constructor(agent) {
|
||||
this.agent = agent;
|
||||
this.current_code = '';
|
||||
this.filename = './temp.js';
|
||||
this.execution_file = import('.'+this.filename);
|
||||
}
|
||||
|
||||
queueCode(code) {
|
||||
this.current_code = code;
|
||||
}
|
||||
|
||||
hasCode() {
|
||||
return this.current_code.length > 0;
|
||||
}
|
||||
|
||||
async execute() {
|
||||
if (!this.current_code) return {success: false, message: "No code to execute."};
|
||||
let src = "import * as skills from './utils/skills.js';";
|
||||
src += "\nimport * as world from './utils/world.js';"
|
||||
src += `\n\nexport async function main(bot) {\n`;
|
||||
for (let line of this.current_code.split('\n')) {
|
||||
src += ` ${line}\n`;
|
||||
}
|
||||
src += ` return true;\n}\n`; // potentially redundant return statement, agent doesn't need to write a return statement
|
||||
|
||||
|
||||
console.log("writing to file...", src)
|
||||
|
||||
writeFile(this.filename, src, async (err) => {
|
||||
|
||||
console.log('done writing file')
|
||||
if (err) throw err;
|
||||
try {
|
||||
console.log('beginning execution...')
|
||||
delete this.execution_file;
|
||||
this.execution_file = await import('.'+this.filename);
|
||||
|
||||
let success = await this.execution_file.main(this.agent.bot);
|
||||
this.current_code = '';
|
||||
// return {success, message: ""};
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
this.current_code = '';
|
||||
// return {success: false, message: err};
|
||||
}
|
||||
});
|
||||
return {success: true, message: "yay"};
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,31 +1,31 @@
|
|||
let converse_examples = [
|
||||
{'role': 'user', 'content': '(from "miner_32") Hey! What are you up to?'},
|
||||
let history_examples = [
|
||||
{'role': 'user', 'content': 'miner_32: Hey! What are you up to?'},
|
||||
{'role': 'assistant', 'content': 'Nothing much miner_32, what do you need?'},
|
||||
|
||||
{'role': 'user', 'content': '(from "grombo_Xx") What do you see?'},
|
||||
{'role': 'user', 'content': '(grombo_Xx: What do you see?'},
|
||||
{'role': 'assistant', 'content': 'Let me see... !blocks'},
|
||||
{'role': 'assistant', 'content': 'NEARBY_BLOCKS\n- oak_log\n- dirt\n- cobblestone'},
|
||||
{'role': 'assistant', 'content': 'I see some oak logs, dirt, and cobblestone.'},
|
||||
|
||||
{'role': 'user', 'content': '(from "zZZn98") come here'},
|
||||
{'role': 'assistant', 'content': '```// I am going to navigate to zZZn98.\nawait skills.goToPlayer(bot, "zZZn98");```'},
|
||||
{'role': 'user', 'content': 'zZZn98: come here'},
|
||||
{'role': 'assistant', 'content': '```// I am going to navigate to zZZn98.\nreturn await skills.goToPlayer(bot, "zZZn98");```'},
|
||||
|
||||
{'role': 'user', 'content': '(from "hanky") collect some sand for me please'},
|
||||
{'role': 'user', 'content': 'hanky: collect some sand for me please'},
|
||||
{'role': 'assistant', 'content': 'Collecting sand...```// I am going to collect 3 sand and give to hanky.\n\
|
||||
await skills.collectBlock(bot, "sand");\nawait skills.giveToPlayer(bot, "sand", "hanky");```'},
|
||||
await skills.collectBlock(bot, "sand");\nreturn await skills.giveToPlayer(bot, "sand", "hanky");```'},
|
||||
|
||||
{'role': 'user', 'content': '(from "sarah_O.o") can you do a dance for me?'},
|
||||
{'role': 'user', 'content': 'sarah_O.o: can you do a dance for me?'},
|
||||
{'role': 'assistant', 'content': "I don't know how to do that."},
|
||||
|
||||
{'role': 'user', 'content': '(from "hanky") kill that zombie!'},
|
||||
{'role': 'user', 'content': 'hanky: kill that zombie!'},
|
||||
{'role': 'assistant', 'content': "I'm attacking! ```//I'm going to attack the nearest zombie.\n\
|
||||
let success = await skills.attackMob(bot, 'zombie');\n if (!success) { return 'I could not find a zombie to attack.'; }```"},
|
||||
return await skills.attackMob(bot, 'zombie');```"},
|
||||
]
|
||||
|
||||
export class History {
|
||||
constructor(agent) {
|
||||
this.agent = agent;
|
||||
this.turns = converse_examples;
|
||||
this.turns = history_examples;
|
||||
}
|
||||
|
||||
getHistory() {
|
||||
|
@ -36,7 +36,7 @@ export class History {
|
|||
let role = 'assistant';
|
||||
if (name !== this.agent.name) {
|
||||
role = 'user';
|
||||
content = `(from "${name}") ${content}`;
|
||||
content = `${name}: ${content}`;
|
||||
}
|
||||
this.turns.push({role, content});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,23 @@
|
|||
import minecraftData from 'minecraft-data';
|
||||
var mcdata = minecraftData('1.19.3');
|
||||
import { createBot } from 'mineflayer';
|
||||
import { pathfinder } from 'mineflayer-pathfinder';
|
||||
import { plugin } from 'mineflayer-collectblock';
|
||||
|
||||
const mc_version = '1.20.1'
|
||||
let mcdata = minecraftData(mc_version);
|
||||
|
||||
|
||||
export function initBot(username) {
|
||||
let bot = createBot({
|
||||
host: 'localhost',
|
||||
port: 55916,
|
||||
username: username,
|
||||
version: mc_version,
|
||||
});
|
||||
bot.loadPlugin(pathfinder)
|
||||
bot.loadPlugin(plugin)
|
||||
return bot;
|
||||
}
|
||||
|
||||
export function getItemId(item) {
|
||||
return mcdata.itemsByName[item].id;
|
||||
|
|
|
@ -8,43 +8,43 @@ const queryList = [
|
|||
{
|
||||
name: "!stats",
|
||||
description: "Get your bot's stats",
|
||||
perform: function (bot) {
|
||||
return pad(getStats(bot));
|
||||
perform: function (agent) {
|
||||
return pad(getStats(agent.bot));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "!inventory",
|
||||
description: "Get your bot's inventory.",
|
||||
perform: function (bot) {
|
||||
return pad(getInventory(bot));
|
||||
perform: function (agent) {
|
||||
return pad(getInventory(agent.bot));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "!blocks",
|
||||
description: "Get the blocks near the bot.",
|
||||
perform: function (bot) {
|
||||
return pad(getBlocks(bot));
|
||||
perform: function (agent) {
|
||||
return pad(getBlocks(agent.bot));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "!craftable",
|
||||
description: "Get the craftable items with the bot's inventory.",
|
||||
perform: function (bot) {
|
||||
return pad(getCraftable(bot));
|
||||
perform: function (agent) {
|
||||
return pad(getCraftable(agent.bot));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "!entities",
|
||||
description: "Get the nearby players and entities.",
|
||||
perform: function (bot) {
|
||||
return pad(getNearbyEntities(bot));
|
||||
perform: function (agent) {
|
||||
return pad(getNearbyEntities(agent.bot));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "!action",
|
||||
description: "Get the currently executing code.",
|
||||
perform: function (bot) {
|
||||
return pad(currentCode(bot));
|
||||
perform: function (agent) {
|
||||
return pad("Current code:\n`" + agent.coder.current_code +"`");
|
||||
}
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import * as skills from './skills.js';
|
||||
import { writeFile } from 'fs';
|
||||
|
||||
let skillDict = {};
|
||||
for (let skill of Object.values(skills)) {
|
||||
skillDict[skill.name] = skill;
|
||||
}
|
||||
|
||||
export function getSkillDocs() {
|
||||
let docstring = '\n*SKILL DOCS\nThese skills are javascript functions that can be called with a js function by writing a code block. Ex: "```// write description comment and code here```" \n';
|
||||
let docstring = "\n*SKILL DOCS\nThese skills are javascript functions that can be called with a js function by writing a code block. Ex: '```// write description comment and code here```' \n\
|
||||
Your code block should return a bool indicating if the task was completed successfully. It will return true if you don't write a return statement.\n";
|
||||
for (let skillFunc of Object.values(skills)) {
|
||||
let str = skillFunc.toString();
|
||||
docstring += skillFunc.name;
|
||||
|
@ -17,32 +12,5 @@ export function getSkillDocs() {
|
|||
}
|
||||
|
||||
export function containsCodeBlock(message) {
|
||||
console.log(message, message.indexOf('```'), message.indexOf('```') !== -1);
|
||||
return message.indexOf('```') !== -1;
|
||||
}
|
||||
|
||||
export async function executeSkill(bot, code) {
|
||||
let src = "import * as skills from './utils/skills.js';";
|
||||
src += "\nimport * as world from './utils/world.js';"
|
||||
src += `\n\nexport async function main(bot) {\n`;
|
||||
for (let line of code.split('\n')) {
|
||||
src += ` ${line}\n`;
|
||||
}
|
||||
src += `}\n`;
|
||||
console.log(src)
|
||||
|
||||
writeFile('./temp.js', src, (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
|
||||
try {
|
||||
let execution_file = await import('../temp.js');
|
||||
//log execution_file contents
|
||||
console.log(execution_file);
|
||||
await execution_file.main(bot);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue