Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: character file plugins import error #2025

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
92 changes: 90 additions & 2 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,9 @@ import net from "net";
import path from "path";
import { fileURLToPath } from "url";
import yargs from "yargs";

import { Plugin } from "@elizaos/core";
import { dominosPlugin } from "@elizaos/plugin-dominos";
import createNFTCollectionsPlugin from "@elizaos/plugin-nft-collections";

const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
const __dirname = path.dirname(__filename); // get the name of the directory
Expand All @@ -127,6 +129,10 @@ const logFetch = async (url: string, options: any) => {
return fetch(url, options);
};

function isAllStrings(arr: unknown[]): boolean {
return Array.isArray(arr) && arr.every((item) => typeof item === "string");
}

export function parseArguments(): {
character?: string;
characters?: string;
Expand Down Expand Up @@ -307,7 +313,89 @@ export async function loadCharacters(
}

try {
const character: Character = await loadCharacter(resolvedPath);
const character = JSON.parse(content);
validateCharacterConfig(character);

// .id isn't really valid
const characterId = character.id || character.name;
const characterPrefix = `CHARACTER.${characterId.toUpperCase().replace(/ /g, "_")}.`;

const characterSettings = Object.entries(process.env)
.filter(([key]) => key.startsWith(characterPrefix))
.reduce((settings, [key, value]) => {
const settingKey = key.slice(characterPrefix.length);
return { ...settings, [settingKey]: value };
}, {});

if (Object.keys(characterSettings).length > 0) {
character.settings = character.settings || {};
character.settings.secrets = {
...characterSettings,
...character.settings.secrets,
};
}

function isPlugin(value: any): value is Plugin {
return (
typeof value === "object" &&
value !== null &&
typeof value.name === "string" &&
typeof value.description === "string" &&
(value.actions === undefined ||
Array.isArray(value.actions)) &&
(value.providers === undefined ||
Array.isArray(value.providers)) &&
(value.evaluators === undefined ||
Array.isArray(value.evaluators)) &&
(value.services === undefined ||
Array.isArray(value.services)) &&
(value.clients === undefined ||
Array.isArray(value.clients))
);
}

// Handle plugins
if (isAllStrings(character.plugins)) {
mbcse marked this conversation as resolved.
Show resolved Hide resolved
elizaLogger.info("Plugins are: ", character.plugins);

const importedPlugins = await Promise.all(
character.plugins.map(async (plugin) => {
try {
// Dynamically import the plugin
const importedPlugin = await import(plugin);

// Check if there's a default export
if (importedPlugin.default) {
return importedPlugin.default;
}

// Check other exports for potential plugins
const possiblePlugins = [];
for (const [key, value] of Object.entries(
importedPlugin
)) {
// Check if the export matches the plugin type
if (isPlugin(value)) {
possiblePlugins.push(value);
}
}

return possiblePlugins.length > 0
? possiblePlugins
: null;
} catch (error) {
elizaLogger.error(
`Failed to import plugin "${plugin}":`,
error
);
return null; // Return null for failed imports
}
})
);

// Flatten and filter out null or empty plugin arrays
character.plugins = importedPlugins.flat().filter(Boolean);
}

loadedCharacters.push(character);
elizaLogger.info(
Expand Down
11 changes: 10 additions & 1 deletion packages/core/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,20 @@ export class AgentRuntime implements IAgentRuntime {

this.token = opts.token;

this.plugins = [
const combinedPlugins = [
...(opts.character?.plugins ?? []),
...(opts.plugins ?? []),
];

const seen = new Set();
this.plugins = combinedPlugins.filter((plugin) => {
if (seen.has(plugin.name)) {
return false;
}
seen.add(plugin.name);
return true;
});

this.plugins.forEach((plugin) => {
plugin.actions?.forEach((action) => {
this.registerAction(action);
Expand Down
Loading