@dtelecom/agents-js 0.1.17 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +26 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +26 -15
- package/dist/index.mjs.map +1 -1
- package/dist/memory/index.d.mts +1 -1
- package/dist/memory/index.d.ts +1 -1
- package/dist/providers/index.d.mts +49 -4
- package/dist/providers/index.d.ts +49 -4
- package/dist/providers/index.js +272 -61
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/index.mjs +271 -61
- package/dist/providers/index.mjs.map +1 -1
- package/dist/{types-AWgTLEQ-.d.mts → types-BBKtiPvm.d.mts} +4 -2
- package/dist/{types-AWgTLEQ-.d.ts → types-BBKtiPvm.d.ts} +4 -2
- package/package.json +1 -1
package/dist/providers/index.mjs
CHANGED
|
@@ -403,9 +403,198 @@ var OpenRouterLLM = class {
|
|
|
403
403
|
}
|
|
404
404
|
};
|
|
405
405
|
|
|
406
|
+
// src/providers/openai-llm.ts
|
|
407
|
+
var log3 = createLogger("OpenAILLM");
|
|
408
|
+
var OPENAI_URL = "https://api.openai.com/v1/chat/completions";
|
|
409
|
+
var OpenAILLM = class {
|
|
410
|
+
apiKey;
|
|
411
|
+
model;
|
|
412
|
+
maxTokens;
|
|
413
|
+
temperature;
|
|
414
|
+
responseFormat;
|
|
415
|
+
constructor(options) {
|
|
416
|
+
if (!options.apiKey) {
|
|
417
|
+
throw new Error("OpenAILLM requires an apiKey");
|
|
418
|
+
}
|
|
419
|
+
this.apiKey = options.apiKey;
|
|
420
|
+
this.model = options.model;
|
|
421
|
+
this.maxTokens = options.maxTokens ?? 512;
|
|
422
|
+
this.temperature = options.temperature ?? 0.7;
|
|
423
|
+
this.responseFormat = options.responseFormat;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Warm up the LLM by sending the system prompt and a short message.
|
|
427
|
+
* Primes the HTTP/TLS connection and model loading on the provider side.
|
|
428
|
+
*/
|
|
429
|
+
async warmup(systemPrompt) {
|
|
430
|
+
log3.info("Warming up LLM connection...");
|
|
431
|
+
const start = performance.now();
|
|
432
|
+
const messages = [
|
|
433
|
+
{ role: "system", content: systemPrompt },
|
|
434
|
+
{ role: "user", content: "Hello" }
|
|
435
|
+
];
|
|
436
|
+
try {
|
|
437
|
+
const gen = this.chat(messages);
|
|
438
|
+
for await (const chunk of gen) {
|
|
439
|
+
if (chunk.type === "done") break;
|
|
440
|
+
}
|
|
441
|
+
log3.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
442
|
+
} catch (err) {
|
|
443
|
+
log3.warn("LLM warmup failed (non-fatal):", err);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
async *chat(messages, signal, options) {
|
|
447
|
+
const body = {
|
|
448
|
+
model: this.model,
|
|
449
|
+
messages,
|
|
450
|
+
max_tokens: this.maxTokens,
|
|
451
|
+
temperature: this.temperature,
|
|
452
|
+
stream: true,
|
|
453
|
+
stream_options: { include_usage: true }
|
|
454
|
+
};
|
|
455
|
+
if (this.responseFormat && !options?.plainText) {
|
|
456
|
+
body.response_format = this.responseFormat;
|
|
457
|
+
}
|
|
458
|
+
log3.debug(`LLM request: model=${this.model}, messages=${messages.length}`);
|
|
459
|
+
const response = await fetch(OPENAI_URL, {
|
|
460
|
+
method: "POST",
|
|
461
|
+
headers: {
|
|
462
|
+
"Content-Type": "application/json",
|
|
463
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
464
|
+
},
|
|
465
|
+
body: JSON.stringify(body),
|
|
466
|
+
signal
|
|
467
|
+
});
|
|
468
|
+
if (!response.ok) {
|
|
469
|
+
const errorText = await response.text();
|
|
470
|
+
throw new Error(`OpenAI API error ${response.status}: ${errorText}`);
|
|
471
|
+
}
|
|
472
|
+
if (!response.body) {
|
|
473
|
+
throw new Error("OpenAI response has no body");
|
|
474
|
+
}
|
|
475
|
+
const reader = response.body.getReader();
|
|
476
|
+
const decoder = new TextDecoder();
|
|
477
|
+
let buffer = "";
|
|
478
|
+
const structured = !!this.responseFormat && !options?.plainText;
|
|
479
|
+
let jsonBuffer = "";
|
|
480
|
+
let segmentsYielded = false;
|
|
481
|
+
let lastUsage;
|
|
482
|
+
let inSegmentsArray = false;
|
|
483
|
+
let objectStart = -1;
|
|
484
|
+
let braceDepth = 0;
|
|
485
|
+
let scanIndex = 0;
|
|
486
|
+
let inString = false;
|
|
487
|
+
let escaped = false;
|
|
488
|
+
function extractSegments(buf) {
|
|
489
|
+
const results = [];
|
|
490
|
+
for (let i = scanIndex; i < buf.length; i++) {
|
|
491
|
+
const ch = buf[i];
|
|
492
|
+
if (escaped) {
|
|
493
|
+
escaped = false;
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
if (ch === "\\" && inString) {
|
|
497
|
+
escaped = true;
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
if (ch === '"') {
|
|
501
|
+
inString = !inString;
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
if (inString) continue;
|
|
505
|
+
if (!inSegmentsArray) {
|
|
506
|
+
if (ch === "[") {
|
|
507
|
+
const before = buf.slice(0, i).trimEnd();
|
|
508
|
+
if (before.endsWith(":") && buf.slice(0, i).includes('"segments"')) {
|
|
509
|
+
inSegmentsArray = true;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (ch === "{") {
|
|
515
|
+
if (braceDepth === 0) objectStart = i;
|
|
516
|
+
braceDepth++;
|
|
517
|
+
} else if (ch === "}") {
|
|
518
|
+
braceDepth--;
|
|
519
|
+
if (braceDepth === 0 && objectStart >= 0) {
|
|
520
|
+
const objStr = buf.slice(objectStart, i + 1);
|
|
521
|
+
try {
|
|
522
|
+
const seg = JSON.parse(objStr);
|
|
523
|
+
if (seg.lang && seg.text) {
|
|
524
|
+
results.push({ lang: seg.lang, text: seg.text });
|
|
525
|
+
}
|
|
526
|
+
} catch {
|
|
527
|
+
}
|
|
528
|
+
objectStart = -1;
|
|
529
|
+
}
|
|
530
|
+
} else if (ch === "]" && braceDepth === 0) {
|
|
531
|
+
inSegmentsArray = false;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
scanIndex = buf.length;
|
|
535
|
+
return results;
|
|
536
|
+
}
|
|
537
|
+
try {
|
|
538
|
+
while (true) {
|
|
539
|
+
if (signal?.aborted) break;
|
|
540
|
+
const { done, value } = await reader.read();
|
|
541
|
+
if (done) break;
|
|
542
|
+
buffer += decoder.decode(value, { stream: true });
|
|
543
|
+
const lines = buffer.split("\n");
|
|
544
|
+
buffer = lines.pop() ?? "";
|
|
545
|
+
for (const line of lines) {
|
|
546
|
+
const trimmed = line.trim();
|
|
547
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
548
|
+
const data = trimmed.slice(6);
|
|
549
|
+
if (data === "[DONE]") break;
|
|
550
|
+
try {
|
|
551
|
+
const parsed = JSON.parse(data);
|
|
552
|
+
const choice = parsed.choices?.[0];
|
|
553
|
+
if (!choice) {
|
|
554
|
+
if (parsed.usage) {
|
|
555
|
+
lastUsage = {
|
|
556
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
557
|
+
completionTokens: parsed.usage.completion_tokens
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
const delta = choice.delta;
|
|
563
|
+
if (delta?.content) {
|
|
564
|
+
if (structured) {
|
|
565
|
+
jsonBuffer += delta.content;
|
|
566
|
+
const segments = extractSegments(jsonBuffer);
|
|
567
|
+
for (const seg of segments) {
|
|
568
|
+
yield { type: "segment", segment: seg };
|
|
569
|
+
segmentsYielded = true;
|
|
570
|
+
}
|
|
571
|
+
} else {
|
|
572
|
+
yield { type: "token", token: delta.content };
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (parsed.usage) {
|
|
576
|
+
lastUsage = {
|
|
577
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
578
|
+
completionTokens: parsed.usage.completion_tokens
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
} catch {
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
} finally {
|
|
586
|
+
reader.releaseLock();
|
|
587
|
+
}
|
|
588
|
+
if (structured && !segmentsYielded && jsonBuffer.length > 0) {
|
|
589
|
+
log3.warn(`LLM returned no segments. Raw JSON: "${jsonBuffer.slice(0, 300)}"`);
|
|
590
|
+
}
|
|
591
|
+
yield { type: "done", ...lastUsage ? { usage: lastUsage } : {} };
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
|
|
406
595
|
// src/providers/cartesia-tts.ts
|
|
407
596
|
import WebSocket2 from "ws";
|
|
408
|
-
var
|
|
597
|
+
var log4 = createLogger("CartesiaTTS");
|
|
409
598
|
var CARTESIA_WS_BASE = "wss://api.cartesia.ai/tts/websocket";
|
|
410
599
|
var DEFAULT_API_VERSION = "2024-06-10";
|
|
411
600
|
var DEFAULT_MODEL = "sonic-3";
|
|
@@ -444,7 +633,7 @@ var CartesiaTTS = class {
|
|
|
444
633
|
/** Close the WebSocket connection to allow clean process exit. */
|
|
445
634
|
close() {
|
|
446
635
|
if (this.ws) {
|
|
447
|
-
|
|
636
|
+
log4.debug("Closing Cartesia WebSocket");
|
|
448
637
|
this.ws.close();
|
|
449
638
|
this.ws = null;
|
|
450
639
|
}
|
|
@@ -453,17 +642,17 @@ var CartesiaTTS = class {
|
|
|
453
642
|
}
|
|
454
643
|
/** Pre-connect the WebSocket so first synthesize() doesn't pay connection cost. */
|
|
455
644
|
async warmup() {
|
|
456
|
-
|
|
645
|
+
log4.info("Warming up TTS connection...");
|
|
457
646
|
const start = performance.now();
|
|
458
647
|
try {
|
|
459
648
|
await this.ensureConnection();
|
|
460
|
-
|
|
649
|
+
log4.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
461
650
|
} catch (err) {
|
|
462
|
-
|
|
651
|
+
log4.warn("TTS warmup failed (non-fatal):", err);
|
|
463
652
|
}
|
|
464
653
|
}
|
|
465
654
|
async *synthesize(text, signal) {
|
|
466
|
-
|
|
655
|
+
log4.debug(`Synthesizing: "${text.slice(0, 60)}"`);
|
|
467
656
|
await this.ensureConnection();
|
|
468
657
|
if (!this.ws || this.ws.readyState !== WebSocket2.OPEN) {
|
|
469
658
|
throw new Error("Cartesia WebSocket not connected");
|
|
@@ -534,12 +723,12 @@ var CartesiaTTS = class {
|
|
|
534
723
|
if (this.connectPromise) return this.connectPromise;
|
|
535
724
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
536
725
|
const url = `${CARTESIA_WS_BASE}?api_key=${this.apiKey}&cartesia_version=${this.apiVersion}`;
|
|
537
|
-
|
|
726
|
+
log4.debug("Connecting to Cartesia...");
|
|
538
727
|
this.ws = new WebSocket2(url);
|
|
539
728
|
this.ws.on("open", () => {
|
|
540
729
|
this._connected = true;
|
|
541
730
|
this.connectPromise = null;
|
|
542
|
-
|
|
731
|
+
log4.info("Cartesia WebSocket connected");
|
|
543
732
|
resolve();
|
|
544
733
|
});
|
|
545
734
|
this.ws.on("message", (data) => {
|
|
@@ -547,12 +736,12 @@ var CartesiaTTS = class {
|
|
|
547
736
|
const msg = JSON.parse(data.toString());
|
|
548
737
|
this.handleMessage(msg);
|
|
549
738
|
} catch (err) {
|
|
550
|
-
|
|
739
|
+
log4.error("Failed to parse Cartesia message:", err);
|
|
551
740
|
}
|
|
552
741
|
});
|
|
553
742
|
this.ws.on("error", (err) => {
|
|
554
743
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
555
|
-
|
|
744
|
+
log4.error("Cartesia WebSocket error:", error);
|
|
556
745
|
for (const ctx of this.contexts.values()) {
|
|
557
746
|
ctx.error = error;
|
|
558
747
|
ctx.wake?.();
|
|
@@ -562,7 +751,7 @@ var CartesiaTTS = class {
|
|
|
562
751
|
reject(error);
|
|
563
752
|
});
|
|
564
753
|
this.ws.on("close", (code, reason) => {
|
|
565
|
-
|
|
754
|
+
log4.debug(`Cartesia WebSocket closed: ${code} ${reason.toString()}`);
|
|
566
755
|
this._connected = false;
|
|
567
756
|
this.connectPromise = null;
|
|
568
757
|
for (const ctx of this.contexts.values()) {
|
|
@@ -587,12 +776,12 @@ var CartesiaTTS = class {
|
|
|
587
776
|
ctx.wake?.();
|
|
588
777
|
}
|
|
589
778
|
} else if (type === "done") {
|
|
590
|
-
|
|
779
|
+
log4.debug(`Cartesia synthesis done for ${contextId} (${ctx.chunks.length} chunks pending)`);
|
|
591
780
|
ctx.done = true;
|
|
592
781
|
ctx.wake?.();
|
|
593
782
|
} else if (type === "error") {
|
|
594
783
|
const errorMsg = msg.error ?? "Unknown Cartesia error";
|
|
595
|
-
|
|
784
|
+
log4.error(`Cartesia error for ${contextId}: ${errorMsg}`);
|
|
596
785
|
ctx.error = new Error(`Cartesia TTS error: ${errorMsg}`);
|
|
597
786
|
ctx.wake?.();
|
|
598
787
|
}
|
|
@@ -601,7 +790,7 @@ var CartesiaTTS = class {
|
|
|
601
790
|
|
|
602
791
|
// src/providers/deepgram-tts.ts
|
|
603
792
|
import WebSocket3 from "ws";
|
|
604
|
-
var
|
|
793
|
+
var log5 = createLogger("DeepgramTTS");
|
|
605
794
|
var DEEPGRAM_WS_BASE = "wss://api.deepgram.com/v1/speak";
|
|
606
795
|
var DEFAULT_SAMPLE_RATE2 = 48e3;
|
|
607
796
|
function parseLangSegments(text, defaultLang) {
|
|
@@ -698,7 +887,7 @@ var DeepgramTTS = class {
|
|
|
698
887
|
/** Close all WebSocket connections to allow clean process exit. */
|
|
699
888
|
close() {
|
|
700
889
|
for (const [lang, ws] of this.connections) {
|
|
701
|
-
|
|
890
|
+
log5.debug(`Closing WebSocket for [${lang}]`);
|
|
702
891
|
ws.close();
|
|
703
892
|
}
|
|
704
893
|
this.connections.clear();
|
|
@@ -707,13 +896,13 @@ var DeepgramTTS = class {
|
|
|
707
896
|
}
|
|
708
897
|
/** Pre-connect all language WebSocket connections. */
|
|
709
898
|
async warmup() {
|
|
710
|
-
|
|
899
|
+
log5.info("Warming up TTS connections...");
|
|
711
900
|
const start = performance.now();
|
|
712
901
|
try {
|
|
713
902
|
await Promise.all(Object.keys(this.models).map((lang) => this.ensureConnection(lang)));
|
|
714
|
-
|
|
903
|
+
log5.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
715
904
|
} catch (err) {
|
|
716
|
-
|
|
905
|
+
log5.warn("TTS warmup failed (non-fatal):", err);
|
|
717
906
|
}
|
|
718
907
|
}
|
|
719
908
|
/** Strip SSML lang tags from text for display/events. */
|
|
@@ -746,7 +935,7 @@ var DeepgramTTS = class {
|
|
|
746
935
|
}
|
|
747
936
|
}
|
|
748
937
|
async *synthesizeSegment(lang, text, signal) {
|
|
749
|
-
|
|
938
|
+
log5.debug(`Synthesizing [${lang}]: "${text.slice(0, 60)}"`);
|
|
750
939
|
await this.ensureConnection(lang);
|
|
751
940
|
const ws = this.connections.get(lang);
|
|
752
941
|
if (!ws || ws.readyState !== WebSocket3.OPEN) {
|
|
@@ -804,7 +993,7 @@ var DeepgramTTS = class {
|
|
|
804
993
|
}
|
|
805
994
|
const promise = new Promise((resolve, reject) => {
|
|
806
995
|
const url = `${DEEPGRAM_WS_BASE}?model=${encodeURIComponent(model)}&encoding=linear16&sample_rate=${this.sampleRate}`;
|
|
807
|
-
|
|
996
|
+
log5.debug(`Connecting to Deepgram for [${lang}]: ${model}`);
|
|
808
997
|
const ws = new WebSocket3(url, {
|
|
809
998
|
headers: {
|
|
810
999
|
Authorization: `Token ${this.apiKey}`
|
|
@@ -813,7 +1002,7 @@ var DeepgramTTS = class {
|
|
|
813
1002
|
ws.on("open", () => {
|
|
814
1003
|
this.connections.set(lang, ws);
|
|
815
1004
|
this.connectPromises.delete(lang);
|
|
816
|
-
|
|
1005
|
+
log5.info(`Deepgram WebSocket connected for [${lang}] (${model})`);
|
|
817
1006
|
resolve();
|
|
818
1007
|
});
|
|
819
1008
|
ws.on("message", (data, isBinary) => {
|
|
@@ -830,16 +1019,16 @@ var DeepgramTTS = class {
|
|
|
830
1019
|
state.flushed = true;
|
|
831
1020
|
state.wake?.();
|
|
832
1021
|
} else if (msg.type === "Warning" || msg.type === "Error") {
|
|
833
|
-
|
|
1022
|
+
log5.warn(`Deepgram [${lang}] ${msg.type}: ${msg.description || msg.message || JSON.stringify(msg)}`);
|
|
834
1023
|
}
|
|
835
1024
|
} catch {
|
|
836
|
-
|
|
1025
|
+
log5.warn(`Failed to parse Deepgram message for [${lang}]`);
|
|
837
1026
|
}
|
|
838
1027
|
}
|
|
839
1028
|
});
|
|
840
1029
|
ws.on("error", (err) => {
|
|
841
1030
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
842
|
-
|
|
1031
|
+
log5.error(`Deepgram WebSocket error [${lang}]:`, error);
|
|
843
1032
|
const state = this.flushStates.get(lang);
|
|
844
1033
|
if (state) {
|
|
845
1034
|
state.error = error;
|
|
@@ -850,7 +1039,7 @@ var DeepgramTTS = class {
|
|
|
850
1039
|
reject(error);
|
|
851
1040
|
});
|
|
852
1041
|
ws.on("close", (code, reason) => {
|
|
853
|
-
|
|
1042
|
+
log5.debug(`Deepgram WebSocket closed [${lang}]: ${code} ${reason.toString()}`);
|
|
854
1043
|
this.connections.delete(lang);
|
|
855
1044
|
this.connectPromises.delete(lang);
|
|
856
1045
|
const state = this.flushStates.get(lang);
|
|
@@ -867,7 +1056,7 @@ var DeepgramTTS = class {
|
|
|
867
1056
|
|
|
868
1057
|
// src/providers/dtelecom-stt.ts
|
|
869
1058
|
import WebSocket4 from "ws";
|
|
870
|
-
var
|
|
1059
|
+
var log6 = createLogger("DtelecomSTT");
|
|
871
1060
|
var KEEPALIVE_INTERVAL_MS2 = 5e3;
|
|
872
1061
|
var DtelecomSTT = class {
|
|
873
1062
|
options;
|
|
@@ -875,6 +1064,9 @@ var DtelecomSTT = class {
|
|
|
875
1064
|
if (!options.serverUrl) {
|
|
876
1065
|
throw new Error("DtelecomSTT requires a serverUrl");
|
|
877
1066
|
}
|
|
1067
|
+
if (!options.sessionKey) {
|
|
1068
|
+
throw new Error("DtelecomSTT requires a sessionKey");
|
|
1069
|
+
}
|
|
878
1070
|
this.options = options;
|
|
879
1071
|
}
|
|
880
1072
|
createStream(options) {
|
|
@@ -885,6 +1077,7 @@ var DtelecomSTT = class {
|
|
|
885
1077
|
var DtelecomSTTStream = class extends BaseSTTStream {
|
|
886
1078
|
ws = null;
|
|
887
1079
|
serverUrl;
|
|
1080
|
+
sessionKey;
|
|
888
1081
|
forceWhisper;
|
|
889
1082
|
_ready = false;
|
|
890
1083
|
_closed = false;
|
|
@@ -894,6 +1087,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
894
1087
|
constructor(options, language) {
|
|
895
1088
|
super();
|
|
896
1089
|
this.serverUrl = options.serverUrl;
|
|
1090
|
+
this.sessionKey = options.sessionKey;
|
|
897
1091
|
this.language = language;
|
|
898
1092
|
this.forceWhisper = options.forceWhisper ?? false;
|
|
899
1093
|
this.connect();
|
|
@@ -921,7 +1115,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
921
1115
|
}
|
|
922
1116
|
if (this.ws?.readyState === WebSocket4.OPEN) {
|
|
923
1117
|
this.ws.send(JSON.stringify(config));
|
|
924
|
-
|
|
1118
|
+
log6.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ", model=whisper" : ""}`);
|
|
925
1119
|
}
|
|
926
1120
|
}
|
|
927
1121
|
async close() {
|
|
@@ -934,14 +1128,20 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
934
1128
|
this.ws.close();
|
|
935
1129
|
this.ws = null;
|
|
936
1130
|
}
|
|
937
|
-
|
|
1131
|
+
log6.debug("DtelecomSTT stream closed");
|
|
938
1132
|
}
|
|
939
1133
|
connect() {
|
|
940
|
-
|
|
941
|
-
|
|
1134
|
+
const url = this.serverUrl.replace(/\/$/, "") + "/v1/stream";
|
|
1135
|
+
const wsUrl = url.replace("https://", "wss://").replace("http://", "ws://");
|
|
1136
|
+
log6.debug(`Connecting to dTelecom STT: ${wsUrl}`);
|
|
1137
|
+
this.ws = new WebSocket4(wsUrl);
|
|
942
1138
|
this.ws.on("open", () => {
|
|
943
|
-
|
|
944
|
-
const config = {
|
|
1139
|
+
log6.info("dTelecom STT WebSocket connected");
|
|
1140
|
+
const config = {
|
|
1141
|
+
type: "config",
|
|
1142
|
+
language: this.language,
|
|
1143
|
+
session_key: this.sessionKey
|
|
1144
|
+
};
|
|
945
1145
|
if (this.forceWhisper) {
|
|
946
1146
|
config.model = "whisper";
|
|
947
1147
|
}
|
|
@@ -953,19 +1153,19 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
953
1153
|
const msg = JSON.parse(data.toString());
|
|
954
1154
|
this.handleMessage(msg);
|
|
955
1155
|
} catch (err) {
|
|
956
|
-
|
|
1156
|
+
log6.error("Failed to parse dTelecom STT message:", err);
|
|
957
1157
|
}
|
|
958
1158
|
});
|
|
959
1159
|
this.ws.on("error", (err) => {
|
|
960
|
-
|
|
1160
|
+
log6.error("dTelecom STT WebSocket error:", err);
|
|
961
1161
|
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
962
1162
|
});
|
|
963
1163
|
this.ws.on("close", (code, reason) => {
|
|
964
|
-
|
|
1164
|
+
log6.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);
|
|
965
1165
|
this._ready = false;
|
|
966
1166
|
this.stopKeepAlive();
|
|
967
1167
|
if (!this._closed) {
|
|
968
|
-
|
|
1168
|
+
log6.info("dTelecom STT connection lost, reconnecting in 1s...");
|
|
969
1169
|
setTimeout(() => {
|
|
970
1170
|
if (!this._closed) this.connect();
|
|
971
1171
|
}, 1e3);
|
|
@@ -984,14 +1184,14 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
984
1184
|
} else if (type === "error") {
|
|
985
1185
|
const errData = msg.error;
|
|
986
1186
|
const errorMsg = msg.message || (typeof errData === "string" ? errData : JSON.stringify(errData)) || "Unknown STT error";
|
|
987
|
-
|
|
1187
|
+
log6.error(`dTelecom STT error: ${errorMsg}`);
|
|
988
1188
|
this.emit("error", new Error(errorMsg));
|
|
989
1189
|
}
|
|
990
1190
|
}
|
|
991
1191
|
handleReady(msg) {
|
|
992
1192
|
const clientId = msg.client_id;
|
|
993
1193
|
const lang = msg.language;
|
|
994
|
-
|
|
1194
|
+
log6.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);
|
|
995
1195
|
this._ready = true;
|
|
996
1196
|
for (const buf of this.pendingAudio) {
|
|
997
1197
|
if (this.ws?.readyState === WebSocket4.OPEN) {
|
|
@@ -1008,7 +1208,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1008
1208
|
const latencyMs = msg.latency_ms;
|
|
1009
1209
|
if (!text) return;
|
|
1010
1210
|
if (isFinal && latencyMs !== void 0) {
|
|
1011
|
-
|
|
1211
|
+
log6.info(`stt_final: ${latencyMs.toFixed(0)}ms "${text.slice(0, 50)}"`);
|
|
1012
1212
|
}
|
|
1013
1213
|
this.emit("transcription", {
|
|
1014
1214
|
text,
|
|
@@ -1019,7 +1219,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1019
1219
|
}
|
|
1020
1220
|
handleVadEvent(msg) {
|
|
1021
1221
|
const event = msg.event;
|
|
1022
|
-
|
|
1222
|
+
log6.debug(`VAD event: ${event}`);
|
|
1023
1223
|
if (event === "speech_start") {
|
|
1024
1224
|
this.emit("transcription", {
|
|
1025
1225
|
text: "",
|
|
@@ -1045,9 +1245,10 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1045
1245
|
|
|
1046
1246
|
// src/providers/dtelecom-tts.ts
|
|
1047
1247
|
import WebSocket5 from "ws";
|
|
1048
|
-
var
|
|
1248
|
+
var log7 = createLogger("DtelecomTTS");
|
|
1049
1249
|
var DtelecomTTS = class {
|
|
1050
1250
|
serverUrl;
|
|
1251
|
+
sessionKey;
|
|
1051
1252
|
voices;
|
|
1052
1253
|
defaultLang;
|
|
1053
1254
|
speed;
|
|
@@ -1062,29 +1263,33 @@ var DtelecomTTS = class {
|
|
|
1062
1263
|
if (!options.serverUrl) {
|
|
1063
1264
|
throw new Error("DtelecomTTS requires a serverUrl");
|
|
1064
1265
|
}
|
|
1266
|
+
if (!options.sessionKey) {
|
|
1267
|
+
throw new Error("DtelecomTTS requires a sessionKey");
|
|
1268
|
+
}
|
|
1065
1269
|
if (!options.voices || Object.keys(options.voices).length === 0) {
|
|
1066
1270
|
throw new Error("DtelecomTTS requires at least one voice config");
|
|
1067
1271
|
}
|
|
1068
1272
|
this.serverUrl = options.serverUrl;
|
|
1273
|
+
this.sessionKey = options.sessionKey;
|
|
1069
1274
|
this.voices = { ...options.voices };
|
|
1070
1275
|
this.defaultLang = options.defaultLanguage ?? Object.keys(this.voices)[0];
|
|
1071
1276
|
this.speed = options.speed ?? 1;
|
|
1072
1277
|
}
|
|
1073
1278
|
/** Pre-connect WebSocket to TTS server. */
|
|
1074
1279
|
async warmup() {
|
|
1075
|
-
|
|
1280
|
+
log7.info("Warming up TTS connection...");
|
|
1076
1281
|
const start = performance.now();
|
|
1077
1282
|
try {
|
|
1078
1283
|
await this.ensureConnection();
|
|
1079
|
-
|
|
1284
|
+
log7.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
1080
1285
|
} catch (err) {
|
|
1081
|
-
|
|
1286
|
+
log7.warn("TTS warmup failed (non-fatal):", err);
|
|
1082
1287
|
}
|
|
1083
1288
|
}
|
|
1084
1289
|
/** Close WebSocket connection. */
|
|
1085
1290
|
close() {
|
|
1086
1291
|
if (this.ws) {
|
|
1087
|
-
|
|
1292
|
+
log7.debug("Closing TTS WebSocket");
|
|
1088
1293
|
this.ws.close();
|
|
1089
1294
|
this.ws = null;
|
|
1090
1295
|
}
|
|
@@ -1146,7 +1351,7 @@ var DtelecomTTS = class {
|
|
|
1146
1351
|
msg.lang_code = voiceConfig.langCode;
|
|
1147
1352
|
msg.speed = this.speed;
|
|
1148
1353
|
}
|
|
1149
|
-
|
|
1354
|
+
log7.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? "default"} lang_code=${voiceConfig?.langCode ?? "default"} "${text.slice(0, 60)}"`);
|
|
1150
1355
|
ws.send(JSON.stringify(msg));
|
|
1151
1356
|
try {
|
|
1152
1357
|
while (true) {
|
|
@@ -1177,22 +1382,26 @@ var DtelecomTTS = class {
|
|
|
1177
1382
|
}
|
|
1178
1383
|
if (this.connectPromise) return this.connectPromise;
|
|
1179
1384
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
1180
|
-
|
|
1181
|
-
const
|
|
1385
|
+
const url = this.serverUrl.replace(/\/$/, "") + "/v1/stream";
|
|
1386
|
+
const wsUrl = url.replace("https://", "wss://").replace("http://", "ws://");
|
|
1387
|
+
log7.debug(`Connecting to dTelecom TTS: ${wsUrl}`);
|
|
1388
|
+
const ws = new WebSocket5(wsUrl);
|
|
1182
1389
|
ws.on("open", () => {
|
|
1183
1390
|
this.ws = ws;
|
|
1184
1391
|
this.connectPromise = null;
|
|
1185
1392
|
const defaultVoice = this.voices[this.defaultLang];
|
|
1393
|
+
const msg = {
|
|
1394
|
+
session_key: this.sessionKey
|
|
1395
|
+
};
|
|
1186
1396
|
if (defaultVoice) {
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
}
|
|
1193
|
-
}));
|
|
1397
|
+
msg.config = {
|
|
1398
|
+
voice: defaultVoice.voice,
|
|
1399
|
+
lang_code: defaultVoice.langCode,
|
|
1400
|
+
speed: this.speed
|
|
1401
|
+
};
|
|
1194
1402
|
}
|
|
1195
|
-
|
|
1403
|
+
ws.send(JSON.stringify(msg));
|
|
1404
|
+
log7.info("dTelecom TTS WebSocket connected");
|
|
1196
1405
|
resolve();
|
|
1197
1406
|
});
|
|
1198
1407
|
ws.on("message", (data, isBinary) => {
|
|
@@ -1213,21 +1422,21 @@ var DtelecomTTS = class {
|
|
|
1213
1422
|
state.done = true;
|
|
1214
1423
|
state.wake?.();
|
|
1215
1424
|
} else if (msg.type === "generating") {
|
|
1216
|
-
|
|
1425
|
+
log7.debug(`TTS generating: "${msg.text?.slice(0, 40)}"`);
|
|
1217
1426
|
} else if (msg.type === "error") {
|
|
1218
1427
|
const errorMsg = msg.message || "Unknown TTS error";
|
|
1219
|
-
|
|
1428
|
+
log7.error(`dTelecom TTS error: ${errorMsg}`);
|
|
1220
1429
|
state.error = new Error(errorMsg);
|
|
1221
1430
|
state.wake?.();
|
|
1222
1431
|
}
|
|
1223
1432
|
} catch {
|
|
1224
|
-
|
|
1433
|
+
log7.warn("Failed to parse dTelecom TTS message");
|
|
1225
1434
|
}
|
|
1226
1435
|
}
|
|
1227
1436
|
});
|
|
1228
1437
|
ws.on("error", (err) => {
|
|
1229
1438
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1230
|
-
|
|
1439
|
+
log7.error("dTelecom TTS WebSocket error:", error);
|
|
1231
1440
|
const state = this.flushState;
|
|
1232
1441
|
if (state) {
|
|
1233
1442
|
state.error = error;
|
|
@@ -1238,7 +1447,7 @@ var DtelecomTTS = class {
|
|
|
1238
1447
|
reject(error);
|
|
1239
1448
|
});
|
|
1240
1449
|
ws.on("close", (code, reason) => {
|
|
1241
|
-
|
|
1450
|
+
log7.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);
|
|
1242
1451
|
this.ws = null;
|
|
1243
1452
|
this.connectPromise = null;
|
|
1244
1453
|
const state = this.flushState;
|
|
@@ -1257,6 +1466,7 @@ export {
|
|
|
1257
1466
|
DeepgramTTS,
|
|
1258
1467
|
DtelecomSTT,
|
|
1259
1468
|
DtelecomTTS,
|
|
1469
|
+
OpenAILLM,
|
|
1260
1470
|
OpenRouterLLM
|
|
1261
1471
|
};
|
|
1262
1472
|
//# sourceMappingURL=index.mjs.map
|