Skip to content

Commit

Permalink
Add postgres, supabase and update plugin loading
Browse files Browse the repository at this point in the history
  • Loading branch information
lalalune committed Feb 9, 2025
1 parent fdfdba6 commit c15254d
Show file tree
Hide file tree
Showing 30 changed files with 3,409 additions and 270 deletions.
Binary file modified bun.lockb
Binary file not shown.
17 changes: 4 additions & 13 deletions packages/agent/src/defaultCharacter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export const defaultCharacter: Character = {
plugins: [
"@elizaos/plugin-node",
"@elizaos/plugin-bootstrap",
"@elizaos/plugin-anthropic",
"@elizaos/plugin-openai",
"@elizaos/plugin-local-ai",
],
settings: {
secrets: {},
Expand Down Expand Up @@ -530,16 +533,4 @@ export const defaultCharacter: Character = {
"provocative",
],
extends: [],
};

if (process.env.ANTHROPIC_API_KEY) {
defaultCharacter.plugins.push("@elizaos/plugin-anthropic");
}

if (process.env.OPENAI_API_KEY) {
defaultCharacter.plugins.push("@elizaos/plugin-openai");
}

if(!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_API_KEY) {
defaultCharacter.plugins.push("@elizaos/plugin-local-ai");
}
};
101 changes: 13 additions & 88 deletions packages/agent/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
// dotenv
import dotenv from "dotenv";
dotenv.config({ path: "../../.env" });

import {
type Adapter,
AgentRuntime,
CacheManager,
CacheStore,
type Character,
type ClientInstance,
DbCacheAdapter,
type IAgentRuntime,
type IDatabaseAdapter,
type IDatabaseCacheAdapter,
logger,
ModelClass,
parseBooleanFromText,
settings,
stringToUuid,
validateCharacterConfig
} from "@elizaos/core";
import { bootstrapPlugin } from "@elizaos/plugin-bootstrap";
import fs from "node:fs";
import net from "node:net";
import path from "node:path";
Expand All @@ -25,10 +26,6 @@ import yargs from "yargs";
import { defaultCharacter } from "./defaultCharacter.ts";
import { CharacterServer } from "./server";

// dotenv
import dotenv from "dotenv";
dotenv.config({ path: "../../.env" });

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 @@ -38,7 +35,7 @@ export const wait = (minTime = 1000, maxTime = 3000) => {
return new Promise((resolve) => setTimeout(resolve, waitTime));
};

