@micdrop/server 2.0.12 → 2.1.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/dist/index.d.mts +6 -27
- package/dist/index.d.ts +6 -27
- package/dist/index.js +29 -118
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29 -114
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -4
package/dist/index.d.mts
CHANGED
|
@@ -145,10 +145,6 @@ declare class MockAgent extends Agent {
|
|
|
145
145
|
cancel(): void;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
declare function convertToPCM(audioStream: Readable, sampleRate?: number, bitDepth?: number): PassThrough;
|
|
149
|
-
declare function convertToOpus(audioStream: Readable, sampleRate?: number): PassThrough;
|
|
150
|
-
declare function convertPCMToOpus(audioStream: Readable, sampleRate?: number): PassThrough;
|
|
151
|
-
|
|
152
148
|
declare enum MicdropErrorCode {
|
|
153
149
|
BadRequest = 4400,
|
|
154
150
|
Unauthorized = 4401,
|
|
@@ -160,38 +156,19 @@ declare class MicdropError extends Error {
|
|
|
160
156
|
}
|
|
161
157
|
declare function handleError(socket: WebSocket, error: unknown): void;
|
|
162
158
|
|
|
163
|
-
declare const MIME_TYPE_TO_EXTENSION: {
|
|
164
|
-
readonly 'audio/wav': "wav";
|
|
165
|
-
readonly 'audio/ogg': "ogg";
|
|
166
|
-
readonly 'audio/mpeg': "mp3";
|
|
167
|
-
readonly 'audio/webm': "webm";
|
|
168
|
-
readonly 'audio/mp4': "mp4";
|
|
169
|
-
readonly 'audio/flac': "flac";
|
|
170
|
-
};
|
|
171
159
|
interface STTEvents {
|
|
172
160
|
Transcript: [string];
|
|
173
161
|
}
|
|
174
162
|
declare abstract class STT extends EventEmitter<STTEvents> {
|
|
175
|
-
protected mimeType?: keyof typeof MIME_TYPE_TO_EXTENSION;
|
|
176
163
|
logger?: Logger;
|
|
177
|
-
transcribe(audioStream: Readable): void;
|
|
164
|
+
abstract transcribe(audioStream: Readable): void;
|
|
178
165
|
protected log(...message: any[]): void;
|
|
179
166
|
destroy(): void;
|
|
180
|
-
protected get extension(): string;
|
|
181
|
-
private detectMimeType;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Abstract class for STT, converting stream to file before transcribing
|
|
186
|
-
*/
|
|
187
|
-
declare abstract class FileSTT extends STT {
|
|
188
|
-
abstract transcribeFile(file: File): Promise<string>;
|
|
189
|
-
transcribe(audioStream: Readable): void;
|
|
190
167
|
}
|
|
191
168
|
|
|
192
|
-
declare class MockSTT extends
|
|
169
|
+
declare class MockSTT extends STT {
|
|
193
170
|
private i;
|
|
194
|
-
|
|
171
|
+
transcribe(): Promise<void>;
|
|
195
172
|
}
|
|
196
173
|
|
|
197
174
|
declare abstract class TTS {
|
|
@@ -226,6 +203,7 @@ declare class MicdropServer {
|
|
|
226
203
|
private operationQueue;
|
|
227
204
|
private isProcessingQueue;
|
|
228
205
|
private currentUserStream?;
|
|
206
|
+
private userSpeechChunks;
|
|
229
207
|
constructor(socket: WebSocket$1, config: MicdropConfig);
|
|
230
208
|
private log;
|
|
231
209
|
private processQueue;
|
|
@@ -233,6 +211,7 @@ declare class MicdropServer {
|
|
|
233
211
|
cancel(): void;
|
|
234
212
|
private onClose;
|
|
235
213
|
private onMessage;
|
|
214
|
+
private onAudioChunk;
|
|
236
215
|
private onMute;
|
|
237
216
|
private onStartSpeaking;
|
|
238
217
|
private onStopSpeaking;
|
|
@@ -248,4 +227,4 @@ declare class MicdropServer {
|
|
|
248
227
|
|
|
249
228
|
declare function waitForParams<CallParams>(socket: WebSocket$1, validate: (params: any) => CallParams): Promise<CallParams>;
|
|
250
229
|
|
|
251
|
-
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,
|
|
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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -145,10 +145,6 @@ declare class MockAgent extends Agent {
|
|
|
145
145
|
cancel(): void;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
declare function convertToPCM(audioStream: Readable, sampleRate?: number, bitDepth?: number): PassThrough;
|
|
149
|
-
declare function convertToOpus(audioStream: Readable, sampleRate?: number): PassThrough;
|
|
150
|
-
declare function convertPCMToOpus(audioStream: Readable, sampleRate?: number): PassThrough;
|
|
151
|
-
|
|
152
148
|
declare enum MicdropErrorCode {
|
|
153
149
|
BadRequest = 4400,
|
|
154
150
|
Unauthorized = 4401,
|
|
@@ -160,38 +156,19 @@ declare class MicdropError extends Error {
|
|
|
160
156
|
}
|
|
161
157
|
declare function handleError(socket: WebSocket, error: unknown): void;
|
|
162
158
|
|
|
163
|
-
declare const MIME_TYPE_TO_EXTENSION: {
|
|
164
|
-
readonly 'audio/wav': "wav";
|
|
165
|
-
readonly 'audio/ogg': "ogg";
|
|
166
|
-
readonly 'audio/mpeg': "mp3";
|
|
167
|
-
readonly 'audio/webm': "webm";
|
|
168
|
-
readonly 'audio/mp4': "mp4";
|
|
169
|
-
readonly 'audio/flac': "flac";
|
|
170
|
-
};
|
|
171
159
|
interface STTEvents {
|
|
172
160
|
Transcript: [string];
|
|
173
161
|
}
|
|
174
162
|
declare abstract class STT extends EventEmitter<STTEvents> {
|
|
175
|
-
protected mimeType?: keyof typeof MIME_TYPE_TO_EXTENSION;
|
|
176
163
|
logger?: Logger;
|
|
177
|
-
transcribe(audioStream: Readable): void;
|
|
164
|
+
abstract transcribe(audioStream: Readable): void;
|
|
178
165
|
protected log(...message: any[]): void;
|
|
179
166
|
destroy(): void;
|
|
180
|
-
protected get extension(): string;
|
|
181
|
-
private detectMimeType;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Abstract class for STT, converting stream to file before transcribing
|
|
186
|
-
*/
|
|
187
|
-
declare abstract class FileSTT extends STT {
|
|
188
|
-
abstract transcribeFile(file: File): Promise<string>;
|
|
189
|
-
transcribe(audioStream: Readable): void;
|
|
190
167
|
}
|
|
191
168
|
|
|
192
|
-
declare class MockSTT extends
|
|
169
|
+
declare class MockSTT extends STT {
|
|
193
170
|
private i;
|
|
194
|
-
|
|
171
|
+
transcribe(): Promise<void>;
|
|
195
172
|
}
|
|
196
173
|
|
|
197
174
|
declare abstract class TTS {
|
|
@@ -226,6 +203,7 @@ declare class MicdropServer {
|
|
|
226
203
|
private operationQueue;
|
|
227
204
|
private isProcessingQueue;
|
|
228
205
|
private currentUserStream?;
|
|
206
|
+
private userSpeechChunks;
|
|
229
207
|
constructor(socket: WebSocket$1, config: MicdropConfig);
|
|
230
208
|
private log;
|
|
231
209
|
private processQueue;
|
|
@@ -233,6 +211,7 @@ declare class MicdropServer {
|
|
|
233
211
|
cancel(): void;
|
|
234
212
|
private onClose;
|
|
235
213
|
private onMessage;
|
|
214
|
+
private onAudioChunk;
|
|
236
215
|
private onMute;
|
|
237
216
|
private onStartSpeaking;
|
|
238
217
|
private onStopSpeaking;
|
|
@@ -248,4 +227,4 @@ declare class MicdropServer {
|
|
|
248
227
|
|
|
249
228
|
declare function waitForParams<CallParams>(socket: WebSocket$1, validate: (params: any) => CallParams): Promise<CallParams>;
|
|
250
229
|
|
|
251
|
-
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,
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,6 @@ __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
|
-
FileSTT: () => FileSTT,
|
|
41
40
|
Logger: () => Logger,
|
|
42
41
|
MicdropClientCommands: () => MicdropClientCommands,
|
|
43
42
|
MicdropError: () => MicdropError,
|
|
@@ -49,9 +48,6 @@ __export(index_exports, {
|
|
|
49
48
|
MockTTS: () => MockTTS,
|
|
50
49
|
STT: () => STT,
|
|
51
50
|
TTS: () => TTS,
|
|
52
|
-
convertPCMToOpus: () => convertPCMToOpus,
|
|
53
|
-
convertToOpus: () => convertToOpus,
|
|
54
|
-
convertToPCM: () => convertToPCM,
|
|
55
51
|
handleError: () => handleError,
|
|
56
52
|
waitForParams: () => waitForParams
|
|
57
53
|
});
|
|
@@ -276,41 +272,6 @@ var MockAgent = class extends Agent {
|
|
|
276
272
|
}
|
|
277
273
|
};
|
|
278
274
|
|
|
279
|
-
// src/audio-convert.ts
|
|
280
|
-
var import_ffmpeg = __toESM(require("@ffmpeg-installer/ffmpeg"));
|
|
281
|
-
var import_fluent_ffmpeg = __toESM(require("fluent-ffmpeg"));
|
|
282
|
-
var import_stream2 = require("stream");
|
|
283
|
-
import_fluent_ffmpeg.default.setFfmpegPath(import_ffmpeg.default.path);
|
|
284
|
-
function convertToPCM(audioStream, sampleRate = 16e3, bitDepth = 16) {
|
|
285
|
-
const pcmStream = new import_stream2.PassThrough();
|
|
286
|
-
(0, import_fluent_ffmpeg.default)(audioStream).audioChannels(1).audioFrequency(sampleRate).audioCodec(`pcm_s${bitDepth}le`).format(`s${bitDepth}le`).on("error", (error) => {
|
|
287
|
-
console.error("Error converting audio stream:", error.message);
|
|
288
|
-
}).pipe(pcmStream);
|
|
289
|
-
return pcmStream;
|
|
290
|
-
}
|
|
291
|
-
function convertToOpus(audioStream, sampleRate = 16e3) {
|
|
292
|
-
const webmStream = new import_stream2.PassThrough();
|
|
293
|
-
ffmpegToOpus((0, import_fluent_ffmpeg.default)(audioStream), sampleRate).pipe(webmStream);
|
|
294
|
-
return webmStream;
|
|
295
|
-
}
|
|
296
|
-
function convertPCMToOpus(audioStream, sampleRate = 16e3) {
|
|
297
|
-
const webmStream = new import_stream2.PassThrough();
|
|
298
|
-
ffmpegToOpus((0, import_fluent_ffmpeg.default)(audioStream), sampleRate).inputFormat("s16le").inputOptions(["-f s16le", "-ar 16000", "-ac 1"]).pipe(webmStream);
|
|
299
|
-
return webmStream;
|
|
300
|
-
}
|
|
301
|
-
function ffmpegToOpus(ffmpegCommand, sampleRate = 16e3) {
|
|
302
|
-
return ffmpegCommand.audioChannels(1).audioFrequency(sampleRate).audioCodec("libopus").format("webm").outputOptions([
|
|
303
|
-
"-application audio",
|
|
304
|
-
`-ac 1`,
|
|
305
|
-
`-ar ${sampleRate}`,
|
|
306
|
-
`-b:a 64k`,
|
|
307
|
-
`-f webm`,
|
|
308
|
-
`-map_metadata -1`
|
|
309
|
-
]).on("error", (error) => {
|
|
310
|
-
console.error("Error converting to Opus: ", error.message);
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
|
|
314
275
|
// src/errors.ts
|
|
315
276
|
var MicdropErrorCode = /* @__PURE__ */ ((MicdropErrorCode2) => {
|
|
316
277
|
MicdropErrorCode2[MicdropErrorCode2["BadRequest"] = 4400] = "BadRequest";
|
|
@@ -346,7 +307,7 @@ var Logger = class {
|
|
|
346
307
|
};
|
|
347
308
|
|
|
348
309
|
// src/MicdropServer.ts
|
|
349
|
-
var
|
|
310
|
+
var import_stream2 = require("stream");
|
|
350
311
|
|
|
351
312
|
// src/types.ts
|
|
352
313
|
var MicdropClientCommands = /* @__PURE__ */ ((MicdropClientCommands2) => {
|
|
@@ -373,6 +334,7 @@ var MicdropServer = class {
|
|
|
373
334
|
// Queue system for operations
|
|
374
335
|
this.operationQueue = [];
|
|
375
336
|
this.isProcessingQueue = false;
|
|
337
|
+
this.userSpeechChunks = 0;
|
|
376
338
|
this.onClose = () => {
|
|
377
339
|
if (!this.config) return;
|
|
378
340
|
this.log("Connection closed");
|
|
@@ -405,12 +367,15 @@ var MicdropServer = class {
|
|
|
405
367
|
this.onStopSpeaking();
|
|
406
368
|
}
|
|
407
369
|
} else if (this.currentUserStream) {
|
|
408
|
-
this.
|
|
409
|
-
this.currentUserStream.write(message);
|
|
370
|
+
this.onAudioChunk(message);
|
|
410
371
|
}
|
|
411
372
|
};
|
|
412
373
|
this.onTranscript = async (transcript) => {
|
|
413
374
|
if (!this.config) return;
|
|
375
|
+
if (transcript === "") {
|
|
376
|
+
this.socket?.send("SkipAnswer" /* SkipAnswer */);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
414
379
|
this.log(`User transcript: "${transcript}"`);
|
|
415
380
|
this.config.agent.addUserMessage(transcript);
|
|
416
381
|
if (!this.currentUserStream) {
|
|
@@ -478,21 +443,34 @@ var MicdropServer = class {
|
|
|
478
443
|
this.config?.agent.cancel();
|
|
479
444
|
this.operationQueue = [];
|
|
480
445
|
}
|
|
446
|
+
onAudioChunk(chunk) {
|
|
447
|
+
this.log(`Received chunk (${chunk.byteLength} bytes)`);
|
|
448
|
+
this.currentUserStream?.write(chunk);
|
|
449
|
+
this.userSpeechChunks++;
|
|
450
|
+
}
|
|
481
451
|
onMute() {
|
|
452
|
+
this.userSpeechChunks = 0;
|
|
482
453
|
this.currentUserStream?.end();
|
|
483
454
|
this.currentUserStream = void 0;
|
|
484
455
|
this.cancel();
|
|
485
456
|
}
|
|
486
457
|
onStartSpeaking() {
|
|
487
458
|
if (!this.config) return;
|
|
459
|
+
this.userSpeechChunks = 0;
|
|
488
460
|
this.currentUserStream?.end();
|
|
489
|
-
this.currentUserStream = new
|
|
461
|
+
this.currentUserStream = new import_stream2.PassThrough();
|
|
490
462
|
this.config.stt.transcribe(this.currentUserStream);
|
|
491
463
|
this.cancel();
|
|
492
464
|
}
|
|
493
465
|
onStopSpeaking() {
|
|
466
|
+
const hasNoUserSpeech = !this.currentUserStream || this.userSpeechChunks === 0;
|
|
494
467
|
this.currentUserStream?.end();
|
|
495
468
|
this.currentUserStream = void 0;
|
|
469
|
+
this.userSpeechChunks = 0;
|
|
470
|
+
if (hasNoUserSpeech) {
|
|
471
|
+
this.socket?.send("SkipAnswer" /* SkipAnswer */);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
496
474
|
const conversation = this.config?.agent.conversation;
|
|
497
475
|
const lastMessage = conversation?.[conversation.length - 1];
|
|
498
476
|
if (lastMessage?.role === "user" && this.lastMessageSpeeched !== lastMessage) {
|
|
@@ -545,7 +523,7 @@ var MicdropServer = class {
|
|
|
545
523
|
if (!this.socket || !this.config) return;
|
|
546
524
|
let textStream;
|
|
547
525
|
if (typeof message === "string") {
|
|
548
|
-
const stream = new
|
|
526
|
+
const stream = new import_stream2.PassThrough();
|
|
549
527
|
stream.write(message);
|
|
550
528
|
stream.end();
|
|
551
529
|
textStream = stream;
|
|
@@ -585,21 +563,7 @@ var MicdropServer = class {
|
|
|
585
563
|
|
|
586
564
|
// src/stt/STT.ts
|
|
587
565
|
var import_eventemitter32 = require("eventemitter3");
|
|
588
|
-
var MIME_TYPE_TO_EXTENSION = {
|
|
589
|
-
"audio/wav": "wav",
|
|
590
|
-
"audio/ogg": "ogg",
|
|
591
|
-
"audio/mpeg": "mp3",
|
|
592
|
-
"audio/webm": "webm",
|
|
593
|
-
"audio/mp4": "mp4",
|
|
594
|
-
"audio/flac": "flac"
|
|
595
|
-
};
|
|
596
566
|
var STT = class extends import_eventemitter32.EventEmitter {
|
|
597
|
-
// Set stream of audio to transcribe
|
|
598
|
-
transcribe(audioStream) {
|
|
599
|
-
audioStream.once("data", (chunk) => {
|
|
600
|
-
this.mimeType = this.detectMimeType(chunk);
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
567
|
log(...message) {
|
|
604
568
|
this.logger?.log(...message);
|
|
605
569
|
}
|
|
@@ -607,73 +571,24 @@ var STT = class extends import_eventemitter32.EventEmitter {
|
|
|
607
571
|
this.log("Destroyed");
|
|
608
572
|
this.removeAllListeners();
|
|
609
573
|
}
|
|
610
|
-
get extension() {
|
|
611
|
-
return this.mimeType && MIME_TYPE_TO_EXTENSION[this.mimeType] || "bin";
|
|
612
|
-
}
|
|
613
|
-
detectMimeType(chunk) {
|
|
614
|
-
if (!chunk || chunk.byteLength === 0) {
|
|
615
|
-
throw new Error("Unable to detect mime type (empty chunk)");
|
|
616
|
-
}
|
|
617
|
-
const arr = new Uint8Array(chunk);
|
|
618
|
-
if (arr[0] === 26 && arr[1] === 69 && arr[2] === 223 && arr[3] === 163) {
|
|
619
|
-
return "audio/webm";
|
|
620
|
-
}
|
|
621
|
-
if (arr[0] === 79 && arr[1] === 103 && arr[2] === 103 && arr[3] === 83) {
|
|
622
|
-
return "audio/ogg";
|
|
623
|
-
}
|
|
624
|
-
if (arr[0] === 82 && arr[1] === 73 && arr[2] === 70 && arr[3] === 70 && arr[8] === 87 && arr[9] === 65 && arr[10] === 86 && arr[11] === 69) {
|
|
625
|
-
return "audio/wav";
|
|
626
|
-
}
|
|
627
|
-
if (arr[0] === 73 && arr[1] === 68 && arr[2] === 51) {
|
|
628
|
-
return "audio/mpeg";
|
|
629
|
-
}
|
|
630
|
-
if (arr[4] === 102 && arr[5] === 116 && arr[6] === 121 && arr[7] === 112) {
|
|
631
|
-
return "audio/mp4";
|
|
632
|
-
}
|
|
633
|
-
if (arr[0] === 102 && arr[1] === 76 && arr[2] === 97 && arr[3] === 67) {
|
|
634
|
-
return "audio/flac";
|
|
635
|
-
}
|
|
636
|
-
this.log("Unable to detect mime type, using default", chunk);
|
|
637
|
-
return "audio/wav";
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
|
|
641
|
-
// src/stt/FileSTT.ts
|
|
642
|
-
var FileSTT = class extends STT {
|
|
643
|
-
transcribe(audioStream) {
|
|
644
|
-
super.transcribe(audioStream);
|
|
645
|
-
this.log("Converting stream to file...");
|
|
646
|
-
const chunks = [];
|
|
647
|
-
audioStream.on("data", (chunk) => {
|
|
648
|
-
chunks.push(chunk);
|
|
649
|
-
});
|
|
650
|
-
audioStream.on("end", async () => {
|
|
651
|
-
if (chunks.length === 0) return;
|
|
652
|
-
const arrayBuffer = Buffer.concat(chunks);
|
|
653
|
-
const file = new File([arrayBuffer], `audio.${this.extension}`, {
|
|
654
|
-
type: this.mimeType
|
|
655
|
-
});
|
|
656
|
-
this.log("Transcribing file...");
|
|
657
|
-
const transcript = await this.transcribeFile(file);
|
|
658
|
-
this.emit("Transcript", transcript);
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
574
|
};
|
|
662
575
|
|
|
663
576
|
// src/stt/MockSTT.ts
|
|
664
|
-
var MockSTT = class extends
|
|
577
|
+
var MockSTT = class extends STT {
|
|
665
578
|
constructor() {
|
|
666
579
|
super(...arguments);
|
|
667
580
|
this.i = 0;
|
|
668
581
|
}
|
|
669
|
-
async
|
|
670
|
-
|
|
582
|
+
async transcribe() {
|
|
583
|
+
setTimeout(() => {
|
|
584
|
+
this.emit("Transcript", `User Message ${this.i++}`);
|
|
585
|
+
}, 300);
|
|
671
586
|
}
|
|
672
587
|
};
|
|
673
588
|
|
|
674
589
|
// src/tts/MockTTS.ts
|
|
675
590
|
var fs = __toESM(require("fs"));
|
|
676
|
-
var
|
|
591
|
+
var import_stream3 = require("stream");
|
|
677
592
|
|
|
678
593
|
// src/tts/TTS.ts
|
|
679
594
|
var TTS = class {
|
|
@@ -693,7 +608,7 @@ var MockTTS = class extends TTS {
|
|
|
693
608
|
this.audioFilePaths = audioFilePaths;
|
|
694
609
|
}
|
|
695
610
|
speak(textStream) {
|
|
696
|
-
const audioStream = new
|
|
611
|
+
const audioStream = new import_stream3.PassThrough();
|
|
697
612
|
textStream.once("data", async () => {
|
|
698
613
|
for (const filePath of this.audioFilePaths) {
|
|
699
614
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
@@ -737,7 +652,6 @@ async function waitForParams(socket, validate) {
|
|
|
737
652
|
AUTO_SEMANTIC_TURN_PROMPT,
|
|
738
653
|
AUTO_SEMANTIC_TURN_TOOL_NAME,
|
|
739
654
|
Agent,
|
|
740
|
-
FileSTT,
|
|
741
655
|
Logger,
|
|
742
656
|
MicdropClientCommands,
|
|
743
657
|
MicdropError,
|
|
@@ -749,9 +663,6 @@ async function waitForParams(socket, validate) {
|
|
|
749
663
|
MockTTS,
|
|
750
664
|
STT,
|
|
751
665
|
TTS,
|
|
752
|
-
convertPCMToOpus,
|
|
753
|
-
convertToOpus,
|
|
754
|
-
convertToPCM,
|
|
755
666
|
handleError,
|
|
756
667
|
waitForParams
|
|
757
668
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/audio-convert.ts","../src/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/stt/STT.ts","../src/stt/FileSTT.ts","../src/stt/MockSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/waitForParams.ts"],"sourcesContent":["export * from './agent'\nexport * from './audio-convert'\nexport * from './errors'\nexport * from './Logger'\nexport * from './MicdropServer'\nexport * from './stt'\nexport * from './tts'\nexport * from './types'\nexport * from './waitForParams'\n","import { EventEmitter } from 'eventemitter3'\nimport { 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 ffmpegInstaller from '@ffmpeg-installer/ffmpeg'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { PassThrough, Readable } from 'stream'\n\n// Setup ffmpeg\nffmpeg.setFfmpegPath(ffmpegInstaller.path)\n\n// Convert stream to WAV/PCM\nexport function convertToPCM(\n audioStream: Readable,\n sampleRate = 16000,\n bitDepth = 16\n) {\n const pcmStream = new PassThrough()\n ffmpeg(audioStream)\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec(`pcm_s${bitDepth}le`)\n .format(`s${bitDepth}le`)\n .on('error', (error) => {\n console.error('Error converting audio stream:', error.message)\n })\n .pipe(pcmStream)\n return pcmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate).pipe(webmStream)\n return webmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertPCMToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate)\n .inputFormat('s16le')\n .inputOptions(['-f s16le', '-ar 16000', '-ac 1'])\n .pipe(webmStream)\n return webmStream\n}\n\nfunction ffmpegToOpus(ffmpegCommand: ffmpeg.FfmpegCommand, sampleRate = 16000) {\n return ffmpegCommand\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec('libopus')\n .format('webm')\n .outputOptions([\n '-application audio',\n `-ac 1`,\n `-ar ${sampleRate}`,\n `-b:a 64k`,\n `-f webm`,\n `-map_metadata -1`,\n ])\n .on('error', (error) => {\n console.error('Error converting to Opus: ', error.message)\n })\n}\n","import WebSocket from 'ws'\n\nexport enum MicdropErrorCode {\n BadRequest = 4400,\n Unauthorized = 4401,\n NotFound = 4404,\n}\n\nexport class MicdropError extends Error {\n code: number\n\n constructor(code: number, message: string) {\n super(message)\n this.code = code\n }\n}\n\nexport function handleError(socket: WebSocket, error: unknown) {\n if (error instanceof MicdropError) {\n socket.close(error.code, error.message)\n } else {\n console.error(error)\n socket.close(1011)\n }\n socket.terminate()\n}\n","export class Logger {\n constructor(private readonly name: string) {}\n\n log(...message: any[]) {\n const time = process.uptime().toFixed(3)\n console.log(`[${this.name} ${time}]`, ...message)\n }\n}\n","import { Duplex, PassThrough, Readable } from 'stream'\nimport { WebSocket } from 'ws'\nimport type { Agent } from './agent'\nimport { Logger } from './Logger'\nimport type { STT } from './stt'\nimport type { TTS } from './tts'\nimport {\n MicdropCallSummary,\n MicdropClientCommands,\n MicdropConversationItem,\n MicdropServerCommands,\n} from './types'\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n onEnd?(call: MicdropCallSummary): void\n}\n\nexport class MicdropServer {\n public socket: WebSocket | null = null\n public config: MicdropConfig | null = null\n public logger?: Logger\n\n private startTime = Date.now()\n private lastMessageSpeeched?: MicdropConversationItem\n\n // 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\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.log(`Received chunk (${message.byteLength} bytes)`)\n this.currentUserStream.write(message)\n }\n }\n\n private onMute() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private onStartSpeaking() {\n if (!this.config) return\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private onStopSpeaking() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n\n const conversation = this.config?.agent.conversation\n const lastMessage = conversation?.[conversation.length - 1]\n if (\n lastMessage?.role === 'user' &&\n this.lastMessageSpeeched !== lastMessage\n ) {\n this.log(\n 'User stopped speaking and a transcript already exists, answering'\n )\n this.cancel()\n this.answer()\n }\n }\n\n private onTranscript = async (transcript: string) => {\n if (!this.config) return\n this.log(`User transcript: \"${transcript}\"`)\n this.config.agent.addUserMessage(transcript)\n\n // Answer if user stopped speaking\n if (!this.currentUserStream) {\n this.log('User stopped speaking, answering')\n this.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\n// Audio mime type to extension map\nconst MIME_TYPE_TO_EXTENSION = {\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/mpeg': 'mp3',\n 'audio/webm': 'webm',\n 'audio/mp4': 'mp4',\n 'audio/flac': 'flac',\n} as const\n\nexport interface STTEvents {\n Transcript: [string]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n protected mimeType?: keyof typeof MIME_TYPE_TO_EXTENSION\n public logger?: Logger\n\n // Set stream of audio to transcribe\n transcribe(audioStream: Readable) {\n // Detect mime type at first chunk\n audioStream.once('data', (chunk) => {\n this.mimeType = this.detectMimeType(chunk)\n })\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n }\n\n protected get extension(): string {\n return (this.mimeType && MIME_TYPE_TO_EXTENSION[this.mimeType]) || 'bin'\n }\n\n private detectMimeType(\n chunk: ArrayBuffer\n ): keyof typeof MIME_TYPE_TO_EXTENSION {\n if (!chunk || chunk.byteLength === 0) {\n throw new Error('Unable to detect mime type (empty chunk)')\n }\n\n const arr = new Uint8Array(chunk)\n\n // WEBM: 1A 45 DF A3\n if (\n arr[0] === 0x1a &&\n arr[1] === 0x45 &&\n arr[2] === 0xdf &&\n arr[3] === 0xa3\n ) {\n return 'audio/webm'\n }\n // OGG: 4F 67 67 53\n if (\n arr[0] === 0x4f &&\n arr[1] === 0x67 &&\n arr[2] === 0x67 &&\n arr[3] === 0x53\n ) {\n return 'audio/ogg'\n }\n // WAV: 52 49 46 46 ... 57 41 56 45\n if (\n arr[0] === 0x52 &&\n arr[1] === 0x49 &&\n arr[2] === 0x46 &&\n arr[3] === 0x46 &&\n arr[8] === 0x57 &&\n arr[9] === 0x41 &&\n arr[10] === 0x56 &&\n arr[11] === 0x45\n ) {\n return 'audio/wav'\n }\n // MP3: 49 44 33\n if (arr[0] === 0x49 && arr[1] === 0x44 && arr[2] === 0x33) {\n return 'audio/mpeg'\n }\n // MP4/M4A: 00 00 00 .. 66 74 79 70\n if (\n arr[4] === 0x66 &&\n arr[5] === 0x74 &&\n arr[6] === 0x79 &&\n arr[7] === 0x70\n ) {\n return 'audio/mp4'\n }\n // FLAC: 66 4c 61 43\n if (\n arr[0] === 0x66 &&\n arr[1] === 0x4c &&\n arr[2] === 0x61 &&\n arr[3] === 0x43\n ) {\n return 'audio/flac'\n }\n this.log('Unable to detect mime type, using default', chunk)\n return 'audio/wav'\n }\n}\n","import { Readable } from 'stream'\nimport { STT } from './STT'\n\n/**\n * Abstract class for STT, converting stream to file before transcribing\n */\n\nexport abstract class FileSTT extends STT {\n abstract transcribeFile(file: File): Promise<string>\n\n transcribe(audioStream: Readable) {\n super.transcribe(audioStream)\n\n // Convert stream to file\n this.log('Converting stream to file...')\n\n const chunks: Buffer[] = []\n audioStream.on('data', (chunk) => {\n chunks.push(chunk)\n })\n\n audioStream.on('end', async () => {\n if (chunks.length === 0) return\n const arrayBuffer = Buffer.concat(chunks)\n const file = new File([arrayBuffer], `audio.${this.extension}`, {\n type: this.mimeType,\n })\n\n // Transcribe file with implementation\n this.log('Transcribing file...')\n const transcript = await this.transcribeFile(file)\n this.emit('Transcript', transcript)\n })\n }\n}\n","import { FileSTT } from './FileSTT'\n\nexport class MockSTT extends FileSTT {\n private i = 0\n\n async transcribeFile(file: File) {\n return `User Message ${this.i++}`\n }\n}\n","import * as fs from 'fs'\nimport { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\n\nexport class MockTTS extends TTS {\n constructor(private audioFilePaths: string[]) {\n super()\n }\n\n speak(textStream: Readable) {\n const audioStream = new PassThrough()\n textStream.once('data', async () => {\n for (const filePath of this.audioFilePaths) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n const audioBuffer = fs.readFileSync(filePath)\n this.log(`Loaded chunk (${audioBuffer.length} bytes)`)\n audioStream.write(audioBuffer)\n }\n audioStream.end()\n })\n return audioStream\n }\n\n cancel() {}\n}\n","import { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport abstract class TTS {\n public logger?: Logger\n\n abstract speak(textStream: Readable): Readable\n abstract cancel(): void\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.cancel()\n }\n}\n","import { WebSocket } from 'ws'\nimport { MicdropError, MicdropErrorCode } from './errors'\n\nexport async function waitForParams<CallParams>(\n socket: WebSocket,\n validate: (params: any) => CallParams\n): Promise<CallParams> {\n return new Promise<CallParams>((resolve, reject) => {\n // Handle timeout\n const timeout = setTimeout(() => {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Missing params'))\n }, 3000)\n\n const onParams = (payload: string) => {\n // Clear timeout and listener\n clearTimeout(timeout)\n socket.off('message', onParams)\n\n try {\n // Parse JSON payload\n const params = validate(JSON.parse(payload))\n resolve(params)\n } catch (error) {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Invalid params'))\n }\n }\n\n // Listen for params\n socket.on('message', onParams)\n })\n}\n"],"mappings":"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","names":["import_stream","ffmpeg","ffmpegInstaller","MicdropErrorCode","import_stream","MicdropClientCommands","MicdropServerCommands","import_eventemitter3","import_stream"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,2BAA6B;AAC7B,oBAAgD;;;ACUzC,IAAM,0BAA0B;AAChC,IAAM,uBACX;AAEK,IAAM,+BAA+B;AACrC,IAAM,4BACX;AAEK,IAAM,mCAAmC;AACzC,IAAM,gCACX;;;ADoDK,IAAe,QAAf,cAEG,kCAA0B;AAAA,EAQlC,YAAsB,SAAkB;AACtC,UAAM;AADc;AAHtB,SAAU,cAAc;AACxB,SAAU,YAAY;AAIpB,SAAK,eAAe,CAAC,EAAE,MAAM,UAAU,SAAS,QAAQ,aAAa,CAAC;AACtE,SAAK,QAAQ,KAAK,gBAAgB;AAAA,EACpC;AAAA,EAKA,SAAmB;AACjB,SAAK,IAAI,iBAAiB;AAC1B,UAAM,cAAc,EAAE,KAAK;AAC3B,UAAM,SAAS,IAAI,0BAAY;AAC/B,SAAK,YAAY;AAEjB,YAAQ,QAAQ,EAEb,KAAK,MAAM,KAAK,QAAQ,gBAAgB,KAAK,IAAI,EAAE,MAAM,CAAC,EAE1D,KAAK,CAAC,SAAS;AACd,UAAI,KAAM;AACV,aAAO,KAAK,eAAe,MAAM;AAAA,IACnC,CAAC,EAEA,QAAQ,MAAM;AACb,UAAI,OAAO,UAAU;AACnB,eAAO,IAAI;AAAA,MACb;AACA,UAAI,gBAAgB,KAAK,aAAa;AACpC,aAAK,YAAY;AAAA,MACnB;AAAA,IACF,CAAC;AAEH,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAc,UAAkC;AAC7D,SAAK,WAAW,QAAQ,MAAM,QAAQ;AAAA,EACxC;AAAA,EAEA,oBAAoB,MAAc,UAAkC;AAClE,SAAK,WAAW,aAAa,MAAM,QAAQ;AAAA,EAC7C;AAAA,EAEA,QAAoC,MAAoB;AACtD,SAAK,MAAM,KAAK,IAAI;AAAA,EACtB;AAAA,EAEA,WAAW,MAAc;AACvB,UAAM,QAAQ,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,SAAS,IAAI;AAC/D,QAAI,UAAU,IAAI;AAChB,WAAK,MAAM,OAAO,OAAO,CAAC;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,QAAQ,MAAgC;AACtC,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI;AAAA,EACrD;AAAA,EAEA,WACE,MACA,MACA,UACA;AACA,SAAK,IAAI,UAAU,IAAI,6BAA6B,IAAI,EAAE;AAC1D,UAAM,UAAsC;AAAA,MAC1C;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF;AACA,SAAK,aAAa,KAAK,OAAO;AAC9B,SAAK,KAAK,WAAW,OAAO;AAAA,EAC9B;AAAA,EAEA,eACE,SACA;AACA,SAAK,IAAI,wBAAwB,OAAO;AACxC,SAAK,aAAa,KAAK,OAAO;AAC9B,SAAK,KAAK,WAAW,OAAO;AAAA,EAC9B;AAAA,EAEU,UAAU;AAClB,SAAK,IAAI,aAAa;AACtB,SAAK,KAAK,SAAS;AAAA,EACrB;AAAA,EAEU,wBAAwB;AAChC,SAAK,IAAI,8BAA8B;AACvC,UAAM,mBAAmB,KAAK,aAAa;AAAA,MACzC,CAAC,YAAY,QAAQ,SAAS;AAAA,IAChC;AACA,QAAI,qBAAqB,IAAI;AAC3B,WAAK,aAAa,OAAO,kBAAkB,CAAC;AAAA,IAC9C;AACA,SAAK,KAAK,uBAAuB;AAAA,EACnC;AAAA,EAEU,aAAa;AACrB,SAAK,IAAI,iBAAiB;AAC1B,SAAK,KAAK,YAAY;AAAA,EACxB;AAAA,EAEU,kBAAkB;AAC1B,UAAM,QAAgB,CAAC;AACvB,QAAI,KAAK,QAAQ,aAAa;AAC5B,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,aACE,OAAO,KAAK,QAAQ,gBAAgB,WAChC,KAAK,QAAQ,cACb;AAAA,QACN,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC9B,CAAC;AAAA,IACH;AACA,QAAI,KAAK,QAAQ,kBAAkB;AACjC,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,aACE,OAAO,KAAK,QAAQ,qBAAqB,WACrC,KAAK,QAAQ,mBACb;AAAA,QACN,YAAY;AAAA,QACZ,SAAS,MAAM,KAAK,WAAW;AAAA,MACjC,CAAC;AAAA,IACH;AACA,QAAI,KAAK,QAAQ,qBAAqB;AACpC,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,aACE,OAAO,KAAK,QAAQ,wBAAwB,WACxC,KAAK,QAAQ,sBACb;AAAA,QACN,YAAY;AAAA,QACZ,SAAS,MAAM,KAAK,sBAAsB;AAAA,MAC5C,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAgB,YAAY,UAAuC;AACjE,QAAI;AACF,YAAM,OAAO,KAAK,QAAQ,SAAS,QAAQ;AAC3C,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,mBAAmB,SAAS,QAAQ,GAAG;AAAA,MACzD;AAEA,WAAK,IAAI,mBAAmB,SAAS,UAAU,SAAS,UAAU;AAGlE,WAAK,eAAe,QAAQ;AAE5B,YAAM,aAAa,KAAK,MAAM,SAAS,UAAU;AACjD,YAAM,SAAS,KAAK,UAAU,MAAM,KAAK,QAAQ,UAAU,IAAI,CAAC;AAGhE,WAAK,eAAe;AAAA,QAClB,MAAM;AAAA,QACN,YAAY,SAAS;AAAA,QACrB,UAAU,SAAS;AAAA,QACnB,QAAQ,KAAK,UAAU,UAAU,IAAI;AAAA,MACvC,CAAC;AAGD,UAAI,KAAK,YAAY;AACnB,aAAK,KAAK,YAAY;AAAA,UACpB,MAAM,SAAS;AAAA,UACf;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL;AAAA,QACA,YAAY,KAAK;AAAA,MACnB;AAAA,IACF,SAAS,OAAY;AACnB,cAAQ,MAAM,uCAAuC,KAAK;AAC1D,aAAO;AAAA,QACL,QAAQ;AAAA,UACN,OAAO,MAAM;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEU,oBAAmD;AAC3D,UAAM,UAAU,KAAK,QAAQ;AAC7B,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,UAAU,WAAW,QAAQ,MAAM;AACrC,aAAO,EAAE,GAAG,SAAS,UAAU,KAAK,QAAQ,IAAI;AAAA,IAClD;AACA,QAAI,cAAc,WAAW,YAAY,SAAS;AAChD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ,SAAiB;AAC9B,UAAM,iBAAiB,KAAK,kBAAkB;AAC9C,QAAI,WAA8C;AAGlD,QAAI,gBAAgB;AAClB,YAAM,gBAAgB,QAAQ,QAAQ,eAAe,QAAQ;AAC7D,UAAI,kBAAkB,IAAI;AAExB,YAAI,cAAc,QAAQ,YAAY,eAAe,MAAM;AAC3D,YAAI,gBAAgB,GAAI,eAAc,QAAQ,SAAS;AAAA,YAClD,gBAAe,eAAe,OAAO;AAC1C,cAAM,gBAAgB,QAAQ,MAAM,eAAe,WAAW,EAAE,KAAK;AAGrE,YAAI;AACF,gBAAM,iBACJ,UAAU,kBAAkB,eAAe,OACvC,KAAK,MAAM,aAAa,IACxB;AAGN,cAAI,eAAe,UAAU;AAC3B,2BAAe,SAAS,cAAc;AAAA,UACxC;AAGA,cAAI,eAAe,gBAAgB;AACjC,uBAAW,EAAE,WAAW,eAAe;AAAA,UACzC;AAAA,QACF,SAAS,OAAO;AACd,kBAAQ;AAAA,YACN,gDAAgD,aAAa;AAAA,YAC7D;AAAA,UACF;AAAA,QACF;AAGA,kBAAU,QAAQ,MAAM,GAAG,aAAa,EAAE,QAAQ;AAAA,MACpD;AAAA,IACF;AACA,WAAO,EAAE,SAAS,SAAS;AAAA,EAC7B;AAAA,EAEU,OAAO,SAAgB;AAC/B,SAAK,QAAQ,IAAI,GAAG,OAAO;AAAA,EAC7B;AAAA,EAEA,UAAU;AACR,SAAK,IAAI,WAAW;AACpB,SAAK,mBAAmB;AACxB,SAAK,OAAO;AAAA,EACd;AACF;;;AE3UO,IAAM,YAAN,cAAwB,MAAM;AAAA,EAGnC,cAAc;AACZ,UAAM,EAAE,cAAc,GAAG,CAAC;AAH5B,SAAQ,IAAI;AAAA,EAIZ;AAAA,EAEA,MAAgB,eAAe,QAAoC;AACjE,UAAM,UAAU,qBAAqB,KAAK,GAAG;AAC7C,SAAK,oBAAoB,OAAO;AAChC,WAAO,MAAM,OAAO;AAAA,EACtB;AAAA,EAEA,SAAS;AAAA,EAAC;AACZ;;;ACfO,IAAK,mBAAL,kBAAKA,sBAAL;AACL,EAAAA,oCAAA,gBAAa,QAAb;AACA,EAAAA,oCAAA,kBAAe,QAAf;AACA,EAAAA,oCAAA,cAAW,QAAX;AAHU,SAAAA;AAAA,GAAA;AAML,IAAM,eAAN,cAA2B,MAAM;AAAA,EAGtC,YAAY,MAAc,SAAiB;AACzC,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,YAAY,QAAmB,OAAgB;AAC7D,MAAI,iBAAiB,cAAc;AACjC,WAAO,MAAM,MAAM,MAAM,MAAM,OAAO;AAAA,EACxC,OAAO;AACL,YAAQ,MAAM,KAAK;AACnB,WAAO,MAAM,IAAI;AAAA,EACnB;AACA,SAAO,UAAU;AACnB;;;ACzBO,IAAM,SAAN,MAAa;AAAA,EAClB,YAA6B,MAAc;AAAd;AAAA,EAAe;AAAA,EAE5C,OAAO,SAAgB;AACrB,UAAM,OAAO,QAAQ,OAAO,EAAE,QAAQ,CAAC;AACvC,YAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,OAAO;AAAA,EAClD;AACF;;;ACPA,IAAAC,iBAA8C;;;ACAvC,IAAK,wBAAL,kBAAKC,2BAAL;AACL,EAAAA,uBAAA,mBAAgB;AAChB,EAAAA,uBAAA,kBAAe;AACf,EAAAA,uBAAA,UAAO;AAHG,SAAAA;AAAA,GAAA;AAML,IAAK,wBAAL,kBAAKC,2BAAL;AACL,EAAAA,uBAAA,aAAU;AACV,EAAAA,uBAAA,2BAAwB;AACxB,EAAAA,uBAAA,gBAAa;AACb,EAAAA,uBAAA,aAAU;AACV,EAAAA,uBAAA,cAAW;AALD,SAAAA;AAAA,GAAA;;;ADgBL,IAAM,gBAAN,MAAoB;AAAA,EAgBzB,YAAY,QAAmB,QAAuB;AAftD,SAAO,SAA2B;AAClC,SAAO,SAA+B;AAGtC,SAAQ,YAAY,KAAK,IAAI;AAI7B;AAAA,SAAQ,iBAA6C,CAAC;AACtD,SAAQ,oBAAoB;AAI5B,SAAQ,mBAAmB;AA0E3B,SAAQ,UAAU,MAAM;AACtB,UAAI,CAAC,KAAK,OAAQ;AAClB,WAAK,IAAI,mBAAmB;AAC5B,YAAM,WAAW,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,aAAa,GAAI;AAGhE,WAAK,OAAO,MAAM,QAAQ;AAC1B,WAAK,OAAO,IAAI,QAAQ;AACxB,WAAK,OAAO,IAAI,QAAQ;AAGxB,WAAK,OAAO,QAAQ;AAAA,QAClB,cAAc,KAAK,OAAO,MAAM,aAAa,MAAM,CAAC;AAAA;AAAA,QACpD;AAAA,MACF,CAAC;AAGD,WAAK,SAAS;AACd,WAAK,SAAS;AAAA,IAChB;AAEA,SAAQ,YAAY,OAAO,YAAoB;AAC7C,UAAI,QAAQ,eAAe,EAAG;AAC9B,UAAI,CAAC,OAAO,SAAS,OAAO,GAAG;AAC7B,aAAK,IAAI,yBAAyB;AAClC;AAAA,MACF;AAGA,UAAI,QAAQ,aAAa,IAAI;AAC3B,cAAM,MAAM,QAAQ,SAAS;AAC7B,aAAK,IAAI,YAAY,GAAG,EAAE;AAE1B,YAAI,6CAA6C;AAE/C,eAAK,gBAAgB;AAAA,QACvB,WAAW,2BAAoC;AAE7C,eAAK,OAAO;AAAA,QACd,WAAW,2CAA4C;AAErD,eAAK,eAAe;AAAA,QACtB;AAAA,MACF,WAGS,KAAK,mBAAmB;AAC/B,aAAK,aAAa,OAAO;AAAA,MAC3B;AAAA,IACF;AAmDA,SAAQ,eAAe,OAAO,eAAuB;AACnD,UAAI,CAAC,KAAK,OAAQ;AAGlB,UAAI,eAAe,IAAI;AACrB,aAAK,QAAQ,kCAAqC;AAClD;AAAA,MACF;AAEA,WAAK,IAAI,qBAAqB,UAAU,GAAG;AAC3C,WAAK,OAAO,MAAM,eAAe,UAAU;AAG3C,UAAI,CAAC,KAAK,mBAAmB;AAC3B,aAAK,IAAI,kCAAkC;AAC3C,aAAK,OAAO;AACZ,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AA7LE,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,IAAI,cAAc;AAGvB,SAAK,OAAO,IAAI,GAAG,cAAc,KAAK,YAAY;AAGlD,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAW,CAAC,YAC/B,KAAK,QAAQ;AAAA,QACX,0BAAgC,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,MAC7D;AAAA,IACF;AACA,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAyB,MAC5C,KAAK,QAAQ,wDAAgD;AAAA,IAC/D;AACA,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAc,MACjC,KAAK,QAAQ,kCAAqC;AAAA,IACpD;AACA,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAW,MAC9B,KAAK,QAAQ,4BAAkC;AAAA,IACjD;AACA,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAY,CAAC,aAChC,KAAK,QAAQ;AAAA,QACX,4BAAiC,IAAI,KAAK,UAAU,QAAQ,CAAC;AAAA,MAC/D;AAAA,IACF;AAGA,SAAK,iBAAiB;AAGtB,WAAO,GAAG,SAAS,KAAK,OAAO;AAC/B,WAAO,GAAG,WAAW,KAAK,SAAS;AAAA,EACrC;AAAA,EAEQ,OAAO,SAAgB;AAC7B,SAAK,QAAQ,IAAI,GAAG,OAAO;AAAA,EAC7B;AAAA,EAEA,MAAc,eAAe;AAC3B,QAAI,KAAK,qBAAqB,KAAK,eAAe,WAAW,EAAG;AAEhE,SAAK,oBAAoB;AAEzB,WAAO,KAAK,eAAe,SAAS,GAAG;AACrC,YAAM,YAAY,KAAK,eAAe,MAAM;AAC5C,UAAI,WAAW;AACb,YAAI;AACF,gBAAM,UAAU;AAAA,QAClB,SAAS,OAAO;AACd,eAAK,IAAI,sCAAsC,KAAK;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AAEA,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEQ,eAAe,WAAgC;AACrD,SAAK,eAAe,KAAK,SAAS;AAClC,SAAK,aAAa;AAAA,EACpB;AAAA,EAEO,SAAS;AACd,SAAK,QAAQ,IAAI,OAAO;AACxB,SAAK,QAAQ,MAAM,OAAO;AAE1B,SAAK,iBAAiB,CAAC;AAAA,EACzB;AAAA,EAqDQ,aAAa,OAAe;AAClC,SAAK,IAAI,mBAAmB,MAAM,UAAU,SAAS;AACrD,SAAK,mBAAmB,MAAM,KAAK;AACnC,SAAK;AAAA,EACP;AAAA,EAEQ,SAAS;AACf,SAAK,mBAAmB;AACxB,SAAK,mBAAmB,IAAI;AAC5B,SAAK,oBAAoB;AACzB,SAAK,OAAO;AAAA,EACd;AAAA,EAEQ,kBAAkB;AACxB,QAAI,CAAC,KAAK,OAAQ;AAClB,SAAK,mBAAmB;AACxB,SAAK,mBAAmB,IAAI;AAC5B,SAAK,oBAAoB,IAAI,2BAAY;AACzC,SAAK,OAAO,IAAI,WAAW,KAAK,iBAAiB;AACjD,SAAK,OAAO;AAAA,EACd;AAAA,EAEQ,iBAAiB;AACvB,UAAM,kBACJ,CAAC,KAAK,qBAAqB,KAAK,qBAAqB;AACvD,SAAK,mBAAmB,IAAI;AAC5B,SAAK,oBAAoB;AACzB,SAAK,mBAAmB;AAGxB,QAAI,iBAAiB;AACnB,WAAK,QAAQ,kCAAqC;AAClD;AAAA,IACF;AAEA,UAAM,eAAe,KAAK,QAAQ,MAAM;AACxC,UAAM,cAAc,eAAe,aAAa,SAAS,CAAC;AAC1D,QACE,aAAa,SAAS,UACtB,KAAK,wBAAwB,aAC7B;AACA,WAAK;AAAA,QACH;AAAA,MACF;AACA,WAAK,OAAO;AACZ,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA,EAsBQ,mBAAmB;AACzB,QAAI,CAAC,KAAK,OAAQ;AAClB,QAAI,KAAK,OAAO,cAAc;AAE5B,WAAK,OAAO,MAAM,oBAAoB,KAAK,OAAO,YAAY;AAC9D,WAAK,MAAM,KAAK,OAAO,YAAY;AAAA,IACrC,WAAW,KAAK,OAAO,sBAAsB;AAE3C,WAAK,OAAO;AAAA,IACd,OAAO;AAGL,WAAK,QAAQ,kCAAqC;AAAA,IACpD;AAAA,EACF;AAAA,EAEO,SAAS;AACd,SAAK,eAAe,YAAY;AAC9B,YAAM,KAAK,QAAQ;AAAA,IACrB,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,UAAU;AACtB,QAAI,CAAC,KAAK,OAAQ;AAGlB,UAAM,cACJ,KAAK,OAAO,MAAM,aAAa,KAAK,OAAO,MAAM,aAAa,SAAS,CAAC;AAC1E,QAAI,KAAK,wBAAwB,aAAa;AAC5C,WAAK,IAAI,4BAA4B;AACrC;AAAA,IACF;AACA,SAAK,sBAAsB;AAE3B,QAAI;AAEF,YAAM,SAAS,KAAK,OAAO,MAAM,OAAO;AAGxC,YAAM,KAAK,OAAO,MAAM;AAAA,IAC1B,SAAS,OAAO;AACd,WAAK,QAAQ,kCAAqC;AAClD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA,EAGO,MAAM,SAA4B;AACvC,SAAK,eAAe,YAAY;AAC9B,YAAM,KAAK,OAAO,OAAO;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,OAAO,SAA4B;AAC/C,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,OAAQ;AAGlC,QAAI;AACJ,QAAI,OAAO,YAAY,UAAU;AAC/B,YAAM,SAAS,IAAI,2BAAY;AAC/B,aAAO,MAAM,OAAO;AACpB,aAAO,IAAI;AACX,mBAAa;AAAA,IACf,OAAO;AACL,mBAAa;AAAA,IACf;AAGA,UAAM,QAAQ,KAAK,OAAO,IAAI,MAAM,UAAU;AAG9C,UAAM,KAAK,WAAW,KAAK;AAAA,EAC7B;AAAA,EAEO,UAAU,OAAiB;AAChC,SAAK,eAAe,YAAY;AAC9B,YAAM,KAAK,WAAW,KAAK;AAAA,IAC7B,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,WAAW,OAAiB;AACxC,QAAI,CAAC,KAAK,OAAQ;AAClB,QAAI,CAAC,MAAM,UAAU;AACnB,WAAK,IAAI,gCAAgC,KAAK;AAC9C;AAAA,IACF;AAGA,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,GAAG,QAAQ,CAAC,UAAU;AAC1B,aAAK,IAAI,qBAAqB,MAAM,UAAU,SAAS;AACvD,aAAK,QAAQ,KAAK,KAAK;AAAA,MACzB,CAAC;AACD,YAAM,GAAG,SAAS,CAAC,UAAU;AAC3B,aAAK,IAAI,yBAAyB,KAAK;AACvC,eAAO,KAAK;AAAA,MACd,CAAC;AACD,YAAM,GAAG,OAAO,MAAM;AACpB,aAAK,IAAI,oBAAoB;AAC7B,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;;;AE7UA,IAAAC,wBAA6B;AAQtB,IAAe,MAAf,cAA2B,mCAAwB;AAAA,EAM9C,OAAO,SAAgB;AAC/B,SAAK,QAAQ,IAAI,GAAG,OAAO;AAAA,EAC7B;AAAA,EAEA,UAAU;AACR,SAAK,IAAI,WAAW;AACpB,SAAK,mBAAmB;AAAA,EAC1B;AACF;;;ACpBO,IAAM,UAAN,cAAsB,IAAI;AAAA,EAA1B;AAAA;AACL,SAAQ,IAAI;AAAA;AAAA,EAEZ,MAAM,aAAa;AACjB,eAAW,MAAM;AACf,WAAK,KAAK,cAAc,gBAAgB,KAAK,GAAG,EAAE;AAAA,IACpD,GAAG,GAAG;AAAA,EACR;AACF;;;ACVA,SAAoB;AACpB,IAAAC,iBAAsC;;;ACE/B,IAAe,MAAf,MAAmB;AAAA,EAMd,OAAO,SAAgB;AAC/B,SAAK,QAAQ,IAAI,GAAG,OAAO;AAAA,EAC7B;AAAA,EAEA,UAAU;AACR,SAAK,IAAI,WAAW;AACpB,SAAK,OAAO;AAAA,EACd;AACF;;;ADbO,IAAM,UAAN,cAAsB,IAAI;AAAA,EAC/B,YAAoB,gBAA0B;AAC5C,UAAM;AADY;AAAA,EAEpB;AAAA,EAEA,MAAM,YAAsB;AAC1B,UAAM,cAAc,IAAI,2BAAY;AACpC,eAAW,KAAK,QAAQ,YAAY;AAClC,iBAAW,YAAY,KAAK,gBAAgB;AAC1C,cAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG,CAAC;AACvD,cAAM,cAAiB,gBAAa,QAAQ;AAC5C,aAAK,IAAI,iBAAiB,YAAY,MAAM,SAAS;AACrD,oBAAY,MAAM,WAAW;AAAA,MAC/B;AACA,kBAAY,IAAI;AAAA,IAClB,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,SAAS;AAAA,EAAC;AACZ;;;AErBA,eAAsB,cACpB,QACA,UACqB;AACrB,SAAO,IAAI,QAAoB,CAAC,SAAS,WAAW;AAElD,UAAM,UAAU,WAAW,MAAM;AAC/B,aAAO,IAAI,oCAA0C,gBAAgB,CAAC;AAAA,IACxE,GAAG,GAAI;AAEP,UAAM,WAAW,CAAC,YAAoB;AAEpC,mBAAa,OAAO;AACpB,aAAO,IAAI,WAAW,QAAQ;AAE9B,UAAI;AAEF,cAAM,SAAS,SAAS,KAAK,MAAM,OAAO,CAAC;AAC3C,gBAAQ,MAAM;AAAA,MAChB,SAAS,OAAO;AACd,eAAO,IAAI,oCAA0C,gBAAgB,CAAC;AAAA,MACxE;AAAA,IACF;AAGA,WAAO,GAAG,WAAW,QAAQ;AAAA,EAC/B,CAAC;AACH;","names":["MicdropErrorCode","import_stream","MicdropClientCommands","MicdropServerCommands","import_eventemitter3","import_stream"]}
|
package/dist/index.mjs
CHANGED
|
@@ -217,41 +217,6 @@ var MockAgent = class extends Agent {
|
|
|
217
217
|
}
|
|
218
218
|
};
|
|
219
219
|
|
|
220
|
-
// src/audio-convert.ts
|
|
221
|
-
import ffmpegInstaller from "@ffmpeg-installer/ffmpeg";
|
|
222
|
-
import ffmpeg from "fluent-ffmpeg";
|
|
223
|
-
import { PassThrough as PassThrough2 } from "stream";
|
|
224
|
-
ffmpeg.setFfmpegPath(ffmpegInstaller.path);
|
|
225
|
-
function convertToPCM(audioStream, sampleRate = 16e3, bitDepth = 16) {
|
|
226
|
-
const pcmStream = new PassThrough2();
|
|
227
|
-
ffmpeg(audioStream).audioChannels(1).audioFrequency(sampleRate).audioCodec(`pcm_s${bitDepth}le`).format(`s${bitDepth}le`).on("error", (error) => {
|
|
228
|
-
console.error("Error converting audio stream:", error.message);
|
|
229
|
-
}).pipe(pcmStream);
|
|
230
|
-
return pcmStream;
|
|
231
|
-
}
|
|
232
|
-
function convertToOpus(audioStream, sampleRate = 16e3) {
|
|
233
|
-
const webmStream = new PassThrough2();
|
|
234
|
-
ffmpegToOpus(ffmpeg(audioStream), sampleRate).pipe(webmStream);
|
|
235
|
-
return webmStream;
|
|
236
|
-
}
|
|
237
|
-
function convertPCMToOpus(audioStream, sampleRate = 16e3) {
|
|
238
|
-
const webmStream = new PassThrough2();
|
|
239
|
-
ffmpegToOpus(ffmpeg(audioStream), sampleRate).inputFormat("s16le").inputOptions(["-f s16le", "-ar 16000", "-ac 1"]).pipe(webmStream);
|
|
240
|
-
return webmStream;
|
|
241
|
-
}
|
|
242
|
-
function ffmpegToOpus(ffmpegCommand, sampleRate = 16e3) {
|
|
243
|
-
return ffmpegCommand.audioChannels(1).audioFrequency(sampleRate).audioCodec("libopus").format("webm").outputOptions([
|
|
244
|
-
"-application audio",
|
|
245
|
-
`-ac 1`,
|
|
246
|
-
`-ar ${sampleRate}`,
|
|
247
|
-
`-b:a 64k`,
|
|
248
|
-
`-f webm`,
|
|
249
|
-
`-map_metadata -1`
|
|
250
|
-
]).on("error", (error) => {
|
|
251
|
-
console.error("Error converting to Opus: ", error.message);
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
220
|
// src/errors.ts
|
|
256
221
|
var MicdropErrorCode = /* @__PURE__ */ ((MicdropErrorCode2) => {
|
|
257
222
|
MicdropErrorCode2[MicdropErrorCode2["BadRequest"] = 4400] = "BadRequest";
|
|
@@ -287,7 +252,7 @@ var Logger = class {
|
|
|
287
252
|
};
|
|
288
253
|
|
|
289
254
|
// src/MicdropServer.ts
|
|
290
|
-
import { PassThrough as
|
|
255
|
+
import { PassThrough as PassThrough2 } from "stream";
|
|
291
256
|
|
|
292
257
|
// src/types.ts
|
|
293
258
|
var MicdropClientCommands = /* @__PURE__ */ ((MicdropClientCommands2) => {
|
|
@@ -314,6 +279,7 @@ var MicdropServer = class {
|
|
|
314
279
|
// Queue system for operations
|
|
315
280
|
this.operationQueue = [];
|
|
316
281
|
this.isProcessingQueue = false;
|
|
282
|
+
this.userSpeechChunks = 0;
|
|
317
283
|
this.onClose = () => {
|
|
318
284
|
if (!this.config) return;
|
|
319
285
|
this.log("Connection closed");
|
|
@@ -346,12 +312,15 @@ var MicdropServer = class {
|
|
|
346
312
|
this.onStopSpeaking();
|
|
347
313
|
}
|
|
348
314
|
} else if (this.currentUserStream) {
|
|
349
|
-
this.
|
|
350
|
-
this.currentUserStream.write(message);
|
|
315
|
+
this.onAudioChunk(message);
|
|
351
316
|
}
|
|
352
317
|
};
|
|
353
318
|
this.onTranscript = async (transcript) => {
|
|
354
319
|
if (!this.config) return;
|
|
320
|
+
if (transcript === "") {
|
|
321
|
+
this.socket?.send("SkipAnswer" /* SkipAnswer */);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
355
324
|
this.log(`User transcript: "${transcript}"`);
|
|
356
325
|
this.config.agent.addUserMessage(transcript);
|
|
357
326
|
if (!this.currentUserStream) {
|
|
@@ -419,21 +388,34 @@ var MicdropServer = class {
|
|
|
419
388
|
this.config?.agent.cancel();
|
|
420
389
|
this.operationQueue = [];
|
|
421
390
|
}
|
|
391
|
+
onAudioChunk(chunk) {
|
|
392
|
+
this.log(`Received chunk (${chunk.byteLength} bytes)`);
|
|
393
|
+
this.currentUserStream?.write(chunk);
|
|
394
|
+
this.userSpeechChunks++;
|
|
395
|
+
}
|
|
422
396
|
onMute() {
|
|
397
|
+
this.userSpeechChunks = 0;
|
|
423
398
|
this.currentUserStream?.end();
|
|
424
399
|
this.currentUserStream = void 0;
|
|
425
400
|
this.cancel();
|
|
426
401
|
}
|
|
427
402
|
onStartSpeaking() {
|
|
428
403
|
if (!this.config) return;
|
|
404
|
+
this.userSpeechChunks = 0;
|
|
429
405
|
this.currentUserStream?.end();
|
|
430
|
-
this.currentUserStream = new
|
|
406
|
+
this.currentUserStream = new PassThrough2();
|
|
431
407
|
this.config.stt.transcribe(this.currentUserStream);
|
|
432
408
|
this.cancel();
|
|
433
409
|
}
|
|
434
410
|
onStopSpeaking() {
|
|
411
|
+
const hasNoUserSpeech = !this.currentUserStream || this.userSpeechChunks === 0;
|
|
435
412
|
this.currentUserStream?.end();
|
|
436
413
|
this.currentUserStream = void 0;
|
|
414
|
+
this.userSpeechChunks = 0;
|
|
415
|
+
if (hasNoUserSpeech) {
|
|
416
|
+
this.socket?.send("SkipAnswer" /* SkipAnswer */);
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
437
419
|
const conversation = this.config?.agent.conversation;
|
|
438
420
|
const lastMessage = conversation?.[conversation.length - 1];
|
|
439
421
|
if (lastMessage?.role === "user" && this.lastMessageSpeeched !== lastMessage) {
|
|
@@ -486,7 +468,7 @@ var MicdropServer = class {
|
|
|
486
468
|
if (!this.socket || !this.config) return;
|
|
487
469
|
let textStream;
|
|
488
470
|
if (typeof message === "string") {
|
|
489
|
-
const stream = new
|
|
471
|
+
const stream = new PassThrough2();
|
|
490
472
|
stream.write(message);
|
|
491
473
|
stream.end();
|
|
492
474
|
textStream = stream;
|
|
@@ -526,21 +508,7 @@ var MicdropServer = class {
|
|
|
526
508
|
|
|
527
509
|
// src/stt/STT.ts
|
|
528
510
|
import { EventEmitter as EventEmitter2 } from "eventemitter3";
|
|
529
|
-
var MIME_TYPE_TO_EXTENSION = {
|
|
530
|
-
"audio/wav": "wav",
|
|
531
|
-
"audio/ogg": "ogg",
|
|
532
|
-
"audio/mpeg": "mp3",
|
|
533
|
-
"audio/webm": "webm",
|
|
534
|
-
"audio/mp4": "mp4",
|
|
535
|
-
"audio/flac": "flac"
|
|
536
|
-
};
|
|
537
511
|
var STT = class extends EventEmitter2 {
|
|
538
|
-
// Set stream of audio to transcribe
|
|
539
|
-
transcribe(audioStream) {
|
|
540
|
-
audioStream.once("data", (chunk) => {
|
|
541
|
-
this.mimeType = this.detectMimeType(chunk);
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
512
|
log(...message) {
|
|
545
513
|
this.logger?.log(...message);
|
|
546
514
|
}
|
|
@@ -548,73 +516,24 @@ var STT = class extends EventEmitter2 {
|
|
|
548
516
|
this.log("Destroyed");
|
|
549
517
|
this.removeAllListeners();
|
|
550
518
|
}
|
|
551
|
-
get extension() {
|
|
552
|
-
return this.mimeType && MIME_TYPE_TO_EXTENSION[this.mimeType] || "bin";
|
|
553
|
-
}
|
|
554
|
-
detectMimeType(chunk) {
|
|
555
|
-
if (!chunk || chunk.byteLength === 0) {
|
|
556
|
-
throw new Error("Unable to detect mime type (empty chunk)");
|
|
557
|
-
}
|
|
558
|
-
const arr = new Uint8Array(chunk);
|
|
559
|
-
if (arr[0] === 26 && arr[1] === 69 && arr[2] === 223 && arr[3] === 163) {
|
|
560
|
-
return "audio/webm";
|
|
561
|
-
}
|
|
562
|
-
if (arr[0] === 79 && arr[1] === 103 && arr[2] === 103 && arr[3] === 83) {
|
|
563
|
-
return "audio/ogg";
|
|
564
|
-
}
|
|
565
|
-
if (arr[0] === 82 && arr[1] === 73 && arr[2] === 70 && arr[3] === 70 && arr[8] === 87 && arr[9] === 65 && arr[10] === 86 && arr[11] === 69) {
|
|
566
|
-
return "audio/wav";
|
|
567
|
-
}
|
|
568
|
-
if (arr[0] === 73 && arr[1] === 68 && arr[2] === 51) {
|
|
569
|
-
return "audio/mpeg";
|
|
570
|
-
}
|
|
571
|
-
if (arr[4] === 102 && arr[5] === 116 && arr[6] === 121 && arr[7] === 112) {
|
|
572
|
-
return "audio/mp4";
|
|
573
|
-
}
|
|
574
|
-
if (arr[0] === 102 && arr[1] === 76 && arr[2] === 97 && arr[3] === 67) {
|
|
575
|
-
return "audio/flac";
|
|
576
|
-
}
|
|
577
|
-
this.log("Unable to detect mime type, using default", chunk);
|
|
578
|
-
return "audio/wav";
|
|
579
|
-
}
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
// src/stt/FileSTT.ts
|
|
583
|
-
var FileSTT = class extends STT {
|
|
584
|
-
transcribe(audioStream) {
|
|
585
|
-
super.transcribe(audioStream);
|
|
586
|
-
this.log("Converting stream to file...");
|
|
587
|
-
const chunks = [];
|
|
588
|
-
audioStream.on("data", (chunk) => {
|
|
589
|
-
chunks.push(chunk);
|
|
590
|
-
});
|
|
591
|
-
audioStream.on("end", async () => {
|
|
592
|
-
if (chunks.length === 0) return;
|
|
593
|
-
const arrayBuffer = Buffer.concat(chunks);
|
|
594
|
-
const file = new File([arrayBuffer], `audio.${this.extension}`, {
|
|
595
|
-
type: this.mimeType
|
|
596
|
-
});
|
|
597
|
-
this.log("Transcribing file...");
|
|
598
|
-
const transcript = await this.transcribeFile(file);
|
|
599
|
-
this.emit("Transcript", transcript);
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
519
|
};
|
|
603
520
|
|
|
604
521
|
// src/stt/MockSTT.ts
|
|
605
|
-
var MockSTT = class extends
|
|
522
|
+
var MockSTT = class extends STT {
|
|
606
523
|
constructor() {
|
|
607
524
|
super(...arguments);
|
|
608
525
|
this.i = 0;
|
|
609
526
|
}
|
|
610
|
-
async
|
|
611
|
-
|
|
527
|
+
async transcribe() {
|
|
528
|
+
setTimeout(() => {
|
|
529
|
+
this.emit("Transcript", `User Message ${this.i++}`);
|
|
530
|
+
}, 300);
|
|
612
531
|
}
|
|
613
532
|
};
|
|
614
533
|
|
|
615
534
|
// src/tts/MockTTS.ts
|
|
616
535
|
import * as fs from "fs";
|
|
617
|
-
import { PassThrough as
|
|
536
|
+
import { PassThrough as PassThrough3 } from "stream";
|
|
618
537
|
|
|
619
538
|
// src/tts/TTS.ts
|
|
620
539
|
var TTS = class {
|
|
@@ -634,7 +553,7 @@ var MockTTS = class extends TTS {
|
|
|
634
553
|
this.audioFilePaths = audioFilePaths;
|
|
635
554
|
}
|
|
636
555
|
speak(textStream) {
|
|
637
|
-
const audioStream = new
|
|
556
|
+
const audioStream = new PassThrough3();
|
|
638
557
|
textStream.once("data", async () => {
|
|
639
558
|
for (const filePath of this.audioFilePaths) {
|
|
640
559
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
@@ -677,7 +596,6 @@ export {
|
|
|
677
596
|
AUTO_SEMANTIC_TURN_PROMPT,
|
|
678
597
|
AUTO_SEMANTIC_TURN_TOOL_NAME,
|
|
679
598
|
Agent,
|
|
680
|
-
FileSTT,
|
|
681
599
|
Logger,
|
|
682
600
|
MicdropClientCommands,
|
|
683
601
|
MicdropError,
|
|
@@ -689,9 +607,6 @@ export {
|
|
|
689
607
|
MockTTS,
|
|
690
608
|
STT,
|
|
691
609
|
TTS,
|
|
692
|
-
convertPCMToOpus,
|
|
693
|
-
convertToOpus,
|
|
694
|
-
convertToPCM,
|
|
695
610
|
handleError,
|
|
696
611
|
waitForParams
|
|
697
612
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/audio-convert.ts","../src/errors.ts","../src/Logger.ts","../src/MicdropServer.ts","../src/types.ts","../src/stt/STT.ts","../src/stt/FileSTT.ts","../src/stt/MockSTT.ts","../src/tts/MockTTS.ts","../src/tts/TTS.ts","../src/waitForParams.ts"],"sourcesContent":["import { EventEmitter } from 'eventemitter3'\nimport { 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 ffmpegInstaller from '@ffmpeg-installer/ffmpeg'\nimport ffmpeg from 'fluent-ffmpeg'\nimport { PassThrough, Readable } from 'stream'\n\n// Setup ffmpeg\nffmpeg.setFfmpegPath(ffmpegInstaller.path)\n\n// Convert stream to WAV/PCM\nexport function convertToPCM(\n audioStream: Readable,\n sampleRate = 16000,\n bitDepth = 16\n) {\n const pcmStream = new PassThrough()\n ffmpeg(audioStream)\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec(`pcm_s${bitDepth}le`)\n .format(`s${bitDepth}le`)\n .on('error', (error) => {\n console.error('Error converting audio stream:', error.message)\n })\n .pipe(pcmStream)\n return pcmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate).pipe(webmStream)\n return webmStream\n}\n\n// Convert PCM stream to WebM/Opus\nexport function convertPCMToOpus(audioStream: Readable, sampleRate = 16000) {\n const webmStream = new PassThrough()\n ffmpegToOpus(ffmpeg(audioStream), sampleRate)\n .inputFormat('s16le')\n .inputOptions(['-f s16le', '-ar 16000', '-ac 1'])\n .pipe(webmStream)\n return webmStream\n}\n\nfunction ffmpegToOpus(ffmpegCommand: ffmpeg.FfmpegCommand, sampleRate = 16000) {\n return ffmpegCommand\n .audioChannels(1)\n .audioFrequency(sampleRate)\n .audioCodec('libopus')\n .format('webm')\n .outputOptions([\n '-application audio',\n `-ac 1`,\n `-ar ${sampleRate}`,\n `-b:a 64k`,\n `-f webm`,\n `-map_metadata -1`,\n ])\n .on('error', (error) => {\n console.error('Error converting to Opus: ', error.message)\n })\n}\n","import WebSocket from 'ws'\n\nexport enum MicdropErrorCode {\n BadRequest = 4400,\n Unauthorized = 4401,\n NotFound = 4404,\n}\n\nexport class MicdropError extends Error {\n code: number\n\n constructor(code: number, message: string) {\n super(message)\n this.code = code\n }\n}\n\nexport function handleError(socket: WebSocket, error: unknown) {\n if (error instanceof MicdropError) {\n socket.close(error.code, error.message)\n } else {\n console.error(error)\n socket.close(1011)\n }\n socket.terminate()\n}\n","export class Logger {\n constructor(private readonly name: string) {}\n\n log(...message: any[]) {\n const time = process.uptime().toFixed(3)\n console.log(`[${this.name} ${time}]`, ...message)\n }\n}\n","import { Duplex, PassThrough, Readable } from 'stream'\nimport { WebSocket } from 'ws'\nimport type { Agent } from './agent'\nimport { Logger } from './Logger'\nimport type { STT } from './stt'\nimport type { TTS } from './tts'\nimport {\n MicdropCallSummary,\n MicdropClientCommands,\n MicdropConversationItem,\n MicdropServerCommands,\n} from './types'\n\nexport interface MicdropConfig {\n firstMessage?: string\n generateFirstMessage?: boolean\n agent: Agent\n stt: STT\n tts: TTS\n onEnd?(call: MicdropCallSummary): void\n}\n\nexport class MicdropServer {\n public socket: WebSocket | null = null\n public config: MicdropConfig | null = null\n public logger?: Logger\n\n private startTime = Date.now()\n private lastMessageSpeeched?: MicdropConversationItem\n\n // 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\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.log(`Received chunk (${message.byteLength} bytes)`)\n this.currentUserStream.write(message)\n }\n }\n\n private onMute() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n this.cancel()\n }\n\n private onStartSpeaking() {\n if (!this.config) return\n this.currentUserStream?.end()\n this.currentUserStream = new PassThrough()\n this.config.stt.transcribe(this.currentUserStream)\n this.cancel()\n }\n\n private onStopSpeaking() {\n this.currentUserStream?.end()\n this.currentUserStream = undefined\n\n const conversation = this.config?.agent.conversation\n const lastMessage = conversation?.[conversation.length - 1]\n if (\n lastMessage?.role === 'user' &&\n this.lastMessageSpeeched !== lastMessage\n ) {\n this.log(\n 'User stopped speaking and a transcript already exists, answering'\n )\n this.cancel()\n this.answer()\n }\n }\n\n private onTranscript = async (transcript: string) => {\n if (!this.config) return\n this.log(`User transcript: \"${transcript}\"`)\n this.config.agent.addUserMessage(transcript)\n\n // Answer if user stopped speaking\n if (!this.currentUserStream) {\n this.log('User stopped speaking, answering')\n this.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\n// Audio mime type to extension map\nconst MIME_TYPE_TO_EXTENSION = {\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/mpeg': 'mp3',\n 'audio/webm': 'webm',\n 'audio/mp4': 'mp4',\n 'audio/flac': 'flac',\n} as const\n\nexport interface STTEvents {\n Transcript: [string]\n}\n\nexport abstract class STT extends EventEmitter<STTEvents> {\n protected mimeType?: keyof typeof MIME_TYPE_TO_EXTENSION\n public logger?: Logger\n\n // Set stream of audio to transcribe\n transcribe(audioStream: Readable) {\n // Detect mime type at first chunk\n audioStream.once('data', (chunk) => {\n this.mimeType = this.detectMimeType(chunk)\n })\n }\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.removeAllListeners()\n }\n\n protected get extension(): string {\n return (this.mimeType && MIME_TYPE_TO_EXTENSION[this.mimeType]) || 'bin'\n }\n\n private detectMimeType(\n chunk: ArrayBuffer\n ): keyof typeof MIME_TYPE_TO_EXTENSION {\n if (!chunk || chunk.byteLength === 0) {\n throw new Error('Unable to detect mime type (empty chunk)')\n }\n\n const arr = new Uint8Array(chunk)\n\n // WEBM: 1A 45 DF A3\n if (\n arr[0] === 0x1a &&\n arr[1] === 0x45 &&\n arr[2] === 0xdf &&\n arr[3] === 0xa3\n ) {\n return 'audio/webm'\n }\n // OGG: 4F 67 67 53\n if (\n arr[0] === 0x4f &&\n arr[1] === 0x67 &&\n arr[2] === 0x67 &&\n arr[3] === 0x53\n ) {\n return 'audio/ogg'\n }\n // WAV: 52 49 46 46 ... 57 41 56 45\n if (\n arr[0] === 0x52 &&\n arr[1] === 0x49 &&\n arr[2] === 0x46 &&\n arr[3] === 0x46 &&\n arr[8] === 0x57 &&\n arr[9] === 0x41 &&\n arr[10] === 0x56 &&\n arr[11] === 0x45\n ) {\n return 'audio/wav'\n }\n // MP3: 49 44 33\n if (arr[0] === 0x49 && arr[1] === 0x44 && arr[2] === 0x33) {\n return 'audio/mpeg'\n }\n // MP4/M4A: 00 00 00 .. 66 74 79 70\n if (\n arr[4] === 0x66 &&\n arr[5] === 0x74 &&\n arr[6] === 0x79 &&\n arr[7] === 0x70\n ) {\n return 'audio/mp4'\n }\n // FLAC: 66 4c 61 43\n if (\n arr[0] === 0x66 &&\n arr[1] === 0x4c &&\n arr[2] === 0x61 &&\n arr[3] === 0x43\n ) {\n return 'audio/flac'\n }\n this.log('Unable to detect mime type, using default', chunk)\n return 'audio/wav'\n }\n}\n","import { Readable } from 'stream'\nimport { STT } from './STT'\n\n/**\n * Abstract class for STT, converting stream to file before transcribing\n */\n\nexport abstract class FileSTT extends STT {\n abstract transcribeFile(file: File): Promise<string>\n\n transcribe(audioStream: Readable) {\n super.transcribe(audioStream)\n\n // Convert stream to file\n this.log('Converting stream to file...')\n\n const chunks: Buffer[] = []\n audioStream.on('data', (chunk) => {\n chunks.push(chunk)\n })\n\n audioStream.on('end', async () => {\n if (chunks.length === 0) return\n const arrayBuffer = Buffer.concat(chunks)\n const file = new File([arrayBuffer], `audio.${this.extension}`, {\n type: this.mimeType,\n })\n\n // Transcribe file with implementation\n this.log('Transcribing file...')\n const transcript = await this.transcribeFile(file)\n this.emit('Transcript', transcript)\n })\n }\n}\n","import { FileSTT } from './FileSTT'\n\nexport class MockSTT extends FileSTT {\n private i = 0\n\n async transcribeFile(file: File) {\n return `User Message ${this.i++}`\n }\n}\n","import * as fs from 'fs'\nimport { PassThrough, Readable } from 'stream'\nimport { TTS } from './TTS'\n\nexport class MockTTS extends TTS {\n constructor(private audioFilePaths: string[]) {\n super()\n }\n\n speak(textStream: Readable) {\n const audioStream = new PassThrough()\n textStream.once('data', async () => {\n for (const filePath of this.audioFilePaths) {\n await new Promise((resolve) => setTimeout(resolve, 200))\n const audioBuffer = fs.readFileSync(filePath)\n this.log(`Loaded chunk (${audioBuffer.length} bytes)`)\n audioStream.write(audioBuffer)\n }\n audioStream.end()\n })\n return audioStream\n }\n\n cancel() {}\n}\n","import { Readable } from 'stream'\nimport { Logger } from '../Logger'\n\nexport abstract class TTS {\n public logger?: Logger\n\n abstract speak(textStream: Readable): Readable\n abstract cancel(): void\n\n protected log(...message: any[]) {\n this.logger?.log(...message)\n }\n\n destroy() {\n this.log('Destroyed')\n this.cancel()\n }\n}\n","import { WebSocket } from 'ws'\nimport { MicdropError, MicdropErrorCode } from './errors'\n\nexport async function waitForParams<CallParams>(\n socket: WebSocket,\n validate: (params: any) => CallParams\n): Promise<CallParams> {\n return new Promise<CallParams>((resolve, reject) => {\n // Handle timeout\n const timeout = setTimeout(() => {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Missing params'))\n }, 3000)\n\n const onParams = (payload: string) => {\n // Clear timeout and listener\n clearTimeout(timeout)\n socket.off('message', onParams)\n\n try {\n // Parse JSON payload\n const params = validate(JSON.parse(payload))\n resolve(params)\n } catch (error) {\n reject(new MicdropError(MicdropErrorCode.BadRequest, 'Invalid params'))\n }\n }\n\n // Listen for params\n socket.on('message', onParams)\n })\n}\n"],"mappings":";AAAA,SAAS,oBAAoB;AAC7B,SAAS,mBAAuC;;;ACUzC,IAAM,0BAA0B;AAChC,IAAM,uBACX;AAEK,IAAM,+BAA+B;AACrC,IAAM,4BACX;AAEK,IAAM,mCAAmC;AACzC,IAAM,gCACX;;;ADoDK,IAAe,QAAf,cAEG,aAA0B;AAAA,EAQlC,YAAsB,SAAkB;AACtC,UAAM;AADc;AAHtB,SAAU,cAAc;AACxB,SAAU,YAAY;AAIpB,SAAK,eAAe,CAAC,EAAE,MAAM,UAAU,SAAS,QAAQ,aAAa,CAAC;AACtE,SAAK,QAAQ,KAAK,gBAAgB;AAAA,EACpC;AAAA,EAKA,SAAmB;AACjB,SAAK,IAAI,iBAAiB;AAC1B,UAAM,cAAc,EAAE,KAAK;AAC3B,UAAM,SAAS,IAAI,YAAY;AAC/B,SAAK,YAAY;AAEjB,YAAQ,QAAQ,EAEb,KAAK,MAAM,KAAK,QAAQ,gBAAgB,KAAK,IAAI,EAAE,MAAM,CAAC,EAE1D,KAAK,CAAC,SAAS;AACd,UAAI,KAAM;AACV,aAAO,KAAK,eAAe,MAAM;AAAA,IACnC,CAAC,EAEA,QAAQ,MAAM;AACb,UAAI,OAAO,UAAU;AACnB,eAAO,IAAI;AAAA,MACb;AACA,UAAI,gBAAgB,KAAK,aAAa;AACpC,aAAK,YAAY;AAAA,MACnB;AAAA,IACF,CAAC;AAEH,WAAO;AAAA,EACT;AAAA,EAEA,eAAe,MAAc,UAAkC;AAC7D,SAAK,WAAW,QAAQ,MAAM,QAAQ;AAAA,EACxC;AAAA,EAEA,oBAAoB,MAAc,UAAkC;AAClE,SAAK,WAAW,aAAa,MAAM,QAAQ;AAAA,EAC7C;AAAA,EAEA,QAAoC,MAAoB;AACtD,SAAK,MAAM,KAAK,IAAI;AAAA,EACtB;AAAA,EAEA,WAAW,MAAc;AACvB,UAAM,QAAQ,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,SAAS,IAAI;AAC/D,QAAI,UAAU,IAAI;AAChB,WAAK,MAAM,OAAO,OAAO,CAAC;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,QAAQ,MAAgC;AACtC,WAAO,KAAK,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI;AAAA,EACrD;AAAA,EAEA,WACE,MACA,MACA,UACA;AACA,SAAK,IAAI,UAAU,IAAI,6BAA6B,IAAI,EAAE;AAC1D,UAAM,UAAsC;AAAA,MAC1C;AAAA,MACA,SAAS;AAAA,MACT;AAAA,IACF;AACA,SAAK,aAAa,KAAK,OAAO;AAC9B,SAAK,KAAK,WAAW,OAAO;AAAA,EAC9B;AAAA,EAEA,eACE,SACA;AACA,SAAK,IAAI,wBAAwB,OAAO;AACxC,SAAK,aAAa,KAAK,OAAO;AAC9B,SAAK,KAAK,WAAW,OAAO;AAAA,EAC9B;AAAA,EAEU,UAAU;AAClB,SAAK,IAAI,aAAa;AACtB,SAAK,KAAK,SAAS;AAAA,EACrB;AAAA,EAEU,wBAAwB;AAChC,SAAK,IAAI,8BAA8B;AACvC,UAAM,mBAAmB,KAAK,aAAa;AAAA,MACzC,CAAC,YAAY,QAAQ,SAAS;AAAA,IAChC;AACA,QAAI,qBAAqB,IAAI;AAC3B,WAAK,aAAa,OAAO,kBAAkB,CAAC;AAAA,IAC9C;AACA,SAAK,KAAK,uBAAuB;AAAA,EACnC;AAAA,EAEU,aAAa;AACrB,SAAK,IAAI,iBAAiB;AAC1B,SAAK,KAAK,YAAY;AAAA,EACxB;AAAA,EAEU,kBAAkB;AAC1B,UAAM,QAAgB,CAAC;AACvB,QAAI,KAAK,QAAQ,aAAa;AAC5B,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,aACE,OAAO,KAAK,QAAQ,gBAAgB,WAChC,KAAK,QAAQ,cACb;AAAA,QACN,SAAS,MAAM,KAAK,QAAQ;AAAA,MAC9B,CAAC;AAAA,IACH;AACA,QAAI,KAAK,QAAQ,kBAAkB;AACjC,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,aACE,OAAO,KAAK,QAAQ,qBAAqB,WACrC,KAAK,QAAQ,mBACb;AAAA,QACN,YAAY;AAAA,QACZ,SAAS,MAAM,KAAK,WAAW;AAAA,MACjC,CAAC;AAAA,IACH;AACA,QAAI,KAAK,QAAQ,qBAAqB;AACpC,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,aACE,OAAO,KAAK,QAAQ,wBAAwB,WACxC,KAAK,QAAQ,sBACb;AAAA,QACN,YAAY;AAAA,QACZ,SAAS,MAAM,KAAK,sBAAsB;AAAA,MAC5C,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAgB,YAAY,UAAuC;AACjE,QAAI;AACF,YAAM,OAAO,KAAK,QAAQ,SAAS,QAAQ;AAC3C,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,MAAM,mBAAmB,SAAS,QAAQ,GAAG;AAAA,MACzD;AAEA,WAAK,IAAI,mBAAmB,SAAS,UAAU,SAAS,UAAU;AAGlE,WAAK,eAAe,QAAQ;AAE5B,YAAM,aAAa,KAAK,MAAM,SAAS,UAAU;AACjD,YAAM,SAAS,KAAK,UAAU,MAAM,KAAK,QAAQ,UAAU,IAAI,CAAC;AAGhE,WAAK,eAAe;AAAA,QAClB,MAAM;AAAA,QACN,YAAY,SAAS;AAAA,QACrB,UAAU,SAAS;AAAA,QACnB,QAAQ,KAAK,UAAU,UAAU,IAAI;AAAA,MACvC,CAAC;AAGD,UAAI,KAAK,YAAY;AACnB,aAAK,KAAK,YAAY;AAAA,UACpB,MAAM,SAAS;AAAA,UACf;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL;AAAA,QACA,YAAY,KAAK;AAAA,MACnB;AAAA,IACF,SAAS,OAAY;AACnB,cAAQ,MAAM,uCAAuC,KAAK;AAC1D,aAAO;AAAA,QACL,QAAQ;AAAA,UACN,OAAO,MAAM;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEU,oBAAmD;AAC3D,UAAM,UAAU,KAAK,QAAQ;AAC7B,QAAI,CAAC,QAAS,QAAO;AACrB,QAAI,UAAU,WAAW,QAAQ,MAAM;AACrC,aAAO,EAAE,GAAG,SAAS,UAAU,KAAK,QAAQ,IAAI;AAAA,IAClD;AACA,QAAI,cAAc,WAAW,YAAY,SAAS;AAChD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EAEO,QAAQ,SAAiB;AAC9B,UAAM,iBAAiB,KAAK,kBAAkB;AAC9C,QAAI,WAA8C;AAGlD,QAAI,gBAAgB;AAClB,YAAM,gBAAgB,QAAQ,QAAQ,eAAe,QAAQ;AAC7D,UAAI,kBAAkB,IAAI;AAExB,YAAI,cAAc,QAAQ,YAAY,eAAe,MAAM;AAC3D,YAAI,gBAAgB,GAAI,eAAc,QAAQ,SAAS;AAAA,YAClD,gBAAe,eAAe,OAAO;AAC1C,cAAM,gBAAgB,QAAQ,MAAM,eAAe,WAAW,EAAE,KAAK;AAGrE,YAAI;AACF,gBAAM,iBACJ,UAAU,kBAAkB,eAAe,OACvC,KAAK,MAAM,aAAa,IACxB;AAGN,cAAI,eAAe,UAAU;AAC3B,2BAAe,SAAS,cAAc;AAAA,UACxC;AAGA,cAAI,eAAe,gBAAgB;AACjC,uBAAW,EAAE,WAAW,eAAe;AAAA,UACzC;AAAA,QACF,SAAS,OAAO;AACd,kBAAQ;AAAA,YACN,gDAAgD,aAAa;AAAA,YAC7D;AAAA,UACF;AAAA,QACF;AAGA,kBAAU,QAAQ,MAAM,GAAG,aAAa,EAAE,QAAQ;AAAA,MACpD;AAAA,IACF;AACA,WAAO,EAAE,SAAS,SAAS;AAAA,EAC7B;AAAA,EAEU,OAAO,SAAgB;AAC/B,SAAK,QAAQ,IAAI,GAAG,OAAO;AAAA,EAC7B;AAAA,EAEA,UAAU;AACR,SAAK,IAAI,WAAW;AACpB,SAAK,mBAAmB;AACxB,SAAK,OAAO;AAAA,EACd;AACF;;;AE3UO,IAAM,YAAN,cAAwB,MAAM;AAAA,EAGnC,cAAc;AACZ,UAAM,EAAE,cAAc,GAAG,CAAC;AAH5B,SAAQ,IAAI;AAAA,EAIZ;AAAA,EAEA,MAAgB,eAAe,QAAoC;AACjE,UAAM,UAAU,qBAAqB,KAAK,GAAG;AAC7C,SAAK,oBAAoB,OAAO;AAChC,WAAO,MAAM,OAAO;AAAA,EACtB;AAAA,EAEA,SAAS;AAAA,EAAC;AACZ;;;ACjBA,OAAO,qBAAqB;AAC5B,OAAO,YAAY;AACnB,SAAS,eAAAA,oBAA6B;AAGtC,OAAO,cAAc,gBAAgB,IAAI;AAGlC,SAAS,aACd,aACA,aAAa,MACb,WAAW,IACX;AACA,QAAM,YAAY,IAAIA,aAAY;AAClC,SAAO,WAAW,EACf,cAAc,CAAC,EACf,eAAe,UAAU,EACzB,WAAW,QAAQ,QAAQ,IAAI,EAC/B,OAAO,IAAI,QAAQ,IAAI,EACvB,GAAG,SAAS,CAAC,UAAU;AACtB,YAAQ,MAAM,kCAAkC,MAAM,OAAO;AAAA,EAC/D,CAAC,EACA,KAAK,SAAS;AACjB,SAAO;AACT;AAGO,SAAS,cAAc,aAAuB,aAAa,MAAO;AACvE,QAAM,aAAa,IAAIA,aAAY;AACnC,eAAa,OAAO,WAAW,GAAG,UAAU,EAAE,KAAK,UAAU;AAC7D,SAAO;AACT;AAGO,SAAS,iBAAiB,aAAuB,aAAa,MAAO;AAC1E,QAAM,aAAa,IAAIA,aAAY;AACnC,eAAa,OAAO,WAAW,GAAG,UAAU,EACzC,YAAY,OAAO,EACnB,aAAa,CAAC,YAAY,aAAa,OAAO,CAAC,EAC/C,KAAK,UAAU;AAClB,SAAO;AACT;AAEA,SAAS,aAAa,eAAqC,aAAa,MAAO;AAC7E,SAAO,cACJ,cAAc,CAAC,EACf,eAAe,UAAU,EACzB,WAAW,SAAS,EACpB,OAAO,MAAM,EACb,cAAc;AAAA,IACb;AAAA,IACA;AAAA,IACA,OAAO,UAAU;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EACA,GAAG,SAAS,CAAC,UAAU;AACtB,YAAQ,MAAM,8BAA8B,MAAM,OAAO;AAAA,EAC3D,CAAC;AACL;;;AC1DO,IAAK,mBAAL,kBAAKC,sBAAL;AACL,EAAAA,oCAAA,gBAAa,QAAb;AACA,EAAAA,oCAAA,kBAAe,QAAf;AACA,EAAAA,oCAAA,cAAW,QAAX;AAHU,SAAAA;AAAA,GAAA;AAML,IAAM,eAAN,cAA2B,MAAM;AAAA,EAGtC,YAAY,MAAc,SAAiB;AACzC,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,YAAY,QAAmB,OAAgB;AAC7D,MAAI,iBAAiB,cAAc;AACjC,WAAO,MAAM,MAAM,MAAM,MAAM,OAAO;AAAA,EACxC,OAAO;AACL,YAAQ,MAAM,KAAK;AACnB,WAAO,MAAM,IAAI;AAAA,EACnB;AACA,SAAO,UAAU;AACnB;;;ACzBO,IAAM,SAAN,MAAa;AAAA,EAClB,YAA6B,MAAc;AAAd;AAAA,EAAe;AAAA,EAE5C,OAAO,SAAgB;AACrB,UAAM,OAAO,QAAQ,OAAO,EAAE,QAAQ,CAAC;AACvC,YAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,OAAO;AAAA,EAClD;AACF;;;ACPA,SAAiB,eAAAC,oBAA6B;;;ACAvC,IAAK,wBAAL,kBAAKC,2BAAL;AACL,EAAAA,uBAAA,mBAAgB;AAChB,EAAAA,uBAAA,kBAAe;AACf,EAAAA,uBAAA,UAAO;AAHG,SAAAA;AAAA,GAAA;AAML,IAAK,wBAAL,kBAAKC,2BAAL;AACL,EAAAA,uBAAA,aAAU;AACV,EAAAA,uBAAA,2BAAwB;AACxB,EAAAA,uBAAA,gBAAa;AACb,EAAAA,uBAAA,aAAU;AACV,EAAAA,uBAAA,cAAW;AALD,SAAAA;AAAA,GAAA;;;ADgBL,IAAM,gBAAN,MAAoB;AAAA,EAezB,YAAY,QAAmB,QAAuB;AAdtD,SAAO,SAA2B;AAClC,SAAO,SAA+B;AAGtC,SAAQ,YAAY,KAAK,IAAI;AAI7B;AAAA,SAAQ,iBAA6C,CAAC;AACtD,SAAQ,oBAAoB;AA6E5B,SAAQ,UAAU,MAAM;AACtB,UAAI,CAAC,KAAK,OAAQ;AAClB,WAAK,IAAI,mBAAmB;AAC5B,YAAM,WAAW,KAAK,OAAO,KAAK,IAAI,IAAI,KAAK,aAAa,GAAI;AAGhE,WAAK,OAAO,MAAM,QAAQ;AAC1B,WAAK,OAAO,IAAI,QAAQ;AACxB,WAAK,OAAO,IAAI,QAAQ;AAGxB,WAAK,OAAO,QAAQ;AAAA,QAClB,cAAc,KAAK,OAAO,MAAM,aAAa,MAAM,CAAC;AAAA;AAAA,QACpD;AAAA,MACF,CAAC;AAGD,WAAK,SAAS;AACd,WAAK,SAAS;AAAA,IAChB;AAEA,SAAQ,YAAY,OAAO,YAAoB;AAC7C,UAAI,QAAQ,eAAe,EAAG;AAC9B,UAAI,CAAC,OAAO,SAAS,OAAO,GAAG;AAC7B,aAAK,IAAI,yBAAyB;AAClC;AAAA,MACF;AAGA,UAAI,QAAQ,aAAa,IAAI;AAC3B,cAAM,MAAM,QAAQ,SAAS;AAC7B,aAAK,IAAI,YAAY,GAAG,EAAE;AAE1B,YAAI,6CAA6C;AAE/C,eAAK,gBAAgB;AAAA,QACvB,WAAW,2BAAoC;AAE7C,eAAK,OAAO;AAAA,QACd,WAAW,2CAA4C;AAErD,eAAK,eAAe;AAAA,QACtB;AAAA,MACF,WAGS,KAAK,mBAAmB;AAC/B,aAAK,IAAI,mBAAmB,QAAQ,UAAU,SAAS;AACvD,aAAK,kBAAkB,MAAM,OAAO;AAAA,MACtC;AAAA,IACF;AAkCA,SAAQ,eAAe,OAAO,eAAuB;AACnD,UAAI,CAAC,KAAK,OAAQ;AAClB,WAAK,IAAI,qBAAqB,UAAU,GAAG;AAC3C,WAAK,OAAO,MAAM,eAAe,UAAU;AAG3C,UAAI,CAAC,KAAK,mBAAmB;AAC3B,aAAK,IAAI,kCAAkC;AAC3C,aAAK,OAAO;AACZ,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAtKE,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,IAAI,cAAc;AAGvB,SAAK,OAAO,IAAI,GAAG,cAAc,KAAK,YAAY;AAGlD,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAW,CAAC,YAC/B,KAAK,QAAQ;AAAA,QACX,0BAAgC,IAAI,KAAK,UAAU,OAAO,CAAC;AAAA,MAC7D;AAAA,IACF;AACA,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAyB,MAC5C,KAAK,QAAQ,wDAAgD;AAAA,IAC/D;AACA,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAc,MACjC,KAAK,QAAQ,kCAAqC;AAAA,IACpD;AACA,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAW,MAC9B,KAAK,QAAQ,4BAAkC;AAAA,IACjD;AACA,SAAK,OAAO,MAAM;AAAA,MAAG;AAAA,MAAY,CAAC,aAChC,KAAK,QAAQ;AAAA,QACX,4BAAiC,IAAI,KAAK,UAAU,QAAQ,CAAC;AAAA,MAC/D;AAAA,IACF;AAGA,SAAK,iBAAiB;AAGtB,WAAO,GAAG,SAAS,KAAK,OAAO;AAC/B,WAAO,GAAG,WAAW,KAAK,SAAS;AAAA,EACrC;AAAA,EAEQ,OAAO,SAAgB;AAC7B,SAAK,QAAQ,IAAI,GAAG,OAAO;AAAA,EAC7B;AAAA,EAEA,MAAc,eAAe;AAC3B,QAAI,KAAK,qBAAqB,KAAK,eAAe,WAAW,EAAG;AAEhE,SAAK,oBAAoB;AAEzB,WAAO,KAAK,eAAe,SAAS,GAAG;AACrC,YAAM,YAAY,KAAK,eAAe,MAAM;AAC5C,UAAI,WAAW;AACb,YAAI;AACF,gBAAM,UAAU;AAAA,QAClB,SAAS,OAAO;AACd,eAAK,IAAI,sCAAsC,KAAK;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AAEA,SAAK,oBAAoB;AAAA,EAC3B;AAAA,EAEQ,eAAe,WAAgC;AACrD,SAAK,eAAe,KAAK,SAAS;AAClC,SAAK,aAAa;AAAA,EACpB;AAAA,EAEO,SAAS;AACd,SAAK,QAAQ,IAAI,OAAO;AACxB,SAAK,QAAQ,MAAM,OAAO;AAE1B,SAAK,iBAAiB,CAAC;AAAA,EACzB;AAAA,EAsDQ,SAAS;AACf,SAAK,mBAAmB,IAAI;AAC5B,SAAK,oBAAoB;AACzB,SAAK,OAAO;AAAA,EACd;AAAA,EAEQ,kBAAkB;AACxB,QAAI,CAAC,KAAK,OAAQ;AAClB,SAAK,mBAAmB,IAAI;AAC5B,SAAK,oBAAoB,IAAIC,aAAY;AACzC,SAAK,OAAO,IAAI,WAAW,KAAK,iBAAiB;AACjD,SAAK,OAAO;AAAA,EACd;AAAA,EAEQ,iBAAiB;AACvB,SAAK,mBAAmB,IAAI;AAC5B,SAAK,oBAAoB;AAEzB,UAAM,eAAe,KAAK,QAAQ,MAAM;AACxC,UAAM,cAAc,eAAe,aAAa,SAAS,CAAC;AAC1D,QACE,aAAa,SAAS,UACtB,KAAK,wBAAwB,aAC7B;AACA,WAAK;AAAA,QACH;AAAA,MACF;AACA,WAAK,OAAO;AACZ,WAAK,OAAO;AAAA,IACd;AAAA,EACF;AAAA,EAeQ,mBAAmB;AACzB,QAAI,CAAC,KAAK,OAAQ;AAClB,QAAI,KAAK,OAAO,cAAc;AAE5B,WAAK,OAAO,MAAM,oBAAoB,KAAK,OAAO,YAAY;AAC9D,WAAK,MAAM,KAAK,OAAO,YAAY;AAAA,IACrC,WAAW,KAAK,OAAO,sBAAsB;AAE3C,WAAK,OAAO;AAAA,IACd,OAAO;AAGL,WAAK,QAAQ,kCAAqC;AAAA,IACpD;AAAA,EACF;AAAA,EAEO,SAAS;AACd,SAAK,eAAe,YAAY;AAC9B,YAAM,KAAK,QAAQ;AAAA,IACrB,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,UAAU;AACtB,QAAI,CAAC,KAAK,OAAQ;AAGlB,UAAM,cACJ,KAAK,OAAO,MAAM,aAAa,KAAK,OAAO,MAAM,aAAa,SAAS,CAAC;AAC1E,QAAI,KAAK,wBAAwB,aAAa;AAC5C,WAAK,IAAI,4BAA4B;AACrC;AAAA,IACF;AACA,SAAK,sBAAsB;AAE3B,QAAI;AAEF,YAAM,SAAS,KAAK,OAAO,MAAM,OAAO;AAGxC,YAAM,KAAK,OAAO,MAAM;AAAA,IAC1B,SAAS,OAAO;AACd,WAAK,QAAQ,kCAAqC;AAClD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA,EAGO,MAAM,SAA4B;AACvC,SAAK,eAAe,YAAY;AAC9B,YAAM,KAAK,OAAO,OAAO;AAAA,IAC3B,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,OAAO,SAA4B;AAC/C,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,OAAQ;AAGlC,QAAI;AACJ,QAAI,OAAO,YAAY,UAAU;AAC/B,YAAM,SAAS,IAAIA,aAAY;AAC/B,aAAO,MAAM,OAAO;AACpB,aAAO,IAAI;AACX,mBAAa;AAAA,IACf,OAAO;AACL,mBAAa;AAAA,IACf;AAGA,UAAM,QAAQ,KAAK,OAAO,IAAI,MAAM,UAAU;AAG9C,UAAM,KAAK,WAAW,KAAK;AAAA,EAC7B;AAAA,EAEO,UAAU,OAAiB;AAChC,SAAK,eAAe,YAAY;AAC9B,YAAM,KAAK,WAAW,KAAK;AAAA,IAC7B,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,WAAW,OAAiB;AACxC,QAAI,CAAC,KAAK,OAAQ;AAClB,QAAI,CAAC,MAAM,UAAU;AACnB,WAAK,IAAI,gCAAgC,KAAK;AAC9C;AAAA,IACF;AAGA,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,GAAG,QAAQ,CAAC,UAAU;AAC1B,aAAK,IAAI,qBAAqB,MAAM,UAAU,SAAS;AACvD,aAAK,QAAQ,KAAK,KAAK;AAAA,MACzB,CAAC;AACD,YAAM,GAAG,SAAS,CAAC,UAAU;AAC3B,aAAK,IAAI,yBAAyB,KAAK;AACvC,eAAO,KAAK;AAAA,MACd,CAAC;AACD,YAAM,GAAG,OAAO,MAAM;AACpB,aAAK,IAAI,oBAAoB;AAC7B,gBAAQ;AAAA,MACV,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;;;AErTA,SAAS,gBAAAC,qBAAoB;AAK7B,IAAM,yBAAyB;AAAA,EAC7B,aAAa;AAAA,EACb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAChB;AAMO,IAAe,MAAf,cAA2BA,cAAwB;AAAA;AAAA,EAKxD,WAAW,aAAuB;AAEhC,gBAAY,KAAK,QAAQ,CAAC,UAAU;AAClC,WAAK,WAAW,KAAK,eAAe,KAAK;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEU,OAAO,SAAgB;AAC/B,SAAK,QAAQ,IAAI,GAAG,OAAO;AAAA,EAC7B;AAAA,EAEA,UAAU;AACR,SAAK,IAAI,WAAW;AACpB,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEA,IAAc,YAAoB;AAChC,WAAQ,KAAK,YAAY,uBAAuB,KAAK,QAAQ,KAAM;AAAA,EACrE;AAAA,EAEQ,eACN,OACqC;AACrC,QAAI,CAAC,SAAS,MAAM,eAAe,GAAG;AACpC,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,UAAM,MAAM,IAAI,WAAW,KAAK;AAGhC,QACE,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,OACX,IAAI,CAAC,MAAM,KACX;AACA,aAAO;AAAA,IACT;AAEA,QACE,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,OACX,IAAI,CAAC,MAAM,OACX,IAAI,CAAC,MAAM,IACX;AACA,aAAO;AAAA,IACT;AAEA,QACE,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,EAAE,MAAM,MACZ,IAAI,EAAE,MAAM,IACZ;AACA,aAAO;AAAA,IACT;AAEA,QAAI,IAAI,CAAC,MAAM,MAAQ,IAAI,CAAC,MAAM,MAAQ,IAAI,CAAC,MAAM,IAAM;AACzD,aAAO;AAAA,IACT;AAEA,QACE,IAAI,CAAC,MAAM,OACX,IAAI,CAAC,MAAM,OACX,IAAI,CAAC,MAAM,OACX,IAAI,CAAC,MAAM,KACX;AACA,aAAO;AAAA,IACT;AAEA,QACE,IAAI,CAAC,MAAM,OACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,MACX,IAAI,CAAC,MAAM,IACX;AACA,aAAO;AAAA,IACT;AACA,SAAK,IAAI,6CAA6C,KAAK;AAC3D,WAAO;AAAA,EACT;AACF;;;ACrGO,IAAe,UAAf,cAA+B,IAAI;AAAA,EAGxC,WAAW,aAAuB;AAChC,UAAM,WAAW,WAAW;AAG5B,SAAK,IAAI,8BAA8B;AAEvC,UAAM,SAAmB,CAAC;AAC1B,gBAAY,GAAG,QAAQ,CAAC,UAAU;AAChC,aAAO,KAAK,KAAK;AAAA,IACnB,CAAC;AAED,gBAAY,GAAG,OAAO,YAAY;AAChC,UAAI,OAAO,WAAW,EAAG;AACzB,YAAM,cAAc,OAAO,OAAO,MAAM;AACxC,YAAM,OAAO,IAAI,KAAK,CAAC,WAAW,GAAG,SAAS,KAAK,SAAS,IAAI;AAAA,QAC9D,MAAM,KAAK;AAAA,MACb,CAAC;AAGD,WAAK,IAAI,sBAAsB;AAC/B,YAAM,aAAa,MAAM,KAAK,eAAe,IAAI;AACjD,WAAK,KAAK,cAAc,UAAU;AAAA,IACpC,CAAC;AAAA,EACH;AACF;;;AChCO,IAAM,UAAN,cAAsB,QAAQ;AAAA,EAA9B;AAAA;AACL,SAAQ,IAAI;AAAA;AAAA,EAEZ,MAAM,eAAe,MAAY;AAC/B,WAAO,gBAAgB,KAAK,GAAG;AAAA,EACjC;AACF;;;ACRA,YAAY,QAAQ;AACpB,SAAS,eAAAC,oBAA6B;;;ACE/B,IAAe,MAAf,MAAmB;AAAA,EAMd,OAAO,SAAgB;AAC/B,SAAK,QAAQ,IAAI,GAAG,OAAO;AAAA,EAC7B;AAAA,EAEA,UAAU;AACR,SAAK,IAAI,WAAW;AACpB,SAAK,OAAO;AAAA,EACd;AACF;;;ADbO,IAAM,UAAN,cAAsB,IAAI;AAAA,EAC/B,YAAoB,gBAA0B;AAC5C,UAAM;AADY;AAAA,EAEpB;AAAA,EAEA,MAAM,YAAsB;AAC1B,UAAM,cAAc,IAAIC,aAAY;AACpC,eAAW,KAAK,QAAQ,YAAY;AAClC,iBAAW,YAAY,KAAK,gBAAgB;AAC1C,cAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG,CAAC;AACvD,cAAM,cAAiB,gBAAa,QAAQ;AAC5C,aAAK,IAAI,iBAAiB,YAAY,MAAM,SAAS;AACrD,oBAAY,MAAM,WAAW;AAAA,MAC/B;AACA,kBAAY,IAAI;AAAA,IAClB,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,SAAS;AAAA,EAAC;AACZ;;;AErBA,eAAsB,cACpB,QACA,UACqB;AACrB,SAAO,IAAI,QAAoB,CAAC,SAAS,WAAW;AAElD,UAAM,UAAU,WAAW,MAAM;AAC/B,aAAO,IAAI,oCAA0C,gBAAgB,CAAC;AAAA,IACxE,GAAG,GAAI;AAEP,UAAM,WAAW,CAAC,YAAoB;AAEpC,mBAAa,OAAO;AACpB,aAAO,IAAI,WAAW,QAAQ;AAE9B,UAAI;AAEF,cAAM,SAAS,SAAS,KAAK,MAAM,OAAO,CAAC;AAC3C,gBAAQ,MAAM;AAAA,MAChB,SAAS,OAAO;AACd,eAAO,IAAI,oCAA0C,gBAAgB,CAAC;AAAA,MACxE;AAAA,IACF;AAGA,WAAO,GAAG,WAAW,QAAQ;AAAA,EAC/B,CAAC;AACH;","names":["PassThrough","MicdropErrorCode","PassThrough","MicdropClientCommands","MicdropServerCommands","PassThrough","EventEmitter","PassThrough","PassThrough"]}
|
|
1
|
+
{"version":3,"sources":["../src/agent/Agent.ts","../src/agent/tools.ts","../src/agent/MockAgent.ts","../src/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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@micdrop/server",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "🖐️🎤 Micdrop: Real-Time Voice Conversations with AI",
|
|
5
5
|
"author": "Lonestone",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,13 +33,10 @@
|
|
|
33
33
|
"conversation"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
37
36
|
"eventemitter3": "^5.0.1",
|
|
38
|
-
"fluent-ffmpeg": "^2.1.3",
|
|
39
37
|
"ws": "^8.18.2"
|
|
40
38
|
},
|
|
41
39
|
"devDependencies": {
|
|
42
|
-
"@types/fluent-ffmpeg": "^2.1.27",
|
|
43
40
|
"@types/node": "^22.13.4",
|
|
44
41
|
"@types/ws": "^8.18.1",
|
|
45
42
|
"tsup": "^8.3.6",
|