@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.js
CHANGED
|
@@ -35,6 +35,7 @@ __export(providers_exports, {
|
|
|
35
35
|
DeepgramTTS: () => DeepgramTTS,
|
|
36
36
|
DtelecomSTT: () => DtelecomSTT,
|
|
37
37
|
DtelecomTTS: () => DtelecomTTS,
|
|
38
|
+
OpenAILLM: () => OpenAILLM,
|
|
38
39
|
OpenRouterLLM: () => OpenRouterLLM
|
|
39
40
|
});
|
|
40
41
|
module.exports = __toCommonJS(providers_exports);
|
|
@@ -494,9 +495,198 @@ var OpenRouterLLM = class {
|
|
|
494
495
|
}
|
|
495
496
|
};
|
|
496
497
|
|
|
498
|
+
// src/providers/openai-llm.ts
|
|
499
|
+
var log3 = createLogger("OpenAILLM");
|
|
500
|
+
var OPENAI_URL = "https://api.openai.com/v1/chat/completions";
|
|
501
|
+
var OpenAILLM = class {
|
|
502
|
+
apiKey;
|
|
503
|
+
model;
|
|
504
|
+
maxTokens;
|
|
505
|
+
temperature;
|
|
506
|
+
responseFormat;
|
|
507
|
+
constructor(options) {
|
|
508
|
+
if (!options.apiKey) {
|
|
509
|
+
throw new Error("OpenAILLM requires an apiKey");
|
|
510
|
+
}
|
|
511
|
+
this.apiKey = options.apiKey;
|
|
512
|
+
this.model = options.model;
|
|
513
|
+
this.maxTokens = options.maxTokens ?? 512;
|
|
514
|
+
this.temperature = options.temperature ?? 0.7;
|
|
515
|
+
this.responseFormat = options.responseFormat;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Warm up the LLM by sending the system prompt and a short message.
|
|
519
|
+
* Primes the HTTP/TLS connection and model loading on the provider side.
|
|
520
|
+
*/
|
|
521
|
+
async warmup(systemPrompt) {
|
|
522
|
+
log3.info("Warming up LLM connection...");
|
|
523
|
+
const start = performance.now();
|
|
524
|
+
const messages = [
|
|
525
|
+
{ role: "system", content: systemPrompt },
|
|
526
|
+
{ role: "user", content: "Hello" }
|
|
527
|
+
];
|
|
528
|
+
try {
|
|
529
|
+
const gen = this.chat(messages);
|
|
530
|
+
for await (const chunk of gen) {
|
|
531
|
+
if (chunk.type === "done") break;
|
|
532
|
+
}
|
|
533
|
+
log3.info(`LLM warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
534
|
+
} catch (err) {
|
|
535
|
+
log3.warn("LLM warmup failed (non-fatal):", err);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
async *chat(messages, signal, options) {
|
|
539
|
+
const body = {
|
|
540
|
+
model: this.model,
|
|
541
|
+
messages,
|
|
542
|
+
max_tokens: this.maxTokens,
|
|
543
|
+
temperature: this.temperature,
|
|
544
|
+
stream: true,
|
|
545
|
+
stream_options: { include_usage: true }
|
|
546
|
+
};
|
|
547
|
+
if (this.responseFormat && !options?.plainText) {
|
|
548
|
+
body.response_format = this.responseFormat;
|
|
549
|
+
}
|
|
550
|
+
log3.debug(`LLM request: model=${this.model}, messages=${messages.length}`);
|
|
551
|
+
const response = await fetch(OPENAI_URL, {
|
|
552
|
+
method: "POST",
|
|
553
|
+
headers: {
|
|
554
|
+
"Content-Type": "application/json",
|
|
555
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
556
|
+
},
|
|
557
|
+
body: JSON.stringify(body),
|
|
558
|
+
signal
|
|
559
|
+
});
|
|
560
|
+
if (!response.ok) {
|
|
561
|
+
const errorText = await response.text();
|
|
562
|
+
throw new Error(`OpenAI API error ${response.status}: ${errorText}`);
|
|
563
|
+
}
|
|
564
|
+
if (!response.body) {
|
|
565
|
+
throw new Error("OpenAI response has no body");
|
|
566
|
+
}
|
|
567
|
+
const reader = response.body.getReader();
|
|
568
|
+
const decoder = new TextDecoder();
|
|
569
|
+
let buffer = "";
|
|
570
|
+
const structured = !!this.responseFormat && !options?.plainText;
|
|
571
|
+
let jsonBuffer = "";
|
|
572
|
+
let segmentsYielded = false;
|
|
573
|
+
let lastUsage;
|
|
574
|
+
let inSegmentsArray = false;
|
|
575
|
+
let objectStart = -1;
|
|
576
|
+
let braceDepth = 0;
|
|
577
|
+
let scanIndex = 0;
|
|
578
|
+
let inString = false;
|
|
579
|
+
let escaped = false;
|
|
580
|
+
function extractSegments(buf) {
|
|
581
|
+
const results = [];
|
|
582
|
+
for (let i = scanIndex; i < buf.length; i++) {
|
|
583
|
+
const ch = buf[i];
|
|
584
|
+
if (escaped) {
|
|
585
|
+
escaped = false;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
if (ch === "\\" && inString) {
|
|
589
|
+
escaped = true;
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
if (ch === '"') {
|
|
593
|
+
inString = !inString;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
if (inString) continue;
|
|
597
|
+
if (!inSegmentsArray) {
|
|
598
|
+
if (ch === "[") {
|
|
599
|
+
const before = buf.slice(0, i).trimEnd();
|
|
600
|
+
if (before.endsWith(":") && buf.slice(0, i).includes('"segments"')) {
|
|
601
|
+
inSegmentsArray = true;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
if (ch === "{") {
|
|
607
|
+
if (braceDepth === 0) objectStart = i;
|
|
608
|
+
braceDepth++;
|
|
609
|
+
} else if (ch === "}") {
|
|
610
|
+
braceDepth--;
|
|
611
|
+
if (braceDepth === 0 && objectStart >= 0) {
|
|
612
|
+
const objStr = buf.slice(objectStart, i + 1);
|
|
613
|
+
try {
|
|
614
|
+
const seg = JSON.parse(objStr);
|
|
615
|
+
if (seg.lang && seg.text) {
|
|
616
|
+
results.push({ lang: seg.lang, text: seg.text });
|
|
617
|
+
}
|
|
618
|
+
} catch {
|
|
619
|
+
}
|
|
620
|
+
objectStart = -1;
|
|
621
|
+
}
|
|
622
|
+
} else if (ch === "]" && braceDepth === 0) {
|
|
623
|
+
inSegmentsArray = false;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
scanIndex = buf.length;
|
|
627
|
+
return results;
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
while (true) {
|
|
631
|
+
if (signal?.aborted) break;
|
|
632
|
+
const { done, value } = await reader.read();
|
|
633
|
+
if (done) break;
|
|
634
|
+
buffer += decoder.decode(value, { stream: true });
|
|
635
|
+
const lines = buffer.split("\n");
|
|
636
|
+
buffer = lines.pop() ?? "";
|
|
637
|
+
for (const line of lines) {
|
|
638
|
+
const trimmed = line.trim();
|
|
639
|
+
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
640
|
+
const data = trimmed.slice(6);
|
|
641
|
+
if (data === "[DONE]") break;
|
|
642
|
+
try {
|
|
643
|
+
const parsed = JSON.parse(data);
|
|
644
|
+
const choice = parsed.choices?.[0];
|
|
645
|
+
if (!choice) {
|
|
646
|
+
if (parsed.usage) {
|
|
647
|
+
lastUsage = {
|
|
648
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
649
|
+
completionTokens: parsed.usage.completion_tokens
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
const delta = choice.delta;
|
|
655
|
+
if (delta?.content) {
|
|
656
|
+
if (structured) {
|
|
657
|
+
jsonBuffer += delta.content;
|
|
658
|
+
const segments = extractSegments(jsonBuffer);
|
|
659
|
+
for (const seg of segments) {
|
|
660
|
+
yield { type: "segment", segment: seg };
|
|
661
|
+
segmentsYielded = true;
|
|
662
|
+
}
|
|
663
|
+
} else {
|
|
664
|
+
yield { type: "token", token: delta.content };
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (parsed.usage) {
|
|
668
|
+
lastUsage = {
|
|
669
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
670
|
+
completionTokens: parsed.usage.completion_tokens
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
} catch {
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
} finally {
|
|
678
|
+
reader.releaseLock();
|
|
679
|
+
}
|
|
680
|
+
if (structured && !segmentsYielded && jsonBuffer.length > 0) {
|
|
681
|
+
log3.warn(`LLM returned no segments. Raw JSON: "${jsonBuffer.slice(0, 300)}"`);
|
|
682
|
+
}
|
|
683
|
+
yield { type: "done", ...lastUsage ? { usage: lastUsage } : {} };
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
497
687
|
// src/providers/cartesia-tts.ts
|
|
498
688
|
var import_ws2 = __toESM(require("ws"));
|
|
499
|
-
var
|
|
689
|
+
var log4 = createLogger("CartesiaTTS");
|
|
500
690
|
var CARTESIA_WS_BASE = "wss://api.cartesia.ai/tts/websocket";
|
|
501
691
|
var DEFAULT_API_VERSION = "2024-06-10";
|
|
502
692
|
var DEFAULT_MODEL = "sonic-3";
|
|
@@ -535,7 +725,7 @@ var CartesiaTTS = class {
|
|
|
535
725
|
/** Close the WebSocket connection to allow clean process exit. */
|
|
536
726
|
close() {
|
|
537
727
|
if (this.ws) {
|
|
538
|
-
|
|
728
|
+
log4.debug("Closing Cartesia WebSocket");
|
|
539
729
|
this.ws.close();
|
|
540
730
|
this.ws = null;
|
|
541
731
|
}
|
|
@@ -544,17 +734,17 @@ var CartesiaTTS = class {
|
|
|
544
734
|
}
|
|
545
735
|
/** Pre-connect the WebSocket so first synthesize() doesn't pay connection cost. */
|
|
546
736
|
async warmup() {
|
|
547
|
-
|
|
737
|
+
log4.info("Warming up TTS connection...");
|
|
548
738
|
const start = performance.now();
|
|
549
739
|
try {
|
|
550
740
|
await this.ensureConnection();
|
|
551
|
-
|
|
741
|
+
log4.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
552
742
|
} catch (err) {
|
|
553
|
-
|
|
743
|
+
log4.warn("TTS warmup failed (non-fatal):", err);
|
|
554
744
|
}
|
|
555
745
|
}
|
|
556
746
|
async *synthesize(text, signal) {
|
|
557
|
-
|
|
747
|
+
log4.debug(`Synthesizing: "${text.slice(0, 60)}"`);
|
|
558
748
|
await this.ensureConnection();
|
|
559
749
|
if (!this.ws || this.ws.readyState !== import_ws2.default.OPEN) {
|
|
560
750
|
throw new Error("Cartesia WebSocket not connected");
|
|
@@ -625,12 +815,12 @@ var CartesiaTTS = class {
|
|
|
625
815
|
if (this.connectPromise) return this.connectPromise;
|
|
626
816
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
627
817
|
const url = `${CARTESIA_WS_BASE}?api_key=${this.apiKey}&cartesia_version=${this.apiVersion}`;
|
|
628
|
-
|
|
818
|
+
log4.debug("Connecting to Cartesia...");
|
|
629
819
|
this.ws = new import_ws2.default(url);
|
|
630
820
|
this.ws.on("open", () => {
|
|
631
821
|
this._connected = true;
|
|
632
822
|
this.connectPromise = null;
|
|
633
|
-
|
|
823
|
+
log4.info("Cartesia WebSocket connected");
|
|
634
824
|
resolve();
|
|
635
825
|
});
|
|
636
826
|
this.ws.on("message", (data) => {
|
|
@@ -638,12 +828,12 @@ var CartesiaTTS = class {
|
|
|
638
828
|
const msg = JSON.parse(data.toString());
|
|
639
829
|
this.handleMessage(msg);
|
|
640
830
|
} catch (err) {
|
|
641
|
-
|
|
831
|
+
log4.error("Failed to parse Cartesia message:", err);
|
|
642
832
|
}
|
|
643
833
|
});
|
|
644
834
|
this.ws.on("error", (err) => {
|
|
645
835
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
646
|
-
|
|
836
|
+
log4.error("Cartesia WebSocket error:", error);
|
|
647
837
|
for (const ctx of this.contexts.values()) {
|
|
648
838
|
ctx.error = error;
|
|
649
839
|
ctx.wake?.();
|
|
@@ -653,7 +843,7 @@ var CartesiaTTS = class {
|
|
|
653
843
|
reject(error);
|
|
654
844
|
});
|
|
655
845
|
this.ws.on("close", (code, reason) => {
|
|
656
|
-
|
|
846
|
+
log4.debug(`Cartesia WebSocket closed: ${code} ${reason.toString()}`);
|
|
657
847
|
this._connected = false;
|
|
658
848
|
this.connectPromise = null;
|
|
659
849
|
for (const ctx of this.contexts.values()) {
|
|
@@ -678,12 +868,12 @@ var CartesiaTTS = class {
|
|
|
678
868
|
ctx.wake?.();
|
|
679
869
|
}
|
|
680
870
|
} else if (type === "done") {
|
|
681
|
-
|
|
871
|
+
log4.debug(`Cartesia synthesis done for ${contextId} (${ctx.chunks.length} chunks pending)`);
|
|
682
872
|
ctx.done = true;
|
|
683
873
|
ctx.wake?.();
|
|
684
874
|
} else if (type === "error") {
|
|
685
875
|
const errorMsg = msg.error ?? "Unknown Cartesia error";
|
|
686
|
-
|
|
876
|
+
log4.error(`Cartesia error for ${contextId}: ${errorMsg}`);
|
|
687
877
|
ctx.error = new Error(`Cartesia TTS error: ${errorMsg}`);
|
|
688
878
|
ctx.wake?.();
|
|
689
879
|
}
|
|
@@ -692,7 +882,7 @@ var CartesiaTTS = class {
|
|
|
692
882
|
|
|
693
883
|
// src/providers/deepgram-tts.ts
|
|
694
884
|
var import_ws3 = __toESM(require("ws"));
|
|
695
|
-
var
|
|
885
|
+
var log5 = createLogger("DeepgramTTS");
|
|
696
886
|
var DEEPGRAM_WS_BASE = "wss://api.deepgram.com/v1/speak";
|
|
697
887
|
var DEFAULT_SAMPLE_RATE2 = 48e3;
|
|
698
888
|
function parseLangSegments(text, defaultLang) {
|
|
@@ -789,7 +979,7 @@ var DeepgramTTS = class {
|
|
|
789
979
|
/** Close all WebSocket connections to allow clean process exit. */
|
|
790
980
|
close() {
|
|
791
981
|
for (const [lang, ws] of this.connections) {
|
|
792
|
-
|
|
982
|
+
log5.debug(`Closing WebSocket for [${lang}]`);
|
|
793
983
|
ws.close();
|
|
794
984
|
}
|
|
795
985
|
this.connections.clear();
|
|
@@ -798,13 +988,13 @@ var DeepgramTTS = class {
|
|
|
798
988
|
}
|
|
799
989
|
/** Pre-connect all language WebSocket connections. */
|
|
800
990
|
async warmup() {
|
|
801
|
-
|
|
991
|
+
log5.info("Warming up TTS connections...");
|
|
802
992
|
const start = performance.now();
|
|
803
993
|
try {
|
|
804
994
|
await Promise.all(Object.keys(this.models).map((lang) => this.ensureConnection(lang)));
|
|
805
|
-
|
|
995
|
+
log5.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
806
996
|
} catch (err) {
|
|
807
|
-
|
|
997
|
+
log5.warn("TTS warmup failed (non-fatal):", err);
|
|
808
998
|
}
|
|
809
999
|
}
|
|
810
1000
|
/** Strip SSML lang tags from text for display/events. */
|
|
@@ -837,7 +1027,7 @@ var DeepgramTTS = class {
|
|
|
837
1027
|
}
|
|
838
1028
|
}
|
|
839
1029
|
async *synthesizeSegment(lang, text, signal) {
|
|
840
|
-
|
|
1030
|
+
log5.debug(`Synthesizing [${lang}]: "${text.slice(0, 60)}"`);
|
|
841
1031
|
await this.ensureConnection(lang);
|
|
842
1032
|
const ws = this.connections.get(lang);
|
|
843
1033
|
if (!ws || ws.readyState !== import_ws3.default.OPEN) {
|
|
@@ -895,7 +1085,7 @@ var DeepgramTTS = class {
|
|
|
895
1085
|
}
|
|
896
1086
|
const promise = new Promise((resolve, reject) => {
|
|
897
1087
|
const url = `${DEEPGRAM_WS_BASE}?model=${encodeURIComponent(model)}&encoding=linear16&sample_rate=${this.sampleRate}`;
|
|
898
|
-
|
|
1088
|
+
log5.debug(`Connecting to Deepgram for [${lang}]: ${model}`);
|
|
899
1089
|
const ws = new import_ws3.default(url, {
|
|
900
1090
|
headers: {
|
|
901
1091
|
Authorization: `Token ${this.apiKey}`
|
|
@@ -904,7 +1094,7 @@ var DeepgramTTS = class {
|
|
|
904
1094
|
ws.on("open", () => {
|
|
905
1095
|
this.connections.set(lang, ws);
|
|
906
1096
|
this.connectPromises.delete(lang);
|
|
907
|
-
|
|
1097
|
+
log5.info(`Deepgram WebSocket connected for [${lang}] (${model})`);
|
|
908
1098
|
resolve();
|
|
909
1099
|
});
|
|
910
1100
|
ws.on("message", (data, isBinary) => {
|
|
@@ -921,16 +1111,16 @@ var DeepgramTTS = class {
|
|
|
921
1111
|
state.flushed = true;
|
|
922
1112
|
state.wake?.();
|
|
923
1113
|
} else if (msg.type === "Warning" || msg.type === "Error") {
|
|
924
|
-
|
|
1114
|
+
log5.warn(`Deepgram [${lang}] ${msg.type}: ${msg.description || msg.message || JSON.stringify(msg)}`);
|
|
925
1115
|
}
|
|
926
1116
|
} catch {
|
|
927
|
-
|
|
1117
|
+
log5.warn(`Failed to parse Deepgram message for [${lang}]`);
|
|
928
1118
|
}
|
|
929
1119
|
}
|
|
930
1120
|
});
|
|
931
1121
|
ws.on("error", (err) => {
|
|
932
1122
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
933
|
-
|
|
1123
|
+
log5.error(`Deepgram WebSocket error [${lang}]:`, error);
|
|
934
1124
|
const state = this.flushStates.get(lang);
|
|
935
1125
|
if (state) {
|
|
936
1126
|
state.error = error;
|
|
@@ -941,7 +1131,7 @@ var DeepgramTTS = class {
|
|
|
941
1131
|
reject(error);
|
|
942
1132
|
});
|
|
943
1133
|
ws.on("close", (code, reason) => {
|
|
944
|
-
|
|
1134
|
+
log5.debug(`Deepgram WebSocket closed [${lang}]: ${code} ${reason.toString()}`);
|
|
945
1135
|
this.connections.delete(lang);
|
|
946
1136
|
this.connectPromises.delete(lang);
|
|
947
1137
|
const state = this.flushStates.get(lang);
|
|
@@ -958,7 +1148,7 @@ var DeepgramTTS = class {
|
|
|
958
1148
|
|
|
959
1149
|
// src/providers/dtelecom-stt.ts
|
|
960
1150
|
var import_ws4 = __toESM(require("ws"));
|
|
961
|
-
var
|
|
1151
|
+
var log6 = createLogger("DtelecomSTT");
|
|
962
1152
|
var KEEPALIVE_INTERVAL_MS2 = 5e3;
|
|
963
1153
|
var DtelecomSTT = class {
|
|
964
1154
|
options;
|
|
@@ -966,6 +1156,9 @@ var DtelecomSTT = class {
|
|
|
966
1156
|
if (!options.serverUrl) {
|
|
967
1157
|
throw new Error("DtelecomSTT requires a serverUrl");
|
|
968
1158
|
}
|
|
1159
|
+
if (!options.sessionKey) {
|
|
1160
|
+
throw new Error("DtelecomSTT requires a sessionKey");
|
|
1161
|
+
}
|
|
969
1162
|
this.options = options;
|
|
970
1163
|
}
|
|
971
1164
|
createStream(options) {
|
|
@@ -976,6 +1169,7 @@ var DtelecomSTT = class {
|
|
|
976
1169
|
var DtelecomSTTStream = class extends BaseSTTStream {
|
|
977
1170
|
ws = null;
|
|
978
1171
|
serverUrl;
|
|
1172
|
+
sessionKey;
|
|
979
1173
|
forceWhisper;
|
|
980
1174
|
_ready = false;
|
|
981
1175
|
_closed = false;
|
|
@@ -985,6 +1179,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
985
1179
|
constructor(options, language) {
|
|
986
1180
|
super();
|
|
987
1181
|
this.serverUrl = options.serverUrl;
|
|
1182
|
+
this.sessionKey = options.sessionKey;
|
|
988
1183
|
this.language = language;
|
|
989
1184
|
this.forceWhisper = options.forceWhisper ?? false;
|
|
990
1185
|
this.connect();
|
|
@@ -1012,7 +1207,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1012
1207
|
}
|
|
1013
1208
|
if (this.ws?.readyState === import_ws4.default.OPEN) {
|
|
1014
1209
|
this.ws.send(JSON.stringify(config));
|
|
1015
|
-
|
|
1210
|
+
log6.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ", model=whisper" : ""}`);
|
|
1016
1211
|
}
|
|
1017
1212
|
}
|
|
1018
1213
|
async close() {
|
|
@@ -1025,14 +1220,20 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1025
1220
|
this.ws.close();
|
|
1026
1221
|
this.ws = null;
|
|
1027
1222
|
}
|
|
1028
|
-
|
|
1223
|
+
log6.debug("DtelecomSTT stream closed");
|
|
1029
1224
|
}
|
|
1030
1225
|
connect() {
|
|
1031
|
-
|
|
1032
|
-
|
|
1226
|
+
const url = this.serverUrl.replace(/\/$/, "") + "/v1/stream";
|
|
1227
|
+
const wsUrl = url.replace("https://", "wss://").replace("http://", "ws://");
|
|
1228
|
+
log6.debug(`Connecting to dTelecom STT: ${wsUrl}`);
|
|
1229
|
+
this.ws = new import_ws4.default(wsUrl);
|
|
1033
1230
|
this.ws.on("open", () => {
|
|
1034
|
-
|
|
1035
|
-
const config = {
|
|
1231
|
+
log6.info("dTelecom STT WebSocket connected");
|
|
1232
|
+
const config = {
|
|
1233
|
+
type: "config",
|
|
1234
|
+
language: this.language,
|
|
1235
|
+
session_key: this.sessionKey
|
|
1236
|
+
};
|
|
1036
1237
|
if (this.forceWhisper) {
|
|
1037
1238
|
config.model = "whisper";
|
|
1038
1239
|
}
|
|
@@ -1044,19 +1245,19 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1044
1245
|
const msg = JSON.parse(data.toString());
|
|
1045
1246
|
this.handleMessage(msg);
|
|
1046
1247
|
} catch (err) {
|
|
1047
|
-
|
|
1248
|
+
log6.error("Failed to parse dTelecom STT message:", err);
|
|
1048
1249
|
}
|
|
1049
1250
|
});
|
|
1050
1251
|
this.ws.on("error", (err) => {
|
|
1051
|
-
|
|
1252
|
+
log6.error("dTelecom STT WebSocket error:", err);
|
|
1052
1253
|
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
1053
1254
|
});
|
|
1054
1255
|
this.ws.on("close", (code, reason) => {
|
|
1055
|
-
|
|
1256
|
+
log6.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);
|
|
1056
1257
|
this._ready = false;
|
|
1057
1258
|
this.stopKeepAlive();
|
|
1058
1259
|
if (!this._closed) {
|
|
1059
|
-
|
|
1260
|
+
log6.info("dTelecom STT connection lost, reconnecting in 1s...");
|
|
1060
1261
|
setTimeout(() => {
|
|
1061
1262
|
if (!this._closed) this.connect();
|
|
1062
1263
|
}, 1e3);
|
|
@@ -1075,14 +1276,14 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1075
1276
|
} else if (type === "error") {
|
|
1076
1277
|
const errData = msg.error;
|
|
1077
1278
|
const errorMsg = msg.message || (typeof errData === "string" ? errData : JSON.stringify(errData)) || "Unknown STT error";
|
|
1078
|
-
|
|
1279
|
+
log6.error(`dTelecom STT error: ${errorMsg}`);
|
|
1079
1280
|
this.emit("error", new Error(errorMsg));
|
|
1080
1281
|
}
|
|
1081
1282
|
}
|
|
1082
1283
|
handleReady(msg) {
|
|
1083
1284
|
const clientId = msg.client_id;
|
|
1084
1285
|
const lang = msg.language;
|
|
1085
|
-
|
|
1286
|
+
log6.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);
|
|
1086
1287
|
this._ready = true;
|
|
1087
1288
|
for (const buf of this.pendingAudio) {
|
|
1088
1289
|
if (this.ws?.readyState === import_ws4.default.OPEN) {
|
|
@@ -1099,7 +1300,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1099
1300
|
const latencyMs = msg.latency_ms;
|
|
1100
1301
|
if (!text) return;
|
|
1101
1302
|
if (isFinal && latencyMs !== void 0) {
|
|
1102
|
-
|
|
1303
|
+
log6.info(`stt_final: ${latencyMs.toFixed(0)}ms "${text.slice(0, 50)}"`);
|
|
1103
1304
|
}
|
|
1104
1305
|
this.emit("transcription", {
|
|
1105
1306
|
text,
|
|
@@ -1110,7 +1311,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1110
1311
|
}
|
|
1111
1312
|
handleVadEvent(msg) {
|
|
1112
1313
|
const event = msg.event;
|
|
1113
|
-
|
|
1314
|
+
log6.debug(`VAD event: ${event}`);
|
|
1114
1315
|
if (event === "speech_start") {
|
|
1115
1316
|
this.emit("transcription", {
|
|
1116
1317
|
text: "",
|
|
@@ -1136,9 +1337,10 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1136
1337
|
|
|
1137
1338
|
// src/providers/dtelecom-tts.ts
|
|
1138
1339
|
var import_ws5 = __toESM(require("ws"));
|
|
1139
|
-
var
|
|
1340
|
+
var log7 = createLogger("DtelecomTTS");
|
|
1140
1341
|
var DtelecomTTS = class {
|
|
1141
1342
|
serverUrl;
|
|
1343
|
+
sessionKey;
|
|
1142
1344
|
voices;
|
|
1143
1345
|
defaultLang;
|
|
1144
1346
|
speed;
|
|
@@ -1153,29 +1355,33 @@ var DtelecomTTS = class {
|
|
|
1153
1355
|
if (!options.serverUrl) {
|
|
1154
1356
|
throw new Error("DtelecomTTS requires a serverUrl");
|
|
1155
1357
|
}
|
|
1358
|
+
if (!options.sessionKey) {
|
|
1359
|
+
throw new Error("DtelecomTTS requires a sessionKey");
|
|
1360
|
+
}
|
|
1156
1361
|
if (!options.voices || Object.keys(options.voices).length === 0) {
|
|
1157
1362
|
throw new Error("DtelecomTTS requires at least one voice config");
|
|
1158
1363
|
}
|
|
1159
1364
|
this.serverUrl = options.serverUrl;
|
|
1365
|
+
this.sessionKey = options.sessionKey;
|
|
1160
1366
|
this.voices = { ...options.voices };
|
|
1161
1367
|
this.defaultLang = options.defaultLanguage ?? Object.keys(this.voices)[0];
|
|
1162
1368
|
this.speed = options.speed ?? 1;
|
|
1163
1369
|
}
|
|
1164
1370
|
/** Pre-connect WebSocket to TTS server. */
|
|
1165
1371
|
async warmup() {
|
|
1166
|
-
|
|
1372
|
+
log7.info("Warming up TTS connection...");
|
|
1167
1373
|
const start = performance.now();
|
|
1168
1374
|
try {
|
|
1169
1375
|
await this.ensureConnection();
|
|
1170
|
-
|
|
1376
|
+
log7.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
1171
1377
|
} catch (err) {
|
|
1172
|
-
|
|
1378
|
+
log7.warn("TTS warmup failed (non-fatal):", err);
|
|
1173
1379
|
}
|
|
1174
1380
|
}
|
|
1175
1381
|
/** Close WebSocket connection. */
|
|
1176
1382
|
close() {
|
|
1177
1383
|
if (this.ws) {
|
|
1178
|
-
|
|
1384
|
+
log7.debug("Closing TTS WebSocket");
|
|
1179
1385
|
this.ws.close();
|
|
1180
1386
|
this.ws = null;
|
|
1181
1387
|
}
|
|
@@ -1237,7 +1443,7 @@ var DtelecomTTS = class {
|
|
|
1237
1443
|
msg.lang_code = voiceConfig.langCode;
|
|
1238
1444
|
msg.speed = this.speed;
|
|
1239
1445
|
}
|
|
1240
|
-
|
|
1446
|
+
log7.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? "default"} lang_code=${voiceConfig?.langCode ?? "default"} "${text.slice(0, 60)}"`);
|
|
1241
1447
|
ws.send(JSON.stringify(msg));
|
|
1242
1448
|
try {
|
|
1243
1449
|
while (true) {
|
|
@@ -1268,22 +1474,26 @@ var DtelecomTTS = class {
|
|
|
1268
1474
|
}
|
|
1269
1475
|
if (this.connectPromise) return this.connectPromise;
|
|
1270
1476
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
1271
|
-
|
|
1272
|
-
const
|
|
1477
|
+
const url = this.serverUrl.replace(/\/$/, "") + "/v1/stream";
|
|
1478
|
+
const wsUrl = url.replace("https://", "wss://").replace("http://", "ws://");
|
|
1479
|
+
log7.debug(`Connecting to dTelecom TTS: ${wsUrl}`);
|
|
1480
|
+
const ws = new import_ws5.default(wsUrl);
|
|
1273
1481
|
ws.on("open", () => {
|
|
1274
1482
|
this.ws = ws;
|
|
1275
1483
|
this.connectPromise = null;
|
|
1276
1484
|
const defaultVoice = this.voices[this.defaultLang];
|
|
1485
|
+
const msg = {
|
|
1486
|
+
session_key: this.sessionKey
|
|
1487
|
+
};
|
|
1277
1488
|
if (defaultVoice) {
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
}
|
|
1284
|
-
}));
|
|
1489
|
+
msg.config = {
|
|
1490
|
+
voice: defaultVoice.voice,
|
|
1491
|
+
lang_code: defaultVoice.langCode,
|
|
1492
|
+
speed: this.speed
|
|
1493
|
+
};
|
|
1285
1494
|
}
|
|
1286
|
-
|
|
1495
|
+
ws.send(JSON.stringify(msg));
|
|
1496
|
+
log7.info("dTelecom TTS WebSocket connected");
|
|
1287
1497
|
resolve();
|
|
1288
1498
|
});
|
|
1289
1499
|
ws.on("message", (data, isBinary) => {
|
|
@@ -1304,21 +1514,21 @@ var DtelecomTTS = class {
|
|
|
1304
1514
|
state.done = true;
|
|
1305
1515
|
state.wake?.();
|
|
1306
1516
|
} else if (msg.type === "generating") {
|
|
1307
|
-
|
|
1517
|
+
log7.debug(`TTS generating: "${msg.text?.slice(0, 40)}"`);
|
|
1308
1518
|
} else if (msg.type === "error") {
|
|
1309
1519
|
const errorMsg = msg.message || "Unknown TTS error";
|
|
1310
|
-
|
|
1520
|
+
log7.error(`dTelecom TTS error: ${errorMsg}`);
|
|
1311
1521
|
state.error = new Error(errorMsg);
|
|
1312
1522
|
state.wake?.();
|
|
1313
1523
|
}
|
|
1314
1524
|
} catch {
|
|
1315
|
-
|
|
1525
|
+
log7.warn("Failed to parse dTelecom TTS message");
|
|
1316
1526
|
}
|
|
1317
1527
|
}
|
|
1318
1528
|
});
|
|
1319
1529
|
ws.on("error", (err) => {
|
|
1320
1530
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1321
|
-
|
|
1531
|
+
log7.error("dTelecom TTS WebSocket error:", error);
|
|
1322
1532
|
const state = this.flushState;
|
|
1323
1533
|
if (state) {
|
|
1324
1534
|
state.error = error;
|
|
@@ -1329,7 +1539,7 @@ var DtelecomTTS = class {
|
|
|
1329
1539
|
reject(error);
|
|
1330
1540
|
});
|
|
1331
1541
|
ws.on("close", (code, reason) => {
|
|
1332
|
-
|
|
1542
|
+
log7.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);
|
|
1333
1543
|
this.ws = null;
|
|
1334
1544
|
this.connectPromise = null;
|
|
1335
1545
|
const state = this.flushState;
|
|
@@ -1349,6 +1559,7 @@ var DtelecomTTS = class {
|
|
|
1349
1559
|
DeepgramTTS,
|
|
1350
1560
|
DtelecomSTT,
|
|
1351
1561
|
DtelecomTTS,
|
|
1562
|
+
OpenAILLM,
|
|
1352
1563
|
OpenRouterLLM
|
|
1353
1564
|
});
|
|
1354
1565
|
//# sourceMappingURL=index.js.map
|