const logFetch = async (url: string, options: any) => {
export const logFetch = async (url: string, options: any) => {
logger.debug(`Fetching ${url}`);
// Disabled to avoid disclosure of sensitive information such as API keys
// logger.debug(JSON.stringify(options, null, 2));
Expand Down Expand Up @@ -67,14 +64,15 @@ export function parseArguments(): {
}
}

function tryLoadFile(filePath: string): string | null {
export function tryLoadFile(filePath: string): string | null {
try {
return fs.readFileSync(filePath, "utf8");
} catch (e) {
return null;
}
}
function mergeCharacters(base: Character, child: Character): Character {

export function mergeCharacters(base: Character, child: Character): Character {
const mergeObjects = (baseObj: any, childObj: any) => {
const result: any = {};
const keys = new Set([
Expand Down Expand Up @@ -107,7 +105,6 @@ function mergeCharacters(base: Character, child: Character): Character {
return mergeObjects(base, child);
}


async function loadCharactersFromUrl(url: string): Promise<Character[]> {
try {
const response = await fetch(url);
Expand Down Expand Up @@ -154,7 +151,6 @@ async function jsonToCharacter(
};
}
// Handle plugins
character.plugins = await handlePluginImporting(character.plugins);
if (character.extends) {
logger.info(
`Merging ${character.name} character with parent characters`
Expand Down Expand Up @@ -304,73 +300,9 @@ export async function loadCharacters(
return loadedCharacters;
}

async function handlePluginImporting(plugins: string[]) {
if (plugins.length > 0) {
logger.info("Plugins are: ", plugins);
const importedPlugins = await Promise.all(
plugins.map(async (plugin) => {
try {
const importedPlugin = await import(plugin);
const functionName =
`${plugin
.replace("@elizaos/plugin-", "")
.replace(/-./g, (x) => x[1].toUpperCase())}Plugin`; // Assumes plugin function is camelCased with Plugin suffix
return (
importedPlugin.default || importedPlugin[functionName]
);
} catch (importError) {
logger.error(
`Failed to import plugin: ${plugin}`,
importError
);
return []; // Return null for failed imports
}
})
);
return importedPlugins;
}
return [];
}

// also adds plugins from character file into the runtime
export async function initializeClients(
character: Character,
runtime: IAgentRuntime
) {
// each client can only register once
// and if we want two we can explicitly support it
const clients: ClientInstance[] = [];
// const clientTypes = clients.map((c) => c.name);
// logger.log("initializeClients", clientTypes, "for", character.name);

// load the character plugins dymamically from string
const plugins = await handlePluginImporting(character.plugins);

if (plugins?.length > 0) {
for (const plugin of plugins) {
if (plugin.clients) {
for (const client of plugin.clients) {
const startedClient = await client.start(runtime);
logger.debug(
`Initializing client: ${client.name}`
);
clients.push(startedClient);
}
}
if (plugin.handlers) {
for (const [modelClass, handler] of Object.entries(plugin.handlers)) {
runtime.registerHandler(modelClass as ModelClass, handler as (params: any) => Promise<any>);
}
}
}
}

runtime.clients = clients;
}

export async function createAgent(
character: Character,
): Promise<AgentRuntime> {
): Promise<IAgentRuntime> {
logger.log(`Creating runtime for character ${character.name}`);
return new AgentRuntime({
character,
Expand Down Expand Up @@ -411,7 +343,7 @@ function initializeCache(
}
}

async function findDatabaseAdapter(runtime: AgentRuntime) {
async function findDatabaseAdapter(runtime: IAgentRuntime) {
const { adapters } = runtime;
let adapter: Adapter | undefined;
// if not found, default to sqlite
Expand All @@ -434,13 +366,13 @@ async function findDatabaseAdapter(runtime: AgentRuntime) {
async function startAgent(
character: Character,
characterServer: CharacterServer
): Promise<AgentRuntime> {
): Promise<IAgentRuntime> {
let db: IDatabaseAdapter & IDatabaseCacheAdapter;
try {
character.id ??= stringToUuid(character.name);
character.username ??= character.name;

const runtime: AgentRuntime = await createAgent(
const runtime: IAgentRuntime = await createAgent(
character
);

Expand All @@ -461,9 +393,6 @@ async function startAgent(
// start services/plugins/process knowledge
await runtime.initialize();

// start assigned clients
await initializeClients(character, runtime);

// add to container
characterServer.registerAgent(runtime);

Expand Down Expand Up @@ -538,11 +467,7 @@ const startAgents = async () => {
// upload some agent functionality into characterServer
// XXX TODO: is this still used?
characterServer.startAgent = async (character) => {
throw new Error('not implemented');

// Handle plugins
character.plugins = await handlePluginImporting(character.plugins);

logger.info(`Starting agent for character ${character.name}`);
// wrap it so we don't have to inject characterServer later
return startAgent(character, characterServer);
};
Expand Down
129 changes: 4 additions & 125 deletions packages/cli/src/commands/agent-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { getConfig } from "@/src/utils/get-config"
import { handleError } from "@/src/utils/handle-error"
import { logger } from "@/src/utils/logger"
import { getPluginRepository, getRegistryIndex } from "@/src/utils/registry"
import { Database, SqliteDatabaseAdapter } from "@elizaos-plugins/sqlite"
import { Command } from "commander"
import { execa } from "execa"

export const agentPlugin = new Command()
.name("plugin")
Expand All @@ -22,31 +19,10 @@ agentPlugin
logger.error("No project.json found. Please run init first.")
process.exit(1)
}

// load all agents from databaseAdapter

// Initialize DB adapter
const db = new Database((config.database.config as any).path)
const adapter = new SqliteDatabaseAdapter(db)
await adapter.init()

// Get agent
const account = await adapter.getAccountById(agentId)
if (!account) {
logger.error(`Agent ${agentId} not found`)
process.exit(1)
}

const plugins = account.details?.plugins || []

if (plugins.length === 0) {
logger.info(`No plugins installed for agent ${account.name}`)
} else {
logger.info(`\nPlugins for agent ${account.name}:`)
for (const plugin of plugins) {
logger.info(` ${plugin}`)
}
}

await adapter.close()
} catch (error) {
handleError(error)
}
Expand All @@ -66,57 +42,8 @@ agentPlugin
process.exit(1)
}

// Check if plugin exists in registry
const registry = await getRegistryIndex(config.plugins.registry)
const repo = await getPluginRepository(pluginName)
if (!repo) {
logger.error(`Plugin ${pluginName} not found in registry`)
process.exit(1)
}

// Initialize DB adapter
const db = new Database(config.database.config.path)
const adapter = new SqliteDatabaseAdapter(db)
await adapter.init()

// Get agent
const account = await adapter.getAccountById(agentId)
if (!account) {
logger.error(`Agent ${agentId} not found`)
process.exit(1)
}

// Update agent plugins
const plugins = new Set(account.details?.plugins || [])
if (plugins.has(pluginName)) {
logger.warn(`Plugin ${pluginName} is already installed for agent ${account.name}`)
process.exit(0)
}

plugins.add(pluginName)
// TODO: Do something here

// Update agent account
await adapter.updateAccount({
...account,
details: {
...account.details,
plugins: Array.from(plugins)
}
})

// Install plugin package if not already installed
if (!config.plugins.installed.includes(pluginName)) {
logger.info(`Installing ${pluginName}...`)
await execa("bun", ["add", repo], {
cwd,
stdio: "inherit"
})
config.plugins.installed.push(pluginName)
}

logger.success(`Added plugin ${pluginName} to agent ${account.name}`)

await adapter.close()
} catch (error) {
handleError(error)
}
Expand All @@ -136,56 +63,8 @@ agentPlugin
process.exit(1)
}

// Initialize DB adapter
const db = new Database(config.database.config.path)
const adapter = new SqliteDatabaseAdapter(db)
await adapter.init()

// Get agent
const account = await adapter.getAccountById(agentId)
if (!account) {
logger.error(`Agent ${agentId} not found`)
process.exit(1)
}

// Update agent plugins
const plugins = new Set(account.details?.plugins || [])
if (!plugins.has(pluginName)) {
logger.warn(`Plugin ${pluginName} is not installed for agent ${account.name}`)
process.exit(0)
}

plugins.delete(pluginName)

// Update agent account
await adapter.updateAccount({
...account,
details: {
...account.details,
plugins: Array.from(plugins)
}
})

// Check if plugin is still used by other agents
const allAgents = await adapter.getAgents()
const stillInUse = allAgents.some(other =>
other.id !== agentId &&
other.details?.plugins?.includes(pluginName)
)

// If plugin is not used by any other agent, remove it
if (!stillInUse) {
logger.info(`Removing unused plugin ${pluginName}...`)
await execa("bun", ["remove", pluginName], {
cwd,
stdio: "inherit"
})
config.plugins.installed = config.plugins.installed.filter(p => p !== pluginName)
}

logger.success(`Removed plugin ${pluginName} from agent ${account.name}`)
// TODO: Some stuff here

await adapter.close()
} catch (error) {
handleError(error)
}
Expand Down
Loading

0 comments on commit c15254d

Please sign in to comment.