@micdrop/server 2.2.0 → 2.2.1
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 +39 -4
- package/dist/index.d.ts +39 -4
- package/dist/index.js +145 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +144 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -7
- package/LICENSE +0 -9
package/dist/index.d.mts
CHANGED
|
@@ -222,15 +222,19 @@ declare class FallbackTTS extends TTS {
|
|
|
222
222
|
private onFailed;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
interface MicdropServerEvents {
|
|
226
|
+
End: [MicdropCallSummary];
|
|
227
|
+
UserAudio: [Buffer];
|
|
228
|
+
AssistantAudio: [Buffer];
|
|
229
|
+
}
|
|
225
230
|
interface MicdropConfig {
|
|
226
231
|
firstMessage?: string;
|
|
227
232
|
generateFirstMessage?: boolean;
|
|
228
233
|
agent: Agent;
|
|
229
234
|
stt: STT;
|
|
230
235
|
tts: TTS;
|
|
231
|
-
onEnd?(call: MicdropCallSummary): void;
|
|
232
236
|
}
|
|
233
|
-
declare class MicdropServer {
|
|
237
|
+
declare class MicdropServer extends EventEmitter<MicdropServerEvents> {
|
|
234
238
|
socket: WebSocket$1 | null;
|
|
235
239
|
config: MicdropConfig | null;
|
|
236
240
|
logger?: Logger;
|
|
@@ -247,7 +251,7 @@ declare class MicdropServer {
|
|
|
247
251
|
cancel(): void;
|
|
248
252
|
private onClose;
|
|
249
253
|
private onMessage;
|
|
250
|
-
private
|
|
254
|
+
private onUserAudio;
|
|
251
255
|
private onMute;
|
|
252
256
|
private onStartSpeaking;
|
|
253
257
|
private onStopSpeaking;
|
|
@@ -260,6 +264,37 @@ declare class MicdropServer {
|
|
|
260
264
|
private _speak;
|
|
261
265
|
}
|
|
262
266
|
|
|
267
|
+
interface AudioMessage {
|
|
268
|
+
buffer: Buffer;
|
|
269
|
+
messageIndex: number;
|
|
270
|
+
message: string;
|
|
271
|
+
role: 'user' | 'assistant';
|
|
272
|
+
}
|
|
273
|
+
interface MicdropRecorderEvents {
|
|
274
|
+
AudioMessage: [AudioMessage];
|
|
275
|
+
Complete: [AudioMessage[]];
|
|
276
|
+
}
|
|
277
|
+
declare class MicdropRecorder extends EventEmitter<MicdropRecorderEvents> {
|
|
278
|
+
private server;
|
|
279
|
+
logger?: Logger;
|
|
280
|
+
private audioMessages;
|
|
281
|
+
private currentUserChunks;
|
|
282
|
+
private currentAssistantChunks;
|
|
283
|
+
private lastUserMessageIndex;
|
|
284
|
+
private lastAssistantMessageIndex;
|
|
285
|
+
constructor(server: MicdropServer);
|
|
286
|
+
private setupListeners;
|
|
287
|
+
private onUserAudio;
|
|
288
|
+
private onAssistantAudio;
|
|
289
|
+
private onMessage;
|
|
290
|
+
private finalizeUserAudio;
|
|
291
|
+
private finalizeAssistantAudio;
|
|
292
|
+
private onEnd;
|
|
293
|
+
getAudioMessages(): AudioMessage[];
|
|
294
|
+
destroy(): void;
|
|
295
|
+
protected log(...message: any[]): void;
|
|
296
|
+
}
|
|
297
|
+
|
|
263
298
|
declare function waitForParams<CallParams>(socket: WebSocket$1, validate: (params: any) => CallParams): Promise<CallParams>;
|
|
264
299
|
|
|
265
|
-
export { AUTO_END_CALL_PROMPT, AUTO_END_CALL_TOOL_NAME, AUTO_IGNORE_USER_NOISE_PROMPT, AUTO_IGNORE_USER_NOISE_TOOL_NAME, AUTO_SEMANTIC_TURN_PROMPT, AUTO_SEMANTIC_TURN_TOOL_NAME, Agent, type AgentEvents, type AgentOptions, type DeepPartial, type ExtractJsonOptions, type ExtractOptions, type ExtractTagOptions, FallbackSTT, type FallbackSTTOptions, FallbackTTS, type FallbackTTSOptions, Logger, type MicdropAnswerMetadata, type MicdropCallSummary, MicdropClientCommands, type MicdropConfig, type MicdropConversation, type MicdropConversationItem, type MicdropConversationMessage, type MicdropConversationToolCall, type MicdropConversationToolResult, MicdropError, MicdropErrorCode, MicdropServer, MicdropServerCommands, type MicdropToolCall, MockAgent, MockSTT, MockTTS, STT, type STTEvents, TTS, type TTSEvents, type Tool, handleError, waitForParams };
|
|
300
|
+
export { AUTO_END_CALL_PROMPT, AUTO_END_CALL_TOOL_NAME, AUTO_IGNORE_USER_NOISE_PROMPT, AUTO_IGNORE_USER_NOISE_TOOL_NAME, AUTO_SEMANTIC_TURN_PROMPT, AUTO_SEMANTIC_TURN_TOOL_NAME, Agent, type AgentEvents, type AgentOptions, type AudioMessage, type DeepPartial, type ExtractJsonOptions, type ExtractOptions, type ExtractTagOptions, FallbackSTT, type FallbackSTTOptions, FallbackTTS, type FallbackTTSOptions, Logger, type MicdropAnswerMetadata, type MicdropCallSummary, MicdropClientCommands, type MicdropConfig, type MicdropConversation, type MicdropConversationItem, type MicdropConversationMessage, type MicdropConversationToolCall, type MicdropConversationToolResult, MicdropError, MicdropErrorCode, MicdropRecorder, type MicdropRecorderEvents, MicdropServer, MicdropServerCommands, type MicdropServerEvents, type MicdropToolCall, MockAgent, MockSTT, MockTTS, STT, type STTEvents, TTS, type TTSEvents, type Tool, handleError, waitForParams };
|
package/dist/index.d.ts
CHANGED
|
@@ -222,15 +222,19 @@ declare class FallbackTTS extends TTS {
|
|
|
222
222
|
private onFailed;
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
+
interface MicdropServerEvents {
|
|
226
|
+
End: [MicdropCallSummary];
|
|
227
|
+
UserAudio: [Buffer];
|
|
228
|
+
AssistantAudio: [Buffer];
|
|
229
|
+
}
|
|
225
230
|
interface MicdropConfig {
|
|
226
231
|
firstMessage?: string;
|
|
227
232
|
generateFirstMessage?: boolean;
|
|
228
233
|
agent: Agent;
|
|
229
234
|
stt: STT;
|
|
230
235
|
tts: TTS;
|
|
231
|
-
onEnd?(call: MicdropCallSummary): void;
|
|
232
236
|
}
|
|
233
|
-
declare class MicdropServer {
|
|
237
|
+
declare class MicdropServer extends EventEmitter<MicdropServerEvents> {
|
|
234
238
|
socket: WebSocket$1 | null;
|
|
235
239
|
config: MicdropConfig | null;
|
|
236
240
|
logger?: Logger;
|
|
@@ -247,7 +251,7 @@ declare class MicdropServer {
|
|
|
247
251
|
cancel(): void;
|
|
248
252
|
private onClose;
|
|
249
253
|
private onMessage;
|
|
250
|
-
private
|
|
254
|
+
private onUserAudio;
|
|
251
255
|
private onMute;
|
|
252
256
|
private onStartSpeaking;
|
|
253
257
|
private onStopSpeaking;
|
|
@@ -260,6 +264,37 @@ declare class MicdropServer {
|
|
|
260
264
|
private _speak;
|
|
261
265
|
}
|
|
262
266
|
|
|
267
|
+
interface AudioMessage {
|
|
268
|
+
buffer: Buffer;
|
|
269
|
+
messageIndex: number;
|
|
270
|
+
message: string;
|
|
271
|
+
role: 'user' | 'assistant';
|
|
272
|
+
}
|
|
273
|
+
interface MicdropRecorderEvents {
|
|
274
|
+
AudioMessage: [AudioMessage];
|
|
275
|
+
Complete: [AudioMessage[]];
|
|
276
|
+
}
|
|
277
|
+
declare class MicdropRecorder extends EventEmitter<MicdropRecorderEvents> {
|
|
278
|
+
private server;
|
|
279
|
+
logger?: Logger;
|
|
280
|
+
private audioMessages;
|
|
281
|
+
private currentUserChunks;
|
|
282
|
+
private currentAssistantChunks;
|
|
283
|
+
private lastUserMessageIndex;
|
|
284
|
+
private lastAssistantMessageIndex;
|
|
285
|
+
constructor(server: MicdropServer);
|
|
286
|
+
private setupListeners;
|
|
287
|
+
private onUserAudio;
|
|
288
|
+
private onAssistantAudio;
|
|
289
|
+
private onMessage;
|
|
290
|
+
private finalizeUserAudio;
|
|
291
|
+
private finalizeAssistantAudio;
|
|
292
|
+
private onEnd;
|
|
293
|
+
getAudioMessages(): AudioMessage[];
|
|
294
|
+
destroy(): void;
|
|
295
|
+
protected log(...message: any[]): void;
|
|
296
|
+
}
|
|
297
|
+
|
|
263
298
|
declare function waitForParams<CallParams>(socket: WebSocket$1, validate: (params: any) => CallParams): Promise<CallParams>;
|
|
264
299
|
|
|
265
|
-
export { AUTO_END_CALL_PROMPT, AUTO_END_CALL_TOOL_NAME, AUTO_IGNORE_USER_NOISE_PROMPT, AUTO_IGNORE_USER_NOISE_TOOL_NAME, AUTO_SEMANTIC_TURN_PROMPT, AUTO_SEMANTIC_TURN_TOOL_NAME, Agent, type AgentEvents, type AgentOptions, type DeepPartial, type ExtractJsonOptions, type ExtractOptions, type ExtractTagOptions, FallbackSTT, type FallbackSTTOptions, FallbackTTS, type FallbackTTSOptions, Logger, type MicdropAnswerMetadata, type MicdropCallSummary, MicdropClientCommands, type MicdropConfig, type MicdropConversation, type MicdropConversationItem, type MicdropConversationMessage, type MicdropConversationToolCall, type MicdropConversationToolResult, MicdropError, MicdropErrorCode, MicdropServer, MicdropServerCommands, type MicdropToolCall, MockAgent, MockSTT, MockTTS, STT, type STTEvents, TTS, type TTSEvents, type Tool, handleError, waitForParams };
|
|
300
|
+
export { AUTO_END_CALL_PROMPT, AUTO_END_CALL_TOOL_NAME, AUTO_IGNORE_USER_NOISE_PROMPT, AUTO_IGNORE_USER_NOISE_TOOL_NAME, AUTO_SEMANTIC_TURN_PROMPT, AUTO_SEMANTIC_TURN_TOOL_NAME, Agent, type AgentEvents, type AgentOptions, type AudioMessage, type DeepPartial, type ExtractJsonOptions, type ExtractOptions, type ExtractTagOptions, FallbackSTT, type FallbackSTTOptions, FallbackTTS, type FallbackTTSOptions, Logger, type MicdropAnswerMetadata, type MicdropCallSummary, MicdropClientCommands, type MicdropConfig, type MicdropConversation, type MicdropConversationItem, type MicdropConversationMessage, type MicdropConversationToolCall, type MicdropConversationToolResult, MicdropError, MicdropErrorCode, MicdropRecorder, type MicdropRecorderEvents, MicdropServer, MicdropServerCommands, type MicdropServerEvents, type MicdropToolCall, MockAgent, MockSTT, MockTTS, STT, type STTEvents, TTS, type TTSEvents, type Tool, handleError, waitForParams };
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ __export(index_exports, {
|
|
|
43
43
|
MicdropClientCommands: () => MicdropClientCommands,
|
|
44
44
|
MicdropError: () => MicdropError,
|
|
45
45
|
MicdropErrorCode: () => MicdropErrorCode,
|
|
46
|
+
MicdropRecorder: () => MicdropRecorder,
|
|
46
47
|
MicdropServer: () => MicdropServer,
|
|
47
48
|
MicdropServerCommands: () => MicdropServerCommands,
|
|
48
49
|
MockAgent: () => MockAgent,
|
|
@@ -309,6 +310,7 @@ var Logger = class {
|
|
|
309
310
|
};
|
|
310
311
|
|
|
311
312
|
// src/MicdropServer.ts
|
|
313
|
+
var import_eventemitter32 = require("eventemitter3");
|
|
312
314
|
var import_stream2 = require("stream");
|
|
313
315
|
|
|
314
316
|
// src/types.ts
|
|
@@ -328,8 +330,9 @@ var MicdropServerCommands = /* @__PURE__ */ ((MicdropServerCommands2) => {
|
|
|
328
330
|
})(MicdropServerCommands || {});
|
|
329
331
|
|
|
330
332
|
// src/MicdropServer.ts
|
|
331
|
-
var MicdropServer = class {
|
|
333
|
+
var MicdropServer = class extends import_eventemitter32.EventEmitter {
|
|
332
334
|
constructor(socket, config) {
|
|
335
|
+
super();
|
|
333
336
|
this.socket = null;
|
|
334
337
|
this.config = null;
|
|
335
338
|
this.startTime = Date.now();
|
|
@@ -344,9 +347,8 @@ var MicdropServer = class {
|
|
|
344
347
|
this.config.agent.destroy();
|
|
345
348
|
this.config.stt.destroy();
|
|
346
349
|
this.config.tts.destroy();
|
|
347
|
-
this.
|
|
348
|
-
conversation: this.config.agent.conversation
|
|
349
|
-
// Remove system message
|
|
350
|
+
this.emit("End", {
|
|
351
|
+
conversation: this.config.agent.conversation,
|
|
350
352
|
duration
|
|
351
353
|
});
|
|
352
354
|
this.socket = null;
|
|
@@ -369,7 +371,7 @@ var MicdropServer = class {
|
|
|
369
371
|
this.onStopSpeaking();
|
|
370
372
|
}
|
|
371
373
|
} else if (this.currentUserStream) {
|
|
372
|
-
this.
|
|
374
|
+
this.onUserAudio(message);
|
|
373
375
|
}
|
|
374
376
|
};
|
|
375
377
|
this.onTranscriptSTT = async (transcript) => {
|
|
@@ -390,6 +392,7 @@ var MicdropServer = class {
|
|
|
390
392
|
if (!this.socket) return;
|
|
391
393
|
this.log(`Send audio chunk (${audio.byteLength} bytes)`);
|
|
392
394
|
this.socket.send(audio);
|
|
395
|
+
this.emit("AssistantAudio", audio);
|
|
393
396
|
};
|
|
394
397
|
this.socket = socket;
|
|
395
398
|
this.config = config;
|
|
@@ -451,10 +454,11 @@ var MicdropServer = class {
|
|
|
451
454
|
this.config?.agent.cancel();
|
|
452
455
|
this.operationQueue = [];
|
|
453
456
|
}
|
|
454
|
-
|
|
457
|
+
onUserAudio(chunk) {
|
|
455
458
|
this.log(`Received chunk (${chunk.byteLength} bytes)`);
|
|
456
459
|
this.currentUserStream?.write(chunk);
|
|
457
460
|
this.userSpeechChunks++;
|
|
461
|
+
this.emit("UserAudio", chunk);
|
|
458
462
|
}
|
|
459
463
|
onMute() {
|
|
460
464
|
this.userSpeechChunks = 0;
|
|
@@ -542,9 +546,139 @@ var MicdropServer = class {
|
|
|
542
546
|
}
|
|
543
547
|
};
|
|
544
548
|
|
|
549
|
+
// src/recorder/MicdropRecorder.ts
|
|
550
|
+
var import_eventemitter33 = require("eventemitter3");
|
|
551
|
+
var MicdropRecorder = class extends import_eventemitter33.EventEmitter {
|
|
552
|
+
constructor(server) {
|
|
553
|
+
super();
|
|
554
|
+
this.server = server;
|
|
555
|
+
this.audioMessages = [];
|
|
556
|
+
this.currentUserChunks = [];
|
|
557
|
+
this.currentAssistantChunks = [];
|
|
558
|
+
this.lastUserMessageIndex = -1;
|
|
559
|
+
this.lastAssistantMessageIndex = -1;
|
|
560
|
+
this.onUserAudio = (chunk) => {
|
|
561
|
+
if (this.currentAssistantChunks.length > 0) {
|
|
562
|
+
if (this.lastAssistantMessageIndex >= 0) {
|
|
563
|
+
this.finalizeAssistantAudio();
|
|
564
|
+
} else {
|
|
565
|
+
this.log("Discarding orphaned assistant audio chunks");
|
|
566
|
+
this.currentAssistantChunks = [];
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
this.log("Recording user audio chunk");
|
|
570
|
+
this.currentUserChunks.push(chunk);
|
|
571
|
+
};
|
|
572
|
+
this.onAssistantAudio = (chunk) => {
|
|
573
|
+
if (this.currentUserChunks.length > 0) {
|
|
574
|
+
if (this.lastUserMessageIndex >= 0) {
|
|
575
|
+
this.finalizeUserAudio();
|
|
576
|
+
} else {
|
|
577
|
+
this.log("Discarding orphaned user audio chunks");
|
|
578
|
+
this.currentUserChunks = [];
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
this.log("Recording assistant audio chunk");
|
|
582
|
+
this.currentAssistantChunks.push(chunk);
|
|
583
|
+
};
|
|
584
|
+
this.onMessage = (message) => {
|
|
585
|
+
const conversation = this.server.config?.agent.conversation;
|
|
586
|
+
if (!conversation) return;
|
|
587
|
+
const messageIndex = conversation.length - 1;
|
|
588
|
+
if (message.role === "user") {
|
|
589
|
+
this.lastUserMessageIndex = messageIndex;
|
|
590
|
+
if (this.currentUserChunks.length > 0) {
|
|
591
|
+
this.finalizeUserAudio();
|
|
592
|
+
}
|
|
593
|
+
} else if (message.role === "assistant") {
|
|
594
|
+
this.lastAssistantMessageIndex = messageIndex;
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
this.onEnd = () => {
|
|
598
|
+
if (this.currentUserChunks.length > 0) {
|
|
599
|
+
this.finalizeUserAudio();
|
|
600
|
+
}
|
|
601
|
+
if (this.currentAssistantChunks.length > 0) {
|
|
602
|
+
this.finalizeAssistantAudio();
|
|
603
|
+
}
|
|
604
|
+
this.log(`Recording complete: ${this.audioMessages.length} audio messages`);
|
|
605
|
+
this.emit("Complete", this.audioMessages);
|
|
606
|
+
};
|
|
607
|
+
this.setupListeners();
|
|
608
|
+
}
|
|
609
|
+
setupListeners() {
|
|
610
|
+
this.server.on("UserAudio", this.onUserAudio);
|
|
611
|
+
this.server.on("AssistantAudio", this.onAssistantAudio);
|
|
612
|
+
this.server.on("End", this.onEnd);
|
|
613
|
+
const agent = this.server.config?.agent;
|
|
614
|
+
if (agent) {
|
|
615
|
+
agent.on("Message", this.onMessage);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
finalizeUserAudio() {
|
|
619
|
+
if (this.currentUserChunks.length === 0) return;
|
|
620
|
+
if (this.lastUserMessageIndex < 0) return;
|
|
621
|
+
const conversation = this.server.config?.agent.conversation;
|
|
622
|
+
if (!conversation) return;
|
|
623
|
+
const message = conversation[this.lastUserMessageIndex];
|
|
624
|
+
const buffer = Buffer.concat(this.currentUserChunks);
|
|
625
|
+
const audioMessage = {
|
|
626
|
+
buffer,
|
|
627
|
+
messageIndex: this.lastUserMessageIndex,
|
|
628
|
+
message: "content" in message ? message.content : "",
|
|
629
|
+
role: "user"
|
|
630
|
+
};
|
|
631
|
+
this.log(
|
|
632
|
+
`Finalized user audio: ${buffer.length} bytes, message index ${this.lastUserMessageIndex}`
|
|
633
|
+
);
|
|
634
|
+
this.audioMessages.push(audioMessage);
|
|
635
|
+
this.emit("AudioMessage", audioMessage);
|
|
636
|
+
this.currentUserChunks = [];
|
|
637
|
+
this.lastUserMessageIndex = -1;
|
|
638
|
+
}
|
|
639
|
+
finalizeAssistantAudio() {
|
|
640
|
+
if (this.currentAssistantChunks.length === 0) return;
|
|
641
|
+
if (this.lastAssistantMessageIndex < 0) return;
|
|
642
|
+
const conversation = this.server.config?.agent.conversation;
|
|
643
|
+
if (!conversation) return;
|
|
644
|
+
const message = conversation[this.lastAssistantMessageIndex];
|
|
645
|
+
const buffer = Buffer.concat(this.currentAssistantChunks);
|
|
646
|
+
const audioMessage = {
|
|
647
|
+
buffer,
|
|
648
|
+
messageIndex: this.lastAssistantMessageIndex,
|
|
649
|
+
message: "content" in message ? message.content : "",
|
|
650
|
+
role: "assistant"
|
|
651
|
+
};
|
|
652
|
+
this.log(
|
|
653
|
+
`Finalized assistant audio: ${buffer.length} bytes, message index ${this.lastAssistantMessageIndex}`
|
|
654
|
+
);
|
|
655
|
+
this.audioMessages.push(audioMessage);
|
|
656
|
+
this.emit("AudioMessage", audioMessage);
|
|
657
|
+
this.currentAssistantChunks = [];
|
|
658
|
+
this.lastAssistantMessageIndex = -1;
|
|
659
|
+
}
|
|
660
|
+
getAudioMessages() {
|
|
661
|
+
return [...this.audioMessages];
|
|
662
|
+
}
|
|
663
|
+
destroy() {
|
|
664
|
+
this.log("Destroyed");
|
|
665
|
+
this.server.off("UserAudio", this.onUserAudio);
|
|
666
|
+
this.server.off("AssistantAudio", this.onAssistantAudio);
|
|
667
|
+
this.server.off("End", this.onEnd);
|
|
668
|
+
const agent = this.server.config?.agent;
|
|
669
|
+
if (agent) {
|
|
670
|
+
agent.off("Message", this.onMessage);
|
|
671
|
+
}
|
|
672
|
+
this.removeAllListeners();
|
|
673
|
+
}
|
|
674
|
+
log(...message) {
|
|
675
|
+
this.logger?.log(...message);
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
|
|
545
679
|
// src/stt/STT.ts
|
|
546
|
-
var
|
|
547
|
-
var STT = class extends
|
|
680
|
+
var import_eventemitter34 = require("eventemitter3");
|
|
681
|
+
var STT = class extends import_eventemitter34.EventEmitter {
|
|
548
682
|
log(...message) {
|
|
549
683
|
this.logger?.log(...message);
|
|
550
684
|
}
|
|
@@ -626,8 +760,8 @@ var fs = __toESM(require("fs"));
|
|
|
626
760
|
var import_stream4 = require("stream");
|
|
627
761
|
|
|
628
762
|
// src/tts/TTS.ts
|
|
629
|
-
var
|
|
630
|
-
var TTS = class extends
|
|
763
|
+
var import_eventemitter35 = require("eventemitter3");
|
|
764
|
+
var TTS = class extends import_eventemitter35.EventEmitter {
|
|
631
765
|
log(...message) {
|
|
632
766
|
this.logger?.log(...message);
|
|
633
767
|
}
|
|
@@ -751,6 +885,7 @@ async function waitForParams(socket, validate) {
|
|
|
751
885
|
MicdropClientCommands,
|
|
752
886
|
MicdropError,
|
|
753
887
|
MicdropErrorCode,
|
|
888
|
+
MicdropRecorder,
|
|
754
889
|
MicdropServer,
|
|
755
890
|
MicdropServerCommands,
|
|
756
891
|
MockAgent,
|
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/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/stt/STT.ts","../src/stt/MockSTT.ts","../src/stt/FallbackSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/tts/FallbackTTS.ts","../src/waitForParams.ts"],"sourcesContent":["export * from './agent'\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 { PassThrough, Readable, Writable } 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 // Function called before any answer is generated\n // Return true to skip generation\n onBeforeAnswer?: (\n this: Agent,\n stream: Writable\n ) => void | boolean | Promise<boolean>\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 protected answerCount = 0\n protected answering = false\n\n constructor(protected options: Options) {\n super()\n this.conversation = [{ role: 'system', content: options.systemPrompt }]\n this.tools = this.getDefaultTools()\n }\n\n protected abstract generateAnswer(stream: PassThrough): Promise<void>\n abstract cancel(): void\n\n answer(): Readable {\n this.log('Start answering')\n const answerCount = ++this.answerCount\n const stream = new PassThrough()\n this.answering = true\n\n Promise.resolve()\n // Call hook onBeforeAnswer\n .then(() => this.options.onBeforeAnswer?.bind(this)(stream))\n // Generate answer (if not skipped)\n .then((skip) => {\n if (skip) return\n return this.generateAnswer(stream)\n })\n // End stream\n .finally(() => {\n if (stream.writable) {\n stream.end()\n }\n if (answerCount === this.answerCount) {\n this.answering = false\n }\n })\n\n return stream\n }\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 const index = this.tools.findIndex((tool) => tool.name === name)\n if (index !== -1) {\n this.tools.splice(index, 1)\n }\n }\n\n getTool(name: string): Tool | undefined {\n return this.tools.find((tool) => tool.name === name)\n }\n\n 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 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 public 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 protected async generateAnswer(stream: PassThrough): Promise<void> {\n const message = `Assistant Message ${this.i++}`\n this.addAssistantMessage(message)\n stream.write(message)\n }\n\n cancel() {}\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(public 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 // Queue system for operations\n private operationQueue: Array<() => Promise<void>> = []\n private isProcessingQueue = false\n\n // When user is speaking, we're streaming chunks for STT\n private currentUserStream?: Duplex\n private userSpeechChunks = 0\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.onTranscriptSTT)\n\n // Setup TTS\n this.config.tts.on('Audio', this.onAudioTTS)\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 async processQueue() {\n if (this.isProcessingQueue || this.operationQueue.length === 0) return\n\n this.isProcessingQueue = true\n\n while (this.operationQueue.length > 0) {\n const operation = this.operationQueue.shift()\n if (operation) {\n try {\n await operation()\n } catch (error) {\n this.log('Error processing queued operation:', error)\n }\n }\n }\n\n this.isProcessingQueue = false\n }\n\n private queueOperation(operation: () => Promise<void>) {\n this.operationQueue.push(operation)\n this.processQueue()\n }\n\n public cancel() {\n this.config?.tts.cancel()\n this.config?.agent.cancel()\n // Clear the queue\n this.operationQueue = []\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 this.onStartSpeaking()\n } else if (cmd === MicdropClientCommands.Mute) {\n // User muted the call\n this.onMute()\n } else if (cmd === MicdropClientCommands.StopSpeaking) {\n // User stopped speaking\n this.onStopSpeaking()\n }\n }\n\n // Audio chunk\n else if (this.currentUserStream) {\n this.onAudioChunk(message)\n }\n }\n\n private onAudioChunk(chunk: Buffer) {\n this.log(`Received chunk (${chunk.byteLength} bytes)`)\n this.currentUserStream?.write(chunk)\n this.userSpeechChunks++\n }\n\n private onMute() {\n this.userSpeechChunks = 0\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private onStartSpeaking() {\n if (!this.config) return\n this.userSpeechChunks = 0\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private onStopSpeaking() {\n const hasNoUserSpeech =\n !this.currentUserStream || this.userSpeechChunks === 0\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.userSpeechChunks = 0\n\n // If user is not speaking or no chunks were received, skip\n if (hasNoUserSpeech) {\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n return\n }\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.cancel()\n this.answer()\n }\n }\n\n private onTranscriptSTT = async (transcript: string) => {\n if (!this.config) return\n\n // Skip answer if transcript is empty\n if (transcript === '') {\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n return\n }\n\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.cancel()\n this.answer()\n }\n }\n\n private onAudioTTS = (audio: Buffer) => {\n if (!this.socket) return\n this.log(`Send audio chunk (${audio.byteLength} bytes)`)\n this.socket.send(audio)\n }\n\n private sendFirstMessage() {\n if (!this.config) return\n if (this.config.firstMessage) {\n // Send first message\n this.config.agent.addAssistantMessage(this.config.firstMessage)\n this.speak(this.config.firstMessage)\n } else if (this.config.generateFirstMessage) {\n // Generate first message\n 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 }\n\n public answer() {\n this.queueOperation(async () => {\n await this._answer()\n })\n }\n\n private async _answer() {\n if (!this.config) return\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 this.socket?.send(MicdropServerCommands.SkipAnswer)\n throw error\n }\n }\n\n // Run text-to-speech and send to client\n public speak(message: string | Readable) {\n this.queueOperation(async () => {\n await this._speak(message)\n })\n }\n\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 this.config.tts.speak(textStream)\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\nexport interface STTEvents {\n Transcript: [string]\n Failed: [Buffer[]]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n public logger?: Logger\n\n // Set stream of audio to transcribe\n abstract transcribe(audioStream: Readable): void\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","import { STT } from './STT'\n\nexport class MockSTT extends STT {\n private i = 0\n\n async transcribe() {\n setTimeout(() => {\n this.emit('Transcript', `User Message ${this.i++}`)\n }, 300)\n }\n}\n","import { PassThrough, Readable } from 'stream'\nimport { STT } from './STT'\nimport { Logger } from '..'\n\nexport interface FallbackSTTOptions {\n factories: Array<() => STT>\n}\n\nexport class FallbackSTT extends STT {\n private stt: STT | null = null\n private sttIndex = -1 // Start at -1 because we need to increment it before using it\n\n constructor(private readonly options: FallbackSTTOptions) {\n super()\n if (this.options.factories.length === 0) {\n throw new Error('FallbackSTT: No factories provided')\n }\n this.startNextSTT()\n }\n\n transcribe(audioStream: Readable) {\n this.stt?.transcribe(audioStream)\n }\n\n destroy() {\n super.destroy()\n this.stt?.destroy()\n this.stt = null\n this.sttIndex = -1\n }\n\n private startNextSTT() {\n this.sttIndex++\n if (this.sttIndex >= this.options.factories.length) {\n this.sttIndex = 0\n }\n this.stt?.destroy()\n this.stt = this.options.factories[this.sttIndex]()\n this.stt.on('Transcript', this.onTranscript)\n this.stt.on('Failed', this.onFailed)\n\n // Set logger after event loop\n setTimeout(() => {\n if (this.stt && this.logger) {\n this.stt.logger = new Logger(this.stt.constructor.name)\n }\n }, 0)\n }\n\n private onTranscript = (transcript: string) => {\n this.emit('Transcript', transcript)\n }\n\n private onFailed = (chunks: Buffer[]) => {\n this.log('STT failed, trying next STT')\n this.startNextSTT()\n\n if (chunks.length > 0) {\n this.log('Sending audio chunks again')\n const stream = new PassThrough()\n this.stt?.transcribe(stream)\n chunks.forEach((chunk) => stream.write(chunk))\n stream.end()\n }\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 { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport interface TTSEvents {\n Audio: [Buffer]\n Failed: [string[]]\n}\n\nexport abstract class TTS extends EventEmitter<TTSEvents> {\n public logger?: Logger\n\n abstract speak(textStream: Readable): void\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 { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\nimport { Logger } from '..'\n\nexport interface FallbackTTSOptions {\n factories: Array<() => TTS>\n}\n\nexport class FallbackTTS extends TTS {\n private tts: TTS | null = null\n private ttsIndex = -1 // Start at -1 because we need to increment it before using it\n\n constructor(private readonly options: FallbackTTSOptions) {\n super()\n if (this.options.factories.length === 0) {\n throw new Error('FallbackTTS: No factories provided')\n }\n this.startNextTTS()\n }\n\n speak(textStream: Readable) {\n this.tts?.speak(textStream)\n }\n\n cancel() {\n this.tts?.cancel()\n }\n\n destroy() {\n super.destroy()\n this.tts?.destroy()\n this.tts = null\n this.ttsIndex = -1\n }\n\n private startNextTTS() {\n this.ttsIndex++\n if (this.ttsIndex >= this.options.factories.length) {\n this.ttsIndex = 0\n }\n this.tts?.destroy()\n this.tts = this.options.factories[this.ttsIndex]()\n this.tts.on('Audio', this.onAudio)\n this.tts.on('Failed', this.onFailed)\n\n // Set logger after event loop\n setTimeout(() => {\n if (this.tts && this.logger) {\n this.tts.logger = new Logger(this.tts.constructor.name)\n }\n }, 0)\n }\n\n private onAudio = (audio: Buffer) => {\n this.emit('Audio', audio)\n }\n\n private onFailed = (chunks: string[]) => {\n this.log('TTS failed, trying next TTS')\n this.startNextTTS()\n\n if (chunks.length > 0) {\n this.log('Sending text chunks again')\n const stream = new PassThrough()\n this.tts?.speak(stream)\n chunks.forEach((chunk) => stream.write(chunk))\n stream.end()\n }\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":["MicdropErrorCode","import_stream","MicdropClientCommands","MicdropServerCommands","import_eventemitter3","import_stream","import_stream","import_eventemitter3","import_stream"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/recorder/MicdropRecorder.ts","../src/stt/STT.ts","../src/stt/MockSTT.ts","../src/stt/FallbackSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/tts/FallbackTTS.ts","../src/waitForParams.ts"],"sourcesContent":["export * from './agent'\nexport * from './errors'\nexport * from './Logger'\nexport * from './MicdropServer'\nexport * from './recorder'\nexport * from './stt'\nexport * from './tts'\nexport * from './types'\nexport * from './waitForParams'\n","import { EventEmitter } from 'eventemitter3'\nimport { PassThrough, Readable, Writable } 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 // Function called before any answer is generated\n // Return true to skip generation\n onBeforeAnswer?: (\n this: Agent,\n stream: Writable\n ) => void | boolean | Promise<boolean>\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 protected answerCount = 0\n protected answering = false\n\n constructor(protected options: Options) {\n super()\n this.conversation = [{ role: 'system', content: options.systemPrompt }]\n this.tools = this.getDefaultTools()\n }\n\n protected abstract generateAnswer(stream: PassThrough): Promise<void>\n abstract cancel(): void\n\n answer(): Readable {\n this.log('Start answering')\n const answerCount = ++this.answerCount\n const stream = new PassThrough()\n this.answering = true\n\n Promise.resolve()\n // Call hook onBeforeAnswer\n .then(() => this.options.onBeforeAnswer?.bind(this)(stream))\n // Generate answer (if not skipped)\n .then((skip) => {\n if (skip) return\n return this.generateAnswer(stream)\n })\n // End stream\n .finally(() => {\n if (stream.writable) {\n stream.end()\n }\n if (answerCount === this.answerCount) {\n this.answering = false\n }\n })\n\n return stream\n }\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 const index = this.tools.findIndex((tool) => tool.name === name)\n if (index !== -1) {\n this.tools.splice(index, 1)\n }\n }\n\n getTool(name: string): Tool | undefined {\n return this.tools.find((tool) => tool.name === name)\n }\n\n 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 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 public 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 protected async generateAnswer(stream: PassThrough): Promise<void> {\n const message = `Assistant Message ${this.i++}`\n this.addAssistantMessage(message)\n stream.write(message)\n }\n\n cancel() {}\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(public 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 { EventEmitter } from 'eventemitter3'\nimport { 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 MicdropServerEvents {\n End: [MicdropCallSummary]\n UserAudio: [Buffer]\n AssistantAudio: [Buffer]\n}\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n}\n\nexport class MicdropServer extends EventEmitter<MicdropServerEvents> {\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 // Queue system for operations\n private operationQueue: Array<() => Promise<void>> = []\n private isProcessingQueue = false\n\n // When user is speaking, we're streaming chunks for STT\n private currentUserStream?: Duplex\n private userSpeechChunks = 0\n\n constructor(socket: WebSocket, config: MicdropConfig) {\n super()\n this.socket = socket\n this.config = config\n this.log(`Call started`)\n\n // Setup STT\n this.config.stt.on('Transcript', this.onTranscriptSTT)\n\n // Setup TTS\n this.config.tts.on('Audio', this.onAudioTTS)\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 async processQueue() {\n if (this.isProcessingQueue || this.operationQueue.length === 0) return\n\n this.isProcessingQueue = true\n\n while (this.operationQueue.length > 0) {\n const operation = this.operationQueue.shift()\n if (operation) {\n try {\n await operation()\n } catch (error) {\n this.log('Error processing queued operation:', error)\n }\n }\n }\n\n this.isProcessingQueue = false\n }\n\n private queueOperation(operation: () => Promise<void>) {\n this.operationQueue.push(operation)\n this.processQueue()\n }\n\n public cancel() {\n this.config?.tts.cancel()\n this.config?.agent.cancel()\n // Clear the queue\n this.operationQueue = []\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 // Emit End event\n this.emit('End', {\n conversation: this.config.agent.conversation,\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 this.onStartSpeaking()\n } else if (cmd === MicdropClientCommands.Mute) {\n // User muted the call\n this.onMute()\n } else if (cmd === MicdropClientCommands.StopSpeaking) {\n // User stopped speaking\n this.onStopSpeaking()\n }\n }\n\n // Audio chunk\n else if (this.currentUserStream) {\n this.onUserAudio(message)\n }\n }\n\n private onUserAudio(chunk: Buffer) {\n this.log(`Received chunk (${chunk.byteLength} bytes)`)\n this.currentUserStream?.write(chunk)\n this.userSpeechChunks++\n this.emit('UserAudio', chunk)\n }\n\n private onMute() {\n this.userSpeechChunks = 0\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private onStartSpeaking() {\n if (!this.config) return\n this.userSpeechChunks = 0\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private onStopSpeaking() {\n const hasNoUserSpeech =\n !this.currentUserStream || this.userSpeechChunks === 0\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.userSpeechChunks = 0\n\n // If user is not speaking or no chunks were received, skip\n if (hasNoUserSpeech) {\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n return\n }\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.cancel()\n this.answer()\n }\n }\n\n private onTranscriptSTT = async (transcript: string) => {\n if (!this.config) return\n\n // Skip answer if transcript is empty\n if (transcript === '') {\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n return\n }\n\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.cancel()\n this.answer()\n }\n }\n\n private onAudioTTS = (audio: Buffer) => {\n if (!this.socket) return\n this.log(`Send audio chunk (${audio.byteLength} bytes)`)\n this.socket.send(audio)\n this.emit('AssistantAudio', audio)\n }\n\n private sendFirstMessage() {\n if (!this.config) return\n if (this.config.firstMessage) {\n // Send first message\n this.config.agent.addAssistantMessage(this.config.firstMessage)\n this.speak(this.config.firstMessage)\n } else if (this.config.generateFirstMessage) {\n // Generate first message\n 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 }\n\n public answer() {\n this.queueOperation(async () => {\n await this._answer()\n })\n }\n\n private async _answer() {\n if (!this.config) return\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 this.socket?.send(MicdropServerCommands.SkipAnswer)\n throw error\n }\n }\n\n // Run text-to-speech and send to client\n public speak(message: string | Readable) {\n this.queueOperation(async () => {\n await this._speak(message)\n })\n }\n\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 this.config.tts.speak(textStream)\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 type { MicdropServer } from '../MicdropServer'\nimport type { MicdropConversationItem } from '../types'\nimport { Logger } from '../Logger'\n\nexport interface AudioMessage {\n buffer: Buffer\n messageIndex: number\n message: string\n role: 'user' | 'assistant'\n}\n\nexport interface MicdropRecorderEvents {\n AudioMessage: [AudioMessage]\n Complete: [AudioMessage[]]\n}\n\nexport class MicdropRecorder extends EventEmitter<MicdropRecorderEvents> {\n public logger?: Logger\n\n private audioMessages: AudioMessage[] = []\n private currentUserChunks: Buffer[] = []\n private currentAssistantChunks: Buffer[] = []\n private lastUserMessageIndex: number = -1\n private lastAssistantMessageIndex: number = -1\n\n constructor(private server: MicdropServer) {\n super()\n this.setupListeners()\n }\n\n private setupListeners() {\n // Listen to audio events from server\n this.server.on('UserAudio', this.onUserAudio)\n this.server.on('AssistantAudio', this.onAssistantAudio)\n this.server.on('End', this.onEnd)\n\n // Listen to message events from agent\n const agent = this.server.config?.agent\n if (agent) {\n agent.on('Message', this.onMessage)\n }\n }\n\n private onUserAudio = (chunk: Buffer) => {\n // Finalize or discard assistant audio when user starts speaking\n if (this.currentAssistantChunks.length > 0) {\n if (this.lastAssistantMessageIndex >= 0) {\n this.finalizeAssistantAudio()\n } else {\n // Discard orphaned chunks (no associated message)\n this.log('Discarding orphaned assistant audio chunks')\n this.currentAssistantChunks = []\n }\n }\n\n this.log('Recording user audio chunk')\n this.currentUserChunks.push(chunk)\n }\n\n private onAssistantAudio = (chunk: Buffer) => {\n // Finalize or discard user audio when assistant starts speaking\n if (this.currentUserChunks.length > 0) {\n if (this.lastUserMessageIndex >= 0) {\n this.finalizeUserAudio()\n } else {\n // Discard orphaned chunks (no associated message)\n this.log('Discarding orphaned user audio chunks')\n this.currentUserChunks = []\n }\n }\n\n this.log('Recording assistant audio chunk')\n this.currentAssistantChunks.push(chunk)\n }\n\n private onMessage = (message: MicdropConversationItem) => {\n const conversation = this.server.config?.agent.conversation\n if (!conversation) return\n\n const messageIndex = conversation.length - 1\n\n if (message.role === 'user') {\n this.lastUserMessageIndex = messageIndex\n // User audio might already be complete, finalize if we have chunks\n // Audio chunks arrive BEFORE message, so we finalize when we know the message\n if (this.currentUserChunks.length > 0) {\n this.finalizeUserAudio()\n }\n } else if (message.role === 'assistant') {\n this.lastAssistantMessageIndex = messageIndex\n // Don't finalize assistant audio here - chunks can still arrive after message\n }\n }\n\n private finalizeUserAudio() {\n if (this.currentUserChunks.length === 0) return\n if (this.lastUserMessageIndex < 0) return\n\n const conversation = this.server.config?.agent.conversation\n if (!conversation) return\n\n const message = conversation[this.lastUserMessageIndex]\n const buffer = Buffer.concat(this.currentUserChunks)\n\n const audioMessage: AudioMessage = {\n buffer,\n messageIndex: this.lastUserMessageIndex,\n message: 'content' in message ? message.content : '',\n role: 'user',\n }\n\n this.log(\n `Finalized user audio: ${buffer.length} bytes, message index ${this.lastUserMessageIndex}`\n )\n this.audioMessages.push(audioMessage)\n this.emit('AudioMessage', audioMessage)\n\n // Reset\n this.currentUserChunks = []\n this.lastUserMessageIndex = -1\n }\n\n private finalizeAssistantAudio() {\n if (this.currentAssistantChunks.length === 0) return\n if (this.lastAssistantMessageIndex < 0) return\n\n const conversation = this.server.config?.agent.conversation\n if (!conversation) return\n\n const message = conversation[this.lastAssistantMessageIndex]\n const buffer = Buffer.concat(this.currentAssistantChunks)\n\n const audioMessage: AudioMessage = {\n buffer,\n messageIndex: this.lastAssistantMessageIndex,\n message: 'content' in message ? message.content : '',\n role: 'assistant',\n }\n\n this.log(\n `Finalized assistant audio: ${buffer.length} bytes, message index ${this.lastAssistantMessageIndex}`\n )\n this.audioMessages.push(audioMessage)\n this.emit('AudioMessage', audioMessage)\n\n // Reset\n this.currentAssistantChunks = []\n this.lastAssistantMessageIndex = -1\n }\n\n private onEnd = () => {\n // Finalize any remaining audio\n if (this.currentUserChunks.length > 0) {\n this.finalizeUserAudio()\n }\n if (this.currentAssistantChunks.length > 0) {\n this.finalizeAssistantAudio()\n }\n\n this.log(`Recording complete: ${this.audioMessages.length} audio messages`)\n this.emit('Complete', this.audioMessages)\n }\n\n public getAudioMessages(): AudioMessage[] {\n return [...this.audioMessages]\n }\n\n public destroy() {\n this.log('Destroyed')\n this.server.off('UserAudio', this.onUserAudio)\n this.server.off('AssistantAudio', this.onAssistantAudio)\n this.server.off('End', this.onEnd)\n\n const agent = this.server.config?.agent\n if (agent) {\n agent.off('Message', this.onMessage)\n }\n\n this.removeAllListeners()\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n}\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport interface STTEvents {\n Transcript: [string]\n Failed: [Buffer[]]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n public logger?: Logger\n\n // Set stream of audio to transcribe\n abstract transcribe(audioStream: Readable): void\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","import { STT } from './STT'\n\nexport class MockSTT extends STT {\n private i = 0\n\n async transcribe() {\n setTimeout(() => {\n this.emit('Transcript', `User Message ${this.i++}`)\n }, 300)\n }\n}\n","import { PassThrough, Readable } from 'stream'\nimport { STT } from './STT'\nimport { Logger } from '..'\n\nexport interface FallbackSTTOptions {\n factories: Array<() => STT>\n}\n\nexport class FallbackSTT extends STT {\n private stt: STT | null = null\n private sttIndex = -1 // Start at -1 because we need to increment it before using it\n\n constructor(private readonly options: FallbackSTTOptions) {\n super()\n if (this.options.factories.length === 0) {\n throw new Error('FallbackSTT: No factories provided')\n }\n this.startNextSTT()\n }\n\n transcribe(audioStream: Readable) {\n this.stt?.transcribe(audioStream)\n }\n\n destroy() {\n super.destroy()\n this.stt?.destroy()\n this.stt = null\n this.sttIndex = -1\n }\n\n private startNextSTT() {\n this.sttIndex++\n if (this.sttIndex >= this.options.factories.length) {\n this.sttIndex = 0\n }\n this.stt?.destroy()\n this.stt = this.options.factories[this.sttIndex]()\n this.stt.on('Transcript', this.onTranscript)\n this.stt.on('Failed', this.onFailed)\n\n // Set logger after event loop\n setTimeout(() => {\n if (this.stt && this.logger) {\n this.stt.logger = new Logger(this.stt.constructor.name)\n }\n }, 0)\n }\n\n private onTranscript = (transcript: string) => {\n this.emit('Transcript', transcript)\n }\n\n private onFailed = (chunks: Buffer[]) => {\n this.log('STT failed, trying next STT')\n this.startNextSTT()\n\n if (chunks.length > 0) {\n this.log('Sending audio chunks again')\n const stream = new PassThrough()\n this.stt?.transcribe(stream)\n chunks.forEach((chunk) => stream.write(chunk))\n stream.end()\n }\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 { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport interface TTSEvents {\n Audio: [Buffer]\n Failed: [string[]]\n}\n\nexport abstract class TTS extends EventEmitter<TTSEvents> {\n public logger?: Logger\n\n abstract speak(textStream: Readable): void\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 { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\nimport { Logger } from '..'\n\nexport interface FallbackTTSOptions {\n factories: Array<() => TTS>\n}\n\nexport class FallbackTTS extends TTS {\n private tts: TTS | null = null\n private ttsIndex = -1 // Start at -1 because we need to increment it before using it\n\n constructor(private readonly options: FallbackTTSOptions) {\n super()\n if (this.options.factories.length === 0) {\n throw new Error('FallbackTTS: No factories provided')\n }\n this.startNextTTS()\n }\n\n speak(textStream: Readable) {\n this.tts?.speak(textStream)\n }\n\n cancel() {\n this.tts?.cancel()\n }\n\n destroy() {\n super.destroy()\n this.tts?.destroy()\n this.tts = null\n this.ttsIndex = -1\n }\n\n private startNextTTS() {\n this.ttsIndex++\n if (this.ttsIndex >= this.options.factories.length) {\n this.ttsIndex = 0\n }\n this.tts?.destroy()\n this.tts = this.options.factories[this.ttsIndex]()\n this.tts.on('Audio', this.onAudio)\n this.tts.on('Failed', this.onFailed)\n\n // Set logger after event loop\n setTimeout(() => {\n if (this.tts && this.logger) {\n this.tts.logger = new Logger(this.tts.constructor.name)\n }\n }, 0)\n }\n\n private onAudio = (audio: Buffer) => {\n this.emit('Audio', audio)\n }\n\n private onFailed = (chunks: string[]) => {\n this.log('TTS failed, trying next TTS')\n this.startNextTTS()\n\n if (chunks.length > 0) {\n this.log('Sending text chunks again')\n const stream = new PassThrough()\n this.tts?.speak(stream)\n chunks.forEach((chunk) => stream.write(chunk))\n stream.end()\n }\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":["MicdropErrorCode","import_eventemitter3","import_stream","MicdropClientCommands","MicdropServerCommands","import_eventemitter3","import_eventemitter3","import_stream","import_stream","import_eventemitter3","import_stream"]}
|
package/dist/index.mjs
CHANGED
|
@@ -252,6 +252,7 @@ var Logger = class {
|
|
|
252
252
|
};
|
|
253
253
|
|
|
254
254
|
// src/MicdropServer.ts
|
|
255
|
+
import { EventEmitter as EventEmitter2 } from "eventemitter3";
|
|
255
256
|
import { PassThrough as PassThrough2 } from "stream";
|
|
256
257
|
|
|
257
258
|
// src/types.ts
|
|
@@ -271,8 +272,9 @@ var MicdropServerCommands = /* @__PURE__ */ ((MicdropServerCommands2) => {
|
|
|
271
272
|
})(MicdropServerCommands || {});
|
|
272
273
|
|
|
273
274
|
// src/MicdropServer.ts
|
|
274
|
-
var MicdropServer = class {
|
|
275
|
+
var MicdropServer = class extends EventEmitter2 {
|
|
275
276
|
constructor(socket, config) {
|
|
277
|
+
super();
|
|
276
278
|
this.socket = null;
|
|
277
279
|
this.config = null;
|
|
278
280
|
this.startTime = Date.now();
|
|
@@ -287,9 +289,8 @@ var MicdropServer = class {
|
|
|
287
289
|
this.config.agent.destroy();
|
|
288
290
|
this.config.stt.destroy();
|
|
289
291
|
this.config.tts.destroy();
|
|
290
|
-
this.
|
|
291
|
-
conversation: this.config.agent.conversation
|
|
292
|
-
// Remove system message
|
|
292
|
+
this.emit("End", {
|
|
293
|
+
conversation: this.config.agent.conversation,
|
|
293
294
|
duration
|
|
294
295
|
});
|
|
295
296
|
this.socket = null;
|
|
@@ -312,7 +313,7 @@ var MicdropServer = class {
|
|
|
312
313
|
this.onStopSpeaking();
|
|
313
314
|
}
|
|
314
315
|
} else if (this.currentUserStream) {
|
|
315
|
-
this.
|
|
316
|
+
this.onUserAudio(message);
|
|
316
317
|
}
|
|
317
318
|
};
|
|
318
319
|
this.onTranscriptSTT = async (transcript) => {
|
|
@@ -333,6 +334,7 @@ var MicdropServer = class {
|
|
|
333
334
|
if (!this.socket) return;
|
|
334
335
|
this.log(`Send audio chunk (${audio.byteLength} bytes)`);
|
|
335
336
|
this.socket.send(audio);
|
|
337
|
+
this.emit("AssistantAudio", audio);
|
|
336
338
|
};
|
|
337
339
|
this.socket = socket;
|
|
338
340
|
this.config = config;
|
|
@@ -394,10 +396,11 @@ var MicdropServer = class {
|
|
|
394
396
|
this.config?.agent.cancel();
|
|
395
397
|
this.operationQueue = [];
|
|
396
398
|
}
|
|
397
|
-
|
|
399
|
+
onUserAudio(chunk) {
|
|
398
400
|
this.log(`Received chunk (${chunk.byteLength} bytes)`);
|
|
399
401
|
this.currentUserStream?.write(chunk);
|
|
400
402
|
this.userSpeechChunks++;
|
|
403
|
+
this.emit("UserAudio", chunk);
|
|
401
404
|
}
|
|
402
405
|
onMute() {
|
|
403
406
|
this.userSpeechChunks = 0;
|
|
@@ -485,9 +488,139 @@ var MicdropServer = class {
|
|
|
485
488
|
}
|
|
486
489
|
};
|
|
487
490
|
|
|
491
|
+
// src/recorder/MicdropRecorder.ts
|
|
492
|
+
import { EventEmitter as EventEmitter3 } from "eventemitter3";
|
|
493
|
+
var MicdropRecorder = class extends EventEmitter3 {
|
|
494
|
+
constructor(server) {
|
|
495
|
+
super();
|
|
496
|
+
this.server = server;
|
|
497
|
+
this.audioMessages = [];
|
|
498
|
+
this.currentUserChunks = [];
|
|
499
|
+
this.currentAssistantChunks = [];
|
|
500
|
+
this.lastUserMessageIndex = -1;
|
|
501
|
+
this.lastAssistantMessageIndex = -1;
|
|
502
|
+
this.onUserAudio = (chunk) => {
|
|
503
|
+
if (this.currentAssistantChunks.length > 0) {
|
|
504
|
+
if (this.lastAssistantMessageIndex >= 0) {
|
|
505
|
+
this.finalizeAssistantAudio();
|
|
506
|
+
} else {
|
|
507
|
+
this.log("Discarding orphaned assistant audio chunks");
|
|
508
|
+
this.currentAssistantChunks = [];
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
this.log("Recording user audio chunk");
|
|
512
|
+
this.currentUserChunks.push(chunk);
|
|
513
|
+
};
|
|
514
|
+
this.onAssistantAudio = (chunk) => {
|
|
515
|
+
if (this.currentUserChunks.length > 0) {
|
|
516
|
+
if (this.lastUserMessageIndex >= 0) {
|
|
517
|
+
this.finalizeUserAudio();
|
|
518
|
+
} else {
|
|
519
|
+
this.log("Discarding orphaned user audio chunks");
|
|
520
|
+
this.currentUserChunks = [];
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
this.log("Recording assistant audio chunk");
|
|
524
|
+
this.currentAssistantChunks.push(chunk);
|
|
525
|
+
};
|
|
526
|
+
this.onMessage = (message) => {
|
|
527
|
+
const conversation = this.server.config?.agent.conversation;
|
|
528
|
+
if (!conversation) return;
|
|
529
|
+
const messageIndex = conversation.length - 1;
|
|
530
|
+
if (message.role === "user") {
|
|
531
|
+
this.lastUserMessageIndex = messageIndex;
|
|
532
|
+
if (this.currentUserChunks.length > 0) {
|
|
533
|
+
this.finalizeUserAudio();
|
|
534
|
+
}
|
|
535
|
+
} else if (message.role === "assistant") {
|
|
536
|
+
this.lastAssistantMessageIndex = messageIndex;
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
this.onEnd = () => {
|
|
540
|
+
if (this.currentUserChunks.length > 0) {
|
|
541
|
+
this.finalizeUserAudio();
|
|
542
|
+
}
|
|
543
|
+
if (this.currentAssistantChunks.length > 0) {
|
|
544
|
+
this.finalizeAssistantAudio();
|
|
545
|
+
}
|
|
546
|
+
this.log(`Recording complete: ${this.audioMessages.length} audio messages`);
|
|
547
|
+
this.emit("Complete", this.audioMessages);
|
|
548
|
+
};
|
|
549
|
+
this.setupListeners();
|
|
550
|
+
}
|
|
551
|
+
setupListeners() {
|
|
552
|
+
this.server.on("UserAudio", this.onUserAudio);
|
|
553
|
+
this.server.on("AssistantAudio", this.onAssistantAudio);
|
|
554
|
+
this.server.on("End", this.onEnd);
|
|
555
|
+
const agent = this.server.config?.agent;
|
|
556
|
+
if (agent) {
|
|
557
|
+
agent.on("Message", this.onMessage);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
finalizeUserAudio() {
|
|
561
|
+
if (this.currentUserChunks.length === 0) return;
|
|
562
|
+
if (this.lastUserMessageIndex < 0) return;
|
|
563
|
+
const conversation = this.server.config?.agent.conversation;
|
|
564
|
+
if (!conversation) return;
|
|
565
|
+
const message = conversation[this.lastUserMessageIndex];
|
|
566
|
+
const buffer = Buffer.concat(this.currentUserChunks);
|
|
567
|
+
const audioMessage = {
|
|
568
|
+
buffer,
|
|
569
|
+
messageIndex: this.lastUserMessageIndex,
|
|
570
|
+
message: "content" in message ? message.content : "",
|
|
571
|
+
role: "user"
|
|
572
|
+
};
|
|
573
|
+
this.log(
|
|
574
|
+
`Finalized user audio: ${buffer.length} bytes, message index ${this.lastUserMessageIndex}`
|
|
575
|
+
);
|
|
576
|
+
this.audioMessages.push(audioMessage);
|
|
577
|
+
this.emit("AudioMessage", audioMessage);
|
|
578
|
+
this.currentUserChunks = [];
|
|
579
|
+
this.lastUserMessageIndex = -1;
|
|
580
|
+
}
|
|
581
|
+
finalizeAssistantAudio() {
|
|
582
|
+
if (this.currentAssistantChunks.length === 0) return;
|
|
583
|
+
if (this.lastAssistantMessageIndex < 0) return;
|
|
584
|
+
const conversation = this.server.config?.agent.conversation;
|
|
585
|
+
if (!conversation) return;
|
|
586
|
+
const message = conversation[this.lastAssistantMessageIndex];
|
|
587
|
+
const buffer = Buffer.concat(this.currentAssistantChunks);
|
|
588
|
+
const audioMessage = {
|
|
589
|
+
buffer,
|
|
590
|
+
messageIndex: this.lastAssistantMessageIndex,
|
|
591
|
+
message: "content" in message ? message.content : "",
|
|
592
|
+
role: "assistant"
|
|
593
|
+
};
|
|
594
|
+
this.log(
|
|
595
|
+
`Finalized assistant audio: ${buffer.length} bytes, message index ${this.lastAssistantMessageIndex}`
|
|
596
|
+
);
|
|
597
|
+
this.audioMessages.push(audioMessage);
|
|
598
|
+
this.emit("AudioMessage", audioMessage);
|
|
599
|
+
this.currentAssistantChunks = [];
|
|
600
|
+
this.lastAssistantMessageIndex = -1;
|
|
601
|
+
}
|
|
602
|
+
getAudioMessages() {
|
|
603
|
+
return [...this.audioMessages];
|
|
604
|
+
}
|
|
605
|
+
destroy() {
|
|
606
|
+
this.log("Destroyed");
|
|
607
|
+
this.server.off("UserAudio", this.onUserAudio);
|
|
608
|
+
this.server.off("AssistantAudio", this.onAssistantAudio);
|
|
609
|
+
this.server.off("End", this.onEnd);
|
|
610
|
+
const agent = this.server.config?.agent;
|
|
611
|
+
if (agent) {
|
|
612
|
+
agent.off("Message", this.onMessage);
|
|
613
|
+
}
|
|
614
|
+
this.removeAllListeners();
|
|
615
|
+
}
|
|
616
|
+
log(...message) {
|
|
617
|
+
this.logger?.log(...message);
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
|
|
488
621
|
// src/stt/STT.ts
|
|
489
|
-
import { EventEmitter as
|
|
490
|
-
var STT = class extends
|
|
622
|
+
import { EventEmitter as EventEmitter4 } from "eventemitter3";
|
|
623
|
+
var STT = class extends EventEmitter4 {
|
|
491
624
|
log(...message) {
|
|
492
625
|
this.logger?.log(...message);
|
|
493
626
|
}
|
|
@@ -569,8 +702,8 @@ import * as fs from "fs";
|
|
|
569
702
|
import { PassThrough as PassThrough4 } from "stream";
|
|
570
703
|
|
|
571
704
|
// src/tts/TTS.ts
|
|
572
|
-
import { EventEmitter as
|
|
573
|
-
var TTS = class extends
|
|
705
|
+
import { EventEmitter as EventEmitter5 } from "eventemitter3";
|
|
706
|
+
var TTS = class extends EventEmitter5 {
|
|
574
707
|
log(...message) {
|
|
575
708
|
this.logger?.log(...message);
|
|
576
709
|
}
|
|
@@ -693,6 +826,7 @@ export {
|
|
|
693
826
|
MicdropClientCommands,
|
|
694
827
|
MicdropError,
|
|
695
828
|
MicdropErrorCode,
|
|
829
|
+
MicdropRecorder,
|
|
696
830
|
MicdropServer,
|
|
697
831
|
MicdropServerCommands,
|
|
698
832
|
MockAgent,
|
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/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/stt/STT.ts","../src/stt/MockSTT.ts","../src/stt/FallbackSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/tts/FallbackTTS.ts","../src/waitForParams.ts"],"sourcesContent":["import { EventEmitter } from 'eventemitter3'\nimport { PassThrough, Readable, Writable } 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 // Function called before any answer is generated\n // Return true to skip generation\n onBeforeAnswer?: (\n this: Agent,\n stream: Writable\n ) => void | boolean | Promise<boolean>\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 protected answerCount = 0\n protected answering = false\n\n constructor(protected options: Options) {\n super()\n this.conversation = [{ role: 'system', content: options.systemPrompt }]\n this.tools = this.getDefaultTools()\n }\n\n protected abstract generateAnswer(stream: PassThrough): Promise<void>\n abstract cancel(): void\n\n answer(): Readable {\n this.log('Start answering')\n const answerCount = ++this.answerCount\n const stream = new PassThrough()\n this.answering = true\n\n Promise.resolve()\n // Call hook onBeforeAnswer\n .then(() => this.options.onBeforeAnswer?.bind(this)(stream))\n // Generate answer (if not skipped)\n .then((skip) => {\n if (skip) return\n return this.generateAnswer(stream)\n })\n // End stream\n .finally(() => {\n if (stream.writable) {\n stream.end()\n }\n if (answerCount === this.answerCount) {\n this.answering = false\n }\n })\n\n return stream\n }\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 const index = this.tools.findIndex((tool) => tool.name === name)\n if (index !== -1) {\n this.tools.splice(index, 1)\n }\n }\n\n getTool(name: string): Tool | undefined {\n return this.tools.find((tool) => tool.name === name)\n }\n\n 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 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 public 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 protected async generateAnswer(stream: PassThrough): Promise<void> {\n const message = `Assistant Message ${this.i++}`\n this.addAssistantMessage(message)\n stream.write(message)\n }\n\n cancel() {}\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(public 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 // Queue system for operations\n private operationQueue: Array<() => Promise<void>> = []\n private isProcessingQueue = false\n\n // When user is speaking, we're streaming chunks for STT\n private currentUserStream?: Duplex\n private userSpeechChunks = 0\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.onTranscriptSTT)\n\n // Setup TTS\n this.config.tts.on('Audio', this.onAudioTTS)\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 async processQueue() {\n if (this.isProcessingQueue || this.operationQueue.length === 0) return\n\n this.isProcessingQueue = true\n\n while (this.operationQueue.length > 0) {\n const operation = this.operationQueue.shift()\n if (operation) {\n try {\n await operation()\n } catch (error) {\n this.log('Error processing queued operation:', error)\n }\n }\n }\n\n this.isProcessingQueue = false\n }\n\n private queueOperation(operation: () => Promise<void>) {\n this.operationQueue.push(operation)\n this.processQueue()\n }\n\n public cancel() {\n this.config?.tts.cancel()\n this.config?.agent.cancel()\n // Clear the queue\n this.operationQueue = []\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 this.onStartSpeaking()\n } else if (cmd === MicdropClientCommands.Mute) {\n // User muted the call\n this.onMute()\n } else if (cmd === MicdropClientCommands.StopSpeaking) {\n // User stopped speaking\n this.onStopSpeaking()\n }\n }\n\n // Audio chunk\n else if (this.currentUserStream) {\n this.onAudioChunk(message)\n }\n }\n\n private onAudioChunk(chunk: Buffer) {\n this.log(`Received chunk (${chunk.byteLength} bytes)`)\n this.currentUserStream?.write(chunk)\n this.userSpeechChunks++\n }\n\n private onMute() {\n this.userSpeechChunks = 0\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private onStartSpeaking() {\n if (!this.config) return\n this.userSpeechChunks = 0\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private onStopSpeaking() {\n const hasNoUserSpeech =\n !this.currentUserStream || this.userSpeechChunks === 0\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.userSpeechChunks = 0\n\n // If user is not speaking or no chunks were received, skip\n if (hasNoUserSpeech) {\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n return\n }\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.cancel()\n this.answer()\n }\n }\n\n private onTranscriptSTT = async (transcript: string) => {\n if (!this.config) return\n\n // Skip answer if transcript is empty\n if (transcript === '') {\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n return\n }\n\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.cancel()\n this.answer()\n }\n }\n\n private onAudioTTS = (audio: Buffer) => {\n if (!this.socket) return\n this.log(`Send audio chunk (${audio.byteLength} bytes)`)\n this.socket.send(audio)\n }\n\n private sendFirstMessage() {\n if (!this.config) return\n if (this.config.firstMessage) {\n // Send first message\n this.config.agent.addAssistantMessage(this.config.firstMessage)\n this.speak(this.config.firstMessage)\n } else if (this.config.generateFirstMessage) {\n // Generate first message\n 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 }\n\n public answer() {\n this.queueOperation(async () => {\n await this._answer()\n })\n }\n\n private async _answer() {\n if (!this.config) return\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 this.socket?.send(MicdropServerCommands.SkipAnswer)\n throw error\n }\n }\n\n // Run text-to-speech and send to client\n public speak(message: string | Readable) {\n this.queueOperation(async () => {\n await this._speak(message)\n })\n }\n\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 this.config.tts.speak(textStream)\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\nexport interface STTEvents {\n Transcript: [string]\n Failed: [Buffer[]]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n public logger?: Logger\n\n // Set stream of audio to transcribe\n abstract transcribe(audioStream: Readable): void\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","import { STT } from './STT'\n\nexport class MockSTT extends STT {\n private i = 0\n\n async transcribe() {\n setTimeout(() => {\n this.emit('Transcript', `User Message ${this.i++}`)\n }, 300)\n }\n}\n","import { PassThrough, Readable } from 'stream'\nimport { STT } from './STT'\nimport { Logger } from '..'\n\nexport interface FallbackSTTOptions {\n factories: Array<() => STT>\n}\n\nexport class FallbackSTT extends STT {\n private stt: STT | null = null\n private sttIndex = -1 // Start at -1 because we need to increment it before using it\n\n constructor(private readonly options: FallbackSTTOptions) {\n super()\n if (this.options.factories.length === 0) {\n throw new Error('FallbackSTT: No factories provided')\n }\n this.startNextSTT()\n }\n\n transcribe(audioStream: Readable) {\n this.stt?.transcribe(audioStream)\n }\n\n destroy() {\n super.destroy()\n this.stt?.destroy()\n this.stt = null\n this.sttIndex = -1\n }\n\n private startNextSTT() {\n this.sttIndex++\n if (this.sttIndex >= this.options.factories.length) {\n this.sttIndex = 0\n }\n this.stt?.destroy()\n this.stt = this.options.factories[this.sttIndex]()\n this.stt.on('Transcript', this.onTranscript)\n this.stt.on('Failed', this.onFailed)\n\n // Set logger after event loop\n setTimeout(() => {\n if (this.stt && this.logger) {\n this.stt.logger = new Logger(this.stt.constructor.name)\n }\n }, 0)\n }\n\n private onTranscript = (transcript: string) => {\n this.emit('Transcript', transcript)\n }\n\n private onFailed = (chunks: Buffer[]) => {\n this.log('STT failed, trying next STT')\n this.startNextSTT()\n\n if (chunks.length > 0) {\n this.log('Sending audio chunks again')\n const stream = new PassThrough()\n this.stt?.transcribe(stream)\n chunks.forEach((chunk) => stream.write(chunk))\n stream.end()\n }\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 { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport interface TTSEvents {\n Audio: [Buffer]\n Failed: [string[]]\n}\n\nexport abstract class TTS extends EventEmitter<TTSEvents> {\n public logger?: Logger\n\n abstract speak(textStream: Readable): void\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 { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\nimport { Logger } from '..'\n\nexport interface FallbackTTSOptions {\n factories: Array<() => TTS>\n}\n\nexport class FallbackTTS extends TTS {\n private tts: TTS | null = null\n private ttsIndex = -1 // Start at -1 because we need to increment it before using it\n\n constructor(private readonly options: FallbackTTSOptions) {\n super()\n if (this.options.factories.length === 0) {\n throw new Error('FallbackTTS: No factories provided')\n }\n this.startNextTTS()\n }\n\n speak(textStream: Readable) {\n this.tts?.speak(textStream)\n }\n\n cancel() {\n this.tts?.cancel()\n }\n\n destroy() {\n super.destroy()\n this.tts?.destroy()\n this.tts = null\n this.ttsIndex = -1\n }\n\n private startNextTTS() {\n this.ttsIndex++\n if (this.ttsIndex >= this.options.factories.length) {\n this.ttsIndex = 0\n }\n this.tts?.destroy()\n this.tts = this.options.factories[this.ttsIndex]()\n this.tts.on('Audio', this.onAudio)\n this.tts.on('Failed', this.onFailed)\n\n // Set logger after event loop\n setTimeout(() => {\n if (this.tts && this.logger) {\n this.tts.logger = new Logger(this.tts.constructor.name)\n }\n }, 0)\n }\n\n private onAudio = (audio: Buffer) => {\n this.emit('Audio', audio)\n }\n\n private onFailed = (chunks: string[]) => {\n this.log('TTS failed, trying next TTS')\n this.startNextTTS()\n\n if (chunks.length > 0) {\n this.log('Sending text chunks again')\n const stream = new PassThrough()\n this.tts?.speak(stream)\n chunks.forEach((chunk) => stream.write(chunk))\n stream.end()\n }\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":["MicdropErrorCode","PassThrough","MicdropClientCommands","MicdropServerCommands","PassThrough","EventEmitter","PassThrough","PassThrough","PassThrough","EventEmitter","PassThrough","PassThrough","PassThrough"]}
|
|
1
|
+
{"version":3,"sources":["../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/recorder/MicdropRecorder.ts","../src/stt/STT.ts","../src/stt/MockSTT.ts","../src/stt/FallbackSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/tts/FallbackTTS.ts","../src/waitForParams.ts"],"sourcesContent":["import { EventEmitter } from 'eventemitter3'\nimport { PassThrough, Readable, Writable } 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 // Function called before any answer is generated\n // Return true to skip generation\n onBeforeAnswer?: (\n this: Agent,\n stream: Writable\n ) => void | boolean | Promise<boolean>\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 protected answerCount = 0\n protected answering = false\n\n constructor(protected options: Options) {\n super()\n this.conversation = [{ role: 'system', content: options.systemPrompt }]\n this.tools = this.getDefaultTools()\n }\n\n protected abstract generateAnswer(stream: PassThrough): Promise<void>\n abstract cancel(): void\n\n answer(): Readable {\n this.log('Start answering')\n const answerCount = ++this.answerCount\n const stream = new PassThrough()\n this.answering = true\n\n Promise.resolve()\n // Call hook onBeforeAnswer\n .then(() => this.options.onBeforeAnswer?.bind(this)(stream))\n // Generate answer (if not skipped)\n .then((skip) => {\n if (skip) return\n return this.generateAnswer(stream)\n })\n // End stream\n .finally(() => {\n if (stream.writable) {\n stream.end()\n }\n if (answerCount === this.answerCount) {\n this.answering = false\n }\n })\n\n return stream\n }\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 const index = this.tools.findIndex((tool) => tool.name === name)\n if (index !== -1) {\n this.tools.splice(index, 1)\n }\n }\n\n getTool(name: string): Tool | undefined {\n return this.tools.find((tool) => tool.name === name)\n }\n\n 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 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 public 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 protected async generateAnswer(stream: PassThrough): Promise<void> {\n const message = `Assistant Message ${this.i++}`\n this.addAssistantMessage(message)\n stream.write(message)\n }\n\n cancel() {}\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(public 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 { EventEmitter } from 'eventemitter3'\nimport { 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 MicdropServerEvents {\n End: [MicdropCallSummary]\n UserAudio: [Buffer]\n AssistantAudio: [Buffer]\n}\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n}\n\nexport class MicdropServer extends EventEmitter<MicdropServerEvents> {\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 // Queue system for operations\n private operationQueue: Array<() => Promise<void>> = []\n private isProcessingQueue = false\n\n // When user is speaking, we're streaming chunks for STT\n private currentUserStream?: Duplex\n private userSpeechChunks = 0\n\n constructor(socket: WebSocket, config: MicdropConfig) {\n super()\n this.socket = socket\n this.config = config\n this.log(`Call started`)\n\n // Setup STT\n this.config.stt.on('Transcript', this.onTranscriptSTT)\n\n // Setup TTS\n this.config.tts.on('Audio', this.onAudioTTS)\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 async processQueue() {\n if (this.isProcessingQueue || this.operationQueue.length === 0) return\n\n this.isProcessingQueue = true\n\n while (this.operationQueue.length > 0) {\n const operation = this.operationQueue.shift()\n if (operation) {\n try {\n await operation()\n } catch (error) {\n this.log('Error processing queued operation:', error)\n }\n }\n }\n\n this.isProcessingQueue = false\n }\n\n private queueOperation(operation: () => Promise<void>) {\n this.operationQueue.push(operation)\n this.processQueue()\n }\n\n public cancel() {\n this.config?.tts.cancel()\n this.config?.agent.cancel()\n // Clear the queue\n this.operationQueue = []\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 // Emit End event\n this.emit('End', {\n conversation: this.config.agent.conversation,\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 this.onStartSpeaking()\n } else if (cmd === MicdropClientCommands.Mute) {\n // User muted the call\n this.onMute()\n } else if (cmd === MicdropClientCommands.StopSpeaking) {\n // User stopped speaking\n this.onStopSpeaking()\n }\n }\n\n // Audio chunk\n else if (this.currentUserStream) {\n this.onUserAudio(message)\n }\n }\n\n private onUserAudio(chunk: Buffer) {\n this.log(`Received chunk (${chunk.byteLength} bytes)`)\n this.currentUserStream?.write(chunk)\n this.userSpeechChunks++\n this.emit('UserAudio', chunk)\n }\n\n private onMute() {\n this.userSpeechChunks = 0\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private onStartSpeaking() {\n if (!this.config) return\n this.userSpeechChunks = 0\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private onStopSpeaking() {\n const hasNoUserSpeech =\n !this.currentUserStream || this.userSpeechChunks === 0\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.userSpeechChunks = 0\n\n // If user is not speaking or no chunks were received, skip\n if (hasNoUserSpeech) {\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n return\n }\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.cancel()\n this.answer()\n }\n }\n\n private onTranscriptSTT = async (transcript: string) => {\n if (!this.config) return\n\n // Skip answer if transcript is empty\n if (transcript === '') {\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n return\n }\n\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.cancel()\n this.answer()\n }\n }\n\n private onAudioTTS = (audio: Buffer) => {\n if (!this.socket) return\n this.log(`Send audio chunk (${audio.byteLength} bytes)`)\n this.socket.send(audio)\n this.emit('AssistantAudio', audio)\n }\n\n private sendFirstMessage() {\n if (!this.config) return\n if (this.config.firstMessage) {\n // Send first message\n this.config.agent.addAssistantMessage(this.config.firstMessage)\n this.speak(this.config.firstMessage)\n } else if (this.config.generateFirstMessage) {\n // Generate first message\n 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 }\n\n public answer() {\n this.queueOperation(async () => {\n await this._answer()\n })\n }\n\n private async _answer() {\n if (!this.config) return\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 this.socket?.send(MicdropServerCommands.SkipAnswer)\n throw error\n }\n }\n\n // Run text-to-speech and send to client\n public speak(message: string | Readable) {\n this.queueOperation(async () => {\n await this._speak(message)\n })\n }\n\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 this.config.tts.speak(textStream)\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 type { MicdropServer } from '../MicdropServer'\nimport type { MicdropConversationItem } from '../types'\nimport { Logger } from '../Logger'\n\nexport interface AudioMessage {\n buffer: Buffer\n messageIndex: number\n message: string\n role: 'user' | 'assistant'\n}\n\nexport interface MicdropRecorderEvents {\n AudioMessage: [AudioMessage]\n Complete: [AudioMessage[]]\n}\n\nexport class MicdropRecorder extends EventEmitter<MicdropRecorderEvents> {\n public logger?: Logger\n\n private audioMessages: AudioMessage[] = []\n private currentUserChunks: Buffer[] = []\n private currentAssistantChunks: Buffer[] = []\n private lastUserMessageIndex: number = -1\n private lastAssistantMessageIndex: number = -1\n\n constructor(private server: MicdropServer) {\n super()\n this.setupListeners()\n }\n\n private setupListeners() {\n // Listen to audio events from server\n this.server.on('UserAudio', this.onUserAudio)\n this.server.on('AssistantAudio', this.onAssistantAudio)\n this.server.on('End', this.onEnd)\n\n // Listen to message events from agent\n const agent = this.server.config?.agent\n if (agent) {\n agent.on('Message', this.onMessage)\n }\n }\n\n private onUserAudio = (chunk: Buffer) => {\n // Finalize or discard assistant audio when user starts speaking\n if (this.currentAssistantChunks.length > 0) {\n if (this.lastAssistantMessageIndex >= 0) {\n this.finalizeAssistantAudio()\n } else {\n // Discard orphaned chunks (no associated message)\n this.log('Discarding orphaned assistant audio chunks')\n this.currentAssistantChunks = []\n }\n }\n\n this.log('Recording user audio chunk')\n this.currentUserChunks.push(chunk)\n }\n\n private onAssistantAudio = (chunk: Buffer) => {\n // Finalize or discard user audio when assistant starts speaking\n if (this.currentUserChunks.length > 0) {\n if (this.lastUserMessageIndex >= 0) {\n this.finalizeUserAudio()\n } else {\n // Discard orphaned chunks (no associated message)\n this.log('Discarding orphaned user audio chunks')\n this.currentUserChunks = []\n }\n }\n\n this.log('Recording assistant audio chunk')\n this.currentAssistantChunks.push(chunk)\n }\n\n private onMessage = (message: MicdropConversationItem) => {\n const conversation = this.server.config?.agent.conversation\n if (!conversation) return\n\n const messageIndex = conversation.length - 1\n\n if (message.role === 'user') {\n this.lastUserMessageIndex = messageIndex\n // User audio might already be complete, finalize if we have chunks\n // Audio chunks arrive BEFORE message, so we finalize when we know the message\n if (this.currentUserChunks.length > 0) {\n this.finalizeUserAudio()\n }\n } else if (message.role === 'assistant') {\n this.lastAssistantMessageIndex = messageIndex\n // Don't finalize assistant audio here - chunks can still arrive after message\n }\n }\n\n private finalizeUserAudio() {\n if (this.currentUserChunks.length === 0) return\n if (this.lastUserMessageIndex < 0) return\n\n const conversation = this.server.config?.agent.conversation\n if (!conversation) return\n\n const message = conversation[this.lastUserMessageIndex]\n const buffer = Buffer.concat(this.currentUserChunks)\n\n const audioMessage: AudioMessage = {\n buffer,\n messageIndex: this.lastUserMessageIndex,\n message: 'content' in message ? message.content : '',\n role: 'user',\n }\n\n this.log(\n `Finalized user audio: ${buffer.length} bytes, message index ${this.lastUserMessageIndex}`\n )\n this.audioMessages.push(audioMessage)\n this.emit('AudioMessage', audioMessage)\n\n // Reset\n this.currentUserChunks = []\n this.lastUserMessageIndex = -1\n }\n\n private finalizeAssistantAudio() {\n if (this.currentAssistantChunks.length === 0) return\n if (this.lastAssistantMessageIndex < 0) return\n\n const conversation = this.server.config?.agent.conversation\n if (!conversation) return\n\n const message = conversation[this.lastAssistantMessageIndex]\n const buffer = Buffer.concat(this.currentAssistantChunks)\n\n const audioMessage: AudioMessage = {\n buffer,\n messageIndex: this.lastAssistantMessageIndex,\n message: 'content' in message ? message.content : '',\n role: 'assistant',\n }\n\n this.log(\n `Finalized assistant audio: ${buffer.length} bytes, message index ${this.lastAssistantMessageIndex}`\n )\n this.audioMessages.push(audioMessage)\n this.emit('AudioMessage', audioMessage)\n\n // Reset\n this.currentAssistantChunks = []\n this.lastAssistantMessageIndex = -1\n }\n\n private onEnd = () => {\n // Finalize any remaining audio\n if (this.currentUserChunks.length > 0) {\n this.finalizeUserAudio()\n }\n if (this.currentAssistantChunks.length > 0) {\n this.finalizeAssistantAudio()\n }\n\n this.log(`Recording complete: ${this.audioMessages.length} audio messages`)\n this.emit('Complete', this.audioMessages)\n }\n\n public getAudioMessages(): AudioMessage[] {\n return [...this.audioMessages]\n }\n\n public destroy() {\n this.log('Destroyed')\n this.server.off('UserAudio', this.onUserAudio)\n this.server.off('AssistantAudio', this.onAssistantAudio)\n this.server.off('End', this.onEnd)\n\n const agent = this.server.config?.agent\n if (agent) {\n agent.off('Message', this.onMessage)\n }\n\n this.removeAllListeners()\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n}\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport interface STTEvents {\n Transcript: [string]\n Failed: [Buffer[]]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n public logger?: Logger\n\n // Set stream of audio to transcribe\n abstract transcribe(audioStream: Readable): void\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","import { STT } from './STT'\n\nexport class MockSTT extends STT {\n private i = 0\n\n async transcribe() {\n setTimeout(() => {\n this.emit('Transcript', `User Message ${this.i++}`)\n }, 300)\n }\n}\n","import { PassThrough, Readable } from 'stream'\nimport { STT } from './STT'\nimport { Logger } from '..'\n\nexport interface FallbackSTTOptions {\n factories: Array<() => STT>\n}\n\nexport class FallbackSTT extends STT {\n private stt: STT | null = null\n private sttIndex = -1 // Start at -1 because we need to increment it before using it\n\n constructor(private readonly options: FallbackSTTOptions) {\n super()\n if (this.options.factories.length === 0) {\n throw new Error('FallbackSTT: No factories provided')\n }\n this.startNextSTT()\n }\n\n transcribe(audioStream: Readable) {\n this.stt?.transcribe(audioStream)\n }\n\n destroy() {\n super.destroy()\n this.stt?.destroy()\n this.stt = null\n this.sttIndex = -1\n }\n\n private startNextSTT() {\n this.sttIndex++\n if (this.sttIndex >= this.options.factories.length) {\n this.sttIndex = 0\n }\n this.stt?.destroy()\n this.stt = this.options.factories[this.sttIndex]()\n this.stt.on('Transcript', this.onTranscript)\n this.stt.on('Failed', this.onFailed)\n\n // Set logger after event loop\n setTimeout(() => {\n if (this.stt && this.logger) {\n this.stt.logger = new Logger(this.stt.constructor.name)\n }\n }, 0)\n }\n\n private onTranscript = (transcript: string) => {\n this.emit('Transcript', transcript)\n }\n\n private onFailed = (chunks: Buffer[]) => {\n this.log('STT failed, trying next STT')\n this.startNextSTT()\n\n if (chunks.length > 0) {\n this.log('Sending audio chunks again')\n const stream = new PassThrough()\n this.stt?.transcribe(stream)\n chunks.forEach((chunk) => stream.write(chunk))\n stream.end()\n }\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 { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport interface TTSEvents {\n Audio: [Buffer]\n Failed: [string[]]\n}\n\nexport abstract class TTS extends EventEmitter<TTSEvents> {\n public logger?: Logger\n\n abstract speak(textStream: Readable): void\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 { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\nimport { Logger } from '..'\n\nexport interface FallbackTTSOptions {\n factories: Array<() => TTS>\n}\n\nexport class FallbackTTS extends TTS {\n private tts: TTS | null = null\n private ttsIndex = -1 // Start at -1 because we need to increment it before using it\n\n constructor(private readonly options: FallbackTTSOptions) {\n super()\n if (this.options.factories.length === 0) {\n throw new Error('FallbackTTS: No factories provided')\n }\n this.startNextTTS()\n }\n\n speak(textStream: Readable) {\n this.tts?.speak(textStream)\n }\n\n cancel() {\n this.tts?.cancel()\n }\n\n destroy() {\n super.destroy()\n this.tts?.destroy()\n this.tts = null\n this.ttsIndex = -1\n }\n\n private startNextTTS() {\n this.ttsIndex++\n if (this.ttsIndex >= this.options.factories.length) {\n this.ttsIndex = 0\n }\n this.tts?.destroy()\n this.tts = this.options.factories[this.ttsIndex]()\n this.tts.on('Audio', this.onAudio)\n this.tts.on('Failed', this.onFailed)\n\n // Set logger after event loop\n setTimeout(() => {\n if (this.tts && this.logger) {\n this.tts.logger = new Logger(this.tts.constructor.name)\n }\n }, 0)\n }\n\n private onAudio = (audio: Buffer) => {\n this.emit('Audio', audio)\n }\n\n private onFailed = (chunks: string[]) => {\n this.log('TTS failed, trying next TTS')\n this.startNextTTS()\n\n if (chunks.length > 0) {\n this.log('Sending text chunks again')\n const stream = new PassThrough()\n this.tts?.speak(stream)\n chunks.forEach((chunk) => stream.write(chunk))\n stream.end()\n }\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":["MicdropErrorCode","EventEmitter","PassThrough","MicdropClientCommands","MicdropServerCommands","EventEmitter","PassThrough","EventEmitter","EventEmitter","PassThrough","PassThrough","PassThrough","EventEmitter","PassThrough","PassThrough","PassThrough"]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@micdrop/server",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "🖐️🎤 Micdrop: Real-Time Voice Conversations with AI",
|
|
5
5
|
"author": "Lonestone",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"module": "./dist/index.mjs",
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup",
|
|
12
|
+
"dev": "tsup --watch",
|
|
13
|
+
"clean": "rm -rf dist",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
10
17
|
"files": [
|
|
11
18
|
"dist"
|
|
12
19
|
],
|
|
@@ -35,11 +42,5 @@
|
|
|
35
42
|
"tsup": "^8.3.6",
|
|
36
43
|
"typescript": "^5.7.3",
|
|
37
44
|
"zod": "^4.0.5"
|
|
38
|
-
},
|
|
39
|
-
"scripts": {
|
|
40
|
-
"build": "tsup",
|
|
41
|
-
"dev": "tsup --watch",
|
|
42
|
-
"clean": "rm -rf dist",
|
|
43
|
-
"typecheck": "tsc --noEmit"
|
|
44
45
|
}
|
|
45
46
|
}
|
package/LICENSE
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2022 Rolebase
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
-
|
|
7
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
-
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|