@dtelecom/agents-js 0.2.0 → 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/providers/index.d.mts +41 -1
- package/dist/providers/index.d.ts +41 -1
- package/dist/providers/index.js +242 -51
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/index.mjs +241 -51
- package/dist/providers/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -94,6 +94,46 @@ declare class OpenRouterLLM implements LLMPlugin {
|
|
|
94
94
|
chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk>;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* OpenAILLM — streaming LLM via OpenAI API.
|
|
99
|
+
*
|
|
100
|
+
* Uses native fetch() with SSE parsing for streaming responses.
|
|
101
|
+
* No SDK dependency — just HTTP.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
interface OpenAILLMOptions {
|
|
105
|
+
apiKey: string;
|
|
106
|
+
/** Model identifier (e.g. 'gpt-4o', 'gpt-4o-mini') */
|
|
107
|
+
model: string;
|
|
108
|
+
/** Max tokens in response (default: 512) */
|
|
109
|
+
maxTokens?: number;
|
|
110
|
+
/** Sampling temperature 0-2 (default: 0.7) */
|
|
111
|
+
temperature?: number;
|
|
112
|
+
/** Structured output via constrained decoding (e.g. for multi-language segment routing) */
|
|
113
|
+
responseFormat?: {
|
|
114
|
+
type: 'json_schema';
|
|
115
|
+
json_schema: {
|
|
116
|
+
name: string;
|
|
117
|
+
strict: boolean;
|
|
118
|
+
schema: Record<string, unknown>;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
declare class OpenAILLM implements LLMPlugin {
|
|
123
|
+
private readonly apiKey;
|
|
124
|
+
private readonly model;
|
|
125
|
+
private readonly maxTokens;
|
|
126
|
+
private readonly temperature;
|
|
127
|
+
private readonly responseFormat?;
|
|
128
|
+
constructor(options: OpenAILLMOptions);
|
|
129
|
+
/**
|
|
130
|
+
* Warm up the LLM by sending the system prompt and a short message.
|
|
131
|
+
* Primes the HTTP/TLS connection and model loading on the provider side.
|
|
132
|
+
*/
|
|
133
|
+
warmup(systemPrompt: string): Promise<void>;
|
|
134
|
+
chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk>;
|
|
135
|
+
}
|
|
136
|
+
|
|
97
137
|
/**
|
|
98
138
|
* CartesiaTTS — real-time streaming TTS via Cartesia WebSocket API.
|
|
99
139
|
*
|
|
@@ -288,4 +328,4 @@ declare class DtelecomTTS implements TTSPlugin {
|
|
|
288
328
|
private ensureConnection;
|
|
289
329
|
}
|
|
290
330
|
|
|
291
|
-
export { CartesiaTTS, type CartesiaTTSOptions, DeepgramSTT, type DeepgramSTTOptions, DeepgramTTS, type DeepgramTTSOptions, DtelecomSTT, type DtelecomSTTOptions, DtelecomTTS, type DtelecomTTSOptions, type VoiceConfig as DtelecomVoiceConfig, OpenRouterLLM, type OpenRouterLLMOptions };
|
|
331
|
+
export { CartesiaTTS, type CartesiaTTSOptions, DeepgramSTT, type DeepgramSTTOptions, DeepgramTTS, type DeepgramTTSOptions, DtelecomSTT, type DtelecomSTTOptions, DtelecomTTS, type DtelecomTTSOptions, type VoiceConfig as DtelecomVoiceConfig, OpenAILLM, type OpenAILLMOptions, OpenRouterLLM, type OpenRouterLLMOptions };
|
|
@@ -94,6 +94,46 @@ declare class OpenRouterLLM implements LLMPlugin {
|
|
|
94
94
|
chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk>;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* OpenAILLM — streaming LLM via OpenAI API.
|
|
99
|
+
*
|
|
100
|
+
* Uses native fetch() with SSE parsing for streaming responses.
|
|
101
|
+
* No SDK dependency — just HTTP.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
interface OpenAILLMOptions {
|
|
105
|
+
apiKey: string;
|
|
106
|
+
/** Model identifier (e.g. 'gpt-4o', 'gpt-4o-mini') */
|
|
107
|
+
model: string;
|
|
108
|
+
/** Max tokens in response (default: 512) */
|
|
109
|
+
maxTokens?: number;
|
|
110
|
+
/** Sampling temperature 0-2 (default: 0.7) */
|
|
111
|
+
temperature?: number;
|
|
112
|
+
/** Structured output via constrained decoding (e.g. for multi-language segment routing) */
|
|
113
|
+
responseFormat?: {
|
|
114
|
+
type: 'json_schema';
|
|
115
|
+
json_schema: {
|
|
116
|
+
name: string;
|
|
117
|
+
strict: boolean;
|
|
118
|
+
schema: Record<string, unknown>;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
declare class OpenAILLM implements LLMPlugin {
|
|
123
|
+
private readonly apiKey;
|
|
124
|
+
private readonly model;
|
|
125
|
+
private readonly maxTokens;
|
|
126
|
+
private readonly temperature;
|
|
127
|
+
private readonly responseFormat?;
|
|
128
|
+
constructor(options: OpenAILLMOptions);
|
|
129
|
+
/**
|
|
130
|
+
* Warm up the LLM by sending the system prompt and a short message.
|
|
131
|
+
* Primes the HTTP/TLS connection and model loading on the provider side.
|
|
132
|
+
*/
|
|
133
|
+
warmup(systemPrompt: string): Promise<void>;
|
|
134
|
+
chat(messages: Message[], signal?: AbortSignal, options?: LLMChatOptions): AsyncGenerator<LLMChunk>;
|
|
135
|
+
}
|
|
136
|
+
|
|
97
137
|
/**
|
|
98
138
|
* CartesiaTTS — real-time streaming TTS via Cartesia WebSocket API.
|
|
99
139
|
*
|
|
@@ -288,4 +328,4 @@ declare class DtelecomTTS implements TTSPlugin {
|
|
|
288
328
|
private ensureConnection;
|
|
289
329
|
}
|
|
290
330
|
|
|
291
|
-
export { CartesiaTTS, type CartesiaTTSOptions, DeepgramSTT, type DeepgramSTTOptions, DeepgramTTS, type DeepgramTTSOptions, DtelecomSTT, type DtelecomSTTOptions, DtelecomTTS, type DtelecomTTSOptions, type VoiceConfig as DtelecomVoiceConfig, OpenRouterLLM, type OpenRouterLLMOptions };
|
|
331
|
+
export { CartesiaTTS, type CartesiaTTSOptions, DeepgramSTT, type DeepgramSTTOptions, DeepgramTTS, type DeepgramTTSOptions, DtelecomSTT, type DtelecomSTTOptions, DtelecomTTS, type DtelecomTTSOptions, type VoiceConfig as DtelecomVoiceConfig, OpenAILLM, type OpenAILLMOptions, OpenRouterLLM, type OpenRouterLLMOptions };
|
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;
|
|
@@ -1017,7 +1207,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1017
1207
|
}
|
|
1018
1208
|
if (this.ws?.readyState === import_ws4.default.OPEN) {
|
|
1019
1209
|
this.ws.send(JSON.stringify(config));
|
|
1020
|
-
|
|
1210
|
+
log6.info(`Reconfiguring STT: language=${language}${options?.forceWhisper ? ", model=whisper" : ""}`);
|
|
1021
1211
|
}
|
|
1022
1212
|
}
|
|
1023
1213
|
async close() {
|
|
@@ -1030,15 +1220,15 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1030
1220
|
this.ws.close();
|
|
1031
1221
|
this.ws = null;
|
|
1032
1222
|
}
|
|
1033
|
-
|
|
1223
|
+
log6.debug("DtelecomSTT stream closed");
|
|
1034
1224
|
}
|
|
1035
1225
|
connect() {
|
|
1036
1226
|
const url = this.serverUrl.replace(/\/$/, "") + "/v1/stream";
|
|
1037
1227
|
const wsUrl = url.replace("https://", "wss://").replace("http://", "ws://");
|
|
1038
|
-
|
|
1228
|
+
log6.debug(`Connecting to dTelecom STT: ${wsUrl}`);
|
|
1039
1229
|
this.ws = new import_ws4.default(wsUrl);
|
|
1040
1230
|
this.ws.on("open", () => {
|
|
1041
|
-
|
|
1231
|
+
log6.info("dTelecom STT WebSocket connected");
|
|
1042
1232
|
const config = {
|
|
1043
1233
|
type: "config",
|
|
1044
1234
|
language: this.language,
|
|
@@ -1055,19 +1245,19 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1055
1245
|
const msg = JSON.parse(data.toString());
|
|
1056
1246
|
this.handleMessage(msg);
|
|
1057
1247
|
} catch (err) {
|
|
1058
|
-
|
|
1248
|
+
log6.error("Failed to parse dTelecom STT message:", err);
|
|
1059
1249
|
}
|
|
1060
1250
|
});
|
|
1061
1251
|
this.ws.on("error", (err) => {
|
|
1062
|
-
|
|
1252
|
+
log6.error("dTelecom STT WebSocket error:", err);
|
|
1063
1253
|
this.emit("error", err instanceof Error ? err : new Error(String(err)));
|
|
1064
1254
|
});
|
|
1065
1255
|
this.ws.on("close", (code, reason) => {
|
|
1066
|
-
|
|
1256
|
+
log6.debug(`dTelecom STT WebSocket closed: ${code} ${reason.toString()}`);
|
|
1067
1257
|
this._ready = false;
|
|
1068
1258
|
this.stopKeepAlive();
|
|
1069
1259
|
if (!this._closed) {
|
|
1070
|
-
|
|
1260
|
+
log6.info("dTelecom STT connection lost, reconnecting in 1s...");
|
|
1071
1261
|
setTimeout(() => {
|
|
1072
1262
|
if (!this._closed) this.connect();
|
|
1073
1263
|
}, 1e3);
|
|
@@ -1086,14 +1276,14 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1086
1276
|
} else if (type === "error") {
|
|
1087
1277
|
const errData = msg.error;
|
|
1088
1278
|
const errorMsg = msg.message || (typeof errData === "string" ? errData : JSON.stringify(errData)) || "Unknown STT error";
|
|
1089
|
-
|
|
1279
|
+
log6.error(`dTelecom STT error: ${errorMsg}`);
|
|
1090
1280
|
this.emit("error", new Error(errorMsg));
|
|
1091
1281
|
}
|
|
1092
1282
|
}
|
|
1093
1283
|
handleReady(msg) {
|
|
1094
1284
|
const clientId = msg.client_id;
|
|
1095
1285
|
const lang = msg.language;
|
|
1096
|
-
|
|
1286
|
+
log6.info(`dTelecom STT ready: client_id=${clientId}, language=${lang}`);
|
|
1097
1287
|
this._ready = true;
|
|
1098
1288
|
for (const buf of this.pendingAudio) {
|
|
1099
1289
|
if (this.ws?.readyState === import_ws4.default.OPEN) {
|
|
@@ -1110,7 +1300,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1110
1300
|
const latencyMs = msg.latency_ms;
|
|
1111
1301
|
if (!text) return;
|
|
1112
1302
|
if (isFinal && latencyMs !== void 0) {
|
|
1113
|
-
|
|
1303
|
+
log6.info(`stt_final: ${latencyMs.toFixed(0)}ms "${text.slice(0, 50)}"`);
|
|
1114
1304
|
}
|
|
1115
1305
|
this.emit("transcription", {
|
|
1116
1306
|
text,
|
|
@@ -1121,7 +1311,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1121
1311
|
}
|
|
1122
1312
|
handleVadEvent(msg) {
|
|
1123
1313
|
const event = msg.event;
|
|
1124
|
-
|
|
1314
|
+
log6.debug(`VAD event: ${event}`);
|
|
1125
1315
|
if (event === "speech_start") {
|
|
1126
1316
|
this.emit("transcription", {
|
|
1127
1317
|
text: "",
|
|
@@ -1147,7 +1337,7 @@ var DtelecomSTTStream = class extends BaseSTTStream {
|
|
|
1147
1337
|
|
|
1148
1338
|
// src/providers/dtelecom-tts.ts
|
|
1149
1339
|
var import_ws5 = __toESM(require("ws"));
|
|
1150
|
-
var
|
|
1340
|
+
var log7 = createLogger("DtelecomTTS");
|
|
1151
1341
|
var DtelecomTTS = class {
|
|
1152
1342
|
serverUrl;
|
|
1153
1343
|
sessionKey;
|
|
@@ -1179,19 +1369,19 @@ var DtelecomTTS = class {
|
|
|
1179
1369
|
}
|
|
1180
1370
|
/** Pre-connect WebSocket to TTS server. */
|
|
1181
1371
|
async warmup() {
|
|
1182
|
-
|
|
1372
|
+
log7.info("Warming up TTS connection...");
|
|
1183
1373
|
const start = performance.now();
|
|
1184
1374
|
try {
|
|
1185
1375
|
await this.ensureConnection();
|
|
1186
|
-
|
|
1376
|
+
log7.info(`TTS warmup complete in ${(performance.now() - start).toFixed(0)}ms`);
|
|
1187
1377
|
} catch (err) {
|
|
1188
|
-
|
|
1378
|
+
log7.warn("TTS warmup failed (non-fatal):", err);
|
|
1189
1379
|
}
|
|
1190
1380
|
}
|
|
1191
1381
|
/** Close WebSocket connection. */
|
|
1192
1382
|
close() {
|
|
1193
1383
|
if (this.ws) {
|
|
1194
|
-
|
|
1384
|
+
log7.debug("Closing TTS WebSocket");
|
|
1195
1385
|
this.ws.close();
|
|
1196
1386
|
this.ws = null;
|
|
1197
1387
|
}
|
|
@@ -1253,7 +1443,7 @@ var DtelecomTTS = class {
|
|
|
1253
1443
|
msg.lang_code = voiceConfig.langCode;
|
|
1254
1444
|
msg.speed = this.speed;
|
|
1255
1445
|
}
|
|
1256
|
-
|
|
1446
|
+
log7.info(`TTS send [${lang}]: voice=${voiceConfig?.voice ?? "default"} lang_code=${voiceConfig?.langCode ?? "default"} "${text.slice(0, 60)}"`);
|
|
1257
1447
|
ws.send(JSON.stringify(msg));
|
|
1258
1448
|
try {
|
|
1259
1449
|
while (true) {
|
|
@@ -1286,7 +1476,7 @@ var DtelecomTTS = class {
|
|
|
1286
1476
|
this.connectPromise = new Promise((resolve, reject) => {
|
|
1287
1477
|
const url = this.serverUrl.replace(/\/$/, "") + "/v1/stream";
|
|
1288
1478
|
const wsUrl = url.replace("https://", "wss://").replace("http://", "ws://");
|
|
1289
|
-
|
|
1479
|
+
log7.debug(`Connecting to dTelecom TTS: ${wsUrl}`);
|
|
1290
1480
|
const ws = new import_ws5.default(wsUrl);
|
|
1291
1481
|
ws.on("open", () => {
|
|
1292
1482
|
this.ws = ws;
|
|
@@ -1303,7 +1493,7 @@ var DtelecomTTS = class {
|
|
|
1303
1493
|
};
|
|
1304
1494
|
}
|
|
1305
1495
|
ws.send(JSON.stringify(msg));
|
|
1306
|
-
|
|
1496
|
+
log7.info("dTelecom TTS WebSocket connected");
|
|
1307
1497
|
resolve();
|
|
1308
1498
|
});
|
|
1309
1499
|
ws.on("message", (data, isBinary) => {
|
|
@@ -1324,21 +1514,21 @@ var DtelecomTTS = class {
|
|
|
1324
1514
|
state.done = true;
|
|
1325
1515
|
state.wake?.();
|
|
1326
1516
|
} else if (msg.type === "generating") {
|
|
1327
|
-
|
|
1517
|
+
log7.debug(`TTS generating: "${msg.text?.slice(0, 40)}"`);
|
|
1328
1518
|
} else if (msg.type === "error") {
|
|
1329
1519
|
const errorMsg = msg.message || "Unknown TTS error";
|
|
1330
|
-
|
|
1520
|
+
log7.error(`dTelecom TTS error: ${errorMsg}`);
|
|
1331
1521
|
state.error = new Error(errorMsg);
|
|
1332
1522
|
state.wake?.();
|
|
1333
1523
|
}
|
|
1334
1524
|
} catch {
|
|
1335
|
-
|
|
1525
|
+
log7.warn("Failed to parse dTelecom TTS message");
|
|
1336
1526
|
}
|
|
1337
1527
|
}
|
|
1338
1528
|
});
|
|
1339
1529
|
ws.on("error", (err) => {
|
|
1340
1530
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
1341
|
-
|
|
1531
|
+
log7.error("dTelecom TTS WebSocket error:", error);
|
|
1342
1532
|
const state = this.flushState;
|
|
1343
1533
|
if (state) {
|
|
1344
1534
|
state.error = error;
|
|
@@ -1349,7 +1539,7 @@ var DtelecomTTS = class {
|
|
|
1349
1539
|
reject(error);
|
|
1350
1540
|
});
|
|
1351
1541
|
ws.on("close", (code, reason) => {
|
|
1352
|
-
|
|
1542
|
+
log7.debug(`dTelecom TTS WebSocket closed: ${code} ${reason.toString()}`);
|
|
1353
1543
|
this.ws = null;
|
|
1354
1544
|
this.connectPromise = null;
|
|
1355
1545
|
const state = this.flushState;
|
|
@@ -1369,6 +1559,7 @@ var DtelecomTTS = class {
|
|
|
1369
1559
|
DeepgramTTS,
|
|
1370
1560
|
DtelecomSTT,
|
|
1371
1561
|
DtelecomTTS,
|
|
1562
|
+
OpenAILLM,
|
|
1372
1563
|
OpenRouterLLM
|
|
1373
1564
|
});
|
|
1374
1565
|
//# sourceMappingURL=index.js.map
|