@micdrop/server 2.1.0 → 2.2.0
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/LICENSE +9 -0
- package/dist/index.d.mts +42 -7
- package/dist/index.d.ts +42 -7
- package/dist/index.js +128 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +126 -33
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
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.
|
package/dist/index.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
|
|
4
4
|
import WebSocket, { WebSocket as WebSocket$1 } from 'ws';
|
|
5
5
|
|
|
6
6
|
declare class Logger {
|
|
7
|
-
|
|
7
|
+
name: string;
|
|
8
8
|
constructor(name: string);
|
|
9
9
|
log(...message: any[]): void;
|
|
10
10
|
}
|
|
@@ -158,6 +158,7 @@ declare function handleError(socket: WebSocket, error: unknown): void;
|
|
|
158
158
|
|
|
159
159
|
interface STTEvents {
|
|
160
160
|
Transcript: [string];
|
|
161
|
+
Failed: [Buffer[]];
|
|
161
162
|
}
|
|
162
163
|
declare abstract class STT extends EventEmitter<STTEvents> {
|
|
163
164
|
logger?: Logger;
|
|
@@ -171,9 +172,28 @@ declare class MockSTT extends STT {
|
|
|
171
172
|
transcribe(): Promise<void>;
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
|
|
175
|
+
interface FallbackSTTOptions {
|
|
176
|
+
factories: Array<() => STT>;
|
|
177
|
+
}
|
|
178
|
+
declare class FallbackSTT extends STT {
|
|
179
|
+
private readonly options;
|
|
180
|
+
private stt;
|
|
181
|
+
private sttIndex;
|
|
182
|
+
constructor(options: FallbackSTTOptions);
|
|
183
|
+
transcribe(audioStream: Readable): void;
|
|
184
|
+
destroy(): void;
|
|
185
|
+
private startNextSTT;
|
|
186
|
+
private onTranscript;
|
|
187
|
+
private onFailed;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
interface TTSEvents {
|
|
191
|
+
Audio: [Buffer];
|
|
192
|
+
Failed: [string[]];
|
|
193
|
+
}
|
|
194
|
+
declare abstract class TTS extends EventEmitter<TTSEvents> {
|
|
175
195
|
logger?: Logger;
|
|
176
|
-
abstract speak(textStream: Readable):
|
|
196
|
+
abstract speak(textStream: Readable): void;
|
|
177
197
|
abstract cancel(): void;
|
|
178
198
|
protected log(...message: any[]): void;
|
|
179
199
|
destroy(): void;
|
|
@@ -186,6 +206,22 @@ declare class MockTTS extends TTS {
|
|
|
186
206
|
cancel(): void;
|
|
187
207
|
}
|
|
188
208
|
|
|
209
|
+
interface FallbackTTSOptions {
|
|
210
|
+
factories: Array<() => TTS>;
|
|
211
|
+
}
|
|
212
|
+
declare class FallbackTTS extends TTS {
|
|
213
|
+
private readonly options;
|
|
214
|
+
private tts;
|
|
215
|
+
private ttsIndex;
|
|
216
|
+
constructor(options: FallbackTTSOptions);
|
|
217
|
+
speak(textStream: Readable): void;
|
|
218
|
+
cancel(): void;
|
|
219
|
+
destroy(): void;
|
|
220
|
+
private startNextTTS;
|
|
221
|
+
private onAudio;
|
|
222
|
+
private onFailed;
|
|
223
|
+
}
|
|
224
|
+
|
|
189
225
|
interface MicdropConfig {
|
|
190
226
|
firstMessage?: string;
|
|
191
227
|
generateFirstMessage?: boolean;
|
|
@@ -215,16 +251,15 @@ declare class MicdropServer {
|
|
|
215
251
|
private onMute;
|
|
216
252
|
private onStartSpeaking;
|
|
217
253
|
private onStopSpeaking;
|
|
218
|
-
private
|
|
254
|
+
private onTranscriptSTT;
|
|
255
|
+
private onAudioTTS;
|
|
219
256
|
private sendFirstMessage;
|
|
220
257
|
answer(): void;
|
|
221
258
|
private _answer;
|
|
222
259
|
speak(message: string | Readable): void;
|
|
223
260
|
private _speak;
|
|
224
|
-
sendAudio(audio: Readable): void;
|
|
225
|
-
private _sendAudio;
|
|
226
261
|
}
|
|
227
262
|
|
|
228
263
|
declare function waitForParams<CallParams>(socket: WebSocket$1, validate: (params: any) => CallParams): Promise<CallParams>;
|
|
229
264
|
|
|
230
|
-
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, 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 Tool, handleError, waitForParams };
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
|
|
4
4
|
import WebSocket, { WebSocket as WebSocket$1 } from 'ws';
|
|
5
5
|
|
|
6
6
|
declare class Logger {
|
|
7
|
-
|
|
7
|
+
name: string;
|
|
8
8
|
constructor(name: string);
|
|
9
9
|
log(...message: any[]): void;
|
|
10
10
|
}
|
|
@@ -158,6 +158,7 @@ declare function handleError(socket: WebSocket, error: unknown): void;
|
|
|
158
158
|
|
|
159
159
|
interface STTEvents {
|
|
160
160
|
Transcript: [string];
|
|
161
|
+
Failed: [Buffer[]];
|
|
161
162
|
}
|
|
162
163
|
declare abstract class STT extends EventEmitter<STTEvents> {
|
|
163
164
|
logger?: Logger;
|
|
@@ -171,9 +172,28 @@ declare class MockSTT extends STT {
|
|
|
171
172
|
transcribe(): Promise<void>;
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
|
|
175
|
+
interface FallbackSTTOptions {
|
|
176
|
+
factories: Array<() => STT>;
|
|
177
|
+
}
|
|
178
|
+
declare class FallbackSTT extends STT {
|
|
179
|
+
private readonly options;
|
|
180
|
+
private stt;
|
|
181
|
+
private sttIndex;
|
|
182
|
+
constructor(options: FallbackSTTOptions);
|
|
183
|
+
transcribe(audioStream: Readable): void;
|
|
184
|
+
destroy(): void;
|
|
185
|
+
private startNextSTT;
|
|
186
|
+
private onTranscript;
|
|
187
|
+
private onFailed;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
interface TTSEvents {
|
|
191
|
+
Audio: [Buffer];
|
|
192
|
+
Failed: [string[]];
|
|
193
|
+
}
|
|
194
|
+
declare abstract class TTS extends EventEmitter<TTSEvents> {
|
|
175
195
|
logger?: Logger;
|
|
176
|
-
abstract speak(textStream: Readable):
|
|
196
|
+
abstract speak(textStream: Readable): void;
|
|
177
197
|
abstract cancel(): void;
|
|
178
198
|
protected log(...message: any[]): void;
|
|
179
199
|
destroy(): void;
|
|
@@ -186,6 +206,22 @@ declare class MockTTS extends TTS {
|
|
|
186
206
|
cancel(): void;
|
|
187
207
|
}
|
|
188
208
|
|
|
209
|
+
interface FallbackTTSOptions {
|
|
210
|
+
factories: Array<() => TTS>;
|
|
211
|
+
}
|
|
212
|
+
declare class FallbackTTS extends TTS {
|
|
213
|
+
private readonly options;
|
|
214
|
+
private tts;
|
|
215
|
+
private ttsIndex;
|
|
216
|
+
constructor(options: FallbackTTSOptions);
|
|
217
|
+
speak(textStream: Readable): void;
|
|
218
|
+
cancel(): void;
|
|
219
|
+
destroy(): void;
|
|
220
|
+
private startNextTTS;
|
|
221
|
+
private onAudio;
|
|
222
|
+
private onFailed;
|
|
223
|
+
}
|
|
224
|
+
|
|
189
225
|
interface MicdropConfig {
|
|
190
226
|
firstMessage?: string;
|
|
191
227
|
generateFirstMessage?: boolean;
|
|
@@ -215,16 +251,15 @@ declare class MicdropServer {
|
|
|
215
251
|
private onMute;
|
|
216
252
|
private onStartSpeaking;
|
|
217
253
|
private onStopSpeaking;
|
|
218
|
-
private
|
|
254
|
+
private onTranscriptSTT;
|
|
255
|
+
private onAudioTTS;
|
|
219
256
|
private sendFirstMessage;
|
|
220
257
|
answer(): void;
|
|
221
258
|
private _answer;
|
|
222
259
|
speak(message: string | Readable): void;
|
|
223
260
|
private _speak;
|
|
224
|
-
sendAudio(audio: Readable): void;
|
|
225
|
-
private _sendAudio;
|
|
226
261
|
}
|
|
227
262
|
|
|
228
263
|
declare function waitForParams<CallParams>(socket: WebSocket$1, validate: (params: any) => CallParams): Promise<CallParams>;
|
|
229
264
|
|
|
230
|
-
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, 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 Tool, handleError, waitForParams };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,8 @@ __export(index_exports, {
|
|
|
37
37
|
AUTO_SEMANTIC_TURN_PROMPT: () => AUTO_SEMANTIC_TURN_PROMPT,
|
|
38
38
|
AUTO_SEMANTIC_TURN_TOOL_NAME: () => AUTO_SEMANTIC_TURN_TOOL_NAME,
|
|
39
39
|
Agent: () => Agent,
|
|
40
|
+
FallbackSTT: () => FallbackSTT,
|
|
41
|
+
FallbackTTS: () => FallbackTTS,
|
|
40
42
|
Logger: () => Logger,
|
|
41
43
|
MicdropClientCommands: () => MicdropClientCommands,
|
|
42
44
|
MicdropError: () => MicdropError,
|
|
@@ -370,7 +372,7 @@ var MicdropServer = class {
|
|
|
370
372
|
this.onAudioChunk(message);
|
|
371
373
|
}
|
|
372
374
|
};
|
|
373
|
-
this.
|
|
375
|
+
this.onTranscriptSTT = async (transcript) => {
|
|
374
376
|
if (!this.config) return;
|
|
375
377
|
if (transcript === "") {
|
|
376
378
|
this.socket?.send("SkipAnswer" /* SkipAnswer */);
|
|
@@ -384,10 +386,16 @@ var MicdropServer = class {
|
|
|
384
386
|
this.answer();
|
|
385
387
|
}
|
|
386
388
|
};
|
|
389
|
+
this.onAudioTTS = (audio) => {
|
|
390
|
+
if (!this.socket) return;
|
|
391
|
+
this.log(`Send audio chunk (${audio.byteLength} bytes)`);
|
|
392
|
+
this.socket.send(audio);
|
|
393
|
+
};
|
|
387
394
|
this.socket = socket;
|
|
388
395
|
this.config = config;
|
|
389
396
|
this.log(`Call started`);
|
|
390
|
-
this.config.stt.on("Transcript", this.
|
|
397
|
+
this.config.stt.on("Transcript", this.onTranscriptSTT);
|
|
398
|
+
this.config.tts.on("Audio", this.onAudioTTS);
|
|
391
399
|
this.config.agent.on(
|
|
392
400
|
"Message",
|
|
393
401
|
(message) => this.socket?.send(
|
|
@@ -530,34 +538,7 @@ var MicdropServer = class {
|
|
|
530
538
|
} else {
|
|
531
539
|
textStream = message;
|
|
532
540
|
}
|
|
533
|
-
|
|
534
|
-
await this._sendAudio(audio);
|
|
535
|
-
}
|
|
536
|
-
sendAudio(audio) {
|
|
537
|
-
this.queueOperation(async () => {
|
|
538
|
-
await this._sendAudio(audio);
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
async _sendAudio(audio) {
|
|
542
|
-
if (!this.socket) return;
|
|
543
|
-
if (!audio.readable) {
|
|
544
|
-
this.log("Non readable audio, skipping", audio);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
await new Promise((resolve, reject) => {
|
|
548
|
-
audio.on("data", (chunk) => {
|
|
549
|
-
this.log(`Send audio chunk (${chunk.byteLength} bytes)`);
|
|
550
|
-
this.socket?.send(chunk);
|
|
551
|
-
});
|
|
552
|
-
audio.on("error", (error) => {
|
|
553
|
-
this.log("Error in audio stream", error);
|
|
554
|
-
reject(error);
|
|
555
|
-
});
|
|
556
|
-
audio.on("end", () => {
|
|
557
|
-
this.log("Audio stream ended");
|
|
558
|
-
resolve();
|
|
559
|
-
});
|
|
560
|
-
});
|
|
541
|
+
this.config.tts.speak(textStream);
|
|
561
542
|
}
|
|
562
543
|
};
|
|
563
544
|
|
|
@@ -586,12 +567,67 @@ var MockSTT = class extends STT {
|
|
|
586
567
|
}
|
|
587
568
|
};
|
|
588
569
|
|
|
570
|
+
// src/stt/FallbackSTT.ts
|
|
571
|
+
var import_stream3 = require("stream");
|
|
572
|
+
var FallbackSTT = class extends STT {
|
|
573
|
+
// Start at -1 because we need to increment it before using it
|
|
574
|
+
constructor(options) {
|
|
575
|
+
super();
|
|
576
|
+
this.options = options;
|
|
577
|
+
this.stt = null;
|
|
578
|
+
this.sttIndex = -1;
|
|
579
|
+
this.onTranscript = (transcript) => {
|
|
580
|
+
this.emit("Transcript", transcript);
|
|
581
|
+
};
|
|
582
|
+
this.onFailed = (chunks) => {
|
|
583
|
+
this.log("STT failed, trying next STT");
|
|
584
|
+
this.startNextSTT();
|
|
585
|
+
if (chunks.length > 0) {
|
|
586
|
+
this.log("Sending audio chunks again");
|
|
587
|
+
const stream = new import_stream3.PassThrough();
|
|
588
|
+
this.stt?.transcribe(stream);
|
|
589
|
+
chunks.forEach((chunk) => stream.write(chunk));
|
|
590
|
+
stream.end();
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
if (this.options.factories.length === 0) {
|
|
594
|
+
throw new Error("FallbackSTT: No factories provided");
|
|
595
|
+
}
|
|
596
|
+
this.startNextSTT();
|
|
597
|
+
}
|
|
598
|
+
transcribe(audioStream) {
|
|
599
|
+
this.stt?.transcribe(audioStream);
|
|
600
|
+
}
|
|
601
|
+
destroy() {
|
|
602
|
+
super.destroy();
|
|
603
|
+
this.stt?.destroy();
|
|
604
|
+
this.stt = null;
|
|
605
|
+
this.sttIndex = -1;
|
|
606
|
+
}
|
|
607
|
+
startNextSTT() {
|
|
608
|
+
this.sttIndex++;
|
|
609
|
+
if (this.sttIndex >= this.options.factories.length) {
|
|
610
|
+
this.sttIndex = 0;
|
|
611
|
+
}
|
|
612
|
+
this.stt?.destroy();
|
|
613
|
+
this.stt = this.options.factories[this.sttIndex]();
|
|
614
|
+
this.stt.on("Transcript", this.onTranscript);
|
|
615
|
+
this.stt.on("Failed", this.onFailed);
|
|
616
|
+
setTimeout(() => {
|
|
617
|
+
if (this.stt && this.logger) {
|
|
618
|
+
this.stt.logger = new Logger(this.stt.constructor.name);
|
|
619
|
+
}
|
|
620
|
+
}, 0);
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
|
|
589
624
|
// src/tts/MockTTS.ts
|
|
590
625
|
var fs = __toESM(require("fs"));
|
|
591
|
-
var
|
|
626
|
+
var import_stream4 = require("stream");
|
|
592
627
|
|
|
593
628
|
// src/tts/TTS.ts
|
|
594
|
-
var
|
|
629
|
+
var import_eventemitter33 = require("eventemitter3");
|
|
630
|
+
var TTS = class extends import_eventemitter33.EventEmitter {
|
|
595
631
|
log(...message) {
|
|
596
632
|
this.logger?.log(...message);
|
|
597
633
|
}
|
|
@@ -608,7 +644,7 @@ var MockTTS = class extends TTS {
|
|
|
608
644
|
this.audioFilePaths = audioFilePaths;
|
|
609
645
|
}
|
|
610
646
|
speak(textStream) {
|
|
611
|
-
const audioStream = new
|
|
647
|
+
const audioStream = new import_stream4.PassThrough();
|
|
612
648
|
textStream.once("data", async () => {
|
|
613
649
|
for (const filePath of this.audioFilePaths) {
|
|
614
650
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
@@ -624,6 +660,63 @@ var MockTTS = class extends TTS {
|
|
|
624
660
|
}
|
|
625
661
|
};
|
|
626
662
|
|
|
663
|
+
// src/tts/FallbackTTS.ts
|
|
664
|
+
var import_stream5 = require("stream");
|
|
665
|
+
var FallbackTTS = class extends TTS {
|
|
666
|
+
// Start at -1 because we need to increment it before using it
|
|
667
|
+
constructor(options) {
|
|
668
|
+
super();
|
|
669
|
+
this.options = options;
|
|
670
|
+
this.tts = null;
|
|
671
|
+
this.ttsIndex = -1;
|
|
672
|
+
this.onAudio = (audio) => {
|
|
673
|
+
this.emit("Audio", audio);
|
|
674
|
+
};
|
|
675
|
+
this.onFailed = (chunks) => {
|
|
676
|
+
this.log("TTS failed, trying next TTS");
|
|
677
|
+
this.startNextTTS();
|
|
678
|
+
if (chunks.length > 0) {
|
|
679
|
+
this.log("Sending text chunks again");
|
|
680
|
+
const stream = new import_stream5.PassThrough();
|
|
681
|
+
this.tts?.speak(stream);
|
|
682
|
+
chunks.forEach((chunk) => stream.write(chunk));
|
|
683
|
+
stream.end();
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
if (this.options.factories.length === 0) {
|
|
687
|
+
throw new Error("FallbackTTS: No factories provided");
|
|
688
|
+
}
|
|
689
|
+
this.startNextTTS();
|
|
690
|
+
}
|
|
691
|
+
speak(textStream) {
|
|
692
|
+
this.tts?.speak(textStream);
|
|
693
|
+
}
|
|
694
|
+
cancel() {
|
|
695
|
+
this.tts?.cancel();
|
|
696
|
+
}
|
|
697
|
+
destroy() {
|
|
698
|
+
super.destroy();
|
|
699
|
+
this.tts?.destroy();
|
|
700
|
+
this.tts = null;
|
|
701
|
+
this.ttsIndex = -1;
|
|
702
|
+
}
|
|
703
|
+
startNextTTS() {
|
|
704
|
+
this.ttsIndex++;
|
|
705
|
+
if (this.ttsIndex >= this.options.factories.length) {
|
|
706
|
+
this.ttsIndex = 0;
|
|
707
|
+
}
|
|
708
|
+
this.tts?.destroy();
|
|
709
|
+
this.tts = this.options.factories[this.ttsIndex]();
|
|
710
|
+
this.tts.on("Audio", this.onAudio);
|
|
711
|
+
this.tts.on("Failed", this.onFailed);
|
|
712
|
+
setTimeout(() => {
|
|
713
|
+
if (this.tts && this.logger) {
|
|
714
|
+
this.tts.logger = new Logger(this.tts.constructor.name);
|
|
715
|
+
}
|
|
716
|
+
}, 0);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
|
|
627
720
|
// src/waitForParams.ts
|
|
628
721
|
async function waitForParams(socket, validate) {
|
|
629
722
|
return new Promise((resolve, reject) => {
|
|
@@ -652,6 +745,8 @@ async function waitForParams(socket, validate) {
|
|
|
652
745
|
AUTO_SEMANTIC_TURN_PROMPT,
|
|
653
746
|
AUTO_SEMANTIC_TURN_TOOL_NAME,
|
|
654
747
|
Agent,
|
|
748
|
+
FallbackSTT,
|
|
749
|
+
FallbackTTS,
|
|
655
750
|
Logger,
|
|
656
751
|
MicdropClientCommands,
|
|
657
752
|
MicdropError,
|
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/tts/MockTTS.ts","../src/tts/TTS.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(private readonly name: string) {}\n\n log(...message: any[]) {\n const time = process.uptime().toFixed(3)\n console.log(`[${this.name} ${time}]`, ...message)\n }\n}\n","import { Duplex, PassThrough, Readable } from 'stream'\nimport { WebSocket } from 'ws'\nimport type { Agent } from './agent'\nimport { Logger } from './Logger'\nimport type { STT } from './stt'\nimport type { TTS } from './tts'\nimport {\n MicdropCallSummary,\n MicdropClientCommands,\n MicdropConversationItem,\n MicdropServerCommands,\n} from './types'\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n onEnd?(call: MicdropCallSummary): void\n}\n\nexport class MicdropServer {\n public socket: WebSocket | null = null\n public config: MicdropConfig | null = null\n public logger?: Logger\n\n private startTime = Date.now()\n private lastMessageSpeeched?: MicdropConversationItem\n\n // 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.onTranscript)\n\n // Setup agent\n this.config.agent.on('Message', (message) =>\n this.socket?.send(\n `${MicdropServerCommands.Message} ${JSON.stringify(message)}`\n )\n )\n this.config.agent.on('CancelLastUserMessage', () =>\n this.socket?.send(MicdropServerCommands.CancelLastUserMessage)\n )\n this.config.agent.on('SkipAnswer', () =>\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n )\n this.config.agent.on('EndCall', () =>\n this.socket?.send(MicdropServerCommands.EndCall)\n )\n this.config.agent.on('ToolCall', (toolCall) =>\n this.socket?.send(\n `${MicdropServerCommands.ToolCall} ${JSON.stringify(toolCall)}`\n )\n )\n\n // Assistant speaks first\n this.sendFirstMessage()\n\n // Listen to events\n socket.on('close', this.onClose)\n socket.on('message', this.onMessage)\n }\n\n private log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n private 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 onTranscript = 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 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 const audio = this.config.tts.speak(textStream)\n\n // Send audio to client\n await this._sendAudio(audio)\n }\n\n public sendAudio(audio: Readable) {\n this.queueOperation(async () => {\n await this._sendAudio(audio)\n })\n }\n\n private async _sendAudio(audio: Readable) {\n if (!this.socket) return\n if (!audio.readable) {\n this.log('Non readable audio, skipping', audio)\n return\n }\n\n // Wait for audio stream to complete\n await new Promise<void>((resolve, reject) => {\n audio.on('data', (chunk) => {\n this.log(`Send audio chunk (${chunk.byteLength} bytes)`)\n this.socket?.send(chunk)\n })\n audio.on('error', (error) => {\n this.log('Error in audio stream', error)\n reject(error)\n })\n audio.on('end', () => {\n this.log('Audio stream ended')\n resolve()\n })\n })\n }\n}\n","export enum MicdropClientCommands {\n StartSpeaking = 'StartSpeaking',\n StopSpeaking = 'StopSpeaking',\n Mute = 'Mute',\n}\n\nexport enum MicdropServerCommands {\n Message = 'Message',\n CancelLastUserMessage = 'CancelLastUserMessage',\n SkipAnswer = 'SkipAnswer',\n EndCall = 'EndCall',\n ToolCall = 'ToolCall',\n}\n\nexport interface MicdropCallSummary {\n conversation: MicdropConversation\n duration: number\n}\n\nexport type MicdropConversationItem =\n | MicdropConversationMessage\n | MicdropConversationToolCall\n | MicdropConversationToolResult\n\nexport type MicdropConversation = Array<MicdropConversationItem>\n\nexport type MicdropAnswerMetadata = {\n [key: string]: any\n}\n\nexport interface MicdropConversationMessage<\n Data extends MicdropAnswerMetadata = MicdropAnswerMetadata,\n> {\n role: 'system' | 'user' | 'assistant'\n content: string\n metadata?: Data\n}\n\nexport interface MicdropConversationToolCall {\n role: 'tool_call'\n toolCallId: string\n toolName: string\n parameters: string\n}\n\nexport interface MicdropConversationToolResult {\n role: 'tool_result'\n toolCallId: string\n toolName: string\n output: string\n}\n\nexport interface MicdropToolCall {\n name: string\n parameters: any\n output: any\n}\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport interface STTEvents {\n Transcript: [string]\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 * as fs from 'fs'\nimport { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\n\nexport class MockTTS extends TTS {\n constructor(private audioFilePaths: string[]) {\n super()\n }\n\n speak(textStream: Readable) {\n const audioStream = new PassThrough()\n textStream.once('data', async () => {\n for (const filePath of this.audioFilePaths) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n const audioBuffer = fs.readFileSync(filePath)\n this.log(`Loaded chunk (${audioBuffer.length} bytes)`)\n audioStream.write(audioBuffer)\n }\n audioStream.end()\n })\n return audioStream\n }\n\n cancel() {}\n}\n","import { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport abstract class TTS {\n public logger?: Logger\n\n abstract speak(textStream: Readable): Readable\n abstract cancel(): void\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.cancel()\n }\n}\n","import { WebSocket } from 'ws'\nimport { MicdropError, MicdropErrorCode } from './errors'\n\nexport async function waitForParams<CallParams>(\n socket: WebSocket,\n validate: (params: any) => CallParams\n): Promise<CallParams> {\n return new Promise<CallParams>((resolve, reject) => {\n // Handle timeout\n const timeout = setTimeout(() => {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Missing params'))\n }, 3000)\n\n const onParams = (payload: string) => {\n // Clear timeout and listener\n clearTimeout(timeout)\n socket.off('message', onParams)\n\n try {\n // Parse JSON payload\n const params = validate(JSON.parse(payload))\n resolve(params)\n } catch (error) {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Invalid params'))\n }\n }\n\n // Listen for params\n socket.on('message', onParams)\n })\n}\n"],"mappings":"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","names":["MicdropErrorCode","import_stream","MicdropClientCommands","MicdropServerCommands","import_eventemitter3","import_stream"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/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"]}
|
package/dist/index.mjs
CHANGED
|
@@ -315,7 +315,7 @@ var MicdropServer = class {
|
|
|
315
315
|
this.onAudioChunk(message);
|
|
316
316
|
}
|
|
317
317
|
};
|
|
318
|
-
this.
|
|
318
|
+
this.onTranscriptSTT = async (transcript) => {
|
|
319
319
|
if (!this.config) return;
|
|
320
320
|
if (transcript === "") {
|
|
321
321
|
this.socket?.send("SkipAnswer" /* SkipAnswer */);
|
|
@@ -329,10 +329,16 @@ var MicdropServer = class {
|
|
|
329
329
|
this.answer();
|
|
330
330
|
}
|
|
331
331
|
};
|
|
332
|
+
this.onAudioTTS = (audio) => {
|
|
333
|
+
if (!this.socket) return;
|
|
334
|
+
this.log(`Send audio chunk (${audio.byteLength} bytes)`);
|
|
335
|
+
this.socket.send(audio);
|
|
336
|
+
};
|
|
332
337
|
this.socket = socket;
|
|
333
338
|
this.config = config;
|
|
334
339
|
this.log(`Call started`);
|
|
335
|
-
this.config.stt.on("Transcript", this.
|
|
340
|
+
this.config.stt.on("Transcript", this.onTranscriptSTT);
|
|
341
|
+
this.config.tts.on("Audio", this.onAudioTTS);
|
|
336
342
|
this.config.agent.on(
|
|
337
343
|
"Message",
|
|
338
344
|
(message) => this.socket?.send(
|
|
@@ -475,34 +481,7 @@ var MicdropServer = class {
|
|
|
475
481
|
} else {
|
|
476
482
|
textStream = message;
|
|
477
483
|
}
|
|
478
|
-
|
|
479
|
-
await this._sendAudio(audio);
|
|
480
|
-
}
|
|
481
|
-
sendAudio(audio) {
|
|
482
|
-
this.queueOperation(async () => {
|
|
483
|
-
await this._sendAudio(audio);
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
async _sendAudio(audio) {
|
|
487
|
-
if (!this.socket) return;
|
|
488
|
-
if (!audio.readable) {
|
|
489
|
-
this.log("Non readable audio, skipping", audio);
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
await new Promise((resolve, reject) => {
|
|
493
|
-
audio.on("data", (chunk) => {
|
|
494
|
-
this.log(`Send audio chunk (${chunk.byteLength} bytes)`);
|
|
495
|
-
this.socket?.send(chunk);
|
|
496
|
-
});
|
|
497
|
-
audio.on("error", (error) => {
|
|
498
|
-
this.log("Error in audio stream", error);
|
|
499
|
-
reject(error);
|
|
500
|
-
});
|
|
501
|
-
audio.on("end", () => {
|
|
502
|
-
this.log("Audio stream ended");
|
|
503
|
-
resolve();
|
|
504
|
-
});
|
|
505
|
-
});
|
|
484
|
+
this.config.tts.speak(textStream);
|
|
506
485
|
}
|
|
507
486
|
};
|
|
508
487
|
|
|
@@ -531,12 +510,67 @@ var MockSTT = class extends STT {
|
|
|
531
510
|
}
|
|
532
511
|
};
|
|
533
512
|
|
|
513
|
+
// src/stt/FallbackSTT.ts
|
|
514
|
+
import { PassThrough as PassThrough3 } from "stream";
|
|
515
|
+
var FallbackSTT = class extends STT {
|
|
516
|
+
// Start at -1 because we need to increment it before using it
|
|
517
|
+
constructor(options) {
|
|
518
|
+
super();
|
|
519
|
+
this.options = options;
|
|
520
|
+
this.stt = null;
|
|
521
|
+
this.sttIndex = -1;
|
|
522
|
+
this.onTranscript = (transcript) => {
|
|
523
|
+
this.emit("Transcript", transcript);
|
|
524
|
+
};
|
|
525
|
+
this.onFailed = (chunks) => {
|
|
526
|
+
this.log("STT failed, trying next STT");
|
|
527
|
+
this.startNextSTT();
|
|
528
|
+
if (chunks.length > 0) {
|
|
529
|
+
this.log("Sending audio chunks again");
|
|
530
|
+
const stream = new PassThrough3();
|
|
531
|
+
this.stt?.transcribe(stream);
|
|
532
|
+
chunks.forEach((chunk) => stream.write(chunk));
|
|
533
|
+
stream.end();
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
if (this.options.factories.length === 0) {
|
|
537
|
+
throw new Error("FallbackSTT: No factories provided");
|
|
538
|
+
}
|
|
539
|
+
this.startNextSTT();
|
|
540
|
+
}
|
|
541
|
+
transcribe(audioStream) {
|
|
542
|
+
this.stt?.transcribe(audioStream);
|
|
543
|
+
}
|
|
544
|
+
destroy() {
|
|
545
|
+
super.destroy();
|
|
546
|
+
this.stt?.destroy();
|
|
547
|
+
this.stt = null;
|
|
548
|
+
this.sttIndex = -1;
|
|
549
|
+
}
|
|
550
|
+
startNextSTT() {
|
|
551
|
+
this.sttIndex++;
|
|
552
|
+
if (this.sttIndex >= this.options.factories.length) {
|
|
553
|
+
this.sttIndex = 0;
|
|
554
|
+
}
|
|
555
|
+
this.stt?.destroy();
|
|
556
|
+
this.stt = this.options.factories[this.sttIndex]();
|
|
557
|
+
this.stt.on("Transcript", this.onTranscript);
|
|
558
|
+
this.stt.on("Failed", this.onFailed);
|
|
559
|
+
setTimeout(() => {
|
|
560
|
+
if (this.stt && this.logger) {
|
|
561
|
+
this.stt.logger = new Logger(this.stt.constructor.name);
|
|
562
|
+
}
|
|
563
|
+
}, 0);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
534
567
|
// src/tts/MockTTS.ts
|
|
535
568
|
import * as fs from "fs";
|
|
536
|
-
import { PassThrough as
|
|
569
|
+
import { PassThrough as PassThrough4 } from "stream";
|
|
537
570
|
|
|
538
571
|
// src/tts/TTS.ts
|
|
539
|
-
|
|
572
|
+
import { EventEmitter as EventEmitter3 } from "eventemitter3";
|
|
573
|
+
var TTS = class extends EventEmitter3 {
|
|
540
574
|
log(...message) {
|
|
541
575
|
this.logger?.log(...message);
|
|
542
576
|
}
|
|
@@ -553,7 +587,7 @@ var MockTTS = class extends TTS {
|
|
|
553
587
|
this.audioFilePaths = audioFilePaths;
|
|
554
588
|
}
|
|
555
589
|
speak(textStream) {
|
|
556
|
-
const audioStream = new
|
|
590
|
+
const audioStream = new PassThrough4();
|
|
557
591
|
textStream.once("data", async () => {
|
|
558
592
|
for (const filePath of this.audioFilePaths) {
|
|
559
593
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
@@ -569,6 +603,63 @@ var MockTTS = class extends TTS {
|
|
|
569
603
|
}
|
|
570
604
|
};
|
|
571
605
|
|
|
606
|
+
// src/tts/FallbackTTS.ts
|
|
607
|
+
import { PassThrough as PassThrough5 } from "stream";
|
|
608
|
+
var FallbackTTS = class extends TTS {
|
|
609
|
+
// Start at -1 because we need to increment it before using it
|
|
610
|
+
constructor(options) {
|
|
611
|
+
super();
|
|
612
|
+
this.options = options;
|
|
613
|
+
this.tts = null;
|
|
614
|
+
this.ttsIndex = -1;
|
|
615
|
+
this.onAudio = (audio) => {
|
|
616
|
+
this.emit("Audio", audio);
|
|
617
|
+
};
|
|
618
|
+
this.onFailed = (chunks) => {
|
|
619
|
+
this.log("TTS failed, trying next TTS");
|
|
620
|
+
this.startNextTTS();
|
|
621
|
+
if (chunks.length > 0) {
|
|
622
|
+
this.log("Sending text chunks again");
|
|
623
|
+
const stream = new PassThrough5();
|
|
624
|
+
this.tts?.speak(stream);
|
|
625
|
+
chunks.forEach((chunk) => stream.write(chunk));
|
|
626
|
+
stream.end();
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
if (this.options.factories.length === 0) {
|
|
630
|
+
throw new Error("FallbackTTS: No factories provided");
|
|
631
|
+
}
|
|
632
|
+
this.startNextTTS();
|
|
633
|
+
}
|
|
634
|
+
speak(textStream) {
|
|
635
|
+
this.tts?.speak(textStream);
|
|
636
|
+
}
|
|
637
|
+
cancel() {
|
|
638
|
+
this.tts?.cancel();
|
|
639
|
+
}
|
|
640
|
+
destroy() {
|
|
641
|
+
super.destroy();
|
|
642
|
+
this.tts?.destroy();
|
|
643
|
+
this.tts = null;
|
|
644
|
+
this.ttsIndex = -1;
|
|
645
|
+
}
|
|
646
|
+
startNextTTS() {
|
|
647
|
+
this.ttsIndex++;
|
|
648
|
+
if (this.ttsIndex >= this.options.factories.length) {
|
|
649
|
+
this.ttsIndex = 0;
|
|
650
|
+
}
|
|
651
|
+
this.tts?.destroy();
|
|
652
|
+
this.tts = this.options.factories[this.ttsIndex]();
|
|
653
|
+
this.tts.on("Audio", this.onAudio);
|
|
654
|
+
this.tts.on("Failed", this.onFailed);
|
|
655
|
+
setTimeout(() => {
|
|
656
|
+
if (this.tts && this.logger) {
|
|
657
|
+
this.tts.logger = new Logger(this.tts.constructor.name);
|
|
658
|
+
}
|
|
659
|
+
}, 0);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
|
|
572
663
|
// src/waitForParams.ts
|
|
573
664
|
async function waitForParams(socket, validate) {
|
|
574
665
|
return new Promise((resolve, reject) => {
|
|
@@ -596,6 +687,8 @@ export {
|
|
|
596
687
|
AUTO_SEMANTIC_TURN_PROMPT,
|
|
597
688
|
AUTO_SEMANTIC_TURN_TOOL_NAME,
|
|
598
689
|
Agent,
|
|
690
|
+
FallbackSTT,
|
|
691
|
+
FallbackTTS,
|
|
599
692
|
Logger,
|
|
600
693
|
MicdropClientCommands,
|
|
601
694
|
MicdropError,
|
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/tts/MockTTS.ts","../src/tts/TTS.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(private readonly name: string) {}\n\n log(...message: any[]) {\n const time = process.uptime().toFixed(3)\n console.log(`[${this.name} ${time}]`, ...message)\n }\n}\n","import { Duplex, PassThrough, Readable } from 'stream'\nimport { WebSocket } from 'ws'\nimport type { Agent } from './agent'\nimport { Logger } from './Logger'\nimport type { STT } from './stt'\nimport type { TTS } from './tts'\nimport {\n MicdropCallSummary,\n MicdropClientCommands,\n MicdropConversationItem,\n MicdropServerCommands,\n} from './types'\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n onEnd?(call: MicdropCallSummary): void\n}\n\nexport class MicdropServer {\n public socket: WebSocket | null = null\n public config: MicdropConfig | null = null\n public logger?: Logger\n\n private startTime = Date.now()\n private lastMessageSpeeched?: MicdropConversationItem\n\n // 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.onTranscript)\n\n // Setup agent\n this.config.agent.on('Message', (message) =>\n this.socket?.send(\n `${MicdropServerCommands.Message} ${JSON.stringify(message)}`\n )\n )\n this.config.agent.on('CancelLastUserMessage', () =>\n this.socket?.send(MicdropServerCommands.CancelLastUserMessage)\n )\n this.config.agent.on('SkipAnswer', () =>\n this.socket?.send(MicdropServerCommands.SkipAnswer)\n )\n this.config.agent.on('EndCall', () =>\n this.socket?.send(MicdropServerCommands.EndCall)\n )\n this.config.agent.on('ToolCall', (toolCall) =>\n this.socket?.send(\n `${MicdropServerCommands.ToolCall} ${JSON.stringify(toolCall)}`\n )\n )\n\n // Assistant speaks first\n this.sendFirstMessage()\n\n // Listen to events\n socket.on('close', this.onClose)\n socket.on('message', this.onMessage)\n }\n\n private log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n private 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 onTranscript = 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 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 const audio = this.config.tts.speak(textStream)\n\n // Send audio to client\n await this._sendAudio(audio)\n }\n\n public sendAudio(audio: Readable) {\n this.queueOperation(async () => {\n await this._sendAudio(audio)\n })\n }\n\n private async _sendAudio(audio: Readable) {\n if (!this.socket) return\n if (!audio.readable) {\n this.log('Non readable audio, skipping', audio)\n return\n }\n\n // Wait for audio stream to complete\n await new Promise<void>((resolve, reject) => {\n audio.on('data', (chunk) => {\n this.log(`Send audio chunk (${chunk.byteLength} bytes)`)\n this.socket?.send(chunk)\n })\n audio.on('error', (error) => {\n this.log('Error in audio stream', error)\n reject(error)\n })\n audio.on('end', () => {\n this.log('Audio stream ended')\n resolve()\n })\n })\n }\n}\n","export enum MicdropClientCommands {\n StartSpeaking = 'StartSpeaking',\n StopSpeaking = 'StopSpeaking',\n Mute = 'Mute',\n}\n\nexport enum MicdropServerCommands {\n Message = 'Message',\n CancelLastUserMessage = 'CancelLastUserMessage',\n SkipAnswer = 'SkipAnswer',\n EndCall = 'EndCall',\n ToolCall = 'ToolCall',\n}\n\nexport interface MicdropCallSummary {\n conversation: MicdropConversation\n duration: number\n}\n\nexport type MicdropConversationItem =\n | MicdropConversationMessage\n | MicdropConversationToolCall\n | MicdropConversationToolResult\n\nexport type MicdropConversation = Array<MicdropConversationItem>\n\nexport type MicdropAnswerMetadata = {\n [key: string]: any\n}\n\nexport interface MicdropConversationMessage<\n Data extends MicdropAnswerMetadata = MicdropAnswerMetadata,\n> {\n role: 'system' | 'user' | 'assistant'\n content: string\n metadata?: Data\n}\n\nexport interface MicdropConversationToolCall {\n role: 'tool_call'\n toolCallId: string\n toolName: string\n parameters: string\n}\n\nexport interface MicdropConversationToolResult {\n role: 'tool_result'\n toolCallId: string\n toolName: string\n output: string\n}\n\nexport interface MicdropToolCall {\n name: string\n parameters: any\n output: any\n}\n\nexport type DeepPartial<T> = T extends object\n ? {\n [P in keyof T]?: DeepPartial<T[P]>\n }\n : T\n","import { EventEmitter } from 'eventemitter3'\nimport { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport interface STTEvents {\n Transcript: [string]\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 * as fs from 'fs'\nimport { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\n\nexport class MockTTS extends TTS {\n constructor(private audioFilePaths: string[]) {\n super()\n }\n\n speak(textStream: Readable) {\n const audioStream = new PassThrough()\n textStream.once('data', async () => {\n for (const filePath of this.audioFilePaths) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n const audioBuffer = fs.readFileSync(filePath)\n this.log(`Loaded chunk (${audioBuffer.length} bytes)`)\n audioStream.write(audioBuffer)\n }\n audioStream.end()\n })\n return audioStream\n }\n\n cancel() {}\n}\n","import { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport abstract class TTS {\n public logger?: Logger\n\n abstract speak(textStream: Readable): Readable\n abstract cancel(): void\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.cancel()\n }\n}\n","import { WebSocket } from 'ws'\nimport { MicdropError, MicdropErrorCode } from './errors'\n\nexport async function waitForParams<CallParams>(\n socket: WebSocket,\n validate: (params: any) => CallParams\n): Promise<CallParams> {\n return new Promise<CallParams>((resolve, reject) => {\n // Handle timeout\n const timeout = setTimeout(() => {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Missing params'))\n }, 3000)\n\n const onParams = (payload: string) => {\n // Clear timeout and listener\n clearTimeout(timeout)\n socket.off('message', onParams)\n\n try {\n // Parse JSON payload\n const params = validate(JSON.parse(payload))\n resolve(params)\n } catch (error) {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Invalid params'))\n }\n }\n\n // Listen for params\n socket.on('message', onParams)\n })\n}\n"],"mappings":"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","names":["MicdropErrorCode","PassThrough","MicdropClientCommands","MicdropServerCommands","PassThrough","EventEmitter","PassThrough","PassThrough"]}
|
|
1
|
+
{"version":3,"sources":["../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/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"]}
|
package/package.json
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@micdrop/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
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
|
-
},
|
|
17
10
|
"files": [
|
|
18
11
|
"dist"
|
|
19
12
|
],
|
|
@@ -42,5 +35,11 @@
|
|
|
42
35
|
"tsup": "^8.3.6",
|
|
43
36
|
"typescript": "^5.7.3",
|
|
44
37
|
"zod": "^4.0.5"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"dev": "tsup --watch",
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
45
44
|
}
|
|
46
|
-
}
|
|
45
|
+
}
|