@micdrop/server 2.0.7 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1 -3
- package/dist/index.d.ts +1 -3
- package/dist/index.js +14 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -16,7 +16,6 @@ declare enum MicdropClientCommands {
|
|
|
16
16
|
}
|
|
17
17
|
declare enum MicdropServerCommands {
|
|
18
18
|
Message = "Message",
|
|
19
|
-
CancelLastAssistantMessage = "CancelLastAssistantMessage",
|
|
20
19
|
CancelLastUserMessage = "CancelLastUserMessage",
|
|
21
20
|
SkipAnswer = "SkipAnswer",
|
|
22
21
|
EndCall = "EndCall",
|
|
@@ -82,7 +81,6 @@ interface AgentOptions {
|
|
|
82
81
|
interface AgentEvents {
|
|
83
82
|
Message: [MicdropConversationItem];
|
|
84
83
|
CancelLastUserMessage: [];
|
|
85
|
-
CancelLastAssistantMessage: [];
|
|
86
84
|
SkipAnswer: [];
|
|
87
85
|
EndCall: [];
|
|
88
86
|
ToolCall: [MicdropToolCall];
|
|
@@ -116,7 +114,6 @@ declare abstract class Agent<Options extends AgentOptions = AgentOptions> extend
|
|
|
116
114
|
protected addToolMessage(message: MicdropConversationToolCall | MicdropConversationToolResult): void;
|
|
117
115
|
protected endCall(): void;
|
|
118
116
|
protected cancelLastUserMessage(): void;
|
|
119
|
-
protected cancelLastAssistantMessage(): void;
|
|
120
117
|
protected skipAnswer(): void;
|
|
121
118
|
protected getDefaultTools(): Tool<z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>[];
|
|
122
119
|
protected executeTool(toolCall: MicdropConversationToolCall): Promise<{
|
|
@@ -221,6 +218,7 @@ declare class MicdropServer {
|
|
|
221
218
|
config: MicdropConfig | null;
|
|
222
219
|
logger?: Logger;
|
|
223
220
|
private startTime;
|
|
221
|
+
private lastMessageSpeeched?;
|
|
224
222
|
private currentUserStream?;
|
|
225
223
|
constructor(socket: WebSocket$1, config: MicdropConfig);
|
|
226
224
|
private log;
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,6 @@ declare enum MicdropClientCommands {
|
|
|
16
16
|
}
|
|
17
17
|
declare enum MicdropServerCommands {
|
|
18
18
|
Message = "Message",
|
|
19
|
-
CancelLastAssistantMessage = "CancelLastAssistantMessage",
|
|
20
19
|
CancelLastUserMessage = "CancelLastUserMessage",
|
|
21
20
|
SkipAnswer = "SkipAnswer",
|
|
22
21
|
EndCall = "EndCall",
|
|
@@ -82,7 +81,6 @@ interface AgentOptions {
|
|
|
82
81
|
interface AgentEvents {
|
|
83
82
|
Message: [MicdropConversationItem];
|
|
84
83
|
CancelLastUserMessage: [];
|
|
85
|
-
CancelLastAssistantMessage: [];
|
|
86
84
|
SkipAnswer: [];
|
|
87
85
|
EndCall: [];
|
|
88
86
|
ToolCall: [MicdropToolCall];
|
|
@@ -116,7 +114,6 @@ declare abstract class Agent<Options extends AgentOptions = AgentOptions> extend
|
|
|
116
114
|
protected addToolMessage(message: MicdropConversationToolCall | MicdropConversationToolResult): void;
|
|
117
115
|
protected endCall(): void;
|
|
118
116
|
protected cancelLastUserMessage(): void;
|
|
119
|
-
protected cancelLastAssistantMessage(): void;
|
|
120
117
|
protected skipAnswer(): void;
|
|
121
118
|
protected getDefaultTools(): Tool<z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>[];
|
|
122
119
|
protected executeTool(toolCall: MicdropConversationToolCall): Promise<{
|
|
@@ -221,6 +218,7 @@ declare class MicdropServer {
|
|
|
221
218
|
config: MicdropConfig | null;
|
|
222
219
|
logger?: Logger;
|
|
223
220
|
private startTime;
|
|
221
|
+
private lastMessageSpeeched?;
|
|
224
222
|
private currentUserStream?;
|
|
225
223
|
constructor(socket: WebSocket$1, config: MicdropConfig);
|
|
226
224
|
private log;
|
package/dist/index.js
CHANGED
|
@@ -112,18 +112,14 @@ var Agent = class extends import_eventemitter3.EventEmitter {
|
|
|
112
112
|
}
|
|
113
113
|
cancelLastUserMessage() {
|
|
114
114
|
this.log("Cancelling last user message");
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const lastMessageIndex = this.conversation.findLastIndex(
|
|
116
|
+
(message) => message.role === "user"
|
|
117
|
+
);
|
|
118
|
+
if (lastMessageIndex !== -1) {
|
|
119
|
+
this.conversation.splice(lastMessageIndex, 1);
|
|
120
|
+
}
|
|
118
121
|
this.emit("CancelLastUserMessage");
|
|
119
122
|
}
|
|
120
|
-
cancelLastAssistantMessage() {
|
|
121
|
-
this.log("Cancelling last assistant message");
|
|
122
|
-
const lastMessage = this.conversation[this.conversation.length - 1];
|
|
123
|
-
if (lastMessage?.role !== "assistant") return;
|
|
124
|
-
this.conversation.pop();
|
|
125
|
-
this.emit("CancelLastAssistantMessage");
|
|
126
|
-
}
|
|
127
123
|
skipAnswer() {
|
|
128
124
|
this.log("Skipping answer");
|
|
129
125
|
this.emit("SkipAnswer");
|
|
@@ -341,7 +337,6 @@ var MicdropClientCommands = /* @__PURE__ */ ((MicdropClientCommands2) => {
|
|
|
341
337
|
})(MicdropClientCommands || {});
|
|
342
338
|
var MicdropServerCommands = /* @__PURE__ */ ((MicdropServerCommands2) => {
|
|
343
339
|
MicdropServerCommands2["Message"] = "Message";
|
|
344
|
-
MicdropServerCommands2["CancelLastAssistantMessage"] = "CancelLastAssistantMessage";
|
|
345
340
|
MicdropServerCommands2["CancelLastUserMessage"] = "CancelLastUserMessage";
|
|
346
341
|
MicdropServerCommands2["SkipAnswer"] = "SkipAnswer";
|
|
347
342
|
MicdropServerCommands2["EndCall"] = "EndCall";
|
|
@@ -414,10 +409,6 @@ var MicdropServer = class {
|
|
|
414
409
|
"CancelLastUserMessage",
|
|
415
410
|
() => this.socket?.send("CancelLastUserMessage" /* CancelLastUserMessage */)
|
|
416
411
|
);
|
|
417
|
-
this.config.agent.on(
|
|
418
|
-
"CancelLastAssistantMessage",
|
|
419
|
-
() => this.socket?.send("CancelLastAssistantMessage" /* CancelLastAssistantMessage */)
|
|
420
|
-
);
|
|
421
412
|
this.config.agent.on(
|
|
422
413
|
"SkipAnswer",
|
|
423
414
|
() => this.socket?.send("SkipAnswer" /* SkipAnswer */)
|
|
@@ -459,7 +450,8 @@ var MicdropServer = class {
|
|
|
459
450
|
this.currentUserStream?.end();
|
|
460
451
|
this.currentUserStream = void 0;
|
|
461
452
|
const conversation = this.config?.agent.conversation;
|
|
462
|
-
|
|
453
|
+
const lastMessage = conversation?.[conversation.length - 1];
|
|
454
|
+
if (lastMessage?.role === "user" && this.lastMessageSpeeched !== lastMessage) {
|
|
463
455
|
this.log(
|
|
464
456
|
"User stopped speaking and a transcript already exists, answering"
|
|
465
457
|
);
|
|
@@ -485,6 +477,12 @@ var MicdropServer = class {
|
|
|
485
477
|
async answer() {
|
|
486
478
|
if (!this.config) return;
|
|
487
479
|
this.cancel();
|
|
480
|
+
const lastMessage = this.config.agent.conversation[this.config.agent.conversation.length - 1];
|
|
481
|
+
if (this.lastMessageSpeeched === lastMessage) {
|
|
482
|
+
this.log("Already answered, skipping");
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
this.lastMessageSpeeched = lastMessage;
|
|
488
486
|
try {
|
|
489
487
|
const stream = this.config.agent.answer();
|
|
490
488
|
await this.speak(stream);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/audio-convert.ts","../src/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/stt/STT.ts","../src/stt/FileSTT.ts","../src/stt/MockSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/waitForParams.ts"],"sourcesContent":["export * from './agent'\nexport * from './audio-convert'\nexport * from './errors'\nexport * from './Logger'\nexport * from './MicdropServer'\nexport * from './stt'\nexport * from './tts'\nexport * from './types'\nexport * from './waitForParams'\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport type { z } from 'zod'\nimport { Logger } from '../Logger'\nimport {\n MicdropAnswerMetadata,\n MicdropConversation,\n MicdropConversationItem,\n MicdropConversationMessage,\n MicdropConversationToolCall,\n MicdropConversationToolResult,\n MicdropToolCall,\n} from '../types'\nimport {\n AUTO_END_CALL_PROMPT,\n AUTO_END_CALL_TOOL_NAME,\n AUTO_IGNORE_USER_NOISE_PROMPT,\n AUTO_IGNORE_USER_NOISE_TOOL_NAME,\n AUTO_SEMANTIC_TURN_PROMPT,\n AUTO_SEMANTIC_TURN_TOOL_NAME,\n Tool,\n} from './tools'\n\nexport interface AgentOptions {\n systemPrompt: string\n\n // Enable auto ending of the call when user asks to end the call\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoEndCall?: boolean | string\n\n // Enable detection of an incomplete sentence, and skip the answer (assistant waits)\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoSemanticTurn?: boolean | string\n\n // Ignore of the last user message when it's meaningless\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoIgnoreUserNoise?: boolean | string\n\n // Extract a value from the answer\n // Value must be at the end of the answer, in JSON or between tags\n extract?: ExtractJsonOptions | ExtractTagOptions\n}\n\nexport interface AgentEvents {\n Message: [MicdropConversationItem]\n CancelLastUserMessage: []\n CancelLastAssistantMessage: []\n SkipAnswer: []\n EndCall: []\n ToolCall: [MicdropToolCall]\n}\n\nexport interface ExtractOptions {\n callback?: (value: string) => void\n saveInMetadata?: boolean\n}\n\nexport interface ExtractJsonOptions extends ExtractOptions {\n json: true\n callback?: (value: any) => void\n}\n\nexport interface ExtractTagOptions extends ExtractOptions {\n startTag: string\n endTag: string\n}\n\nexport abstract class Agent<\n Options extends AgentOptions = AgentOptions,\n> extends EventEmitter<AgentEvents> {\n public logger?: Logger\n public conversation: MicdropConversation\n\n protected tools: Tool[]\n\n constructor(protected options: Options) {\n super()\n this.conversation = [{ role: 'system', content: options.systemPrompt }]\n this.tools = this.getDefaultTools()\n }\n\n abstract answer(): Readable\n abstract cancel(): void\n\n addUserMessage(text: string, metadata?: MicdropAnswerMetadata) {\n this.addMessage('user', text, metadata)\n }\n\n addAssistantMessage(text: string, metadata?: MicdropAnswerMetadata) {\n this.addMessage('assistant', text, metadata)\n }\n\n addTool<Schema extends z.ZodObject>(tool: Tool<Schema>) {\n this.tools.push(tool)\n }\n\n removeTool(name: string) {\n this.tools = this.tools.filter((tool) => tool.name !== name)\n }\n\n getTool(name: string): Tool | undefined {\n return this.tools.find((tool) => tool.name === name)\n }\n\n protected addMessage(\n role: 'user' | 'assistant' | 'system',\n text: string,\n metadata?: MicdropAnswerMetadata\n ) {\n this.log(`Adding ${role} message to conversation: ${text}`)\n const message: MicdropConversationMessage = {\n role,\n content: text,\n metadata,\n }\n this.conversation.push(message)\n this.emit('Message', message)\n }\n\n protected addToolMessage(\n message: MicdropConversationToolCall | MicdropConversationToolResult\n ) {\n this.log('Adding tool message:', message)\n this.conversation.push(message)\n this.emit('Message', message)\n }\n\n protected endCall() {\n this.log('Ending call')\n this.emit('EndCall')\n }\n\n protected cancelLastUserMessage() {\n this.log('Cancelling last user message')\n const lastMessage = this.conversation[this.conversation.length - 1]\n if (lastMessage?.role !== 'user') return\n this.conversation.pop()\n this.emit('CancelLastUserMessage')\n }\n\n protected cancelLastAssistantMessage() {\n this.log('Cancelling last assistant message')\n const lastMessage = this.conversation[this.conversation.length - 1]\n if (lastMessage?.role !== 'assistant') return\n this.conversation.pop()\n this.emit('CancelLastAssistantMessage')\n }\n\n protected skipAnswer() {\n this.log('Skipping answer')\n this.emit('SkipAnswer')\n }\n\n protected getDefaultTools() {\n const tools: Tool[] = []\n if (this.options.autoEndCall) {\n tools.push({\n name: AUTO_END_CALL_TOOL_NAME,\n description:\n typeof this.options.autoEndCall === 'string'\n ? this.options.autoEndCall\n : AUTO_END_CALL_PROMPT,\n execute: () => this.endCall(),\n })\n }\n if (this.options.autoSemanticTurn) {\n tools.push({\n name: AUTO_SEMANTIC_TURN_TOOL_NAME,\n description:\n typeof this.options.autoSemanticTurn === 'string'\n ? this.options.autoSemanticTurn\n : AUTO_SEMANTIC_TURN_PROMPT,\n skipAnswer: true,\n execute: () => this.skipAnswer(),\n })\n }\n if (this.options.autoIgnoreUserNoise) {\n tools.push({\n name: AUTO_IGNORE_USER_NOISE_TOOL_NAME,\n description:\n typeof this.options.autoIgnoreUserNoise === 'string'\n ? this.options.autoIgnoreUserNoise\n : AUTO_IGNORE_USER_NOISE_PROMPT,\n skipAnswer: true,\n execute: () => this.cancelLastUserMessage(),\n })\n }\n return tools\n }\n\n protected async executeTool(toolCall: MicdropConversationToolCall) {\n try {\n const tool = this.getTool(toolCall.toolName)\n if (!tool) {\n throw new Error(`Tool not found \"${toolCall.toolName}\"`)\n }\n\n this.log('Executing tool:', toolCall.toolName, toolCall.parameters)\n\n // Save tool call in conversation\n this.addToolMessage(toolCall)\n\n const parameters = JSON.parse(toolCall.parameters)\n const output = tool.execute ? await tool.execute(parameters) : {}\n\n // Save tool result in conversation\n this.addToolMessage({\n role: 'tool_result',\n toolCallId: toolCall.toolCallId,\n toolName: toolCall.toolName,\n output: JSON.stringify(output ?? null),\n })\n\n // Emit output\n if (tool.emitOutput) {\n this.emit('ToolCall', {\n name: toolCall.toolName,\n parameters,\n output,\n })\n }\n\n return {\n output,\n skipAnswer: tool.skipAnswer,\n }\n } catch (error: any) {\n console.error('[OpenaiAgent] Error executing tool:', error)\n return {\n output: {\n error: error.message,\n },\n }\n }\n }\n\n protected getExtractOptions(): ExtractTagOptions | undefined {\n const extract = this.options.extract\n if (!extract) return undefined\n if ('json' in extract && extract.json) {\n return { ...extract, startTag: '{', endTag: '}' }\n }\n if ('startTag' in extract && 'endTag' in extract) {\n return extract\n }\n return undefined\n }\n\n protected extract(message: string) {\n const extractOptions = this.getExtractOptions()\n let metadata: MicdropAnswerMetadata | undefined = undefined\n\n // Extract value?\n if (extractOptions) {\n const startTagIndex = message.indexOf(extractOptions.startTag)\n if (startTagIndex !== -1) {\n // Find end tag\n let endTagIndex = message.lastIndexOf(extractOptions.endTag)\n if (endTagIndex === -1) endTagIndex = message.length + 1\n else endTagIndex += extractOptions.endTag.length\n const extractedText = message.slice(startTagIndex, endTagIndex).trim()\n\n // Parse extracted value\n try {\n const extractedValue =\n 'json' in extractOptions && extractOptions.json\n ? JSON.parse(extractedText)\n : extractedText\n\n // Call callback\n if (extractOptions.callback) {\n extractOptions.callback(extractedValue)\n }\n\n // Save in metadata\n if (extractOptions.saveInMetadata) {\n metadata = { extracted: extractedValue }\n }\n } catch (error) {\n console.error(\n `[OpenaiAgent] Error parsing extracted value (${extractedText}):`,\n error\n )\n }\n\n // Remove extracted value from message\n message = message.slice(0, startTagIndex).trimEnd()\n }\n }\n return { message, metadata }\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n this.cancel()\n }\n}\n","import type { z } from 'zod'\n\nexport interface Tool<Schema extends z.ZodObject = z.ZodObject> {\n name: string\n description: string\n inputSchema?: Schema\n execute?: (input: z.infer<Schema>) => any | Promise<any>\n skipAnswer?: boolean\n emitOutput?: boolean\n}\n\nexport const AUTO_END_CALL_TOOL_NAME = 'end_call'\nexport const AUTO_END_CALL_PROMPT =\n 'Call this tool only if user asks to end the call'\n\nexport const AUTO_SEMANTIC_TURN_TOOL_NAME = 'semantic_turn'\nexport const AUTO_SEMANTIC_TURN_PROMPT =\n 'Call this tool only if last user message is obviously an incomplete sentence that you need to wait for the end before answering'\n\nexport const AUTO_IGNORE_USER_NOISE_TOOL_NAME = 'ignore_user_noise'\nexport const AUTO_IGNORE_USER_NOISE_PROMPT =\n 'Call this tool only if last user message is just an interjection or a sound that expresses emotion, hesitation, or reaction (ex: \"Uh\", \"Ahem\", \"Hmm\", \"Ah\") but doesn\\'t carry any clear meaning like agreeing, refusing, or commanding'\n","import { PassThrough } from 'stream'\nimport { Agent } from './Agent'\n\nexport class MockAgent extends Agent {\n private i = 0\n\n constructor() {\n super({ systemPrompt: '' })\n }\n\n answer() {\n const stream = new PassThrough()\n\n // Answer message\n const message = `Assistant Message ${this.i++}`\n this.addAssistantMessage(message)\n stream.write(message)\n stream.end()\n return stream\n }\n\n cancel() {}\n}\n","import ffmpegInstaller from '@ffmpeg-installer/ffmpeg'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { PassThrough, Readable } from 'stream'\n\n// Setup ffmpeg\nffmpeg.setFfmpegPath(ffmpegInstaller.path)\n\n// Convert stream to WAV/PCM\nexport function convertToPCM(\n audioStream: Readable,\n sampleRate = 16000,\n bitDepth = 16\n) {\n const pcmStream = new PassThrough()\n ffmpeg(audioStream)\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec(`pcm_s${bitDepth}le`)\n .format(`s${bitDepth}le`)\n .on('error', (error) => {\n console.error('Error converting audio stream:', error.message)\n })\n .pipe(pcmStream)\n return pcmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate).pipe(webmStream)\n return webmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertPCMToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate)\n .inputFormat('s16le')\n .inputOptions(['-f s16le', '-ar 16000', '-ac 1'])\n .pipe(webmStream)\n return webmStream\n}\n\nfunction ffmpegToOpus(ffmpegCommand: ffmpeg.FfmpegCommand, sampleRate = 16000) {\n return ffmpegCommand\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec('libopus')\n .format('webm')\n .outputOptions([\n '-application audio',\n `-ac 1`,\n `-ar ${sampleRate}`,\n `-b:a 64k`,\n `-f webm`,\n `-map_metadata -1`,\n ])\n .on('error', (error) => {\n console.error('Error converting to Opus: ', error.message)\n })\n}\n","import WebSocket from 'ws'\n\nexport enum MicdropErrorCode {\n BadRequest = 4400,\n Unauthorized = 4401,\n NotFound = 4404,\n}\n\nexport class MicdropError extends Error {\n code: number\n\n constructor(code: number, message: string) {\n super(message)\n this.code = code\n }\n}\n\nexport function handleError(socket: WebSocket, error: unknown) {\n if (error instanceof MicdropError) {\n socket.close(error.code, error.message)\n } else {\n console.error(error)\n socket.close(1011)\n }\n socket.terminate()\n}\n","export class Logger {\n constructor(private readonly name: string) {}\n\n log(...message: any[]) {\n const time = process.uptime().toFixed(3)\n console.log(`[${this.name} ${time}]`, ...message)\n }\n}\n","import { Duplex, PassThrough, Readable } from 'stream'\nimport { WebSocket } from 'ws'\nimport type { Agent } from './agent'\nimport { Logger } from './Logger'\nimport type { STT } from './stt'\nimport type { TTS } from './tts'\nimport {\n MicdropCallSummary,\n MicdropClientCommands,\n MicdropServerCommands,\n} from './types'\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n onEnd?(call: MicdropCallSummary): void\n}\n\nexport class MicdropServer {\n public socket: WebSocket | null = null\n public config: MicdropConfig | null = null\n public logger?: Logger\n\n private startTime = Date.now()\n\n // When user is speaking, we're streaming chunks for STT\n private currentUserStream?: Duplex\n\n constructor(socket: WebSocket, config: MicdropConfig) {\n this.socket = socket\n this.config = config\n this.log(`Call started`)\n\n // Setup STT\n this.config.stt.on('Transcript', this.onTranscript)\n\n // Setup agent\n this.config.agent.on('Message', (message) =>\n this.socket?.send(\n `${MicdropServerCommands.Message} ${JSON.stringify(message)}`\n )\n )\n this.config.agent.on('CancelLastUserMessage', () =>\n this.socket?.send(MicdropServerCommands.CancelLastUserMessage)\n )\n this.config.agent.on('CancelLastAssistantMessage', () =>\n this.socket?.send(MicdropServerCommands.CancelLastAssistantMessage)\n )\n this.config.agent.on('SkipAnswer', () =>\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n )\n this.config.agent.on('EndCall', () =>\n this.socket?.send(MicdropServerCommands.EndCall)\n )\n this.config.agent.on('ToolCall', (toolCall) =>\n this.socket?.send(\n `${MicdropServerCommands.ToolCall} ${JSON.stringify(toolCall)}`\n )\n )\n\n // Assistant speaks first\n this.sendFirstMessage()\n\n // Listen to events\n socket.on('close', this.onClose)\n socket.on('message', this.onMessage)\n }\n\n private log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n private cancel() {\n this.config?.tts.cancel()\n this.config?.agent.cancel()\n }\n\n private onClose = () => {\n if (!this.config) return\n this.log('Connection closed')\n const duration = Math.round((Date.now() - this.startTime) / 1000)\n\n // Destroy instances\n this.config.agent.destroy()\n this.config.stt.destroy()\n this.config.tts.destroy()\n\n // End call callback\n this.config.onEnd?.({\n conversation: this.config.agent.conversation.slice(1), // Remove system message\n duration,\n })\n\n // Unset params\n this.socket = null\n this.config = null\n }\n\n private onMessage = async (message: Buffer) => {\n if (message.byteLength === 0) return\n if (!Buffer.isBuffer(message)) {\n this.log('Message is not a buffer')\n return\n }\n\n // Commands\n if (message.byteLength < 15) {\n const cmd = message.toString()\n this.log(`Command: ${cmd}`)\n\n if (cmd === MicdropClientCommands.StartSpeaking) {\n // User started speaking\n await this.onStartSpeaking()\n } else if (cmd === MicdropClientCommands.Mute) {\n // User muted the call\n await this.onMute()\n } else if (cmd === MicdropClientCommands.StopSpeaking) {\n // User stopped speaking\n await this.onStopSpeaking()\n }\n }\n\n // Audio chunk\n else if (this.currentUserStream) {\n this.log(`Received chunk (${message.byteLength} bytes)`)\n this.currentUserStream.write(message)\n }\n }\n\n private async onMute() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private async onStartSpeaking() {\n if (!this.config) return\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private async onStopSpeaking() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n\n const conversation = this.config?.agent.conversation\n if (conversation && conversation[conversation.length - 1].role === 'user') {\n this.log(\n 'User stopped speaking and a transcript already exists, answering'\n )\n this.answer()\n }\n }\n\n private onTranscript = async (transcript: string) => {\n if (!this.config) return\n this.log(`User transcript: \"${transcript}\"`)\n this.config.agent.addUserMessage(transcript)\n\n // Answer if user stopped speaking\n if (!this.currentUserStream) {\n this.log('User stopped speaking, answering')\n this.answer()\n }\n }\n\n private async sendFirstMessage() {\n if (!this.config) return\n try {\n if (this.config.firstMessage) {\n // Send first message\n this.config.agent.addAssistantMessage(this.config.firstMessage)\n await this.speak(this.config.firstMessage)\n } else if (this.config.generateFirstMessage) {\n // Generate first message\n await this.answer()\n } else {\n // Skip answer if no first message is provided\n // to avoid keeping the client in a processing state\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n } catch (error) {\n console.error('[MicdropServer]', error)\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n }\n\n private async answer() {\n if (!this.config) return\n this.cancel()\n try {\n // LLM: Generate answer\n const stream = this.config.agent.answer()\n\n // TTS: Generate answer audio\n await this.speak(stream)\n } catch (error) {\n console.error('[MicdropServer]', error)\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n }\n\n // Run text-to-speech and send to client\n private async speak(message: string | Readable) {\n if (!this.socket || !this.config) return\n\n // Convert message to stream if needed\n let textStream: Readable\n if (typeof message === 'string') {\n const stream = new PassThrough()\n stream.write(message)\n stream.end()\n textStream = stream\n } else {\n textStream = message\n }\n\n // Run TTS\n const audio = this.config.tts.speak(textStream)\n\n // Send audio to client\n await this.sendAudio(audio)\n }\n\n private async sendAudio(audio: Readable) {\n if (!this.socket) return\n if (!audio.readable) {\n this.log('Non readable audio, skipping', audio)\n return\n }\n\n // Stream audio\n audio.on('data', (chunk) => {\n this.log(`Send audio chunk (${chunk.byteLength} bytes)`)\n this.socket?.send(chunk)\n })\n audio.on('error', (error) => {\n this.log('Error in audio stream', error)\n })\n audio.on('end', () => {\n this.log('Audio stream ended')\n })\n }\n}\n","export enum MicdropClientCommands {\n StartSpeaking = 'StartSpeaking',\n StopSpeaking = 'StopSpeaking',\n Mute = 'Mute',\n}\n\nexport enum MicdropServerCommands {\n Message = 'Message',\n CancelLastAssistantMessage = 'CancelLastAssistantMessage',\n CancelLastUserMessage = 'CancelLastUserMessage',\n SkipAnswer = 'SkipAnswer',\n EndCall = 'EndCall',\n ToolCall = 'ToolCall',\n}\n\nexport interface MicdropCallSummary {\n conversation: MicdropConversation\n duration: number\n}\n\nexport type MicdropConversationItem =\n | MicdropConversationMessage\n | MicdropConversationToolCall\n | MicdropConversationToolResult\n\nexport type MicdropConversation = Array<MicdropConversationItem>\n\nexport type MicdropAnswerMetadata = {\n [key: string]: any\n}\n\nexport interface MicdropConversationMessage<\n Data extends MicdropAnswerMetadata = MicdropAnswerMetadata,\n> {\n role: 'system' | 'user' | 'assistant'\n content: string\n metadata?: Data\n}\n\nexport interface MicdropConversationToolCall {\n role: 'tool_call'\n toolCallId: string\n toolName: string\n parameters: string\n}\n\nexport interface MicdropConversationToolResult {\n role: 'tool_result'\n toolCallId: string\n toolName: string\n output: string\n}\n\nexport interface MicdropToolCall {\n name: string\n parameters: any\n output: any\n}\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\n// Audio mime type to extension map\nconst MIME_TYPE_TO_EXTENSION = {\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/mpeg': 'mp3',\n 'audio/webm': 'webm',\n 'audio/mp4': 'mp4',\n 'audio/flac': 'flac',\n} as const\n\nexport interface STTEvents {\n Transcript: [string]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n protected mimeType?: keyof typeof MIME_TYPE_TO_EXTENSION\n public logger?: Logger\n\n // Set stream of audio to transcribe\n transcribe(audioStream: Readable) {\n // Detect mime type at first chunk\n audioStream.once('data', (chunk) => {\n this.mimeType = this.detectMimeType(chunk)\n })\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n }\n\n protected get extension(): string {\n return (this.mimeType && MIME_TYPE_TO_EXTENSION[this.mimeType]) || 'bin'\n }\n\n private detectMimeType(\n chunk: ArrayBuffer\n ): keyof typeof MIME_TYPE_TO_EXTENSION {\n if (!chunk || chunk.byteLength === 0) {\n throw new Error('Unable to detect mime type (empty chunk)')\n }\n\n const arr = new Uint8Array(chunk)\n\n // WEBM: 1A 45 DF A3\n if (\n arr[0] === 0x1a &&\n arr[1] === 0x45 &&\n arr[2] === 0xdf &&\n arr[3] === 0xa3\n ) {\n return 'audio/webm'\n }\n // OGG: 4F 67 67 53\n if (\n arr[0] === 0x4f &&\n arr[1] === 0x67 &&\n arr[2] === 0x67 &&\n arr[3] === 0x53\n ) {\n return 'audio/ogg'\n }\n // WAV: 52 49 46 46 ... 57 41 56 45\n if (\n arr[0] === 0x52 &&\n arr[1] === 0x49 &&\n arr[2] === 0x46 &&\n arr[3] === 0x46 &&\n arr[8] === 0x57 &&\n arr[9] === 0x41 &&\n arr[10] === 0x56 &&\n arr[11] === 0x45\n ) {\n return 'audio/wav'\n }\n // MP3: 49 44 33\n if (arr[0] === 0x49 && arr[1] === 0x44 && arr[2] === 0x33) {\n return 'audio/mpeg'\n }\n // MP4/M4A: 00 00 00 .. 66 74 79 70\n if (\n arr[4] === 0x66 &&\n arr[5] === 0x74 &&\n arr[6] === 0x79 &&\n arr[7] === 0x70\n ) {\n return 'audio/mp4'\n }\n // FLAC: 66 4c 61 43\n if (\n arr[0] === 0x66 &&\n arr[1] === 0x4c &&\n arr[2] === 0x61 &&\n arr[3] === 0x43\n ) {\n return 'audio/flac'\n }\n this.log('Unable to detect mime type, using default', chunk)\n return 'audio/wav'\n }\n}\n","import { Readable } from 'stream'\nimport { STT } from './STT'\n\n/**\n * Abstract class for STT, converting stream to file before transcribing\n */\n\nexport abstract class FileSTT extends STT {\n abstract transcribeFile(file: File): Promise<string>\n\n transcribe(audioStream: Readable) {\n super.transcribe(audioStream)\n\n // Convert stream to file\n this.log('Converting stream to file...')\n\n const chunks: Buffer[] = []\n audioStream.on('data', (chunk) => {\n chunks.push(chunk)\n })\n\n audioStream.on('end', async () => {\n if (chunks.length === 0) return\n const arrayBuffer = Buffer.concat(chunks)\n const file = new File([arrayBuffer], `audio.${this.extension}`, {\n type: this.mimeType,\n })\n\n // Transcribe file with implementation\n this.log('Transcribing file...')\n const transcript = await this.transcribeFile(file)\n this.emit('Transcript', transcript)\n })\n }\n}\n","import { FileSTT } from './FileSTT'\n\nexport class MockSTT extends FileSTT {\n private i = 0\n\n async transcribeFile(file: File) {\n return `User Message ${this.i++}`\n }\n}\n","import * as fs from 'fs'\nimport { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\n\nexport class MockTTS extends TTS {\n constructor(private audioFilePaths: string[]) {\n super()\n }\n\n speak(textStream: Readable) {\n const audioStream = new PassThrough()\n textStream.once('data', async () => {\n for (const filePath of this.audioFilePaths) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n const audioBuffer = fs.readFileSync(filePath)\n this.log(`Loaded chunk (${audioBuffer.length} bytes)`)\n audioStream.write(audioBuffer)\n }\n audioStream.end()\n })\n return audioStream\n }\n\n cancel() {}\n}\n","import { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport abstract class TTS {\n public logger?: Logger\n\n abstract speak(textStream: Readable): Readable\n abstract cancel(): void\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.cancel()\n }\n}\n","import { WebSocket } from 'ws'\nimport { MicdropError, MicdropErrorCode } from './errors'\n\nexport async function waitForParams<CallParams>(\n socket: WebSocket,\n validate: (params: any) => CallParams\n): Promise<CallParams> {\n return new Promise<CallParams>((resolve, reject) => {\n // Handle timeout\n const timeout = setTimeout(() => {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Missing params'))\n }, 3000)\n\n const onParams = (payload: string) => {\n // Clear timeout and listener\n clearTimeout(timeout)\n socket.off('message', onParams)\n\n try {\n // Parse JSON payload\n const params = validate(JSON.parse(payload))\n resolve(params)\n } catch (error) {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Invalid params'))\n }\n }\n\n // Listen for params\n socket.on('message', onParams)\n })\n}\n"],"mappings":"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","names":["import_stream","ffmpeg","ffmpegInstaller","MicdropErrorCode","import_stream","MicdropClientCommands","MicdropServerCommands","import_eventemitter3","import_stream"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/audio-convert.ts","../src/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/stt/STT.ts","../src/stt/FileSTT.ts","../src/stt/MockSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/waitForParams.ts"],"sourcesContent":["export * from './agent'\nexport * from './audio-convert'\nexport * from './errors'\nexport * from './Logger'\nexport * from './MicdropServer'\nexport * from './stt'\nexport * from './tts'\nexport * from './types'\nexport * from './waitForParams'\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport type { z } from 'zod'\nimport { Logger } from '../Logger'\nimport {\n MicdropAnswerMetadata,\n MicdropConversation,\n MicdropConversationItem,\n MicdropConversationMessage,\n MicdropConversationToolCall,\n MicdropConversationToolResult,\n MicdropToolCall,\n} from '../types'\nimport {\n AUTO_END_CALL_PROMPT,\n AUTO_END_CALL_TOOL_NAME,\n AUTO_IGNORE_USER_NOISE_PROMPT,\n AUTO_IGNORE_USER_NOISE_TOOL_NAME,\n AUTO_SEMANTIC_TURN_PROMPT,\n AUTO_SEMANTIC_TURN_TOOL_NAME,\n Tool,\n} from './tools'\n\nexport interface AgentOptions {\n systemPrompt: string\n\n // Enable auto ending of the call when user asks to end the call\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoEndCall?: boolean | string\n\n // Enable detection of an incomplete sentence, and skip the answer (assistant waits)\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoSemanticTurn?: boolean | string\n\n // Ignore of the last user message when it's meaningless\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoIgnoreUserNoise?: boolean | string\n\n // Extract a value from the answer\n // Value must be at the end of the answer, in JSON or between tags\n extract?: ExtractJsonOptions | ExtractTagOptions\n}\n\nexport interface AgentEvents {\n Message: [MicdropConversationItem]\n CancelLastUserMessage: []\n SkipAnswer: []\n EndCall: []\n ToolCall: [MicdropToolCall]\n}\n\nexport interface ExtractOptions {\n callback?: (value: string) => void\n saveInMetadata?: boolean\n}\n\nexport interface ExtractJsonOptions extends ExtractOptions {\n json: true\n callback?: (value: any) => void\n}\n\nexport interface ExtractTagOptions extends ExtractOptions {\n startTag: string\n endTag: string\n}\n\nexport abstract class Agent<\n Options extends AgentOptions = AgentOptions,\n> extends EventEmitter<AgentEvents> {\n public logger?: Logger\n public conversation: MicdropConversation\n\n protected tools: Tool[]\n\n constructor(protected options: Options) {\n super()\n this.conversation = [{ role: 'system', content: options.systemPrompt }]\n this.tools = this.getDefaultTools()\n }\n\n abstract answer(): Readable\n abstract cancel(): void\n\n addUserMessage(text: string, metadata?: MicdropAnswerMetadata) {\n this.addMessage('user', text, metadata)\n }\n\n addAssistantMessage(text: string, metadata?: MicdropAnswerMetadata) {\n this.addMessage('assistant', text, metadata)\n }\n\n addTool<Schema extends z.ZodObject>(tool: Tool<Schema>) {\n this.tools.push(tool)\n }\n\n removeTool(name: string) {\n this.tools = this.tools.filter((tool) => tool.name !== name)\n }\n\n getTool(name: string): Tool | undefined {\n return this.tools.find((tool) => tool.name === name)\n }\n\n protected addMessage(\n role: 'user' | 'assistant' | 'system',\n text: string,\n metadata?: MicdropAnswerMetadata\n ) {\n this.log(`Adding ${role} message to conversation: ${text}`)\n const message: MicdropConversationMessage = {\n role,\n content: text,\n metadata,\n }\n this.conversation.push(message)\n this.emit('Message', message)\n }\n\n protected addToolMessage(\n message: MicdropConversationToolCall | MicdropConversationToolResult\n ) {\n this.log('Adding tool message:', message)\n this.conversation.push(message)\n this.emit('Message', message)\n }\n\n protected endCall() {\n this.log('Ending call')\n this.emit('EndCall')\n }\n\n protected cancelLastUserMessage() {\n this.log('Cancelling last user message')\n const lastMessageIndex = this.conversation.findLastIndex(\n (message) => message.role === 'user'\n )\n if (lastMessageIndex !== -1) {\n this.conversation.splice(lastMessageIndex, 1)\n }\n this.emit('CancelLastUserMessage')\n }\n\n protected skipAnswer() {\n this.log('Skipping answer')\n this.emit('SkipAnswer')\n }\n\n protected getDefaultTools() {\n const tools: Tool[] = []\n if (this.options.autoEndCall) {\n tools.push({\n name: AUTO_END_CALL_TOOL_NAME,\n description:\n typeof this.options.autoEndCall === 'string'\n ? this.options.autoEndCall\n : AUTO_END_CALL_PROMPT,\n execute: () => this.endCall(),\n })\n }\n if (this.options.autoSemanticTurn) {\n tools.push({\n name: AUTO_SEMANTIC_TURN_TOOL_NAME,\n description:\n typeof this.options.autoSemanticTurn === 'string'\n ? this.options.autoSemanticTurn\n : AUTO_SEMANTIC_TURN_PROMPT,\n skipAnswer: true,\n execute: () => this.skipAnswer(),\n })\n }\n if (this.options.autoIgnoreUserNoise) {\n tools.push({\n name: AUTO_IGNORE_USER_NOISE_TOOL_NAME,\n description:\n typeof this.options.autoIgnoreUserNoise === 'string'\n ? this.options.autoIgnoreUserNoise\n : AUTO_IGNORE_USER_NOISE_PROMPT,\n skipAnswer: true,\n execute: () => this.cancelLastUserMessage(),\n })\n }\n return tools\n }\n\n protected async executeTool(toolCall: MicdropConversationToolCall) {\n try {\n const tool = this.getTool(toolCall.toolName)\n if (!tool) {\n throw new Error(`Tool not found \"${toolCall.toolName}\"`)\n }\n\n this.log('Executing tool:', toolCall.toolName, toolCall.parameters)\n\n // Save tool call in conversation\n this.addToolMessage(toolCall)\n\n const parameters = JSON.parse(toolCall.parameters)\n const output = tool.execute ? await tool.execute(parameters) : {}\n\n // Save tool result in conversation\n this.addToolMessage({\n role: 'tool_result',\n toolCallId: toolCall.toolCallId,\n toolName: toolCall.toolName,\n output: JSON.stringify(output ?? null),\n })\n\n // Emit output\n if (tool.emitOutput) {\n this.emit('ToolCall', {\n name: toolCall.toolName,\n parameters,\n output,\n })\n }\n\n return {\n output,\n skipAnswer: tool.skipAnswer,\n }\n } catch (error: any) {\n console.error('[OpenaiAgent] Error executing tool:', error)\n return {\n output: {\n error: error.message,\n },\n }\n }\n }\n\n protected getExtractOptions(): ExtractTagOptions | undefined {\n const extract = this.options.extract\n if (!extract) return undefined\n if ('json' in extract && extract.json) {\n return { ...extract, startTag: '{', endTag: '}' }\n }\n if ('startTag' in extract && 'endTag' in extract) {\n return extract\n }\n return undefined\n }\n\n protected extract(message: string) {\n const extractOptions = this.getExtractOptions()\n let metadata: MicdropAnswerMetadata | undefined = undefined\n\n // Extract value?\n if (extractOptions) {\n const startTagIndex = message.indexOf(extractOptions.startTag)\n if (startTagIndex !== -1) {\n // Find end tag\n let endTagIndex = message.lastIndexOf(extractOptions.endTag)\n if (endTagIndex === -1) endTagIndex = message.length + 1\n else endTagIndex += extractOptions.endTag.length\n const extractedText = message.slice(startTagIndex, endTagIndex).trim()\n\n // Parse extracted value\n try {\n const extractedValue =\n 'json' in extractOptions && extractOptions.json\n ? JSON.parse(extractedText)\n : extractedText\n\n // Call callback\n if (extractOptions.callback) {\n extractOptions.callback(extractedValue)\n }\n\n // Save in metadata\n if (extractOptions.saveInMetadata) {\n metadata = { extracted: extractedValue }\n }\n } catch (error) {\n console.error(\n `[OpenaiAgent] Error parsing extracted value (${extractedText}):`,\n error\n )\n }\n\n // Remove extracted value from message\n message = message.slice(0, startTagIndex).trimEnd()\n }\n }\n return { message, metadata }\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n this.cancel()\n }\n}\n","import type { z } from 'zod'\n\nexport interface Tool<Schema extends z.ZodObject = z.ZodObject> {\n name: string\n description: string\n inputSchema?: Schema\n execute?: (input: z.infer<Schema>) => any | Promise<any>\n skipAnswer?: boolean\n emitOutput?: boolean\n}\n\nexport const AUTO_END_CALL_TOOL_NAME = 'end_call'\nexport const AUTO_END_CALL_PROMPT =\n 'Call this tool only if user asks to end the call'\n\nexport const AUTO_SEMANTIC_TURN_TOOL_NAME = 'semantic_turn'\nexport const AUTO_SEMANTIC_TURN_PROMPT =\n 'Call this tool only if last user message is obviously an incomplete sentence that you need to wait for the end before answering'\n\nexport const AUTO_IGNORE_USER_NOISE_TOOL_NAME = 'ignore_user_noise'\nexport const AUTO_IGNORE_USER_NOISE_PROMPT =\n 'Call this tool only if last user message is just an interjection or a sound that expresses emotion, hesitation, or reaction (ex: \"Uh\", \"Ahem\", \"Hmm\", \"Ah\") but doesn\\'t carry any clear meaning like agreeing, refusing, or commanding'\n","import { PassThrough } from 'stream'\nimport { Agent } from './Agent'\n\nexport class MockAgent extends Agent {\n private i = 0\n\n constructor() {\n super({ systemPrompt: '' })\n }\n\n answer() {\n const stream = new PassThrough()\n\n // Answer message\n const message = `Assistant Message ${this.i++}`\n this.addAssistantMessage(message)\n stream.write(message)\n stream.end()\n return stream\n }\n\n cancel() {}\n}\n","import ffmpegInstaller from '@ffmpeg-installer/ffmpeg'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { PassThrough, Readable } from 'stream'\n\n// Setup ffmpeg\nffmpeg.setFfmpegPath(ffmpegInstaller.path)\n\n// Convert stream to WAV/PCM\nexport function convertToPCM(\n audioStream: Readable,\n sampleRate = 16000,\n bitDepth = 16\n) {\n const pcmStream = new PassThrough()\n ffmpeg(audioStream)\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec(`pcm_s${bitDepth}le`)\n .format(`s${bitDepth}le`)\n .on('error', (error) => {\n console.error('Error converting audio stream:', error.message)\n })\n .pipe(pcmStream)\n return pcmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate).pipe(webmStream)\n return webmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertPCMToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate)\n .inputFormat('s16le')\n .inputOptions(['-f s16le', '-ar 16000', '-ac 1'])\n .pipe(webmStream)\n return webmStream\n}\n\nfunction ffmpegToOpus(ffmpegCommand: ffmpeg.FfmpegCommand, sampleRate = 16000) {\n return ffmpegCommand\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec('libopus')\n .format('webm')\n .outputOptions([\n '-application audio',\n `-ac 1`,\n `-ar ${sampleRate}`,\n `-b:a 64k`,\n `-f webm`,\n `-map_metadata -1`,\n ])\n .on('error', (error) => {\n console.error('Error converting to Opus: ', error.message)\n })\n}\n","import WebSocket from 'ws'\n\nexport enum MicdropErrorCode {\n BadRequest = 4400,\n Unauthorized = 4401,\n NotFound = 4404,\n}\n\nexport class MicdropError extends Error {\n code: number\n\n constructor(code: number, message: string) {\n super(message)\n this.code = code\n }\n}\n\nexport function handleError(socket: WebSocket, error: unknown) {\n if (error instanceof MicdropError) {\n socket.close(error.code, error.message)\n } else {\n console.error(error)\n socket.close(1011)\n }\n socket.terminate()\n}\n","export class Logger {\n constructor(private readonly name: string) {}\n\n log(...message: any[]) {\n const time = process.uptime().toFixed(3)\n console.log(`[${this.name} ${time}]`, ...message)\n }\n}\n","import { Duplex, PassThrough, Readable } from 'stream'\nimport { WebSocket } from 'ws'\nimport type { Agent } from './agent'\nimport { Logger } from './Logger'\nimport type { STT } from './stt'\nimport type { TTS } from './tts'\nimport {\n MicdropCallSummary,\n MicdropClientCommands,\n MicdropConversationItem,\n MicdropServerCommands,\n} from './types'\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n onEnd?(call: MicdropCallSummary): void\n}\n\nexport class MicdropServer {\n public socket: WebSocket | null = null\n public config: MicdropConfig | null = null\n public logger?: Logger\n\n private startTime = Date.now()\n private lastMessageSpeeched?: MicdropConversationItem\n\n // When user is speaking, we're streaming chunks for STT\n private currentUserStream?: Duplex\n\n constructor(socket: WebSocket, config: MicdropConfig) {\n this.socket = socket\n this.config = config\n this.log(`Call started`)\n\n // Setup STT\n this.config.stt.on('Transcript', this.onTranscript)\n\n // Setup agent\n this.config.agent.on('Message', (message) =>\n this.socket?.send(\n `${MicdropServerCommands.Message} ${JSON.stringify(message)}`\n )\n )\n this.config.agent.on('CancelLastUserMessage', () =>\n this.socket?.send(MicdropServerCommands.CancelLastUserMessage)\n )\n this.config.agent.on('SkipAnswer', () =>\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n )\n this.config.agent.on('EndCall', () =>\n this.socket?.send(MicdropServerCommands.EndCall)\n )\n this.config.agent.on('ToolCall', (toolCall) =>\n this.socket?.send(\n `${MicdropServerCommands.ToolCall} ${JSON.stringify(toolCall)}`\n )\n )\n\n // Assistant speaks first\n this.sendFirstMessage()\n\n // Listen to events\n socket.on('close', this.onClose)\n socket.on('message', this.onMessage)\n }\n\n private log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n private cancel() {\n this.config?.tts.cancel()\n this.config?.agent.cancel()\n }\n\n private onClose = () => {\n if (!this.config) return\n this.log('Connection closed')\n const duration = Math.round((Date.now() - this.startTime) / 1000)\n\n // Destroy instances\n this.config.agent.destroy()\n this.config.stt.destroy()\n this.config.tts.destroy()\n\n // End call callback\n this.config.onEnd?.({\n conversation: this.config.agent.conversation.slice(1), // Remove system message\n duration,\n })\n\n // Unset params\n this.socket = null\n this.config = null\n }\n\n private onMessage = async (message: Buffer) => {\n if (message.byteLength === 0) return\n if (!Buffer.isBuffer(message)) {\n this.log('Message is not a buffer')\n return\n }\n\n // Commands\n if (message.byteLength < 15) {\n const cmd = message.toString()\n this.log(`Command: ${cmd}`)\n\n if (cmd === MicdropClientCommands.StartSpeaking) {\n // User started speaking\n await this.onStartSpeaking()\n } else if (cmd === MicdropClientCommands.Mute) {\n // User muted the call\n await this.onMute()\n } else if (cmd === MicdropClientCommands.StopSpeaking) {\n // User stopped speaking\n await this.onStopSpeaking()\n }\n }\n\n // Audio chunk\n else if (this.currentUserStream) {\n this.log(`Received chunk (${message.byteLength} bytes)`)\n this.currentUserStream.write(message)\n }\n }\n\n private async onMute() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private async onStartSpeaking() {\n if (!this.config) return\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private async onStopSpeaking() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n\n const conversation = this.config?.agent.conversation\n const lastMessage = conversation?.[conversation.length - 1]\n if (\n lastMessage?.role === 'user' &&\n this.lastMessageSpeeched !== lastMessage\n ) {\n this.log(\n 'User stopped speaking and a transcript already exists, answering'\n )\n this.answer()\n }\n }\n\n private onTranscript = async (transcript: string) => {\n if (!this.config) return\n this.log(`User transcript: \"${transcript}\"`)\n this.config.agent.addUserMessage(transcript)\n\n // Answer if user stopped speaking\n if (!this.currentUserStream) {\n this.log('User stopped speaking, answering')\n this.answer()\n }\n }\n\n private async sendFirstMessage() {\n if (!this.config) return\n try {\n if (this.config.firstMessage) {\n // Send first message\n this.config.agent.addAssistantMessage(this.config.firstMessage)\n await this.speak(this.config.firstMessage)\n } else if (this.config.generateFirstMessage) {\n // Generate first message\n await this.answer()\n } else {\n // Skip answer if no first message is provided\n // to avoid keeping the client in a processing state\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n } catch (error) {\n console.error('[MicdropServer]', error)\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n }\n\n private async answer() {\n if (!this.config) return\n this.cancel()\n\n // Prevent answering twice\n const lastMessage =\n this.config.agent.conversation[this.config.agent.conversation.length - 1]\n if (this.lastMessageSpeeched === lastMessage) {\n this.log('Already answered, skipping')\n return\n }\n this.lastMessageSpeeched = lastMessage\n\n try {\n // LLM: Generate answer\n const stream = this.config.agent.answer()\n\n // TTS: Generate answer audio\n await this.speak(stream)\n } catch (error) {\n console.error('[MicdropServer]', error)\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n }\n\n // Run text-to-speech and send to client\n private async speak(message: string | Readable) {\n if (!this.socket || !this.config) return\n\n // Convert message to stream if needed\n let textStream: Readable\n if (typeof message === 'string') {\n const stream = new PassThrough()\n stream.write(message)\n stream.end()\n textStream = stream\n } else {\n textStream = message\n }\n\n // Run TTS\n const audio = this.config.tts.speak(textStream)\n\n // Send audio to client\n await this.sendAudio(audio)\n }\n\n private async sendAudio(audio: Readable) {\n if (!this.socket) return\n if (!audio.readable) {\n this.log('Non readable audio, skipping', audio)\n return\n }\n\n // Stream audio\n audio.on('data', (chunk) => {\n this.log(`Send audio chunk (${chunk.byteLength} bytes)`)\n this.socket?.send(chunk)\n })\n audio.on('error', (error) => {\n this.log('Error in audio stream', error)\n })\n audio.on('end', () => {\n this.log('Audio stream ended')\n })\n }\n}\n","export enum MicdropClientCommands {\n StartSpeaking = 'StartSpeaking',\n StopSpeaking = 'StopSpeaking',\n Mute = 'Mute',\n}\n\nexport enum MicdropServerCommands {\n Message = 'Message',\n CancelLastUserMessage = 'CancelLastUserMessage',\n SkipAnswer = 'SkipAnswer',\n EndCall = 'EndCall',\n ToolCall = 'ToolCall',\n}\n\nexport interface MicdropCallSummary {\n conversation: MicdropConversation\n duration: number\n}\n\nexport type MicdropConversationItem =\n | MicdropConversationMessage\n | MicdropConversationToolCall\n | MicdropConversationToolResult\n\nexport type MicdropConversation = Array<MicdropConversationItem>\n\nexport type MicdropAnswerMetadata = {\n [key: string]: any\n}\n\nexport interface MicdropConversationMessage<\n Data extends MicdropAnswerMetadata = MicdropAnswerMetadata,\n> {\n role: 'system' | 'user' | 'assistant'\n content: string\n metadata?: Data\n}\n\nexport interface MicdropConversationToolCall {\n role: 'tool_call'\n toolCallId: string\n toolName: string\n parameters: string\n}\n\nexport interface MicdropConversationToolResult {\n role: 'tool_result'\n toolCallId: string\n toolName: string\n output: string\n}\n\nexport interface MicdropToolCall {\n name: string\n parameters: any\n output: any\n}\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\n// Audio mime type to extension map\nconst MIME_TYPE_TO_EXTENSION = {\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/mpeg': 'mp3',\n 'audio/webm': 'webm',\n 'audio/mp4': 'mp4',\n 'audio/flac': 'flac',\n} as const\n\nexport interface STTEvents {\n Transcript: [string]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n protected mimeType?: keyof typeof MIME_TYPE_TO_EXTENSION\n public logger?: Logger\n\n // Set stream of audio to transcribe\n transcribe(audioStream: Readable) {\n // Detect mime type at first chunk\n audioStream.once('data', (chunk) => {\n this.mimeType = this.detectMimeType(chunk)\n })\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n }\n\n protected get extension(): string {\n return (this.mimeType && MIME_TYPE_TO_EXTENSION[this.mimeType]) || 'bin'\n }\n\n private detectMimeType(\n chunk: ArrayBuffer\n ): keyof typeof MIME_TYPE_TO_EXTENSION {\n if (!chunk || chunk.byteLength === 0) {\n throw new Error('Unable to detect mime type (empty chunk)')\n }\n\n const arr = new Uint8Array(chunk)\n\n // WEBM: 1A 45 DF A3\n if (\n arr[0] === 0x1a &&\n arr[1] === 0x45 &&\n arr[2] === 0xdf &&\n arr[3] === 0xa3\n ) {\n return 'audio/webm'\n }\n // OGG: 4F 67 67 53\n if (\n arr[0] === 0x4f &&\n arr[1] === 0x67 &&\n arr[2] === 0x67 &&\n arr[3] === 0x53\n ) {\n return 'audio/ogg'\n }\n // WAV: 52 49 46 46 ... 57 41 56 45\n if (\n arr[0] === 0x52 &&\n arr[1] === 0x49 &&\n arr[2] === 0x46 &&\n arr[3] === 0x46 &&\n arr[8] === 0x57 &&\n arr[9] === 0x41 &&\n arr[10] === 0x56 &&\n arr[11] === 0x45\n ) {\n return 'audio/wav'\n }\n // MP3: 49 44 33\n if (arr[0] === 0x49 && arr[1] === 0x44 && arr[2] === 0x33) {\n return 'audio/mpeg'\n }\n // MP4/M4A: 00 00 00 .. 66 74 79 70\n if (\n arr[4] === 0x66 &&\n arr[5] === 0x74 &&\n arr[6] === 0x79 &&\n arr[7] === 0x70\n ) {\n return 'audio/mp4'\n }\n // FLAC: 66 4c 61 43\n if (\n arr[0] === 0x66 &&\n arr[1] === 0x4c &&\n arr[2] === 0x61 &&\n arr[3] === 0x43\n ) {\n return 'audio/flac'\n }\n this.log('Unable to detect mime type, using default', chunk)\n return 'audio/wav'\n }\n}\n","import { Readable } from 'stream'\nimport { STT } from './STT'\n\n/**\n * Abstract class for STT, converting stream to file before transcribing\n */\n\nexport abstract class FileSTT extends STT {\n abstract transcribeFile(file: File): Promise<string>\n\n transcribe(audioStream: Readable) {\n super.transcribe(audioStream)\n\n // Convert stream to file\n this.log('Converting stream to file...')\n\n const chunks: Buffer[] = []\n audioStream.on('data', (chunk) => {\n chunks.push(chunk)\n })\n\n audioStream.on('end', async () => {\n if (chunks.length === 0) return\n const arrayBuffer = Buffer.concat(chunks)\n const file = new File([arrayBuffer], `audio.${this.extension}`, {\n type: this.mimeType,\n })\n\n // Transcribe file with implementation\n this.log('Transcribing file...')\n const transcript = await this.transcribeFile(file)\n this.emit('Transcript', transcript)\n })\n }\n}\n","import { FileSTT } from './FileSTT'\n\nexport class MockSTT extends FileSTT {\n private i = 0\n\n async transcribeFile(file: File) {\n return `User Message ${this.i++}`\n }\n}\n","import * as fs from 'fs'\nimport { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\n\nexport class MockTTS extends TTS {\n constructor(private audioFilePaths: string[]) {\n super()\n }\n\n speak(textStream: Readable) {\n const audioStream = new PassThrough()\n textStream.once('data', async () => {\n for (const filePath of this.audioFilePaths) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n const audioBuffer = fs.readFileSync(filePath)\n this.log(`Loaded chunk (${audioBuffer.length} bytes)`)\n audioStream.write(audioBuffer)\n }\n audioStream.end()\n })\n return audioStream\n }\n\n cancel() {}\n}\n","import { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport abstract class TTS {\n public logger?: Logger\n\n abstract speak(textStream: Readable): Readable\n abstract cancel(): void\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.cancel()\n }\n}\n","import { WebSocket } from 'ws'\nimport { MicdropError, MicdropErrorCode } from './errors'\n\nexport async function waitForParams<CallParams>(\n socket: WebSocket,\n validate: (params: any) => CallParams\n): Promise<CallParams> {\n return new Promise<CallParams>((resolve, reject) => {\n // Handle timeout\n const timeout = setTimeout(() => {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Missing params'))\n }, 3000)\n\n const onParams = (payload: string) => {\n // Clear timeout and listener\n clearTimeout(timeout)\n socket.off('message', onParams)\n\n try {\n // Parse JSON payload\n const params = validate(JSON.parse(payload))\n resolve(params)\n } catch (error) {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Invalid params'))\n }\n }\n\n // Listen for params\n socket.on('message', onParams)\n })\n}\n"],"mappings":"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","names":["import_stream","ffmpeg","ffmpegInstaller","MicdropErrorCode","import_stream","MicdropClientCommands","MicdropServerCommands","import_eventemitter3","import_stream"]}
|
package/dist/index.mjs
CHANGED
|
@@ -53,18 +53,14 @@ var Agent = class extends EventEmitter {
|
|
|
53
53
|
}
|
|
54
54
|
cancelLastUserMessage() {
|
|
55
55
|
this.log("Cancelling last user message");
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
const lastMessageIndex = this.conversation.findLastIndex(
|
|
57
|
+
(message) => message.role === "user"
|
|
58
|
+
);
|
|
59
|
+
if (lastMessageIndex !== -1) {
|
|
60
|
+
this.conversation.splice(lastMessageIndex, 1);
|
|
61
|
+
}
|
|
59
62
|
this.emit("CancelLastUserMessage");
|
|
60
63
|
}
|
|
61
|
-
cancelLastAssistantMessage() {
|
|
62
|
-
this.log("Cancelling last assistant message");
|
|
63
|
-
const lastMessage = this.conversation[this.conversation.length - 1];
|
|
64
|
-
if (lastMessage?.role !== "assistant") return;
|
|
65
|
-
this.conversation.pop();
|
|
66
|
-
this.emit("CancelLastAssistantMessage");
|
|
67
|
-
}
|
|
68
64
|
skipAnswer() {
|
|
69
65
|
this.log("Skipping answer");
|
|
70
66
|
this.emit("SkipAnswer");
|
|
@@ -282,7 +278,6 @@ var MicdropClientCommands = /* @__PURE__ */ ((MicdropClientCommands2) => {
|
|
|
282
278
|
})(MicdropClientCommands || {});
|
|
283
279
|
var MicdropServerCommands = /* @__PURE__ */ ((MicdropServerCommands2) => {
|
|
284
280
|
MicdropServerCommands2["Message"] = "Message";
|
|
285
|
-
MicdropServerCommands2["CancelLastAssistantMessage"] = "CancelLastAssistantMessage";
|
|
286
281
|
MicdropServerCommands2["CancelLastUserMessage"] = "CancelLastUserMessage";
|
|
287
282
|
MicdropServerCommands2["SkipAnswer"] = "SkipAnswer";
|
|
288
283
|
MicdropServerCommands2["EndCall"] = "EndCall";
|
|
@@ -355,10 +350,6 @@ var MicdropServer = class {
|
|
|
355
350
|
"CancelLastUserMessage",
|
|
356
351
|
() => this.socket?.send("CancelLastUserMessage" /* CancelLastUserMessage */)
|
|
357
352
|
);
|
|
358
|
-
this.config.agent.on(
|
|
359
|
-
"CancelLastAssistantMessage",
|
|
360
|
-
() => this.socket?.send("CancelLastAssistantMessage" /* CancelLastAssistantMessage */)
|
|
361
|
-
);
|
|
362
353
|
this.config.agent.on(
|
|
363
354
|
"SkipAnswer",
|
|
364
355
|
() => this.socket?.send("SkipAnswer" /* SkipAnswer */)
|
|
@@ -400,7 +391,8 @@ var MicdropServer = class {
|
|
|
400
391
|
this.currentUserStream?.end();
|
|
401
392
|
this.currentUserStream = void 0;
|
|
402
393
|
const conversation = this.config?.agent.conversation;
|
|
403
|
-
|
|
394
|
+
const lastMessage = conversation?.[conversation.length - 1];
|
|
395
|
+
if (lastMessage?.role === "user" && this.lastMessageSpeeched !== lastMessage) {
|
|
404
396
|
this.log(
|
|
405
397
|
"User stopped speaking and a transcript already exists, answering"
|
|
406
398
|
);
|
|
@@ -426,6 +418,12 @@ var MicdropServer = class {
|
|
|
426
418
|
async answer() {
|
|
427
419
|
if (!this.config) return;
|
|
428
420
|
this.cancel();
|
|
421
|
+
const lastMessage = this.config.agent.conversation[this.config.agent.conversation.length - 1];
|
|
422
|
+
if (this.lastMessageSpeeched === lastMessage) {
|
|
423
|
+
this.log("Already answered, skipping");
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
this.lastMessageSpeeched = lastMessage;
|
|
429
427
|
try {
|
|
430
428
|
const stream = this.config.agent.answer();
|
|
431
429
|
await this.speak(stream);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/audio-convert.ts","../src/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/stt/STT.ts","../src/stt/FileSTT.ts","../src/stt/MockSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/waitForParams.ts"],"sourcesContent":["import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport type { z } from 'zod'\nimport { Logger } from '../Logger'\nimport {\n MicdropAnswerMetadata,\n MicdropConversation,\n MicdropConversationItem,\n MicdropConversationMessage,\n MicdropConversationToolCall,\n MicdropConversationToolResult,\n MicdropToolCall,\n} from '../types'\nimport {\n AUTO_END_CALL_PROMPT,\n AUTO_END_CALL_TOOL_NAME,\n AUTO_IGNORE_USER_NOISE_PROMPT,\n AUTO_IGNORE_USER_NOISE_TOOL_NAME,\n AUTO_SEMANTIC_TURN_PROMPT,\n AUTO_SEMANTIC_TURN_TOOL_NAME,\n Tool,\n} from './tools'\n\nexport interface AgentOptions {\n systemPrompt: string\n\n // Enable auto ending of the call when user asks to end the call\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoEndCall?: boolean | string\n\n // Enable detection of an incomplete sentence, and skip the answer (assistant waits)\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoSemanticTurn?: boolean | string\n\n // Ignore of the last user message when it's meaningless\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoIgnoreUserNoise?: boolean | string\n\n // Extract a value from the answer\n // Value must be at the end of the answer, in JSON or between tags\n extract?: ExtractJsonOptions | ExtractTagOptions\n}\n\nexport interface AgentEvents {\n Message: [MicdropConversationItem]\n CancelLastUserMessage: []\n CancelLastAssistantMessage: []\n SkipAnswer: []\n EndCall: []\n ToolCall: [MicdropToolCall]\n}\n\nexport interface ExtractOptions {\n callback?: (value: string) => void\n saveInMetadata?: boolean\n}\n\nexport interface ExtractJsonOptions extends ExtractOptions {\n json: true\n callback?: (value: any) => void\n}\n\nexport interface ExtractTagOptions extends ExtractOptions {\n startTag: string\n endTag: string\n}\n\nexport abstract class Agent<\n Options extends AgentOptions = AgentOptions,\n> extends EventEmitter<AgentEvents> {\n public logger?: Logger\n public conversation: MicdropConversation\n\n protected tools: Tool[]\n\n constructor(protected options: Options) {\n super()\n this.conversation = [{ role: 'system', content: options.systemPrompt }]\n this.tools = this.getDefaultTools()\n }\n\n abstract answer(): Readable\n abstract cancel(): void\n\n addUserMessage(text: string, metadata?: MicdropAnswerMetadata) {\n this.addMessage('user', text, metadata)\n }\n\n addAssistantMessage(text: string, metadata?: MicdropAnswerMetadata) {\n this.addMessage('assistant', text, metadata)\n }\n\n addTool<Schema extends z.ZodObject>(tool: Tool<Schema>) {\n this.tools.push(tool)\n }\n\n removeTool(name: string) {\n this.tools = this.tools.filter((tool) => tool.name !== name)\n }\n\n getTool(name: string): Tool | undefined {\n return this.tools.find((tool) => tool.name === name)\n }\n\n protected addMessage(\n role: 'user' | 'assistant' | 'system',\n text: string,\n metadata?: MicdropAnswerMetadata\n ) {\n this.log(`Adding ${role} message to conversation: ${text}`)\n const message: MicdropConversationMessage = {\n role,\n content: text,\n metadata,\n }\n this.conversation.push(message)\n this.emit('Message', message)\n }\n\n protected addToolMessage(\n message: MicdropConversationToolCall | MicdropConversationToolResult\n ) {\n this.log('Adding tool message:', message)\n this.conversation.push(message)\n this.emit('Message', message)\n }\n\n protected endCall() {\n this.log('Ending call')\n this.emit('EndCall')\n }\n\n protected cancelLastUserMessage() {\n this.log('Cancelling last user message')\n const lastMessage = this.conversation[this.conversation.length - 1]\n if (lastMessage?.role !== 'user') return\n this.conversation.pop()\n this.emit('CancelLastUserMessage')\n }\n\n protected cancelLastAssistantMessage() {\n this.log('Cancelling last assistant message')\n const lastMessage = this.conversation[this.conversation.length - 1]\n if (lastMessage?.role !== 'assistant') return\n this.conversation.pop()\n this.emit('CancelLastAssistantMessage')\n }\n\n protected skipAnswer() {\n this.log('Skipping answer')\n this.emit('SkipAnswer')\n }\n\n protected getDefaultTools() {\n const tools: Tool[] = []\n if (this.options.autoEndCall) {\n tools.push({\n name: AUTO_END_CALL_TOOL_NAME,\n description:\n typeof this.options.autoEndCall === 'string'\n ? this.options.autoEndCall\n : AUTO_END_CALL_PROMPT,\n execute: () => this.endCall(),\n })\n }\n if (this.options.autoSemanticTurn) {\n tools.push({\n name: AUTO_SEMANTIC_TURN_TOOL_NAME,\n description:\n typeof this.options.autoSemanticTurn === 'string'\n ? this.options.autoSemanticTurn\n : AUTO_SEMANTIC_TURN_PROMPT,\n skipAnswer: true,\n execute: () => this.skipAnswer(),\n })\n }\n if (this.options.autoIgnoreUserNoise) {\n tools.push({\n name: AUTO_IGNORE_USER_NOISE_TOOL_NAME,\n description:\n typeof this.options.autoIgnoreUserNoise === 'string'\n ? this.options.autoIgnoreUserNoise\n : AUTO_IGNORE_USER_NOISE_PROMPT,\n skipAnswer: true,\n execute: () => this.cancelLastUserMessage(),\n })\n }\n return tools\n }\n\n protected async executeTool(toolCall: MicdropConversationToolCall) {\n try {\n const tool = this.getTool(toolCall.toolName)\n if (!tool) {\n throw new Error(`Tool not found \"${toolCall.toolName}\"`)\n }\n\n this.log('Executing tool:', toolCall.toolName, toolCall.parameters)\n\n // Save tool call in conversation\n this.addToolMessage(toolCall)\n\n const parameters = JSON.parse(toolCall.parameters)\n const output = tool.execute ? await tool.execute(parameters) : {}\n\n // Save tool result in conversation\n this.addToolMessage({\n role: 'tool_result',\n toolCallId: toolCall.toolCallId,\n toolName: toolCall.toolName,\n output: JSON.stringify(output ?? null),\n })\n\n // Emit output\n if (tool.emitOutput) {\n this.emit('ToolCall', {\n name: toolCall.toolName,\n parameters,\n output,\n })\n }\n\n return {\n output,\n skipAnswer: tool.skipAnswer,\n }\n } catch (error: any) {\n console.error('[OpenaiAgent] Error executing tool:', error)\n return {\n output: {\n error: error.message,\n },\n }\n }\n }\n\n protected getExtractOptions(): ExtractTagOptions | undefined {\n const extract = this.options.extract\n if (!extract) return undefined\n if ('json' in extract && extract.json) {\n return { ...extract, startTag: '{', endTag: '}' }\n }\n if ('startTag' in extract && 'endTag' in extract) {\n return extract\n }\n return undefined\n }\n\n protected extract(message: string) {\n const extractOptions = this.getExtractOptions()\n let metadata: MicdropAnswerMetadata | undefined = undefined\n\n // Extract value?\n if (extractOptions) {\n const startTagIndex = message.indexOf(extractOptions.startTag)\n if (startTagIndex !== -1) {\n // Find end tag\n let endTagIndex = message.lastIndexOf(extractOptions.endTag)\n if (endTagIndex === -1) endTagIndex = message.length + 1\n else endTagIndex += extractOptions.endTag.length\n const extractedText = message.slice(startTagIndex, endTagIndex).trim()\n\n // Parse extracted value\n try {\n const extractedValue =\n 'json' in extractOptions && extractOptions.json\n ? JSON.parse(extractedText)\n : extractedText\n\n // Call callback\n if (extractOptions.callback) {\n extractOptions.callback(extractedValue)\n }\n\n // Save in metadata\n if (extractOptions.saveInMetadata) {\n metadata = { extracted: extractedValue }\n }\n } catch (error) {\n console.error(\n `[OpenaiAgent] Error parsing extracted value (${extractedText}):`,\n error\n )\n }\n\n // Remove extracted value from message\n message = message.slice(0, startTagIndex).trimEnd()\n }\n }\n return { message, metadata }\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n this.cancel()\n }\n}\n","import type { z } from 'zod'\n\nexport interface Tool<Schema extends z.ZodObject = z.ZodObject> {\n name: string\n description: string\n inputSchema?: Schema\n execute?: (input: z.infer<Schema>) => any | Promise<any>\n skipAnswer?: boolean\n emitOutput?: boolean\n}\n\nexport const AUTO_END_CALL_TOOL_NAME = 'end_call'\nexport const AUTO_END_CALL_PROMPT =\n 'Call this tool only if user asks to end the call'\n\nexport const AUTO_SEMANTIC_TURN_TOOL_NAME = 'semantic_turn'\nexport const AUTO_SEMANTIC_TURN_PROMPT =\n 'Call this tool only if last user message is obviously an incomplete sentence that you need to wait for the end before answering'\n\nexport const AUTO_IGNORE_USER_NOISE_TOOL_NAME = 'ignore_user_noise'\nexport const AUTO_IGNORE_USER_NOISE_PROMPT =\n 'Call this tool only if last user message is just an interjection or a sound that expresses emotion, hesitation, or reaction (ex: \"Uh\", \"Ahem\", \"Hmm\", \"Ah\") but doesn\\'t carry any clear meaning like agreeing, refusing, or commanding'\n","import { PassThrough } from 'stream'\nimport { Agent } from './Agent'\n\nexport class MockAgent extends Agent {\n private i = 0\n\n constructor() {\n super({ systemPrompt: '' })\n }\n\n answer() {\n const stream = new PassThrough()\n\n // Answer message\n const message = `Assistant Message ${this.i++}`\n this.addAssistantMessage(message)\n stream.write(message)\n stream.end()\n return stream\n }\n\n cancel() {}\n}\n","import ffmpegInstaller from '@ffmpeg-installer/ffmpeg'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { PassThrough, Readable } from 'stream'\n\n// Setup ffmpeg\nffmpeg.setFfmpegPath(ffmpegInstaller.path)\n\n// Convert stream to WAV/PCM\nexport function convertToPCM(\n audioStream: Readable,\n sampleRate = 16000,\n bitDepth = 16\n) {\n const pcmStream = new PassThrough()\n ffmpeg(audioStream)\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec(`pcm_s${bitDepth}le`)\n .format(`s${bitDepth}le`)\n .on('error', (error) => {\n console.error('Error converting audio stream:', error.message)\n })\n .pipe(pcmStream)\n return pcmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate).pipe(webmStream)\n return webmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertPCMToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate)\n .inputFormat('s16le')\n .inputOptions(['-f s16le', '-ar 16000', '-ac 1'])\n .pipe(webmStream)\n return webmStream\n}\n\nfunction ffmpegToOpus(ffmpegCommand: ffmpeg.FfmpegCommand, sampleRate = 16000) {\n return ffmpegCommand\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec('libopus')\n .format('webm')\n .outputOptions([\n '-application audio',\n `-ac 1`,\n `-ar ${sampleRate}`,\n `-b:a 64k`,\n `-f webm`,\n `-map_metadata -1`,\n ])\n .on('error', (error) => {\n console.error('Error converting to Opus: ', error.message)\n })\n}\n","import WebSocket from 'ws'\n\nexport enum MicdropErrorCode {\n BadRequest = 4400,\n Unauthorized = 4401,\n NotFound = 4404,\n}\n\nexport class MicdropError extends Error {\n code: number\n\n constructor(code: number, message: string) {\n super(message)\n this.code = code\n }\n}\n\nexport function handleError(socket: WebSocket, error: unknown) {\n if (error instanceof MicdropError) {\n socket.close(error.code, error.message)\n } else {\n console.error(error)\n socket.close(1011)\n }\n socket.terminate()\n}\n","export class Logger {\n constructor(private readonly name: string) {}\n\n log(...message: any[]) {\n const time = process.uptime().toFixed(3)\n console.log(`[${this.name} ${time}]`, ...message)\n }\n}\n","import { Duplex, PassThrough, Readable } from 'stream'\nimport { WebSocket } from 'ws'\nimport type { Agent } from './agent'\nimport { Logger } from './Logger'\nimport type { STT } from './stt'\nimport type { TTS } from './tts'\nimport {\n MicdropCallSummary,\n MicdropClientCommands,\n MicdropServerCommands,\n} from './types'\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n onEnd?(call: MicdropCallSummary): void\n}\n\nexport class MicdropServer {\n public socket: WebSocket | null = null\n public config: MicdropConfig | null = null\n public logger?: Logger\n\n private startTime = Date.now()\n\n // When user is speaking, we're streaming chunks for STT\n private currentUserStream?: Duplex\n\n constructor(socket: WebSocket, config: MicdropConfig) {\n this.socket = socket\n this.config = config\n this.log(`Call started`)\n\n // Setup STT\n this.config.stt.on('Transcript', this.onTranscript)\n\n // Setup agent\n this.config.agent.on('Message', (message) =>\n this.socket?.send(\n `${MicdropServerCommands.Message} ${JSON.stringify(message)}`\n )\n )\n this.config.agent.on('CancelLastUserMessage', () =>\n this.socket?.send(MicdropServerCommands.CancelLastUserMessage)\n )\n this.config.agent.on('CancelLastAssistantMessage', () =>\n this.socket?.send(MicdropServerCommands.CancelLastAssistantMessage)\n )\n this.config.agent.on('SkipAnswer', () =>\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n )\n this.config.agent.on('EndCall', () =>\n this.socket?.send(MicdropServerCommands.EndCall)\n )\n this.config.agent.on('ToolCall', (toolCall) =>\n this.socket?.send(\n `${MicdropServerCommands.ToolCall} ${JSON.stringify(toolCall)}`\n )\n )\n\n // Assistant speaks first\n this.sendFirstMessage()\n\n // Listen to events\n socket.on('close', this.onClose)\n socket.on('message', this.onMessage)\n }\n\n private log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n private cancel() {\n this.config?.tts.cancel()\n this.config?.agent.cancel()\n }\n\n private onClose = () => {\n if (!this.config) return\n this.log('Connection closed')\n const duration = Math.round((Date.now() - this.startTime) / 1000)\n\n // Destroy instances\n this.config.agent.destroy()\n this.config.stt.destroy()\n this.config.tts.destroy()\n\n // End call callback\n this.config.onEnd?.({\n conversation: this.config.agent.conversation.slice(1), // Remove system message\n duration,\n })\n\n // Unset params\n this.socket = null\n this.config = null\n }\n\n private onMessage = async (message: Buffer) => {\n if (message.byteLength === 0) return\n if (!Buffer.isBuffer(message)) {\n this.log('Message is not a buffer')\n return\n }\n\n // Commands\n if (message.byteLength < 15) {\n const cmd = message.toString()\n this.log(`Command: ${cmd}`)\n\n if (cmd === MicdropClientCommands.StartSpeaking) {\n // User started speaking\n await this.onStartSpeaking()\n } else if (cmd === MicdropClientCommands.Mute) {\n // User muted the call\n await this.onMute()\n } else if (cmd === MicdropClientCommands.StopSpeaking) {\n // User stopped speaking\n await this.onStopSpeaking()\n }\n }\n\n // Audio chunk\n else if (this.currentUserStream) {\n this.log(`Received chunk (${message.byteLength} bytes)`)\n this.currentUserStream.write(message)\n }\n }\n\n private async onMute() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private async onStartSpeaking() {\n if (!this.config) return\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private async onStopSpeaking() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n\n const conversation = this.config?.agent.conversation\n if (conversation && conversation[conversation.length - 1].role === 'user') {\n this.log(\n 'User stopped speaking and a transcript already exists, answering'\n )\n this.answer()\n }\n }\n\n private onTranscript = async (transcript: string) => {\n if (!this.config) return\n this.log(`User transcript: \"${transcript}\"`)\n this.config.agent.addUserMessage(transcript)\n\n // Answer if user stopped speaking\n if (!this.currentUserStream) {\n this.log('User stopped speaking, answering')\n this.answer()\n }\n }\n\n private async sendFirstMessage() {\n if (!this.config) return\n try {\n if (this.config.firstMessage) {\n // Send first message\n this.config.agent.addAssistantMessage(this.config.firstMessage)\n await this.speak(this.config.firstMessage)\n } else if (this.config.generateFirstMessage) {\n // Generate first message\n await this.answer()\n } else {\n // Skip answer if no first message is provided\n // to avoid keeping the client in a processing state\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n } catch (error) {\n console.error('[MicdropServer]', error)\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n }\n\n private async answer() {\n if (!this.config) return\n this.cancel()\n try {\n // LLM: Generate answer\n const stream = this.config.agent.answer()\n\n // TTS: Generate answer audio\n await this.speak(stream)\n } catch (error) {\n console.error('[MicdropServer]', error)\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n }\n\n // Run text-to-speech and send to client\n private async speak(message: string | Readable) {\n if (!this.socket || !this.config) return\n\n // Convert message to stream if needed\n let textStream: Readable\n if (typeof message === 'string') {\n const stream = new PassThrough()\n stream.write(message)\n stream.end()\n textStream = stream\n } else {\n textStream = message\n }\n\n // Run TTS\n const audio = this.config.tts.speak(textStream)\n\n // Send audio to client\n await this.sendAudio(audio)\n }\n\n private async sendAudio(audio: Readable) {\n if (!this.socket) return\n if (!audio.readable) {\n this.log('Non readable audio, skipping', audio)\n return\n }\n\n // Stream audio\n audio.on('data', (chunk) => {\n this.log(`Send audio chunk (${chunk.byteLength} bytes)`)\n this.socket?.send(chunk)\n })\n audio.on('error', (error) => {\n this.log('Error in audio stream', error)\n })\n audio.on('end', () => {\n this.log('Audio stream ended')\n })\n }\n}\n","export enum MicdropClientCommands {\n StartSpeaking = 'StartSpeaking',\n StopSpeaking = 'StopSpeaking',\n Mute = 'Mute',\n}\n\nexport enum MicdropServerCommands {\n Message = 'Message',\n CancelLastAssistantMessage = 'CancelLastAssistantMessage',\n CancelLastUserMessage = 'CancelLastUserMessage',\n SkipAnswer = 'SkipAnswer',\n EndCall = 'EndCall',\n ToolCall = 'ToolCall',\n}\n\nexport interface MicdropCallSummary {\n conversation: MicdropConversation\n duration: number\n}\n\nexport type MicdropConversationItem =\n | MicdropConversationMessage\n | MicdropConversationToolCall\n | MicdropConversationToolResult\n\nexport type MicdropConversation = Array<MicdropConversationItem>\n\nexport type MicdropAnswerMetadata = {\n [key: string]: any\n}\n\nexport interface MicdropConversationMessage<\n Data extends MicdropAnswerMetadata = MicdropAnswerMetadata,\n> {\n role: 'system' | 'user' | 'assistant'\n content: string\n metadata?: Data\n}\n\nexport interface MicdropConversationToolCall {\n role: 'tool_call'\n toolCallId: string\n toolName: string\n parameters: string\n}\n\nexport interface MicdropConversationToolResult {\n role: 'tool_result'\n toolCallId: string\n toolName: string\n output: string\n}\n\nexport interface MicdropToolCall {\n name: string\n parameters: any\n output: any\n}\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\n// Audio mime type to extension map\nconst MIME_TYPE_TO_EXTENSION = {\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/mpeg': 'mp3',\n 'audio/webm': 'webm',\n 'audio/mp4': 'mp4',\n 'audio/flac': 'flac',\n} as const\n\nexport interface STTEvents {\n Transcript: [string]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n protected mimeType?: keyof typeof MIME_TYPE_TO_EXTENSION\n public logger?: Logger\n\n // Set stream of audio to transcribe\n transcribe(audioStream: Readable) {\n // Detect mime type at first chunk\n audioStream.once('data', (chunk) => {\n this.mimeType = this.detectMimeType(chunk)\n })\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n }\n\n protected get extension(): string {\n return (this.mimeType && MIME_TYPE_TO_EXTENSION[this.mimeType]) || 'bin'\n }\n\n private detectMimeType(\n chunk: ArrayBuffer\n ): keyof typeof MIME_TYPE_TO_EXTENSION {\n if (!chunk || chunk.byteLength === 0) {\n throw new Error('Unable to detect mime type (empty chunk)')\n }\n\n const arr = new Uint8Array(chunk)\n\n // WEBM: 1A 45 DF A3\n if (\n arr[0] === 0x1a &&\n arr[1] === 0x45 &&\n arr[2] === 0xdf &&\n arr[3] === 0xa3\n ) {\n return 'audio/webm'\n }\n // OGG: 4F 67 67 53\n if (\n arr[0] === 0x4f &&\n arr[1] === 0x67 &&\n arr[2] === 0x67 &&\n arr[3] === 0x53\n ) {\n return 'audio/ogg'\n }\n // WAV: 52 49 46 46 ... 57 41 56 45\n if (\n arr[0] === 0x52 &&\n arr[1] === 0x49 &&\n arr[2] === 0x46 &&\n arr[3] === 0x46 &&\n arr[8] === 0x57 &&\n arr[9] === 0x41 &&\n arr[10] === 0x56 &&\n arr[11] === 0x45\n ) {\n return 'audio/wav'\n }\n // MP3: 49 44 33\n if (arr[0] === 0x49 && arr[1] === 0x44 && arr[2] === 0x33) {\n return 'audio/mpeg'\n }\n // MP4/M4A: 00 00 00 .. 66 74 79 70\n if (\n arr[4] === 0x66 &&\n arr[5] === 0x74 &&\n arr[6] === 0x79 &&\n arr[7] === 0x70\n ) {\n return 'audio/mp4'\n }\n // FLAC: 66 4c 61 43\n if (\n arr[0] === 0x66 &&\n arr[1] === 0x4c &&\n arr[2] === 0x61 &&\n arr[3] === 0x43\n ) {\n return 'audio/flac'\n }\n this.log('Unable to detect mime type, using default', chunk)\n return 'audio/wav'\n }\n}\n","import { Readable } from 'stream'\nimport { STT } from './STT'\n\n/**\n * Abstract class for STT, converting stream to file before transcribing\n */\n\nexport abstract class FileSTT extends STT {\n abstract transcribeFile(file: File): Promise<string>\n\n transcribe(audioStream: Readable) {\n super.transcribe(audioStream)\n\n // Convert stream to file\n this.log('Converting stream to file...')\n\n const chunks: Buffer[] = []\n audioStream.on('data', (chunk) => {\n chunks.push(chunk)\n })\n\n audioStream.on('end', async () => {\n if (chunks.length === 0) return\n const arrayBuffer = Buffer.concat(chunks)\n const file = new File([arrayBuffer], `audio.${this.extension}`, {\n type: this.mimeType,\n })\n\n // Transcribe file with implementation\n this.log('Transcribing file...')\n const transcript = await this.transcribeFile(file)\n this.emit('Transcript', transcript)\n })\n }\n}\n","import { FileSTT } from './FileSTT'\n\nexport class MockSTT extends FileSTT {\n private i = 0\n\n async transcribeFile(file: File) {\n return `User Message ${this.i++}`\n }\n}\n","import * as fs from 'fs'\nimport { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\n\nexport class MockTTS extends TTS {\n constructor(private audioFilePaths: string[]) {\n super()\n }\n\n speak(textStream: Readable) {\n const audioStream = new PassThrough()\n textStream.once('data', async () => {\n for (const filePath of this.audioFilePaths) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n const audioBuffer = fs.readFileSync(filePath)\n this.log(`Loaded chunk (${audioBuffer.length} bytes)`)\n audioStream.write(audioBuffer)\n }\n audioStream.end()\n })\n return audioStream\n }\n\n cancel() {}\n}\n","import { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport abstract class TTS {\n public logger?: Logger\n\n abstract speak(textStream: Readable): Readable\n abstract cancel(): void\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.cancel()\n }\n}\n","import { WebSocket } from 'ws'\nimport { MicdropError, MicdropErrorCode } from './errors'\n\nexport async function waitForParams<CallParams>(\n socket: WebSocket,\n validate: (params: any) => CallParams\n): Promise<CallParams> {\n return new Promise<CallParams>((resolve, reject) => {\n // Handle timeout\n const timeout = setTimeout(() => {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Missing params'))\n }, 3000)\n\n const onParams = (payload: string) => {\n // Clear timeout and listener\n clearTimeout(timeout)\n socket.off('message', onParams)\n\n try {\n // Parse JSON payload\n const params = validate(JSON.parse(payload))\n resolve(params)\n } catch (error) {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Invalid params'))\n }\n }\n\n // Listen for params\n socket.on('message', onParams)\n })\n}\n"],"mappings":"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","names":["PassThrough","MicdropErrorCode","PassThrough","MicdropClientCommands","MicdropServerCommands","PassThrough","EventEmitter","PassThrough","PassThrough"]}
|
|
1
|
+
{"version":3,"sources":["../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/audio-convert.ts","../src/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/stt/STT.ts","../src/stt/FileSTT.ts","../src/stt/MockSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/waitForParams.ts"],"sourcesContent":["import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport type { z } from 'zod'\nimport { Logger } from '../Logger'\nimport {\n MicdropAnswerMetadata,\n MicdropConversation,\n MicdropConversationItem,\n MicdropConversationMessage,\n MicdropConversationToolCall,\n MicdropConversationToolResult,\n MicdropToolCall,\n} from '../types'\nimport {\n AUTO_END_CALL_PROMPT,\n AUTO_END_CALL_TOOL_NAME,\n AUTO_IGNORE_USER_NOISE_PROMPT,\n AUTO_IGNORE_USER_NOISE_TOOL_NAME,\n AUTO_SEMANTIC_TURN_PROMPT,\n AUTO_SEMANTIC_TURN_TOOL_NAME,\n Tool,\n} from './tools'\n\nexport interface AgentOptions {\n systemPrompt: string\n\n // Enable auto ending of the call when user asks to end the call\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoEndCall?: boolean | string\n\n // Enable detection of an incomplete sentence, and skip the answer (assistant waits)\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoSemanticTurn?: boolean | string\n\n // Ignore of the last user message when it's meaningless\n // You can provide a custom prompt to use instead of the default one by passing a string\n autoIgnoreUserNoise?: boolean | string\n\n // Extract a value from the answer\n // Value must be at the end of the answer, in JSON or between tags\n extract?: ExtractJsonOptions | ExtractTagOptions\n}\n\nexport interface AgentEvents {\n Message: [MicdropConversationItem]\n CancelLastUserMessage: []\n SkipAnswer: []\n EndCall: []\n ToolCall: [MicdropToolCall]\n}\n\nexport interface ExtractOptions {\n callback?: (value: string) => void\n saveInMetadata?: boolean\n}\n\nexport interface ExtractJsonOptions extends ExtractOptions {\n json: true\n callback?: (value: any) => void\n}\n\nexport interface ExtractTagOptions extends ExtractOptions {\n startTag: string\n endTag: string\n}\n\nexport abstract class Agent<\n Options extends AgentOptions = AgentOptions,\n> extends EventEmitter<AgentEvents> {\n public logger?: Logger\n public conversation: MicdropConversation\n\n protected tools: Tool[]\n\n constructor(protected options: Options) {\n super()\n this.conversation = [{ role: 'system', content: options.systemPrompt }]\n this.tools = this.getDefaultTools()\n }\n\n abstract answer(): Readable\n abstract cancel(): void\n\n addUserMessage(text: string, metadata?: MicdropAnswerMetadata) {\n this.addMessage('user', text, metadata)\n }\n\n addAssistantMessage(text: string, metadata?: MicdropAnswerMetadata) {\n this.addMessage('assistant', text, metadata)\n }\n\n addTool<Schema extends z.ZodObject>(tool: Tool<Schema>) {\n this.tools.push(tool)\n }\n\n removeTool(name: string) {\n this.tools = this.tools.filter((tool) => tool.name !== name)\n }\n\n getTool(name: string): Tool | undefined {\n return this.tools.find((tool) => tool.name === name)\n }\n\n protected addMessage(\n role: 'user' | 'assistant' | 'system',\n text: string,\n metadata?: MicdropAnswerMetadata\n ) {\n this.log(`Adding ${role} message to conversation: ${text}`)\n const message: MicdropConversationMessage = {\n role,\n content: text,\n metadata,\n }\n this.conversation.push(message)\n this.emit('Message', message)\n }\n\n protected addToolMessage(\n message: MicdropConversationToolCall | MicdropConversationToolResult\n ) {\n this.log('Adding tool message:', message)\n this.conversation.push(message)\n this.emit('Message', message)\n }\n\n protected endCall() {\n this.log('Ending call')\n this.emit('EndCall')\n }\n\n protected cancelLastUserMessage() {\n this.log('Cancelling last user message')\n const lastMessageIndex = this.conversation.findLastIndex(\n (message) => message.role === 'user'\n )\n if (lastMessageIndex !== -1) {\n this.conversation.splice(lastMessageIndex, 1)\n }\n this.emit('CancelLastUserMessage')\n }\n\n protected skipAnswer() {\n this.log('Skipping answer')\n this.emit('SkipAnswer')\n }\n\n protected getDefaultTools() {\n const tools: Tool[] = []\n if (this.options.autoEndCall) {\n tools.push({\n name: AUTO_END_CALL_TOOL_NAME,\n description:\n typeof this.options.autoEndCall === 'string'\n ? this.options.autoEndCall\n : AUTO_END_CALL_PROMPT,\n execute: () => this.endCall(),\n })\n }\n if (this.options.autoSemanticTurn) {\n tools.push({\n name: AUTO_SEMANTIC_TURN_TOOL_NAME,\n description:\n typeof this.options.autoSemanticTurn === 'string'\n ? this.options.autoSemanticTurn\n : AUTO_SEMANTIC_TURN_PROMPT,\n skipAnswer: true,\n execute: () => this.skipAnswer(),\n })\n }\n if (this.options.autoIgnoreUserNoise) {\n tools.push({\n name: AUTO_IGNORE_USER_NOISE_TOOL_NAME,\n description:\n typeof this.options.autoIgnoreUserNoise === 'string'\n ? this.options.autoIgnoreUserNoise\n : AUTO_IGNORE_USER_NOISE_PROMPT,\n skipAnswer: true,\n execute: () => this.cancelLastUserMessage(),\n })\n }\n return tools\n }\n\n protected async executeTool(toolCall: MicdropConversationToolCall) {\n try {\n const tool = this.getTool(toolCall.toolName)\n if (!tool) {\n throw new Error(`Tool not found \"${toolCall.toolName}\"`)\n }\n\n this.log('Executing tool:', toolCall.toolName, toolCall.parameters)\n\n // Save tool call in conversation\n this.addToolMessage(toolCall)\n\n const parameters = JSON.parse(toolCall.parameters)\n const output = tool.execute ? await tool.execute(parameters) : {}\n\n // Save tool result in conversation\n this.addToolMessage({\n role: 'tool_result',\n toolCallId: toolCall.toolCallId,\n toolName: toolCall.toolName,\n output: JSON.stringify(output ?? null),\n })\n\n // Emit output\n if (tool.emitOutput) {\n this.emit('ToolCall', {\n name: toolCall.toolName,\n parameters,\n output,\n })\n }\n\n return {\n output,\n skipAnswer: tool.skipAnswer,\n }\n } catch (error: any) {\n console.error('[OpenaiAgent] Error executing tool:', error)\n return {\n output: {\n error: error.message,\n },\n }\n }\n }\n\n protected getExtractOptions(): ExtractTagOptions | undefined {\n const extract = this.options.extract\n if (!extract) return undefined\n if ('json' in extract && extract.json) {\n return { ...extract, startTag: '{', endTag: '}' }\n }\n if ('startTag' in extract && 'endTag' in extract) {\n return extract\n }\n return undefined\n }\n\n protected extract(message: string) {\n const extractOptions = this.getExtractOptions()\n let metadata: MicdropAnswerMetadata | undefined = undefined\n\n // Extract value?\n if (extractOptions) {\n const startTagIndex = message.indexOf(extractOptions.startTag)\n if (startTagIndex !== -1) {\n // Find end tag\n let endTagIndex = message.lastIndexOf(extractOptions.endTag)\n if (endTagIndex === -1) endTagIndex = message.length + 1\n else endTagIndex += extractOptions.endTag.length\n const extractedText = message.slice(startTagIndex, endTagIndex).trim()\n\n // Parse extracted value\n try {\n const extractedValue =\n 'json' in extractOptions && extractOptions.json\n ? JSON.parse(extractedText)\n : extractedText\n\n // Call callback\n if (extractOptions.callback) {\n extractOptions.callback(extractedValue)\n }\n\n // Save in metadata\n if (extractOptions.saveInMetadata) {\n metadata = { extracted: extractedValue }\n }\n } catch (error) {\n console.error(\n `[OpenaiAgent] Error parsing extracted value (${extractedText}):`,\n error\n )\n }\n\n // Remove extracted value from message\n message = message.slice(0, startTagIndex).trimEnd()\n }\n }\n return { message, metadata }\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n this.cancel()\n }\n}\n","import type { z } from 'zod'\n\nexport interface Tool<Schema extends z.ZodObject = z.ZodObject> {\n name: string\n description: string\n inputSchema?: Schema\n execute?: (input: z.infer<Schema>) => any | Promise<any>\n skipAnswer?: boolean\n emitOutput?: boolean\n}\n\nexport const AUTO_END_CALL_TOOL_NAME = 'end_call'\nexport const AUTO_END_CALL_PROMPT =\n 'Call this tool only if user asks to end the call'\n\nexport const AUTO_SEMANTIC_TURN_TOOL_NAME = 'semantic_turn'\nexport const AUTO_SEMANTIC_TURN_PROMPT =\n 'Call this tool only if last user message is obviously an incomplete sentence that you need to wait for the end before answering'\n\nexport const AUTO_IGNORE_USER_NOISE_TOOL_NAME = 'ignore_user_noise'\nexport const AUTO_IGNORE_USER_NOISE_PROMPT =\n 'Call this tool only if last user message is just an interjection or a sound that expresses emotion, hesitation, or reaction (ex: \"Uh\", \"Ahem\", \"Hmm\", \"Ah\") but doesn\\'t carry any clear meaning like agreeing, refusing, or commanding'\n","import { PassThrough } from 'stream'\nimport { Agent } from './Agent'\n\nexport class MockAgent extends Agent {\n private i = 0\n\n constructor() {\n super({ systemPrompt: '' })\n }\n\n answer() {\n const stream = new PassThrough()\n\n // Answer message\n const message = `Assistant Message ${this.i++}`\n this.addAssistantMessage(message)\n stream.write(message)\n stream.end()\n return stream\n }\n\n cancel() {}\n}\n","import ffmpegInstaller from '@ffmpeg-installer/ffmpeg'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { PassThrough, Readable } from 'stream'\n\n// Setup ffmpeg\nffmpeg.setFfmpegPath(ffmpegInstaller.path)\n\n// Convert stream to WAV/PCM\nexport function convertToPCM(\n audioStream: Readable,\n sampleRate = 16000,\n bitDepth = 16\n) {\n const pcmStream = new PassThrough()\n ffmpeg(audioStream)\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec(`pcm_s${bitDepth}le`)\n .format(`s${bitDepth}le`)\n .on('error', (error) => {\n console.error('Error converting audio stream:', error.message)\n })\n .pipe(pcmStream)\n return pcmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate).pipe(webmStream)\n return webmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertPCMToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate)\n .inputFormat('s16le')\n .inputOptions(['-f s16le', '-ar 16000', '-ac 1'])\n .pipe(webmStream)\n return webmStream\n}\n\nfunction ffmpegToOpus(ffmpegCommand: ffmpeg.FfmpegCommand, sampleRate = 16000) {\n return ffmpegCommand\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec('libopus')\n .format('webm')\n .outputOptions([\n '-application audio',\n `-ac 1`,\n `-ar ${sampleRate}`,\n `-b:a 64k`,\n `-f webm`,\n `-map_metadata -1`,\n ])\n .on('error', (error) => {\n console.error('Error converting to Opus: ', error.message)\n })\n}\n","import WebSocket from 'ws'\n\nexport enum MicdropErrorCode {\n BadRequest = 4400,\n Unauthorized = 4401,\n NotFound = 4404,\n}\n\nexport class MicdropError extends Error {\n code: number\n\n constructor(code: number, message: string) {\n super(message)\n this.code = code\n }\n}\n\nexport function handleError(socket: WebSocket, error: unknown) {\n if (error instanceof MicdropError) {\n socket.close(error.code, error.message)\n } else {\n console.error(error)\n socket.close(1011)\n }\n socket.terminate()\n}\n","export class Logger {\n constructor(private readonly name: string) {}\n\n log(...message: any[]) {\n const time = process.uptime().toFixed(3)\n console.log(`[${this.name} ${time}]`, ...message)\n }\n}\n","import { Duplex, PassThrough, Readable } from 'stream'\nimport { WebSocket } from 'ws'\nimport type { Agent } from './agent'\nimport { Logger } from './Logger'\nimport type { STT } from './stt'\nimport type { TTS } from './tts'\nimport {\n MicdropCallSummary,\n MicdropClientCommands,\n MicdropConversationItem,\n MicdropServerCommands,\n} from './types'\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n onEnd?(call: MicdropCallSummary): void\n}\n\nexport class MicdropServer {\n public socket: WebSocket | null = null\n public config: MicdropConfig | null = null\n public logger?: Logger\n\n private startTime = Date.now()\n private lastMessageSpeeched?: MicdropConversationItem\n\n // When user is speaking, we're streaming chunks for STT\n private currentUserStream?: Duplex\n\n constructor(socket: WebSocket, config: MicdropConfig) {\n this.socket = socket\n this.config = config\n this.log(`Call started`)\n\n // Setup STT\n this.config.stt.on('Transcript', this.onTranscript)\n\n // Setup agent\n this.config.agent.on('Message', (message) =>\n this.socket?.send(\n `${MicdropServerCommands.Message} ${JSON.stringify(message)}`\n )\n )\n this.config.agent.on('CancelLastUserMessage', () =>\n this.socket?.send(MicdropServerCommands.CancelLastUserMessage)\n )\n this.config.agent.on('SkipAnswer', () =>\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n )\n this.config.agent.on('EndCall', () =>\n this.socket?.send(MicdropServerCommands.EndCall)\n )\n this.config.agent.on('ToolCall', (toolCall) =>\n this.socket?.send(\n `${MicdropServerCommands.ToolCall} ${JSON.stringify(toolCall)}`\n )\n )\n\n // Assistant speaks first\n this.sendFirstMessage()\n\n // Listen to events\n socket.on('close', this.onClose)\n socket.on('message', this.onMessage)\n }\n\n private log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n private cancel() {\n this.config?.tts.cancel()\n this.config?.agent.cancel()\n }\n\n private onClose = () => {\n if (!this.config) return\n this.log('Connection closed')\n const duration = Math.round((Date.now() - this.startTime) / 1000)\n\n // Destroy instances\n this.config.agent.destroy()\n this.config.stt.destroy()\n this.config.tts.destroy()\n\n // End call callback\n this.config.onEnd?.({\n conversation: this.config.agent.conversation.slice(1), // Remove system message\n duration,\n })\n\n // Unset params\n this.socket = null\n this.config = null\n }\n\n private onMessage = async (message: Buffer) => {\n if (message.byteLength === 0) return\n if (!Buffer.isBuffer(message)) {\n this.log('Message is not a buffer')\n return\n }\n\n // Commands\n if (message.byteLength < 15) {\n const cmd = message.toString()\n this.log(`Command: ${cmd}`)\n\n if (cmd === MicdropClientCommands.StartSpeaking) {\n // User started speaking\n await this.onStartSpeaking()\n } else if (cmd === MicdropClientCommands.Mute) {\n // User muted the call\n await this.onMute()\n } else if (cmd === MicdropClientCommands.StopSpeaking) {\n // User stopped speaking\n await this.onStopSpeaking()\n }\n }\n\n // Audio chunk\n else if (this.currentUserStream) {\n this.log(`Received chunk (${message.byteLength} bytes)`)\n this.currentUserStream.write(message)\n }\n }\n\n private async onMute() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private async onStartSpeaking() {\n if (!this.config) return\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private async onStopSpeaking() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n\n const conversation = this.config?.agent.conversation\n const lastMessage = conversation?.[conversation.length - 1]\n if (\n lastMessage?.role === 'user' &&\n this.lastMessageSpeeched !== lastMessage\n ) {\n this.log(\n 'User stopped speaking and a transcript already exists, answering'\n )\n this.answer()\n }\n }\n\n private onTranscript = async (transcript: string) => {\n if (!this.config) return\n this.log(`User transcript: \"${transcript}\"`)\n this.config.agent.addUserMessage(transcript)\n\n // Answer if user stopped speaking\n if (!this.currentUserStream) {\n this.log('User stopped speaking, answering')\n this.answer()\n }\n }\n\n private async sendFirstMessage() {\n if (!this.config) return\n try {\n if (this.config.firstMessage) {\n // Send first message\n this.config.agent.addAssistantMessage(this.config.firstMessage)\n await this.speak(this.config.firstMessage)\n } else if (this.config.generateFirstMessage) {\n // Generate first message\n await this.answer()\n } else {\n // Skip answer if no first message is provided\n // to avoid keeping the client in a processing state\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n } catch (error) {\n console.error('[MicdropServer]', error)\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n }\n\n private async answer() {\n if (!this.config) return\n this.cancel()\n\n // Prevent answering twice\n const lastMessage =\n this.config.agent.conversation[this.config.agent.conversation.length - 1]\n if (this.lastMessageSpeeched === lastMessage) {\n this.log('Already answered, skipping')\n return\n }\n this.lastMessageSpeeched = lastMessage\n\n try {\n // LLM: Generate answer\n const stream = this.config.agent.answer()\n\n // TTS: Generate answer audio\n await this.speak(stream)\n } catch (error) {\n console.error('[MicdropServer]', error)\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n }\n }\n\n // Run text-to-speech and send to client\n private async speak(message: string | Readable) {\n if (!this.socket || !this.config) return\n\n // Convert message to stream if needed\n let textStream: Readable\n if (typeof message === 'string') {\n const stream = new PassThrough()\n stream.write(message)\n stream.end()\n textStream = stream\n } else {\n textStream = message\n }\n\n // Run TTS\n const audio = this.config.tts.speak(textStream)\n\n // Send audio to client\n await this.sendAudio(audio)\n }\n\n private async sendAudio(audio: Readable) {\n if (!this.socket) return\n if (!audio.readable) {\n this.log('Non readable audio, skipping', audio)\n return\n }\n\n // Stream audio\n audio.on('data', (chunk) => {\n this.log(`Send audio chunk (${chunk.byteLength} bytes)`)\n this.socket?.send(chunk)\n })\n audio.on('error', (error) => {\n this.log('Error in audio stream', error)\n })\n audio.on('end', () => {\n this.log('Audio stream ended')\n })\n }\n}\n","export enum MicdropClientCommands {\n StartSpeaking = 'StartSpeaking',\n StopSpeaking = 'StopSpeaking',\n Mute = 'Mute',\n}\n\nexport enum MicdropServerCommands {\n Message = 'Message',\n CancelLastUserMessage = 'CancelLastUserMessage',\n SkipAnswer = 'SkipAnswer',\n EndCall = 'EndCall',\n ToolCall = 'ToolCall',\n}\n\nexport interface MicdropCallSummary {\n conversation: MicdropConversation\n duration: number\n}\n\nexport type MicdropConversationItem =\n | MicdropConversationMessage\n | MicdropConversationToolCall\n | MicdropConversationToolResult\n\nexport type MicdropConversation = Array<MicdropConversationItem>\n\nexport type MicdropAnswerMetadata = {\n [key: string]: any\n}\n\nexport interface MicdropConversationMessage<\n Data extends MicdropAnswerMetadata = MicdropAnswerMetadata,\n> {\n role: 'system' | 'user' | 'assistant'\n content: string\n metadata?: Data\n}\n\nexport interface MicdropConversationToolCall {\n role: 'tool_call'\n toolCallId: string\n toolName: string\n parameters: string\n}\n\nexport interface MicdropConversationToolResult {\n role: 'tool_result'\n toolCallId: string\n toolName: string\n output: string\n}\n\nexport interface MicdropToolCall {\n name: string\n parameters: any\n output: any\n}\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\n// Audio mime type to extension map\nconst MIME_TYPE_TO_EXTENSION = {\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/mpeg': 'mp3',\n 'audio/webm': 'webm',\n 'audio/mp4': 'mp4',\n 'audio/flac': 'flac',\n} as const\n\nexport interface STTEvents {\n Transcript: [string]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n protected mimeType?: keyof typeof MIME_TYPE_TO_EXTENSION\n public logger?: Logger\n\n // Set stream of audio to transcribe\n transcribe(audioStream: Readable) {\n // Detect mime type at first chunk\n audioStream.once('data', (chunk) => {\n this.mimeType = this.detectMimeType(chunk)\n })\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n }\n\n protected get extension(): string {\n return (this.mimeType && MIME_TYPE_TO_EXTENSION[this.mimeType]) || 'bin'\n }\n\n private detectMimeType(\n chunk: ArrayBuffer\n ): keyof typeof MIME_TYPE_TO_EXTENSION {\n if (!chunk || chunk.byteLength === 0) {\n throw new Error('Unable to detect mime type (empty chunk)')\n }\n\n const arr = new Uint8Array(chunk)\n\n // WEBM: 1A 45 DF A3\n if (\n arr[0] === 0x1a &&\n arr[1] === 0x45 &&\n arr[2] === 0xdf &&\n arr[3] === 0xa3\n ) {\n return 'audio/webm'\n }\n // OGG: 4F 67 67 53\n if (\n arr[0] === 0x4f &&\n arr[1] === 0x67 &&\n arr[2] === 0x67 &&\n arr[3] === 0x53\n ) {\n return 'audio/ogg'\n }\n // WAV: 52 49 46 46 ... 57 41 56 45\n if (\n arr[0] === 0x52 &&\n arr[1] === 0x49 &&\n arr[2] === 0x46 &&\n arr[3] === 0x46 &&\n arr[8] === 0x57 &&\n arr[9] === 0x41 &&\n arr[10] === 0x56 &&\n arr[11] === 0x45\n ) {\n return 'audio/wav'\n }\n // MP3: 49 44 33\n if (arr[0] === 0x49 && arr[1] === 0x44 && arr[2] === 0x33) {\n return 'audio/mpeg'\n }\n // MP4/M4A: 00 00 00 .. 66 74 79 70\n if (\n arr[4] === 0x66 &&\n arr[5] === 0x74 &&\n arr[6] === 0x79 &&\n arr[7] === 0x70\n ) {\n return 'audio/mp4'\n }\n // FLAC: 66 4c 61 43\n if (\n arr[0] === 0x66 &&\n arr[1] === 0x4c &&\n arr[2] === 0x61 &&\n arr[3] === 0x43\n ) {\n return 'audio/flac'\n }\n this.log('Unable to detect mime type, using default', chunk)\n return 'audio/wav'\n }\n}\n","import { Readable } from 'stream'\nimport { STT } from './STT'\n\n/**\n * Abstract class for STT, converting stream to file before transcribing\n */\n\nexport abstract class FileSTT extends STT {\n abstract transcribeFile(file: File): Promise<string>\n\n transcribe(audioStream: Readable) {\n super.transcribe(audioStream)\n\n // Convert stream to file\n this.log('Converting stream to file...')\n\n const chunks: Buffer[] = []\n audioStream.on('data', (chunk) => {\n chunks.push(chunk)\n })\n\n audioStream.on('end', async () => {\n if (chunks.length === 0) return\n const arrayBuffer = Buffer.concat(chunks)\n const file = new File([arrayBuffer], `audio.${this.extension}`, {\n type: this.mimeType,\n })\n\n // Transcribe file with implementation\n this.log('Transcribing file...')\n const transcript = await this.transcribeFile(file)\n this.emit('Transcript', transcript)\n })\n }\n}\n","import { FileSTT } from './FileSTT'\n\nexport class MockSTT extends FileSTT {\n private i = 0\n\n async transcribeFile(file: File) {\n return `User Message ${this.i++}`\n }\n}\n","import * as fs from 'fs'\nimport { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\n\nexport class MockTTS extends TTS {\n constructor(private audioFilePaths: string[]) {\n super()\n }\n\n speak(textStream: Readable) {\n const audioStream = new PassThrough()\n textStream.once('data', async () => {\n for (const filePath of this.audioFilePaths) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n const audioBuffer = fs.readFileSync(filePath)\n this.log(`Loaded chunk (${audioBuffer.length} bytes)`)\n audioStream.write(audioBuffer)\n }\n audioStream.end()\n })\n return audioStream\n }\n\n cancel() {}\n}\n","import { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport abstract class TTS {\n public logger?: Logger\n\n abstract speak(textStream: Readable): Readable\n abstract cancel(): void\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.cancel()\n }\n}\n","import { WebSocket } from 'ws'\nimport { MicdropError, MicdropErrorCode } from './errors'\n\nexport async function waitForParams<CallParams>(\n socket: WebSocket,\n validate: (params: any) => CallParams\n): Promise<CallParams> {\n return new Promise<CallParams>((resolve, reject) => {\n // Handle timeout\n const timeout = setTimeout(() => {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Missing params'))\n }, 3000)\n\n const onParams = (payload: string) => {\n // Clear timeout and listener\n clearTimeout(timeout)\n socket.off('message', onParams)\n\n try {\n // Parse JSON payload\n const params = validate(JSON.parse(payload))\n resolve(params)\n } catch (error) {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Invalid params'))\n }\n }\n\n // Listen for params\n socket.on('message', onParams)\n })\n}\n"],"mappings":"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","names":["PassThrough","MicdropErrorCode","PassThrough","MicdropClientCommands","MicdropServerCommands","PassThrough","EventEmitter","PassThrough","PassThrough"]}
|