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

feat: services #412

Merged
merged 9 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions packages/agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import { DiscordClientInterface } from "@ai16z/client-discord";
import { AutoClientInterface } from "@ai16z/client-auto";
import { TelegramClientInterface } from "@ai16z/client-telegram";
import { TwitterClientInterface } from "@ai16z/client-twitter";
import { defaultCharacter } from "@ai16z/eliza";
import { AgentRuntime } from "@ai16z/eliza";
import { settings } from "@ai16z/eliza";
import {
defaultCharacter,
AgentRuntime,
settings,
Character,
IAgentRuntime,
IDatabaseAdapter,
ModelProviderName,
} from "@ai16z/eliza";
import { bootstrapPlugin } from "@ai16z/plugin-bootstrap";
Expand Down
4 changes: 2 additions & 2 deletions packages/client-discord/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import { VoiceManager } from "./voice.ts";

export class DiscordClient extends EventEmitter {
apiToken: string;
private client: Client;
private runtime: IAgentRuntime;
client: Client;
runtime: IAgentRuntime;
character: Character;
private messageManager: MessageManager;
private voiceManager: VoiceManager;
Expand Down
60 changes: 42 additions & 18 deletions packages/client-discord/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,17 @@ export class MessageManager {
}
if (message.channel.type === ChannelType.GuildVoice) {
// For voice channels, use text-to-speech
const audioStream = await this.runtime
.getService(ServiceType.SPEECH_GENERATION)
.getInstance<ISpeechService>()
.generate(this.runtime, content.text);

const speechService =
this.runtime.getService<ISpeechService>(
ServiceType.SPEECH_GENERATION
);
ponderingdemocritus marked this conversation as resolved.
Show resolved Hide resolved

const audioStream = await speechService.generate(
this.runtime,
content.text
);

await this.voiceManager.playAudioStream(
userId,
audioStream
Expand Down Expand Up @@ -603,10 +610,18 @@ export class MessageManager {
if (message.channel.type === ChannelType.GuildVoice) {
// For voice channels, use text-to-speech for the error message
const errorMessage = "Sorry, I had a glitch. What was that?";
const audioStream = await this.runtime
.getService(ServiceType.SPEECH_GENERATION)
.getInstance<ISpeechService>()
.generate(this.runtime, errorMessage);

const speechService = this.runtime.getService<ISpeechService>(
ServiceType.SPEECH_GENERATION
);
if (!speechService) {
throw new Error("Speech generation service not found");
}

const audioStream = await speechService.generate(
this.runtime,
errorMessage
);
await this.voiceManager.playAudioStream(userId, audioStream);
} else {
// For text channels, send the error message
Expand Down Expand Up @@ -670,14 +685,17 @@ export class MessageManager {
for (const url of urls) {
if (
this.runtime
.getService(ServiceType.VIDEO)
.getInstance<IVideoService>()
.getService<IVideoService>(ServiceType.VIDEO)
.isVideoUrl(url)
) {
const videoInfo = await this.runtime
.getService(ServiceType.VIDEO)
.getInstance<IVideoService>()
.processVideo(url);
const videoService = this.runtime.getService<IVideoService>(
ServiceType.VIDEO
);
if (!videoService) {
throw new Error("Video service not found");
}
const videoInfo = await videoService.processVideo(url);

attachments.push({
id: `youtube-${Date.now()}`,
url: url,
Expand All @@ -687,10 +705,16 @@ export class MessageManager {
text: videoInfo.text,
});
} else {
const { title, bodyContent } = await this.runtime
.getService(ServiceType.BROWSER)
.getInstance<IBrowserService>()
.getPageContent(url, this.runtime);
const browserService = this.runtime.getService<IBrowserService>(
ServiceType.BROWSER
);
if (!browserService) {
throw new Error("Browser service not found");
}

const { title, bodyContent } =
await browserService.getPageContent(url, this.runtime);

const { title: newTitle, description } = await generateSummary(
this.runtime,
title + "\n" + bodyContent
Expand Down
47 changes: 31 additions & 16 deletions packages/client-discord/src/voice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export function getWavHeader(
}

import { messageCompletionFooter } from "@ai16z/eliza/src/parsing.ts";
import { DiscordClient } from ".";

const discordVoiceHandlerTemplate =
`# Task: Generate conversational voice dialog for {{agentName}}.
Expand Down Expand Up @@ -183,7 +184,7 @@ export class VoiceManager extends EventEmitter {
{ channel: BaseGuildVoiceChannel; monitor: AudioMonitor }
> = new Map();

constructor(client: any) {
constructor(client: DiscordClient) {
super();
this.client = client.client;
this.runtime = client.runtime;
Expand Down Expand Up @@ -260,10 +261,10 @@ export class VoiceManager extends EventEmitter {
member: GuildMember,
channel: BaseGuildVoiceChannel
) {
const userId = member.id;
const userName = member.user.username;
const name = member.user.displayName;
const connection = getVoiceConnection(member.guild.id);
const userId = member?.id;
const userName = member?.user?.username;
const name = member?.user?.displayName;
const connection = getVoiceConnection(member?.guild?.id);
const receiveStream = connection?.receiver.subscribe(userId, {
autoDestroy: true,
emitClose: true,
Expand Down Expand Up @@ -368,13 +369,12 @@ export class VoiceManager extends EventEmitter {
let lastChunkTime = Date.now();
let transcriptionStarted = false;
let transcriptionText = "";
console.log("new audio monitor for: ", userId);

const monitor = new AudioMonitor(
audioStream,
10000000,
async (buffer) => {
console.log("buffer: ", buffer);
// console.log("buffer: ", buffer);
ponderingdemocritus marked this conversation as resolved.
Show resolved Hide resolved
const currentTime = Date.now();
const silenceDuration = currentTime - lastChunkTime;
if (!buffer) {
Expand All @@ -397,11 +397,14 @@ export class VoiceManager extends EventEmitter {
const wavBuffer =
await this.convertOpusToWav(inputBuffer);

console.log("starting transcription");
const text = await this.runtime
.getService(ServiceType.TRANSCRIPTION)
.getInstance<ITranscriptionService>()
.transcribe(wavBuffer);
const transcriptionService =
this.runtime.getService<ITranscriptionService>(
ServiceType.TRANSCRIPTION
);

const text =
await transcriptionService.transcribe(wavBuffer);
sirkitree marked this conversation as resolved.
Show resolved Hide resolved

console.log("transcribed text: ", text);
transcriptionText += text;
} catch (error) {
Expand Down Expand Up @@ -539,10 +542,22 @@ export class VoiceManager extends EventEmitter {
await this.runtime.updateRecentMessageState(
state
);
const responseStream = await this.runtime
.getService(ServiceType.SPEECH_GENERATION)
.getInstance<ISpeechService>()
.generate(this.runtime, content.text);

const speechService =
this.runtime.getService<ISpeechService>(
ServiceType.SPEECH_GENERATION
);
if (!speechService) {
throw new Error(
"Speech generation service not found"
);
}

const responseStream =
await speechService.generate(
this.runtime,
content.text
);

if (responseStream) {
await this.playAudioStream(
Expand Down
2 changes: 1 addition & 1 deletion packages/client-telegram/src/telegramClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class TelegramClient {
this.bot.botInfo = botInfo;
});

console.log(`Bot username: @${this.bot.botInfo?.username}`);
elizaLogger.success(`Bot username: @${this.bot.botInfo?.username}`);

this.messageManager.bot = this.bot;

Expand Down
2 changes: 1 addition & 1 deletion packages/client-twitter/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export class ClientBase extends EventEmitter {
this.runtime.getSetting("TWITTER_EMAIL"),
this.runtime.getSetting("TWITTER_2FA_SECRET")
);
console.log("Logged in to Twitter");
elizaLogger.log("Logged in to Twitter");
const cookies = await this.twitterClient.getCookies();
fs.writeFileSync(
cookiesFilePath,
Expand Down
4 changes: 2 additions & 2 deletions packages/client-twitter/src/interactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export class TwitterInteractionClient extends ClientBase {
);
}

console.log("Thread: ", thread);
elizaLogger.debug("Thread: ", thread);
const formattedConversation = thread
.map(
(tweet) => `@${tweet.username} (${new Date(
Expand All @@ -253,7 +253,7 @@ export class TwitterInteractionClient extends ClientBase {
)
.join("\n\n");

console.log("formattedConversation: ", formattedConversation);
elizaLogger.debug("formattedConversation: ", formattedConversation);

const formattedHomeTimeline =
`# ${this.runtime.character.name}'s Home Timeline\n\n` +
Expand Down
41 changes: 26 additions & 15 deletions packages/core/src/generation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,17 +244,23 @@ export async function generateText({
elizaLogger.debug(
"Using local Llama model for text completion."
);
response = await runtime
.getService(ServiceType.TEXT_GENERATION)
.getInstance<ITextGenerationService>()
.queueTextCompletion(
context,
temperature,
_stop,
frequency_penalty,
presence_penalty,
max_response_length
const textGenerationService =
runtime.getService<ITextGenerationService>(
ServiceType.TEXT_GENERATION
);

if (!textGenerationService) {
throw new Error("Text generation service not found");
}

response = await textGenerationService.queueTextCompletion(
context,
temperature,
_stop,
frequency_penalty,
presence_penalty,
max_response_length
);
elizaLogger.debug("Received response from local Llama model.");
break;
}
Expand Down Expand Up @@ -852,16 +858,21 @@ export const generateCaption = async (
description: string;
}> => {
const { imageUrl } = data;
const resp = await runtime
.getService(ServiceType.IMAGE_DESCRIPTION)
.getInstance<IImageDescriptionService>()
.describeImage(imageUrl);
const imageDescriptionService =
runtime.getService<IImageDescriptionService>(
ServiceType.IMAGE_DESCRIPTION
);

if (!imageDescriptionService) {
throw new Error("Image description service not found");
}

const resp = await imageDescriptionService.describeImage(imageUrl);
return {
title: resp.title.trim(),
description: resp.description.trim(),
};
};

/**
* Configuration options for generating objects with a model.
*/
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,16 +150,17 @@ export class AgentRuntime implements IAgentRuntime {
return this.memoryManagers.get(tableName) || null;
}

getService(service: ServiceType): typeof Service | null {
getService<T extends Service>(service: ServiceType): T | null {
const serviceInstance = this.services.get(service);
if (!serviceInstance) {
elizaLogger.error(`Service ${service} not found`);
return null;
}
return serviceInstance as typeof Service;
return serviceInstance as T;
}

registerService(service: Service): void {
const serviceType = (service as typeof Service).serviceType;
const serviceType = service.serviceType;
elizaLogger.log("Registering service:", serviceType);
if (this.services.has(serviceType)) {
elizaLogger.warn(
Expand All @@ -168,7 +169,7 @@ export class AgentRuntime implements IAgentRuntime {
return;
}

this.services.set((service as typeof Service).serviceType, service);
this.services.set(serviceType, service);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ export interface IMemoryManager {

export abstract class Service {
private static instance: Service | null = null;
static serviceType: ServiceType;
serviceType: ServiceType;

public static getInstance<T extends Service>(): T {
if (!Service.instance) {
Expand Down Expand Up @@ -556,7 +556,7 @@ export interface IAgentRuntime {

getMemoryManager(name: string): IMemoryManager | null;

getService(service: string): typeof Service | null;
getService<T extends Service>(service: ServiceType): T | null;

registerService(service: Service): void;

Expand Down Expand Up @@ -608,6 +608,7 @@ export interface IImageDescriptionService extends Service {
}

export interface ITranscriptionService extends Service {
getInstance(): ITranscriptionService;
transcribeAttachment(audioBuffer: ArrayBuffer): Promise<string | null>;
transcribeAttachmentLocally(
audioBuffer: ArrayBuffer
Expand All @@ -617,6 +618,7 @@ export interface ITranscriptionService extends Service {
}

export interface IVideoService extends Service {
getInstance(): IVideoService;
isVideoUrl(url: string): boolean;
processVideo(url: string): Promise<Media>;
fetchVideoInfo(url: string): Promise<Media>;
Expand Down
13 changes: 9 additions & 4 deletions packages/plugin-node/src/services/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,10 +327,15 @@ export class VideoService extends Service {

console.log("Starting transcription...");
const startTime = Date.now();
const transcript = await runtime
.getService(ServiceType.TRANSCRIPTION)
.getInstance<ITranscriptionService>()
.transcribe(audioBuffer);
const transcriptionService = runtime.getService<ITranscriptionService>(
ServiceType.TRANSCRIPTION
);
if (!transcriptionService) {
throw new Error("Transcription service not found");
}

const transcript = await transcriptionService.transcribe(audioBuffer);

const endTime = Date.now();
console.log(
`Transcription completed in ${(endTime - startTime) / 1000} seconds`
Expand Down