This commit is contained in:
aeromechanic 2025-05-25 08:06:31 +01:00 committed by GitHub
commit c44ff42177
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 114 additions and 3 deletions

2
.gitignore vendored
View file

@ -1,4 +1,6 @@
.vscode/
.fslckout
._*
.idea/
node_modules/
package-lock.json

View file

@ -1,6 +1,6 @@
{
"name": "andy",
"model": "gpt-4o-mini"
"model": "qwen-max"
}

View file

@ -45,6 +45,7 @@ const settings = {
"narrate_behavior": true, // chat simple automatic actions ('Picking up item!')
"chat_bot_messages": true, // publicly chat messages to other bots
"log_all_prompts": false, // log ALL prompts to file
// "plugins" : ["Dance"], // plugin will be loaded if and only if it's name appears here
}
// these environment variables override certain settings

Binary file not shown.

View file

@ -7,6 +7,7 @@ import { initBot } from '../utils/mcdata.js';
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction, blacklistCommands } from './commands/index.js';
import { ActionManager } from './action_manager.js';
import { NPCContoller } from './npc/controller.js';
import { PluginManager } from './plugin.js';
import { MemoryBank } from './memory_bank.js';
import { SelfPrompter } from './self_prompter.js';
import convoManager from './conversation.js';
@ -39,6 +40,8 @@ export class Agent {
this.coder = new Coder(this);
console.log('Initializing npc controller...');
this.npc = new NPCContoller(this);
console.log('Initializing plugin manager...');
this.plugin = new PluginManager(this);
console.log('Initializing memory bank...');
this.memory_bank = new MemoryBank();
console.log('Initializing self prompter...');
@ -457,6 +460,8 @@ export class Agent {
// Init NPC controller
this.npc.init();
// Init plugins manager
this.plugin.init();
// This update loop ensures that each update() is called one at a time, even if it takes longer than the interval
const INTERVAL = 300;

View file

@ -2,8 +2,7 @@ import * as skills from '../library/skills.js';
import settings from '../../../settings.js';
import convoManager from '../conversation.js';
function runAsAction (actionFn, resume = false, timeout = -1) {
export function runAsAction (actionFn, resume = false, timeout = -1) {
let actionLabel = null; // Will be set on first use
const wrappedAction = async function (agent, ...args) {

View file

@ -26,6 +26,18 @@ export function blacklistCommands(commands) {
}
}
export function addPluginActions(plugin, actions) {
for (let action of actions) {
if (commandMap[action.name]) {
console.log(`Command already exists. Can't add ${action.name} from plugin ${plugin}.`)
} else {
commandMap[action.name] = action;
commandList.push(action);
actionsList.push(action);
}
}
}
const commandRegex = /!(\w+)(?:\(((?:-?\d+(?:\.\d+)?|true|false|"[^"]*")(?:\s*,\s*(?:-?\d+(?:\.\d+)?|true|false|"[^"]*"))*)\))?/
const argRegex = /-?\d+(?:\.\d+)?|true|false|"[^"]*"/g;

59
src/agent/plugin.js Normal file
View file

@ -0,0 +1,59 @@
import { readdirSync, readFileSync } from 'fs';
import { join, relative, isAbsolute } from 'path';
import { pathToFileURL } from 'url';
import settings from '../../settings.js';
import { addPluginActions } from './commands/index.js';
export class PluginManager {
constructor(agent) {
this.agent = agent;
this.plugins = {};
}
init() {
this.importPlugins()
.then((plugins) => {
this.plugins = plugins;
for (let plugin in this.plugins) {
addPluginActions(plugin, this.plugins[plugin].getPluginActions());
}
console.log("Load plugins:", Object.keys(this.plugins).join(", "));
})
.catch((error) => {
console.error("Error importing plugins:", error);
});
}
async importPlugin(dir, name) {
let path = join(dir, name, "main.js");
let instance = null;
try {
const plugin = await import(pathToFileURL(path).href);
if (plugin.PluginInstance) {
instance = new plugin.PluginInstance(this.agent);
instance.init();
} else {
console.error(`Can't find PluginInstance in ${path}.`);
}
} catch (error) {
console.error(`Error import plugin ${path}:`, error);
}
return instance;
}
async importPlugins(dir = "src/plugins") {
let plugins = {};
try {
for (let file of readdirSync(dir, { withFileTypes: true })) {
if (settings.plugins && settings.plugins.includes(file.name) && file.isDirectory && !file.name.startsWith('.')) {
let instance = await this.importPlugin(dir, file.name);
plugins[file.name] = instance;
}
}
} catch (error) {
console.error(`Error importing plugins in ${dir}:`, error);
}
return plugins;
}
}

33
src/plugins/Dance/main.js Normal file
View file

@ -0,0 +1,33 @@
import { Vec3 } from 'vec3';
import { readdirSync, readFileSync } from 'fs';
import * as skills from '../../agent/library/skills.js';
import * as world from '../../agent/library/world.js';
import { runAsAction } from '../../agent/commands/actions.js';
import * as mc from '../../utils/mcdata.js';
export class PluginInstance {
constructor(agent) {
this.agent = agent;
}
init() {
}
getPluginActions() {
return [
{
name: '!dancePoping',
description: 'Dance poping.',
params: {
'duration': {type: 'int', description: 'The time duration (in millions seconds, i.e. 1000 for 1 second) of dancing.'},
},
perform: runAsAction(async (agent, duration) => {
this.agent.bot.chat("I am dancing~");
this.agent.bot.setControlState("jump", true);
await new Promise((resolve) => setTimeout(resolve, duration));
this.agent.bot.setControlState("jump", false);
})
},
]
}
}