@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.
@@ -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 log3 = createLogger("CartesiaTTS");
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
- log3.debug("Closing Cartesia WebSocket");
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
- log3.info("Warming up TTS connection...");
737
+ log4.info("Warming up TTS connection...");
548
738
  const start = performance.now();
549
739
  try {
550
740
  await this.ensureConnection();
551
- log3.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
741
+ log4.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
552
742
  } catch (err) {
553
- log3.warn("TTS warmup failed (non-fatal):", err);
743
+ log4.warn("TTS warmup failed (non-fatal):", err);
554
744
  }
555
745
  }
556
746
  async *synthesize(text, signal) {
557
- log3.debug(`Synthesizing: "${text.slice(0, 60)}"`);
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
- log3.debug("Connecting to Cartesia...");
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
- log3.info("Cartesia WebSocket connected");
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
- log3.error("Failed to parse Cartesia message:", err);
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
- log3.error("Cartesia WebSocket error:", error);
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
- log3.debug(`Cartesia WebSocket closed: ${code} ${reason.toString()}`);
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
- log3.debug(`Cartesia synthesis done for ${contextId} (${ctx.chunks.length} chunks pending)`);
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
- log3.error(`Cartesia error for ${contextId}: ${errorMsg}`);
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 log4 = createLogger("DeepgramTTS");
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
- log4.debug(`Closing WebSocket for [${lang}]`);
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
- log4.info("Warming up TTS connections...");
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
- log4.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
995
+ log5.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
806
996
  } catch (err) {
807
- log4.warn("TTS warmup failed (non-fatal):", err);
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
- log4.debug(`Synthesizing [${lang}]: "${text.slice(0, 60)}"`);
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
- log4.debug(`Connecting to Deepgram for [${lang}]: ${model}`);
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
- log4.info(`Deepgram WebSocket connected for [${lang}] (${model})`);
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
- log4.warn(`Deepgram [${lang}] ${msg.type}: ${msg.description || msg.message || JSON.stringify(msg)}`);
1114
+ log5.warn(`Deepgram [${lang}] ${msg.type}: ${msg.description || msg.message || JSON.stringify(msg)}`);
925
1115
  }
926
1116
  } catch {
927
- log4.warn(`Failed to parse Deepgram message for [${lang}]`);
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
- log4.error(`Deepgram WebSocket error [${lang}]:`, error);
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
- log4.debug(`Deepgram WebSocket closed [${lang}]: ${code} ${reason.toString()}`);
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 log5 = createLogger("DtelecomSTT");
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
- log5.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ", model=whisper" : ""}`);
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
- log5.debug("DtelecomSTT stream closed");
1223
+ log6.debug("DtelecomSTT stream closed");
1029
1224
  }
1030
1225
  connect() {
1031
- log5.debug(`Connecting to dTelecom STT: ${this.serverUrl}`);
1032
- this.ws = new import_ws4.default(this.serverUrl);
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
- log5.info("dTelecom STT WebSocket connected");
1035
- const config = { type: "config", language: this.language };
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
- log5.error("Failed to parse dTelecom STT message:", err);
1248
+ log6.error("Failed to parse dTelecom STT message:", err);
1048
1249
  }
1049
1250
  });
1050
1251
  this.ws.on("error", (err) => {
1051
- log5.error("dTelecom STT WebSocket error:", err);
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
- log5.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);
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
- log5.info("dTelecom STT connection lost, reconnecting in 1s...");
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
- log5.error(`dTelecom STT error: ${errorMsg}`);
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
- log5.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);
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
- log5.info(`stt_final: ${latencyMs.toFixed(0)}ms "${text.slice(0, 50)}"`);
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
- log5.debug(`VAD event: ${event}`);
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 log6 = createLogger("DtelecomTTS");
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
- log6.info("Warming up TTS connection...");
1372
+ log7.info("Warming up TTS connection...");
1167
1373
  const start = performance.now();
1168
1374
  try {
1169
1375
  await this.ensureConnection();
1170
- log6.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
1376
+ log7.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
1171
1377
  } catch (err) {
1172
- log6.warn("TTS warmup failed (non-fatal):", err);
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
- log6.debug("Closing TTS WebSocket");
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
- log6.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? "default"} lang_code=${voiceConfig?.langCode ?? "default"} "${text.slice(0, 60)}"`);
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
- log6.debug(`Connecting to dTelecom TTS: ${this.serverUrl}`);
1272
- const ws = new import_ws5.default(this.serverUrl);
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
- ws.send(JSON.stringify({
1279
- config: {
1280
- voice: defaultVoice.voice,
1281
- lang_code: defaultVoice.langCode,
1282
- speed: this.speed
1283
- }
1284
- }));
1489
+ msg.config = {
1490
+ voice: defaultVoice.voice,
1491
+ lang_code: defaultVoice.langCode,
1492
+ speed: this.speed
1493
+ };
1285
1494
  }
1286
- log6.info("dTelecom TTS WebSocket connected");
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
- log6.debug(`TTS generating: "${msg.text?.slice(0, 40)}"`);
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
- log6.error(`dTelecom TTS error: ${errorMsg}`);
1520
+ log7.error(`dTelecom TTS error: ${errorMsg}`);
1311
1521
  state.error = new Error(errorMsg);
1312
1522
  state.wake?.();
1313
1523
  }
1314
1524
  } catch {
1315
- log6.warn("Failed to parse dTelecom TTS message");
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
- log6.error("dTelecom TTS WebSocket error:", error);
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
- log6.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);
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