Skip to content

Commit ccd70d4

Browse files
authored
Merge pull request #1029 from odilitime/rest-upgrade
feat: client-discord stop implementation / agent improvements
2 parents f4d8dfe + 4465102 commit ccd70d4

File tree

5 files changed

+341
-264
lines changed

5 files changed

+341
-264
lines changed

agent/src/index.ts

+35-17
Original file line numberDiff line numberDiff line change
@@ -323,42 +323,53 @@ function initializeDatabase(dataDir: string) {
323323
}
324324
}
325325

326+
// also adds plugins from character file into the runtime
326327
export async function initializeClients(
327328
character: Character,
328329
runtime: IAgentRuntime
329330
) {
330-
const clients = [];
331-
const clientTypes =
331+
// each client can only register once
332+
// and if we want two we can explicitly support it
333+
const clients: Record<string, any> = {};
334+
const clientTypes:string[] =
332335
character.clients?.map((str) => str.toLowerCase()) || [];
336+
elizaLogger.log('initializeClients', clientTypes, 'for', character.name)
333337

334338
if (clientTypes.includes("auto")) {
335339
const autoClient = await AutoClientInterface.start(runtime);
336-
if (autoClient) clients.push(autoClient);
340+
if (autoClient) clients.auto = autoClient;
337341
}
338342

339343
if (clientTypes.includes("discord")) {
340-
clients.push(await DiscordClientInterface.start(runtime));
344+
const discordClient = await DiscordClientInterface.start(runtime);
345+
if (discordClient) clients.discord = discordClient;
341346
}
342347

343348
if (clientTypes.includes("telegram")) {
344349
const telegramClient = await TelegramClientInterface.start(runtime);
345-
if (telegramClient) clients.push(telegramClient);
350+
if (telegramClient) clients.telegram = telegramClient;
346351
}
347352

348353
if (clientTypes.includes("twitter")) {
349354
TwitterClientInterface.enableSearch = !isFalsish(getSecret(character, "TWITTER_SEARCH_ENABLE"));
350-
const twitterClients = await TwitterClientInterface.start(runtime);
351-
clients.push(twitterClients);
355+
const twitterClient = await TwitterClientInterface.start(runtime);
356+
if (twitterClient) clients.twitter = twitterClient;
352357
}
353358

354359
if (clientTypes.includes("farcaster")) {
355-
const farcasterClients = new FarcasterAgentClient(runtime);
356-
farcasterClients.start();
357-
clients.push(farcasterClients);
360+
// why is this one different :(
361+
const farcasterClient = new FarcasterAgentClient(runtime);
362+
if (farcasterClient) {
363+
farcasterClient.start();
364+
clients.farcaster = farcasterClient;
365+
}
358366
}
359367

368+
elizaLogger.log('client keys', Object.keys(clients));
369+
360370
if (character.plugins?.length > 0) {
361371
for (const plugin of character.plugins) {
372+
// if plugin has clients, add those..
362373
if (plugin.clients) {
363374
for (const client of plugin.clients) {
364375
clients.push(await client.start(runtime));
@@ -387,7 +398,7 @@ function isFalsish(input: any): boolean {
387398
}
388399

389400
function getSecret(character: Character, secret: string) {
390-
return character.settings.secrets?.[secret] || process.env[secret];
401+
return character.settings?.secrets?.[secret] || process.env[secret];
391402
}
392403

393404
let nodePlugin: any | undefined;
@@ -397,7 +408,7 @@ export async function createAgent(
397408
db: IDatabaseAdapter,
398409
cache: ICacheManager,
399410
token: string
400-
) {
411+
):AgentRuntime {
401412
elizaLogger.success(
402413
elizaLogger.successesTitle,
403414
"Creating runtime for character",
@@ -430,6 +441,7 @@ export async function createAgent(
430441
modelProvider: character.modelProvider,
431442
evaluators: [],
432443
character,
444+
// character.plugins are handled when clients are added
433445
plugins: [
434446
bootstrapPlugin,
435447
getSecret(character, "CONFLUX_CORE_PRIVATE_KEY")
@@ -500,7 +512,7 @@ function initializeDbCache(character: Character, db: IDatabaseCacheAdapter) {
500512
return cache;
501513
}
502514

503-
async function startAgent(character: Character, directClient) {
515+
async function startAgent(character: Character, directClient):AgentRuntime {
504516
let db: IDatabaseAdapter & IDatabaseCacheAdapter;
505517
try {
506518
character.id ??= stringToUuid(character.name);
@@ -519,15 +531,21 @@ async function startAgent(character: Character, directClient) {
519531
await db.init();
520532

521533
const cache = initializeDbCache(character, db);
522-
const runtime = await createAgent(character, db, cache, token);
534+
const runtime:AgentRuntime = await createAgent(character, db, cache, token);
523535

536+
// start services/plugins/process knowledge
524537
await runtime.initialize();
525538

526-
const clients = await initializeClients(character, runtime);
539+
// start assigned clients
540+
runtime.clients = await initializeClients(character, runtime);
527541

542+
// add to container
528543
directClient.registerAgent(runtime);
529544

530-
return clients;
545+
// report to console
546+
elizaLogger.debug(`Started ${character.name} as ${runtime.agentId}`)
547+
548+
return runtime;
531549
} catch (error) {
532550
elizaLogger.error(
533551
`Error starting agent for character ${character.name}:`,
@@ -571,8 +589,8 @@ const startAgents = async () => {
571589
});
572590
}
573591

574-
elizaLogger.log("Chat started. Type 'exit' to quit.");
575592
if (!args["non-interactive"]) {
593+
elizaLogger.log("Chat started. Type 'exit' to quit.");
576594
chat();
577595
}
578596
};

packages/client-discord/src/index.ts

+26-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
import { getEmbeddingZeroVector } from "@ai16z/eliza";
2-
import { Character, Client as ElizaClient, IAgentRuntime } from "@ai16z/eliza";
3-
import { stringToUuid } from "@ai16z/eliza";
4-
import { elizaLogger } from "@ai16z/eliza";
1+
import {
2+
getEmbeddingZeroVector,
3+
stringToUuid,
4+
elizaLogger,
5+
Character,
6+
Client as ElizaClient,
7+
IAgentRuntime,
8+
} from "@ai16z/eliza";
59
import {
610
Client,
711
Events,
@@ -111,6 +115,16 @@ export class DiscordClient extends EventEmitter {
111115
);
112116
}
113117

118+
async stop() {
119+
try {
120+
// disconnect websocket
121+
// this unbinds all the listeners
122+
await this.client.destroy();
123+
} catch(e) {
124+
elizaLogger.error('client-discord instance stop err', e);
125+
}
126+
}
127+
114128
private async onClientReady(readyClient: { user: { tag: any; id: any } }) {
115129
elizaLogger.success(`Logged in as ${readyClient.user?.tag}`);
116130

@@ -388,7 +402,13 @@ export function startDiscord(runtime: IAgentRuntime) {
388402

389403
export const DiscordClientInterface: ElizaClient = {
390404
start: async (runtime: IAgentRuntime) => new DiscordClient(runtime),
391-
stop: async (_runtime: IAgentRuntime) => {
392-
console.warn("Discord client does not support stopping yet");
405+
stop: async (runtime: IAgentRuntime) => {
406+
try {
407+
// stop it
408+
elizaLogger.log('Stopping discord client', runtime.agentId)
409+
await runtime.clients.discord.stop()
410+
} catch(e) {
411+
elizaLogger.error('client-discord interface stop error', e);
412+
}
393413
},
394414
};

packages/core/src/runtime.ts

+20
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ export class AgentRuntime implements IAgentRuntime {
142142
services: Map<ServiceType, Service> = new Map();
143143
memoryManagers: Map<string, IMemoryManager> = new Map();
144144
cacheManager: ICacheManager;
145+
clients: Record<string, any>;
145146

146147
registerMemoryManager(manager: IMemoryManager): void {
147148
if (!manager.tableName) {
@@ -405,6 +406,25 @@ export class AgentRuntime implements IAgentRuntime {
405406
}
406407
}
407408

409+
async stop() {
410+
elizaLogger.debug('runtime::stop - character', this.character)
411+
// stop services, they don't have a stop function
412+
// just initialize
413+
414+
// plugins
415+
// have actions, providers, evaluators (no start/stop)
416+
// services (just initialized), clients
417+
418+
// client have a start
419+
for(const cStr in this.clients) {
420+
const c = this.clients[cStr]
421+
elizaLogger.log('runtime::stop - requesting', cStr, 'client stop for', this.character.name)
422+
c.stop()
423+
}
424+
// we don't need to unregister with directClient
425+
// don't need to worry about knowledge
426+
}
427+
408428
/**
409429
* Processes character knowledge by creating document memories and fragment memories.
410430
* This function takes an array of knowledge items, creates a document memory for each item if it doesn't exist,

packages/core/src/types.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,9 @@ export type Plugin = {
605605
*/
606606
export enum Clients {
607607
DISCORD = "discord",
608-
DIRECT = "direct",
608+
// you can't specify this in characters
609+
// all characters are registered with this
610+
// DIRECT = "direct",
609611
TWITTER = "twitter",
610612
TELEGRAM = "telegram",
611613
FARCASTER = "farcaster",
@@ -1010,6 +1012,9 @@ export interface IAgentRuntime {
10101012
cacheManager: ICacheManager;
10111013

10121014
services: Map<ServiceType, Service>;
1015+
// any could be EventEmitter
1016+
// but I think the real solution is forthcoming as a base client interface
1017+
clients: Record<string, any>;
10131018

10141019
initialize(): Promise<void>;
10151020

0 commit comments

Comments
 (0)