@dtelecom/agents-js 0.1.14 → 0.1.16
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 +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +79 -53
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +79 -53
- 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 +85 -2
- package/dist/providers/index.d.ts +85 -2
- package/dist/providers/index.js +469 -29
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/index.mjs +467 -29
- package/dist/providers/index.mjs.map +1 -1
- package/dist/{types-BVMiP1bW.d.mts → types-MPHcuMhp.d.mts} +4 -0
- package/dist/{types-BVMiP1bW.d.ts → types-MPHcuMhp.d.ts} +4 -0
- package/package.json +1 -1
package/dist/providers/index.mjs
CHANGED
|
@@ -296,8 +296,63 @@ var OpenRouterLLM = class {
|
|
|
296
296
|
let buffer = "";
|
|
297
297
|
const structured = !!this.responseFormat;
|
|
298
298
|
let jsonBuffer = "";
|
|
299
|
-
let
|
|
300
|
-
|
|
299
|
+
let segmentsYielded = false;
|
|
300
|
+
let lastUsage;
|
|
301
|
+
let inSegmentsArray = false;
|
|
302
|
+
let objectStart = -1;
|
|
303
|
+
let braceDepth = 0;
|
|
304
|
+
let scanIndex = 0;
|
|
305
|
+
let inString = false;
|
|
306
|
+
let escaped = false;
|
|
307
|
+
function extractSegments(buf) {
|
|
308
|
+
const results = [];
|
|
309
|
+
for (let i = scanIndex; i < buf.length; i++) {
|
|
310
|
+
const ch = buf[i];
|
|
311
|
+
if (escaped) {
|
|
312
|
+
escaped = false;
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (ch === "\\" && inString) {
|
|
316
|
+
escaped = true;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
if (ch === '"') {
|
|
320
|
+
inString = !inString;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (inString) continue;
|
|
324
|
+
if (!inSegmentsArray) {
|
|
325
|
+
if (ch === "[") {
|
|
326
|
+
const before = buf.slice(0, i).trimEnd();
|
|
327
|
+
if (before.endsWith(":") && buf.slice(0, i).includes('"segments"')) {
|
|
328
|
+
inSegmentsArray = true;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (ch === "{") {
|
|
334
|
+
if (braceDepth === 0) objectStart = i;
|
|
335
|
+
braceDepth++;
|
|
336
|
+
} else if (ch === "}") {
|
|
337
|
+
braceDepth--;
|
|
338
|
+
if (braceDepth === 0 && objectStart >= 0) {
|
|
339
|
+
const objStr = buf.slice(objectStart, i + 1);
|
|
340
|
+
try {
|
|
341
|
+
const seg = JSON.parse(objStr);
|
|
342
|
+
if (seg.lang && seg.text) {
|
|
343
|
+
results.push({ lang: seg.lang, text: seg.text });
|
|
344
|
+
}
|
|
345
|
+
} catch {
|
|
346
|
+
}
|
|
347
|
+
objectStart = -1;
|
|
348
|
+
}
|
|
349
|
+
} else if (ch === "]" && braceDepth === 0) {
|
|
350
|
+
inSegmentsArray = false;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
scanIndex = buf.length;
|
|
354
|
+
return results;
|
|
355
|
+
}
|
|
301
356
|
try {
|
|
302
357
|
while (true) {
|
|
303
358
|
if (signal?.aborted) break;
|
|
@@ -310,10 +365,7 @@ var OpenRouterLLM = class {
|
|
|
310
365
|
const trimmed = line.trim();
|
|
311
366
|
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
312
367
|
const data = trimmed.slice(6);
|
|
313
|
-
if (data === "[DONE]")
|
|
314
|
-
yield { type: "done" };
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
368
|
+
if (data === "[DONE]") break;
|
|
317
369
|
try {
|
|
318
370
|
const parsed = JSON.parse(data);
|
|
319
371
|
const choice = parsed.choices?.[0];
|
|
@@ -322,31 +374,20 @@ var OpenRouterLLM = class {
|
|
|
322
374
|
if (delta?.content) {
|
|
323
375
|
if (structured) {
|
|
324
376
|
jsonBuffer += delta.content;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const text = match[2].replace(/\\(.)/g, (_, c) => {
|
|
330
|
-
if (c === "n") return "\n";
|
|
331
|
-
if (c === "t") return " ";
|
|
332
|
-
return c;
|
|
333
|
-
});
|
|
334
|
-
lastSegmentIndex = segmentRe.lastIndex;
|
|
335
|
-
yield { type: "segment", segment: { lang, text } };
|
|
377
|
+
const segments = extractSegments(jsonBuffer);
|
|
378
|
+
for (const seg of segments) {
|
|
379
|
+
yield { type: "segment", segment: seg };
|
|
380
|
+
segmentsYielded = true;
|
|
336
381
|
}
|
|
337
382
|
} else {
|
|
338
383
|
yield { type: "token", token: delta.content };
|
|
339
384
|
}
|
|
340
385
|
}
|
|
341
386
|
if (parsed.usage) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
promptTokens: parsed.usage.prompt_tokens,
|
|
346
|
-
completionTokens: parsed.usage.completion_tokens
|
|
347
|
-
}
|
|
387
|
+
lastUsage = {
|
|
388
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
389
|
+
completionTokens: parsed.usage.completion_tokens
|
|
348
390
|
};
|
|
349
|
-
return;
|
|
350
391
|
}
|
|
351
392
|
} catch {
|
|
352
393
|
}
|
|
@@ -355,10 +396,10 @@ var OpenRouterLLM = class {
|
|
|
355
396
|
} finally {
|
|
356
397
|
reader.releaseLock();
|
|
357
398
|
}
|
|
358
|
-
if (structured &&
|
|
359
|
-
log2.warn(`
|
|
399
|
+
if (structured && !segmentsYielded && jsonBuffer.length > 0) {
|
|
400
|
+
log2.warn(`LLM returned no segments. Raw JSON: "${jsonBuffer.slice(0, 300)}"`);
|
|
360
401
|
}
|
|
361
|
-
yield { type: "done" };
|
|
402
|
+
yield { type: "done", ...lastUsage ? { usage: lastUsage } : {} };
|
|
362
403
|
}
|
|
363
404
|
};
|
|
364
405
|
|
|
@@ -681,13 +722,21 @@ var DeepgramTTS = class {
|
|
|
681
722
|
}
|
|
682
723
|
async *synthesize(text, signal) {
|
|
683
724
|
if (signal?.aborted) return;
|
|
684
|
-
const
|
|
725
|
+
const rawSegments = this.multiLanguage ? parseLangSegments(text, this.defaultLang) : [{ lang: this.defaultLang, text }];
|
|
726
|
+
const segments = [];
|
|
727
|
+
for (const seg of rawSegments) {
|
|
728
|
+
if (!seg.text.trim()) continue;
|
|
729
|
+
if (!/\p{L}/u.test(seg.text) && segments.length > 0) {
|
|
730
|
+
segments[segments.length - 1].text += seg.text;
|
|
731
|
+
} else {
|
|
732
|
+
segments.push(seg);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
685
735
|
const silenceBytes = Math.round(this.sampleRate * 0.2) * 2;
|
|
686
736
|
const silence = Buffer.alloc(silenceBytes);
|
|
687
737
|
let prevLang = null;
|
|
688
738
|
for (const segment of segments) {
|
|
689
739
|
if (signal?.aborted) break;
|
|
690
|
-
if (!segment.text.trim()) continue;
|
|
691
740
|
const lang = this.models[segment.lang] ? segment.lang : this.defaultLang;
|
|
692
741
|
if (prevLang !== null && lang !== prevLang) {
|
|
693
742
|
yield silence;
|
|
@@ -815,10 +864,399 @@ var DeepgramTTS = class {
|
|
|
815
864
|
return promise;
|
|
816
865
|
}
|
|
817
866
|
};
|
|
867
|
+
|
|
868
|
+
// src/providers/dtelecom-stt.ts
|
|
869
|
+
import WebSocket4 from "ws";
|
|
870
|
+
var log5 = createLogger("DtelecomSTT");
|
|
871
|
+
var KEEPALIVE_INTERVAL_MS2 = 5e3;
|
|
872
|
+
var DtelecomSTT = class {
|
|
873
|
+
options;
|
|
874
|
+
constructor(options) {
|
|
875
|
+
if (!options.serverUrl) {
|
|
876
|
+
throw new Error("DtelecomSTT requires a serverUrl");
|
|
877
|
+
}
|
|
878
|
+
this.options = options;
|
|
879
|
+
}
|
|
880
|
+
createStream(options) {
|
|
881
|
+
const language = options?.language ?? this.options.language ?? "auto";
|
|
882
|
+
return new DtelecomSTTStream(this.options, language);
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
var DtelecomSTTStream = class extends BaseSTTStream {
|
|
886
|
+
ws = null;
|
|
887
|
+
serverUrl;
|
|
888
|
+
forceWhisper;
|
|
889
|
+
_ready = false;
|
|
890
|
+
_closed = false;
|
|
891
|
+
pendingAudio = [];
|
|
892
|
+
keepAliveTimer = null;
|
|
893
|
+
language;
|
|
894
|
+
constructor(options, language) {
|
|
895
|
+
super();
|
|
896
|
+
this.serverUrl = options.serverUrl;
|
|
897
|
+
this.language = language;
|
|
898
|
+
this.forceWhisper = options.forceWhisper ?? false;
|
|
899
|
+
this.connect();
|
|
900
|
+
}
|
|
901
|
+
sendAudio(pcm16) {
|
|
902
|
+
if (this._closed) return;
|
|
903
|
+
if (!this._ready) {
|
|
904
|
+
this.pendingAudio.push(pcm16);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
if (this.ws?.readyState === WebSocket4.OPEN) {
|
|
908
|
+
this.ws.send(pcm16);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Switch language mid-session.
|
|
913
|
+
* Sends a reconfigure message to the server; clears buffers and updates model routing.
|
|
914
|
+
*/
|
|
915
|
+
setLanguage(language, options) {
|
|
916
|
+
if (this._closed) return;
|
|
917
|
+
this.language = language;
|
|
918
|
+
const config = { type: "config", language };
|
|
919
|
+
if (options?.forceWhisper) {
|
|
920
|
+
config.model = "whisper";
|
|
921
|
+
}
|
|
922
|
+
if (this.ws?.readyState === WebSocket4.OPEN) {
|
|
923
|
+
this.ws.send(JSON.stringify(config));
|
|
924
|
+
log5.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ", model=whisper" : ""}`);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
async close() {
|
|
928
|
+
if (this._closed) return;
|
|
929
|
+
this._closed = true;
|
|
930
|
+
this._ready = false;
|
|
931
|
+
this.pendingAudio = [];
|
|
932
|
+
this.stopKeepAlive();
|
|
933
|
+
if (this.ws) {
|
|
934
|
+
this.ws.close();
|
|
935
|
+
this.ws = null;
|
|
936
|
+
}
|
|
937
|
+
log5.debug("DtelecomSTT stream closed");
|
|
938
|
+
}
|
|
939
|
+
connect() {
|
|
940
|
+
log5.debug(`Connecting to dTelecom STT: ${this.serverUrl}`);
|
|
941
|
+
this.ws = new WebSocket4(this.serverUrl);
|
|
942
|
+
this.ws.on("open", () => {
|
|
943
|
+
log5.info("dTelecom STT WebSocket connected");
|
|
944
|
+
const config = { type: "config", language: this.language };
|
|
945
|
+
if (this.forceWhisper) {
|
|
946
|
+
config.model = "whisper";
|
|
947
|
+
}
|
|
948
|
+
this.ws.send(JSON.stringify(config));
|
|
949
|
+
});
|
|
950
|
+
this.ws.on("message", (data, isBinary) => {
|
|
951
|
+
if (isBinary) return;
|
|
952
|
+
try {
|
|
953
|
+
const msg = JSON.parse(data.toString());
|
|
954
|
+
this.handleMessage(msg);
|
|
955
|
+
} catch (err) {
|
|
956
|
+
log5.error("Failed to parse dTelecom STT message:", err);
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
this.ws.on("error", (err) => {
|
|
960
|
+
log5.error("dTelecom STT WebSocket error:", err);
|
|
961
|
+
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
962
|
+
});
|
|
963
|
+
this.ws.on("close", (code, reason) => {
|
|
964
|
+
log5.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);
|
|
965
|
+
this._ready = false;
|
|
966
|
+
this.stopKeepAlive();
|
|
967
|
+
if (!this._closed) {
|
|
968
|
+
log5.info("dTelecom STT connection lost, reconnecting in 1s...");
|
|
969
|
+
setTimeout(() => {
|
|
970
|
+
if (!this._closed) this.connect();
|
|
971
|
+
}, 1e3);
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
handleMessage(msg) {
|
|
976
|
+
const type = msg.type;
|
|
977
|
+
if (type === "ready") {
|
|
978
|
+
this.handleReady(msg);
|
|
979
|
+
} else if (type === "transcription") {
|
|
980
|
+
this.handleTranscription(msg);
|
|
981
|
+
} else if (type === "vad_event") {
|
|
982
|
+
this.handleVadEvent(msg);
|
|
983
|
+
} else if (type === "pong") {
|
|
984
|
+
} else if (type === "error") {
|
|
985
|
+
const errData = msg.error;
|
|
986
|
+
const errorMsg = msg.message || (typeof errData === "string" ? errData : JSON.stringify(errData)) || "Unknown STT error";
|
|
987
|
+
log5.error(`dTelecom STT error: ${errorMsg}`);
|
|
988
|
+
this.emit("error", new Error(errorMsg));
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
handleReady(msg) {
|
|
992
|
+
const clientId = msg.client_id;
|
|
993
|
+
const lang = msg.language;
|
|
994
|
+
log5.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);
|
|
995
|
+
this._ready = true;
|
|
996
|
+
for (const buf of this.pendingAudio) {
|
|
997
|
+
if (this.ws?.readyState === WebSocket4.OPEN) {
|
|
998
|
+
this.ws.send(buf);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
this.pendingAudio = [];
|
|
1002
|
+
this.startKeepAlive();
|
|
1003
|
+
}
|
|
1004
|
+
handleTranscription(msg) {
|
|
1005
|
+
const text = msg.text ?? "";
|
|
1006
|
+
const isFinal = msg.is_final ?? false;
|
|
1007
|
+
const language = msg.language;
|
|
1008
|
+
const latencyMs = msg.latency_ms;
|
|
1009
|
+
if (!text) return;
|
|
1010
|
+
if (isFinal && latencyMs !== void 0) {
|
|
1011
|
+
log5.info(`stt_final: ${latencyMs.toFixed(0)}ms "${text.slice(0, 50)}"`);
|
|
1012
|
+
}
|
|
1013
|
+
this.emit("transcription", {
|
|
1014
|
+
text,
|
|
1015
|
+
isFinal,
|
|
1016
|
+
language,
|
|
1017
|
+
sttDuration: isFinal ? latencyMs : void 0
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
handleVadEvent(msg) {
|
|
1021
|
+
const event = msg.event;
|
|
1022
|
+
log5.debug(`VAD event: ${event}`);
|
|
1023
|
+
if (event === "speech_start") {
|
|
1024
|
+
this.emit("transcription", {
|
|
1025
|
+
text: "",
|
|
1026
|
+
isFinal: false
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
startKeepAlive() {
|
|
1031
|
+
this.stopKeepAlive();
|
|
1032
|
+
this.keepAliveTimer = setInterval(() => {
|
|
1033
|
+
if (this.ws?.readyState === WebSocket4.OPEN) {
|
|
1034
|
+
this.ws.send(JSON.stringify({ type: "ping" }));
|
|
1035
|
+
}
|
|
1036
|
+
}, KEEPALIVE_INTERVAL_MS2);
|
|
1037
|
+
}
|
|
1038
|
+
stopKeepAlive() {
|
|
1039
|
+
if (this.keepAliveTimer) {
|
|
1040
|
+
clearInterval(this.keepAliveTimer);
|
|
1041
|
+
this.keepAliveTimer = null;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
// src/providers/dtelecom-tts.ts
|
|
1047
|
+
import WebSocket5 from "ws";
|
|
1048
|
+
var log6 = createLogger("DtelecomTTS");
|
|
1049
|
+
var DtelecomTTS = class {
|
|
1050
|
+
serverUrl;
|
|
1051
|
+
voices;
|
|
1052
|
+
defaultLang;
|
|
1053
|
+
speed;
|
|
1054
|
+
ws = null;
|
|
1055
|
+
connectPromise = null;
|
|
1056
|
+
flushState = null;
|
|
1057
|
+
/** Default language code for untagged text (e.g. 'en'). */
|
|
1058
|
+
get defaultLanguage() {
|
|
1059
|
+
return this.defaultLang;
|
|
1060
|
+
}
|
|
1061
|
+
constructor(options) {
|
|
1062
|
+
if (!options.serverUrl) {
|
|
1063
|
+
throw new Error("DtelecomTTS requires a serverUrl");
|
|
1064
|
+
}
|
|
1065
|
+
if (!options.voices || Object.keys(options.voices).length === 0) {
|
|
1066
|
+
throw new Error("DtelecomTTS requires at least one voice config");
|
|
1067
|
+
}
|
|
1068
|
+
this.serverUrl = options.serverUrl;
|
|
1069
|
+
this.voices = { ...options.voices };
|
|
1070
|
+
this.defaultLang = options.defaultLanguage ?? Object.keys(this.voices)[0];
|
|
1071
|
+
this.speed = options.speed ?? 1;
|
|
1072
|
+
}
|
|
1073
|
+
/** Pre-connect WebSocket to TTS server. */
|
|
1074
|
+
async warmup() {
|
|
1075
|
+
log6.info("Warming up TTS connection...");
|
|
1076
|
+
const start = performance.now();
|
|
1077
|
+
try {
|
|
1078
|
+
await this.ensureConnection();
|
|
1079
|
+
log6.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
1080
|
+
} catch (err) {
|
|
1081
|
+
log6.warn("TTS warmup failed (non-fatal):", err);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
/** Close WebSocket connection. */
|
|
1085
|
+
close() {
|
|
1086
|
+
if (this.ws) {
|
|
1087
|
+
log6.debug("Closing TTS WebSocket");
|
|
1088
|
+
this.ws.close();
|
|
1089
|
+
this.ws = null;
|
|
1090
|
+
}
|
|
1091
|
+
this.connectPromise = null;
|
|
1092
|
+
this.flushState = null;
|
|
1093
|
+
}
|
|
1094
|
+
/** Strip SSML lang tags from text for display/events. */
|
|
1095
|
+
cleanText(text) {
|
|
1096
|
+
return parseLangSegments(text, this.defaultLang).map((s) => s.text).join(" ").replace(/\s+/g, " ").trim();
|
|
1097
|
+
}
|
|
1098
|
+
async *synthesize(text, signal) {
|
|
1099
|
+
if (signal?.aborted) return;
|
|
1100
|
+
const rawSegments = parseLangSegments(text, this.defaultLang);
|
|
1101
|
+
const segments = [];
|
|
1102
|
+
for (const seg of rawSegments) {
|
|
1103
|
+
if (!seg.text.trim()) continue;
|
|
1104
|
+
if (!/\p{L}/u.test(seg.text) && segments.length > 0) {
|
|
1105
|
+
segments[segments.length - 1].text += seg.text;
|
|
1106
|
+
} else {
|
|
1107
|
+
segments.push(seg);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
const silenceBytes = Math.round(48e3 * 0.2) * 2;
|
|
1111
|
+
const silence = Buffer.alloc(silenceBytes);
|
|
1112
|
+
let prevLang = null;
|
|
1113
|
+
for (const segment of segments) {
|
|
1114
|
+
if (signal?.aborted) break;
|
|
1115
|
+
const lang = this.voices[segment.lang] ? segment.lang : this.defaultLang;
|
|
1116
|
+
if (prevLang !== null && lang !== prevLang) {
|
|
1117
|
+
yield silence;
|
|
1118
|
+
}
|
|
1119
|
+
prevLang = lang;
|
|
1120
|
+
yield* this.synthesizeSegment(lang, segment.text, signal);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
async *synthesizeSegment(lang, text, signal) {
|
|
1124
|
+
await this.ensureConnection();
|
|
1125
|
+
const ws = this.ws;
|
|
1126
|
+
if (!ws || ws.readyState !== WebSocket5.OPEN) {
|
|
1127
|
+
throw new Error("dTelecom TTS WebSocket not connected");
|
|
1128
|
+
}
|
|
1129
|
+
const state = { chunks: [], done: false, cleared: false, error: null, wake: null };
|
|
1130
|
+
this.flushState = state;
|
|
1131
|
+
const onAbort = () => {
|
|
1132
|
+
state.done = true;
|
|
1133
|
+
state.wake?.();
|
|
1134
|
+
if (ws.readyState === WebSocket5.OPEN) {
|
|
1135
|
+
try {
|
|
1136
|
+
ws.send(JSON.stringify({ type: "clear" }));
|
|
1137
|
+
} catch {
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1142
|
+
const voiceConfig = this.voices[lang];
|
|
1143
|
+
const msg = { text };
|
|
1144
|
+
if (voiceConfig) {
|
|
1145
|
+
msg.voice = voiceConfig.voice;
|
|
1146
|
+
msg.lang_code = voiceConfig.langCode;
|
|
1147
|
+
msg.speed = this.speed;
|
|
1148
|
+
}
|
|
1149
|
+
log6.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? "default"} lang_code=${voiceConfig?.langCode ?? "default"} "${text.slice(0, 60)}"`);
|
|
1150
|
+
ws.send(JSON.stringify(msg));
|
|
1151
|
+
try {
|
|
1152
|
+
while (true) {
|
|
1153
|
+
if (signal?.aborted) break;
|
|
1154
|
+
if (state.error) throw state.error;
|
|
1155
|
+
if (state.chunks.length > 0) {
|
|
1156
|
+
yield state.chunks.shift();
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
if (state.done) break;
|
|
1160
|
+
await new Promise((resolve) => {
|
|
1161
|
+
state.wake = resolve;
|
|
1162
|
+
});
|
|
1163
|
+
state.wake = null;
|
|
1164
|
+
}
|
|
1165
|
+
while (state.chunks.length > 0) {
|
|
1166
|
+
yield state.chunks.shift();
|
|
1167
|
+
}
|
|
1168
|
+
} finally {
|
|
1169
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1170
|
+
this.flushState = null;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
/** Ensure a WebSocket connection exists and is open. */
|
|
1174
|
+
ensureConnection() {
|
|
1175
|
+
if (this.ws && this.ws.readyState === WebSocket5.OPEN) {
|
|
1176
|
+
return Promise.resolve();
|
|
1177
|
+
}
|
|
1178
|
+
if (this.connectPromise) return this.connectPromise;
|
|
1179
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
1180
|
+
log6.debug(`Connecting to dTelecom TTS: ${this.serverUrl}`);
|
|
1181
|
+
const ws = new WebSocket5(this.serverUrl);
|
|
1182
|
+
ws.on("open", () => {
|
|
1183
|
+
this.ws = ws;
|
|
1184
|
+
this.connectPromise = null;
|
|
1185
|
+
const defaultVoice = this.voices[this.defaultLang];
|
|
1186
|
+
if (defaultVoice) {
|
|
1187
|
+
ws.send(JSON.stringify({
|
|
1188
|
+
config: {
|
|
1189
|
+
voice: defaultVoice.voice,
|
|
1190
|
+
lang_code: defaultVoice.langCode,
|
|
1191
|
+
speed: this.speed
|
|
1192
|
+
}
|
|
1193
|
+
}));
|
|
1194
|
+
}
|
|
1195
|
+
log6.info("dTelecom TTS WebSocket connected");
|
|
1196
|
+
resolve();
|
|
1197
|
+
});
|
|
1198
|
+
ws.on("message", (data, isBinary) => {
|
|
1199
|
+
const state = this.flushState;
|
|
1200
|
+
if (!state) return;
|
|
1201
|
+
if (isBinary) {
|
|
1202
|
+
const buf = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
1203
|
+
state.chunks.push(buf);
|
|
1204
|
+
state.wake?.();
|
|
1205
|
+
} else {
|
|
1206
|
+
try {
|
|
1207
|
+
const msg = JSON.parse(data.toString());
|
|
1208
|
+
if (msg.type === "done") {
|
|
1209
|
+
state.done = true;
|
|
1210
|
+
state.wake?.();
|
|
1211
|
+
} else if (msg.type === "cleared") {
|
|
1212
|
+
state.cleared = true;
|
|
1213
|
+
state.done = true;
|
|
1214
|
+
state.wake?.();
|
|
1215
|
+
} else if (msg.type === "generating") {
|
|
1216
|
+
log6.debug(`TTS generating: "${msg.text?.slice(0, 40)}"`);
|
|
1217
|
+
} else if (msg.type === "error") {
|
|
1218
|
+
const errorMsg = msg.message || "Unknown TTS error";
|
|
1219
|
+
log6.error(`dTelecom TTS error: ${errorMsg}`);
|
|
1220
|
+
state.error = new Error(errorMsg);
|
|
1221
|
+
state.wake?.();
|
|
1222
|
+
}
|
|
1223
|
+
} catch {
|
|
1224
|
+
log6.warn("Failed to parse dTelecom TTS message");
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
ws.on("error", (err) => {
|
|
1229
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1230
|
+
log6.error("dTelecom TTS WebSocket error:", error);
|
|
1231
|
+
const state = this.flushState;
|
|
1232
|
+
if (state) {
|
|
1233
|
+
state.error = error;
|
|
1234
|
+
state.wake?.();
|
|
1235
|
+
}
|
|
1236
|
+
this.ws = null;
|
|
1237
|
+
this.connectPromise = null;
|
|
1238
|
+
reject(error);
|
|
1239
|
+
});
|
|
1240
|
+
ws.on("close", (code, reason) => {
|
|
1241
|
+
log6.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);
|
|
1242
|
+
this.ws = null;
|
|
1243
|
+
this.connectPromise = null;
|
|
1244
|
+
const state = this.flushState;
|
|
1245
|
+
if (state) {
|
|
1246
|
+
state.done = true;
|
|
1247
|
+
state.wake?.();
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
});
|
|
1251
|
+
return this.connectPromise;
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
818
1254
|
export {
|
|
819
1255
|
CartesiaTTS,
|
|
820
1256
|
DeepgramSTT,
|
|
821
1257
|
DeepgramTTS,
|
|
1258
|
+
DtelecomSTT,
|
|
1259
|
+
DtelecomTTS,
|
|
822
1260
|
OpenRouterLLM
|
|
823
1261
|
};
|
|
824
1262
|
//# sourceMappingURL=index.mjs.map
|