@framers/agentos 0.1.227 → 0.1.229
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/README.md +1 -0
- package/dist/api/editImage.d.ts +21 -0
- package/dist/api/editImage.d.ts.map +1 -1
- package/dist/api/editImage.js +30 -2
- package/dist/api/editImage.js.map +1 -1
- package/dist/api/generateImage.d.ts +12 -1
- package/dist/api/generateImage.d.ts.map +1 -1
- package/dist/api/generateImage.js +7 -2
- package/dist/api/generateImage.js.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.js +71 -12
- package/dist/core/llm/providers/implementations/AnthropicProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.js +38 -11
- package/dist/core/llm/providers/implementations/OpenAIProvider.js.map +1 -1
- package/dist/media/images/providers/ReplicateImageProvider.d.ts.map +1 -1
- package/dist/media/images/providers/ReplicateImageProvider.js +7 -0
- package/dist/media/images/providers/ReplicateImageProvider.js.map +1 -1
- package/dist/voice-pipeline/AudioRingBuffer.d.ts +28 -0
- package/dist/voice-pipeline/AudioRingBuffer.d.ts.map +1 -0
- package/dist/voice-pipeline/AudioRingBuffer.js +43 -0
- package/dist/voice-pipeline/AudioRingBuffer.js.map +1 -0
- package/dist/voice-pipeline/CircuitBreaker.d.ts +42 -0
- package/dist/voice-pipeline/CircuitBreaker.d.ts.map +1 -0
- package/dist/voice-pipeline/CircuitBreaker.js +96 -0
- package/dist/voice-pipeline/CircuitBreaker.js.map +1 -0
- package/dist/voice-pipeline/HealthyProvider.d.ts +44 -0
- package/dist/voice-pipeline/HealthyProvider.d.ts.map +1 -0
- package/dist/voice-pipeline/HealthyProvider.js +29 -0
- package/dist/voice-pipeline/HealthyProvider.js.map +1 -0
- package/dist/voice-pipeline/TranscriptDedupe.d.ts +43 -0
- package/dist/voice-pipeline/TranscriptDedupe.d.ts.map +1 -0
- package/dist/voice-pipeline/TranscriptDedupe.js +96 -0
- package/dist/voice-pipeline/TranscriptDedupe.js.map +1 -0
- package/dist/voice-pipeline/VoiceMetricsReporter.d.ts +48 -0
- package/dist/voice-pipeline/VoiceMetricsReporter.d.ts.map +1 -0
- package/dist/voice-pipeline/VoiceMetricsReporter.js +33 -0
- package/dist/voice-pipeline/VoiceMetricsReporter.js.map +1 -0
- package/dist/voice-pipeline/VoicePipelineError.d.ts +44 -0
- package/dist/voice-pipeline/VoicePipelineError.d.ts.map +1 -0
- package/dist/voice-pipeline/VoicePipelineError.js +74 -0
- package/dist/voice-pipeline/VoicePipelineError.js.map +1 -0
- package/dist/voice-pipeline/env-constructor.d.ts +48 -0
- package/dist/voice-pipeline/env-constructor.d.ts.map +1 -0
- package/dist/voice-pipeline/env-constructor.js +85 -0
- package/dist/voice-pipeline/env-constructor.js.map +1 -0
- package/dist/voice-pipeline/index.d.ts +8 -1
- package/dist/voice-pipeline/index.d.ts.map +1 -1
- package/dist/voice-pipeline/index.js +9 -1
- package/dist/voice-pipeline/index.js.map +1 -1
- package/dist/voice-pipeline/providers/DeepgramStreamingSTT.d.ts +24 -1
- package/dist/voice-pipeline/providers/DeepgramStreamingSTT.d.ts.map +1 -1
- package/dist/voice-pipeline/providers/DeepgramStreamingSTT.js +51 -0
- package/dist/voice-pipeline/providers/DeepgramStreamingSTT.js.map +1 -1
- package/dist/voice-pipeline/providers/ElevenLabsBatchTTS.d.ts +17 -1
- package/dist/voice-pipeline/providers/ElevenLabsBatchTTS.d.ts.map +1 -1
- package/dist/voice-pipeline/providers/ElevenLabsBatchTTS.js +46 -0
- package/dist/voice-pipeline/providers/ElevenLabsBatchTTS.js.map +1 -1
- package/dist/voice-pipeline/providers/ElevenLabsStreamingSTT.d.ts +16 -1
- package/dist/voice-pipeline/providers/ElevenLabsStreamingSTT.d.ts.map +1 -1
- package/dist/voice-pipeline/providers/ElevenLabsStreamingSTT.js +46 -0
- package/dist/voice-pipeline/providers/ElevenLabsStreamingSTT.js.map +1 -1
- package/dist/voice-pipeline/providers/ElevenLabsStreamingTTS.d.ts +16 -1
- package/dist/voice-pipeline/providers/ElevenLabsStreamingTTS.d.ts.map +1 -1
- package/dist/voice-pipeline/providers/ElevenLabsStreamingTTS.js +46 -0
- package/dist/voice-pipeline/providers/ElevenLabsStreamingTTS.js.map +1 -1
- package/dist/voice-pipeline/providers/OpenAIBatchTTS.d.ts +16 -1
- package/dist/voice-pipeline/providers/OpenAIBatchTTS.d.ts.map +1 -1
- package/dist/voice-pipeline/providers/OpenAIBatchTTS.js +46 -0
- package/dist/voice-pipeline/providers/OpenAIBatchTTS.js.map +1 -1
- package/dist/voice-pipeline/providers/OpenAIRealtimeTTS.d.ts +16 -1
- package/dist/voice-pipeline/providers/OpenAIRealtimeTTS.d.ts.map +1 -1
- package/dist/voice-pipeline/providers/OpenAIRealtimeTTS.js +46 -0
- package/dist/voice-pipeline/providers/OpenAIRealtimeTTS.js.map +1 -1
- package/dist/voice-pipeline/providers/StreamingSTTChain.d.ts +78 -0
- package/dist/voice-pipeline/providers/StreamingSTTChain.d.ts.map +1 -0
- package/dist/voice-pipeline/providers/StreamingSTTChain.js +225 -0
- package/dist/voice-pipeline/providers/StreamingSTTChain.js.map +1 -0
- package/dist/voice-pipeline/providers/StreamingTTSChain.d.ts +67 -0
- package/dist/voice-pipeline/providers/StreamingTTSChain.d.ts.map +1 -0
- package/dist/voice-pipeline/providers/StreamingTTSChain.js +212 -0
- package/dist/voice-pipeline/providers/StreamingTTSChain.js.map +1 -0
- package/dist/voice-pipeline/providers/index.d.ts +2 -0
- package/dist/voice-pipeline/providers/index.d.ts.map +1 -1
- package/dist/voice-pipeline/providers/index.js +2 -0
- package/dist/voice-pipeline/providers/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/TranscriptDedupe
|
|
3
|
+
*
|
|
4
|
+
* Duplicate-transcript detector used by StreamingSTTChain after a mid-
|
|
5
|
+
* utterance failover. The backup provider replays the ring buffer and
|
|
6
|
+
* re-transcribes audio the primary already saw; without dedupe the session
|
|
7
|
+
* sees "hello world" twice.
|
|
8
|
+
*
|
|
9
|
+
* Dedupe signal: audio-clock overlap (primary fact) + fuzzy string match
|
|
10
|
+
* (tie-breaker). Two transcripts overlap if their [audioStartMs, audioEndMs]
|
|
11
|
+
* ranges intersect; when they do, we compare normalized text.
|
|
12
|
+
*
|
|
13
|
+
* Same-provider observations are never considered duplicates — interim
|
|
14
|
+
* transcripts from a single streaming provider are part of its protocol.
|
|
15
|
+
*/
|
|
16
|
+
const PUNCT_RE = /[.,!?;:"'()\[\]{}]/g;
|
|
17
|
+
const WS_RE = /\s+/g;
|
|
18
|
+
function normalize(text) {
|
|
19
|
+
return text.toLowerCase().replace(PUNCT_RE, '').replace(WS_RE, ' ').trim();
|
|
20
|
+
}
|
|
21
|
+
function tokenSet(text) {
|
|
22
|
+
return new Set(normalize(text).split(' ').filter(Boolean));
|
|
23
|
+
}
|
|
24
|
+
function tokenSetSimilarity(a, b) {
|
|
25
|
+
const ta = tokenSet(a);
|
|
26
|
+
const tb = tokenSet(b);
|
|
27
|
+
if (ta.size === 0 && tb.size === 0)
|
|
28
|
+
return 1;
|
|
29
|
+
if (ta.size === 0 || tb.size === 0)
|
|
30
|
+
return 0;
|
|
31
|
+
let intersect = 0;
|
|
32
|
+
for (const t of ta)
|
|
33
|
+
if (tb.has(t))
|
|
34
|
+
intersect++;
|
|
35
|
+
return intersect / Math.max(ta.size, tb.size);
|
|
36
|
+
}
|
|
37
|
+
function rangesOverlap(a, b) {
|
|
38
|
+
return a.audioStartMs < b.audioEndMs && b.audioStartMs < a.audioEndMs;
|
|
39
|
+
}
|
|
40
|
+
export class TranscriptDedupe {
|
|
41
|
+
constructor(opts = {}) {
|
|
42
|
+
this.recent = [];
|
|
43
|
+
this.fuzzyThreshold = opts.fuzzyThreshold ?? 0.85;
|
|
44
|
+
this.retainMs = opts.retainMs ?? 10000;
|
|
45
|
+
}
|
|
46
|
+
evaluate(obs) {
|
|
47
|
+
this.prune(obs.audioEndMs);
|
|
48
|
+
for (const prev of this.recent) {
|
|
49
|
+
if (prev.provider === obs.provider)
|
|
50
|
+
continue;
|
|
51
|
+
if (!rangesOverlap(prev, obs))
|
|
52
|
+
continue;
|
|
53
|
+
const na = normalize(prev.text);
|
|
54
|
+
const nb = normalize(obs.text);
|
|
55
|
+
if (na === nb) {
|
|
56
|
+
this.recent.push(obs);
|
|
57
|
+
return {
|
|
58
|
+
isDuplicate: true,
|
|
59
|
+
reason: 'exact',
|
|
60
|
+
against: { provider: prev.provider, text: prev.text },
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Longer transcript subsumes shorter one (primary saw more audio by
|
|
64
|
+
// the time backup caught up — suppress the backup's shorter view).
|
|
65
|
+
if (na.includes(nb) || nb.includes(na)) {
|
|
66
|
+
if (na.length >= nb.length) {
|
|
67
|
+
this.recent.push(obs);
|
|
68
|
+
return {
|
|
69
|
+
isDuplicate: true,
|
|
70
|
+
reason: 'superset',
|
|
71
|
+
against: { provider: prev.provider, text: prev.text },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const sim = tokenSetSimilarity(prev.text, obs.text);
|
|
76
|
+
if (sim >= this.fuzzyThreshold) {
|
|
77
|
+
this.recent.push(obs);
|
|
78
|
+
return {
|
|
79
|
+
isDuplicate: true,
|
|
80
|
+
reason: 'fuzzy',
|
|
81
|
+
against: { provider: prev.provider, text: prev.text },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.recent.push(obs);
|
|
86
|
+
return { isDuplicate: false };
|
|
87
|
+
}
|
|
88
|
+
reset() {
|
|
89
|
+
this.recent = [];
|
|
90
|
+
}
|
|
91
|
+
prune(upToMs) {
|
|
92
|
+
const cutoff = upToMs - this.retainMs;
|
|
93
|
+
this.recent = this.recent.filter((o) => o.audioEndMs >= cutoff);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=TranscriptDedupe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TranscriptDedupe.js","sourceRoot":"","sources":["../../src/voice-pipeline/TranscriptDedupe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/VoiceMetricsReporter
|
|
3
|
+
*
|
|
4
|
+
* Typed pub/sub bus for voice-pipeline lifecycle events. Chains and
|
|
5
|
+
* circuit breakers emit structured events here; host applications
|
|
6
|
+
* subscribe to forward them to clients (WebSocket frames), metrics
|
|
7
|
+
* systems (Prometheus, Datadog), or logs.
|
|
8
|
+
*
|
|
9
|
+
* Listener errors are swallowed — one bad subscriber must not poison the
|
|
10
|
+
* fan-out path for others.
|
|
11
|
+
*/
|
|
12
|
+
import type { HealthErrorClass } from './VoicePipelineError.js';
|
|
13
|
+
export type VoiceMetricEvent = {
|
|
14
|
+
type: 'provider_selected';
|
|
15
|
+
kind: 'stt' | 'tts';
|
|
16
|
+
providerId: string;
|
|
17
|
+
attempt: number;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'provider_failed';
|
|
20
|
+
kind: 'stt' | 'tts';
|
|
21
|
+
providerId: string;
|
|
22
|
+
errorClass: HealthErrorClass;
|
|
23
|
+
message: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'provider_failover';
|
|
26
|
+
kind: 'stt' | 'tts';
|
|
27
|
+
from: string;
|
|
28
|
+
to: string;
|
|
29
|
+
reason: HealthErrorClass;
|
|
30
|
+
lostMs: number;
|
|
31
|
+
} | {
|
|
32
|
+
type: 'provider_degraded';
|
|
33
|
+
kind: 'stt' | 'tts';
|
|
34
|
+
providerId: string;
|
|
35
|
+
latencyMs: number;
|
|
36
|
+
thresholdMs: number;
|
|
37
|
+
} | {
|
|
38
|
+
type: 'provider_unavailable';
|
|
39
|
+
kind: 'stt' | 'tts';
|
|
40
|
+
checkedProviders: string[];
|
|
41
|
+
};
|
|
42
|
+
export type VoiceMetricListener = (event: VoiceMetricEvent) => void;
|
|
43
|
+
export declare class VoiceMetricsReporter {
|
|
44
|
+
private readonly listeners;
|
|
45
|
+
subscribe(fn: VoiceMetricListener): () => void;
|
|
46
|
+
emit(event: VoiceMetricEvent): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=VoiceMetricsReporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoiceMetricsReporter.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceMetricsReporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B,CAAC;AAEN,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;AAEpE,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAE5D,SAAS,CAAC,EAAE,EAAE,mBAAmB,GAAG,MAAM,IAAI;IAO9C,IAAI,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;CASpC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/VoiceMetricsReporter
|
|
3
|
+
*
|
|
4
|
+
* Typed pub/sub bus for voice-pipeline lifecycle events. Chains and
|
|
5
|
+
* circuit breakers emit structured events here; host applications
|
|
6
|
+
* subscribe to forward them to clients (WebSocket frames), metrics
|
|
7
|
+
* systems (Prometheus, Datadog), or logs.
|
|
8
|
+
*
|
|
9
|
+
* Listener errors are swallowed — one bad subscriber must not poison the
|
|
10
|
+
* fan-out path for others.
|
|
11
|
+
*/
|
|
12
|
+
export class VoiceMetricsReporter {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.listeners = new Set();
|
|
15
|
+
}
|
|
16
|
+
subscribe(fn) {
|
|
17
|
+
this.listeners.add(fn);
|
|
18
|
+
return () => {
|
|
19
|
+
this.listeners.delete(fn);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
emit(event) {
|
|
23
|
+
for (const fn of this.listeners) {
|
|
24
|
+
try {
|
|
25
|
+
fn(event);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
/* swallow — one bad listener must not poison the rest */
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=VoiceMetricsReporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoiceMetricsReporter.js","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceMetricsReporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAyCH,MAAM,OAAO,oBAAoB;IAAjC;QACmB,cAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;IAkB9D,CAAC;IAhBC,SAAS,CAAC,EAAuB;QAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,KAAuB;QAC1B,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC;gBACH,EAAE,CAAC,KAAK,CAAC,CAAC;YACZ,CAAC;YAAC,MAAM,CAAC;gBACP,yDAAyD;YAC3D,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/VoicePipelineError
|
|
3
|
+
*
|
|
4
|
+
* Structured error class for voice pipeline failures. Carries enough shape
|
|
5
|
+
* for chains and circuit breakers to classify and react without stringly
|
|
6
|
+
* matching error.message.
|
|
7
|
+
*/
|
|
8
|
+
export type HealthErrorClass = 'auth' | 'quota' | 'network' | 'service' | 'unknown';
|
|
9
|
+
export interface VoicePipelineErrorInit {
|
|
10
|
+
kind: 'stt' | 'tts' | 'transport';
|
|
11
|
+
provider: string;
|
|
12
|
+
errorClass: HealthErrorClass;
|
|
13
|
+
message: string;
|
|
14
|
+
cause?: unknown;
|
|
15
|
+
retryable: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare class VoicePipelineError extends Error {
|
|
18
|
+
readonly kind: VoicePipelineErrorInit['kind'];
|
|
19
|
+
readonly provider: string;
|
|
20
|
+
readonly errorClass: HealthErrorClass;
|
|
21
|
+
readonly retryable: boolean;
|
|
22
|
+
readonly cause?: unknown;
|
|
23
|
+
constructor(init: VoicePipelineErrorInit);
|
|
24
|
+
/**
|
|
25
|
+
* Best-effort classification of an arbitrary error into a voice-pipeline
|
|
26
|
+
* error with a well-known errorClass. Preserves the original error as
|
|
27
|
+
* `cause` so upstream inspection can still recover provider-specific
|
|
28
|
+
* detail.
|
|
29
|
+
*/
|
|
30
|
+
static classifyError(err: unknown, meta: {
|
|
31
|
+
kind: VoicePipelineErrorInit['kind'];
|
|
32
|
+
provider: string;
|
|
33
|
+
}): VoicePipelineError;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Aggregate thrown by `StreamingSTTChain` / `StreamingTTSChain` when every
|
|
37
|
+
* candidate provider fails. Carries the per-provider error list so callers
|
|
38
|
+
* can display a breakdown rather than a single confusing message.
|
|
39
|
+
*/
|
|
40
|
+
export declare class AggregateVoiceError extends Error {
|
|
41
|
+
readonly attempts: VoicePipelineError[];
|
|
42
|
+
constructor(attempts: VoicePipelineError[]);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=VoicePipelineError.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoicePipelineError.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/VoicePipelineError.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,OAAO,GACP,SAAS,GACT,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,WAAW,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEb,IAAI,EAAE,sBAAsB;IAUxC;;;;;OAKG;IACH,MAAM,CAAC,aAAa,CAClB,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE;QAAE,IAAI,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAC/D,kBAAkB;CAoCtB;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,EAAE,CAAC;gBAE5B,QAAQ,EAAE,kBAAkB,EAAE;CAQ3C"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/VoicePipelineError
|
|
3
|
+
*
|
|
4
|
+
* Structured error class for voice pipeline failures. Carries enough shape
|
|
5
|
+
* for chains and circuit breakers to classify and react without stringly
|
|
6
|
+
* matching error.message.
|
|
7
|
+
*/
|
|
8
|
+
export class VoicePipelineError extends Error {
|
|
9
|
+
constructor(init) {
|
|
10
|
+
super(init.message);
|
|
11
|
+
this.name = 'VoicePipelineError';
|
|
12
|
+
this.kind = init.kind;
|
|
13
|
+
this.provider = init.provider;
|
|
14
|
+
this.errorClass = init.errorClass;
|
|
15
|
+
this.retryable = init.retryable;
|
|
16
|
+
this.cause = init.cause;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Best-effort classification of an arbitrary error into a voice-pipeline
|
|
20
|
+
* error with a well-known errorClass. Preserves the original error as
|
|
21
|
+
* `cause` so upstream inspection can still recover provider-specific
|
|
22
|
+
* detail.
|
|
23
|
+
*/
|
|
24
|
+
static classifyError(err, meta) {
|
|
25
|
+
const raw = err instanceof Error ? err : new Error(String(err));
|
|
26
|
+
const msg = raw.message ?? '';
|
|
27
|
+
const code = err?.code ?? '';
|
|
28
|
+
let errorClass = 'unknown';
|
|
29
|
+
let retryable = true;
|
|
30
|
+
if (/\b401\b|unauthori[sz]ed|invalid api key|forbidden|\b403\b/i.test(msg)) {
|
|
31
|
+
errorClass = 'auth';
|
|
32
|
+
retryable = false;
|
|
33
|
+
}
|
|
34
|
+
else if (/\b429\b|rate.?limit|too many/i.test(msg)) {
|
|
35
|
+
errorClass = 'quota';
|
|
36
|
+
retryable = true;
|
|
37
|
+
}
|
|
38
|
+
else if (/\b5\d\d\b|internal server|bad gateway|gateway timeout|service unavailable/i.test(msg)) {
|
|
39
|
+
errorClass = 'service';
|
|
40
|
+
retryable = true;
|
|
41
|
+
}
|
|
42
|
+
else if (code === 'ECONNRESET' ||
|
|
43
|
+
code === 'ETIMEDOUT' ||
|
|
44
|
+
code === 'ENOTFOUND' ||
|
|
45
|
+
/econnreset|etimedout|enotfound|socket hang up|network/i.test(msg)) {
|
|
46
|
+
errorClass = 'network';
|
|
47
|
+
retryable = true;
|
|
48
|
+
}
|
|
49
|
+
return new VoicePipelineError({
|
|
50
|
+
kind: meta.kind,
|
|
51
|
+
provider: meta.provider,
|
|
52
|
+
errorClass,
|
|
53
|
+
message: msg || 'unknown voice pipeline error',
|
|
54
|
+
cause: err,
|
|
55
|
+
retryable,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Aggregate thrown by `StreamingSTTChain` / `StreamingTTSChain` when every
|
|
61
|
+
* candidate provider fails. Carries the per-provider error list so callers
|
|
62
|
+
* can display a breakdown rather than a single confusing message.
|
|
63
|
+
*/
|
|
64
|
+
export class AggregateVoiceError extends Error {
|
|
65
|
+
constructor(attempts) {
|
|
66
|
+
const summary = attempts
|
|
67
|
+
.map((a) => `${a.provider}: ${a.errorClass} \u2014 ${a.message}`)
|
|
68
|
+
.join('; ');
|
|
69
|
+
super(`All ${attempts.length} providers failed \u2014 ${summary}`);
|
|
70
|
+
this.name = 'AggregateVoiceError';
|
|
71
|
+
this.attempts = attempts;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=VoicePipelineError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoicePipelineError.js","sourceRoot":"","sources":["../../src/voice-pipeline/VoicePipelineError.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAkBH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAO3C,YAAY,IAA4B;QACtC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAC1B,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,aAAa,CAClB,GAAY,EACZ,IAAgE;QAEhE,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAI,GAAgC,EAAE,IAAI,IAAI,EAAE,CAAC;QAE3D,IAAI,UAAU,GAAqB,SAAS,CAAC;QAC7C,IAAI,SAAS,GAAG,IAAI,CAAC;QAErB,IAAI,4DAA4D,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3E,UAAU,GAAG,MAAM,CAAC;YACpB,SAAS,GAAG,KAAK,CAAC;QACpB,CAAC;aAAM,IAAI,+BAA+B,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACrD,UAAU,GAAG,OAAO,CAAC;YACrB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;aAAM,IAAI,4EAA4E,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAClG,UAAU,GAAG,SAAS,CAAC;YACvB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;aAAM,IACL,IAAI,KAAK,YAAY;YACrB,IAAI,KAAK,WAAW;YACpB,IAAI,KAAK,WAAW;YACpB,wDAAwD,CAAC,IAAI,CAAC,GAAG,CAAC,EAClE,CAAC;YACD,UAAU,GAAG,SAAS,CAAC;YACvB,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;QAED,OAAO,IAAI,kBAAkB,CAAC;YAC5B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,UAAU;YACV,OAAO,EAAE,GAAG,IAAI,8BAA8B;YAC9C,KAAK,EAAE,GAAG;YACV,SAAS;SACV,CAAC,CAAC;IACL,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAG5C,YAAY,QAA8B;QACxC,MAAM,OAAO,GAAG,QAAQ;aACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,UAAU,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;aAChE,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,KAAK,CAAC,OAAO,QAAQ,CAAC,MAAM,4BAA4B,OAAO,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/env-constructor
|
|
3
|
+
*
|
|
4
|
+
* Batteries-included constructor for `StreamingSTTChain` +
|
|
5
|
+
* `StreamingTTSChain`. Reads provider keys from an env-like object and
|
|
6
|
+
* builds priority-ordered chains with shared circuit breaker and metrics
|
|
7
|
+
* reporter. Host apps can skip the manual wiring and use this factory as
|
|
8
|
+
* the default integration point.
|
|
9
|
+
*/
|
|
10
|
+
import { StreamingSTTChain } from './providers/StreamingSTTChain.js';
|
|
11
|
+
import { StreamingTTSChain } from './providers/StreamingTTSChain.js';
|
|
12
|
+
import { CircuitBreaker } from './CircuitBreaker.js';
|
|
13
|
+
import { VoiceMetricsReporter } from './VoiceMetricsReporter.js';
|
|
14
|
+
export declare class NoVoiceProvidersAvailableError extends Error {
|
|
15
|
+
readonly checkedEnvVars: string[];
|
|
16
|
+
constructor(checked: string[]);
|
|
17
|
+
}
|
|
18
|
+
export interface VoiceProviderEnvConfig {
|
|
19
|
+
/** Environment source. Defaults to process.env. */
|
|
20
|
+
env?: Record<string, string | undefined>;
|
|
21
|
+
/** Prefer streaming-class providers for first-try. Default true. */
|
|
22
|
+
preferStreaming?: boolean;
|
|
23
|
+
/** Language hint — providers whose capabilities don't match are still
|
|
24
|
+
* included (capability filtering is host-app policy), but this value
|
|
25
|
+
* is passed through to StreamingTTSConfig / StreamingSTTConfig via
|
|
26
|
+
* startSession consumers. */
|
|
27
|
+
languageHint?: string;
|
|
28
|
+
/** Target cost tier. Reserved for future per-session routing; not used yet. */
|
|
29
|
+
tier?: 'cheap' | 'standard' | 'premium';
|
|
30
|
+
/** Whether the STT chain keeps a ring buffer + re-routes mid-utterance.
|
|
31
|
+
* Default true — this is the whole point of the resilience work. */
|
|
32
|
+
enableMidUtteranceFailover?: boolean;
|
|
33
|
+
/** Whether the TTS chain re-sends accumulated tokens on primary
|
|
34
|
+
* failure. Default true. */
|
|
35
|
+
enableMidSynthesisFailover?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface VoiceProviderBundle {
|
|
38
|
+
stt: StreamingSTTChain;
|
|
39
|
+
tts: StreamingTTSChain;
|
|
40
|
+
metrics: VoiceMetricsReporter;
|
|
41
|
+
breaker: CircuitBreaker;
|
|
42
|
+
/** Release any global resources the bundle owns. Currently a no-op
|
|
43
|
+
* because sessions clean up themselves; exposed now so host apps can
|
|
44
|
+
* depend on the shape. */
|
|
45
|
+
dispose(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
export declare function createVoiceProvidersFromEnv(config?: VoiceProviderEnvConfig): VoiceProviderBundle;
|
|
48
|
+
//# sourceMappingURL=env-constructor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-constructor.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/env-constructor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAIjE,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;gBAEtB,OAAO,EAAE,MAAM,EAAE;CAO9B;AAED,MAAM,WAAW,sBAAsB;IACrC,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,oEAAoE;IACpE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;kCAG8B;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAC;IACxC;yEACqE;IACrE,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC;iCAC6B;IAC7B,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,iBAAiB,CAAC;IACvB,GAAG,EAAE,iBAAiB,CAAC;IACvB,OAAO,EAAE,oBAAoB,CAAC;IAC9B,OAAO,EAAE,cAAc,CAAC;IACxB;;+BAE2B;IAC3B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,wBAAgB,2BAA2B,CACzC,MAAM,GAAE,sBAA2B,GAClC,mBAAmB,CA8ErB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/env-constructor
|
|
3
|
+
*
|
|
4
|
+
* Batteries-included constructor for `StreamingSTTChain` +
|
|
5
|
+
* `StreamingTTSChain`. Reads provider keys from an env-like object and
|
|
6
|
+
* builds priority-ordered chains with shared circuit breaker and metrics
|
|
7
|
+
* reporter. Host apps can skip the manual wiring and use this factory as
|
|
8
|
+
* the default integration point.
|
|
9
|
+
*/
|
|
10
|
+
import { DeepgramStreamingSTT } from './providers/DeepgramStreamingSTT.js';
|
|
11
|
+
import { ElevenLabsStreamingSTT } from './providers/ElevenLabsStreamingSTT.js';
|
|
12
|
+
import { ElevenLabsStreamingTTS } from './providers/ElevenLabsStreamingTTS.js';
|
|
13
|
+
import { OpenAIRealtimeTTS } from './providers/OpenAIRealtimeTTS.js';
|
|
14
|
+
import { ElevenLabsBatchTTS } from './providers/ElevenLabsBatchTTS.js';
|
|
15
|
+
import { OpenAIBatchTTS } from './providers/OpenAIBatchTTS.js';
|
|
16
|
+
import { StreamingSTTChain } from './providers/StreamingSTTChain.js';
|
|
17
|
+
import { StreamingTTSChain } from './providers/StreamingTTSChain.js';
|
|
18
|
+
import { CircuitBreaker } from './CircuitBreaker.js';
|
|
19
|
+
import { VoiceMetricsReporter } from './VoiceMetricsReporter.js';
|
|
20
|
+
export class NoVoiceProvidersAvailableError extends Error {
|
|
21
|
+
constructor(checked) {
|
|
22
|
+
super(`No voice providers available. Set any of: ${checked.join(', ')} in the server env.`);
|
|
23
|
+
this.name = 'NoVoiceProvidersAvailableError';
|
|
24
|
+
this.checkedEnvVars = checked;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function createVoiceProvidersFromEnv(config = {}) {
|
|
28
|
+
const env = config.env ?? globalThis.process?.env ?? {};
|
|
29
|
+
const checkedKeys = [
|
|
30
|
+
'DEEPGRAM_API_KEY',
|
|
31
|
+
'ELEVENLABS_API_KEY',
|
|
32
|
+
'OPENAI_API_KEY',
|
|
33
|
+
];
|
|
34
|
+
const deepgramKey = env['DEEPGRAM_API_KEY'];
|
|
35
|
+
const elevenLabsKey = env['ELEVENLABS_API_KEY'];
|
|
36
|
+
const openaiKey = env['OPENAI_API_KEY'];
|
|
37
|
+
const metrics = new VoiceMetricsReporter();
|
|
38
|
+
const breaker = new CircuitBreaker({
|
|
39
|
+
failureThreshold: 3,
|
|
40
|
+
windowMs: 60000,
|
|
41
|
+
cooldownMs: 60000,
|
|
42
|
+
});
|
|
43
|
+
const sttProviders = [];
|
|
44
|
+
if (deepgramKey) {
|
|
45
|
+
sttProviders.push(new DeepgramStreamingSTT({ apiKey: deepgramKey, priority: 10 }));
|
|
46
|
+
}
|
|
47
|
+
if (elevenLabsKey) {
|
|
48
|
+
sttProviders.push(new ElevenLabsStreamingSTT({ apiKey: elevenLabsKey, priority: 20 }));
|
|
49
|
+
}
|
|
50
|
+
const ttsProviders = [];
|
|
51
|
+
if (elevenLabsKey) {
|
|
52
|
+
ttsProviders.push(new ElevenLabsStreamingTTS({ apiKey: elevenLabsKey, priority: 10 }));
|
|
53
|
+
}
|
|
54
|
+
if (openaiKey) {
|
|
55
|
+
ttsProviders.push(new OpenAIRealtimeTTS({ apiKey: openaiKey, priority: 20 }));
|
|
56
|
+
ttsProviders.push(new OpenAIBatchTTS({ apiKey: openaiKey, priority: 90 }));
|
|
57
|
+
}
|
|
58
|
+
if (elevenLabsKey) {
|
|
59
|
+
ttsProviders.push(new ElevenLabsBatchTTS({ apiKey: elevenLabsKey, priority: 80 }));
|
|
60
|
+
}
|
|
61
|
+
if (sttProviders.length === 0 || ttsProviders.length === 0) {
|
|
62
|
+
throw new NoVoiceProvidersAvailableError(checkedKeys);
|
|
63
|
+
}
|
|
64
|
+
const stt = new StreamingSTTChain(sttProviders, {
|
|
65
|
+
breaker,
|
|
66
|
+
metrics,
|
|
67
|
+
enableMidUtteranceFailover: config.enableMidUtteranceFailover ?? true,
|
|
68
|
+
ringBufferCapacityMs: 3000,
|
|
69
|
+
});
|
|
70
|
+
const tts = new StreamingTTSChain(ttsProviders, {
|
|
71
|
+
breaker,
|
|
72
|
+
metrics,
|
|
73
|
+
enableMidSynthesisFailover: config.enableMidSynthesisFailover ?? true,
|
|
74
|
+
});
|
|
75
|
+
return {
|
|
76
|
+
stt,
|
|
77
|
+
tts,
|
|
78
|
+
metrics,
|
|
79
|
+
breaker,
|
|
80
|
+
async dispose() {
|
|
81
|
+
/* Sessions clean themselves up; nothing global to release today. */
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=env-constructor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-constructor.js","sourceRoot":"","sources":["../../src/voice-pipeline/env-constructor.ts"],"names":[],"mappings":"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"}
|
|
@@ -104,5 +104,12 @@ export { WebSocketStreamTransport } from './WebSocketStreamTransport.js';
|
|
|
104
104
|
export { WebRTCStreamTransport, createWebRTCTransport } from './WebRTCStreamTransport.js';
|
|
105
105
|
export { VoicePipelineOrchestrator } from './VoicePipelineOrchestrator.js';
|
|
106
106
|
export { VoiceInterruptError } from './VoiceInterruptError.js';
|
|
107
|
-
export { DeepgramStreamingSTT, type DeepgramStreamingSTTConfig, ElevenLabsStreamingSTT, type ElevenLabsStreamingSTTConfig, ElevenLabsStreamingTTS, type ElevenLabsStreamingTTSConfig, AgentSessionVoiceAdapter, } from './providers/index.js';
|
|
107
|
+
export { DeepgramStreamingSTT, type DeepgramStreamingSTTConfig, ElevenLabsStreamingSTT, type ElevenLabsStreamingSTTConfig, ElevenLabsStreamingTTS, type ElevenLabsStreamingTTSConfig, AgentSessionVoiceAdapter, StreamingSTTChain, type StreamingSTTChainOptions, type ProviderSelectedEvent, type ProviderFailedEvent, type ProviderFailoverEvent, StreamingTTSChain, type StreamingTTSChainOptions, type TTSProviderSelectedEvent, type TTSProviderFailedEvent, type TTSProviderFailoverEvent, } from './providers/index.js';
|
|
108
|
+
export { VoicePipelineError, AggregateVoiceError, type HealthErrorClass, type VoicePipelineErrorInit, } from './VoicePipelineError.js';
|
|
109
|
+
export { type HealthyProvider, type ProviderCapabilities, type HealthCheckResult, defaultCapabilities, supportsLanguage, } from './HealthyProvider.js';
|
|
110
|
+
export { CircuitBreaker, type BreakerState, type CircuitBreakerOptions, type StateChangeEvent, } from './CircuitBreaker.js';
|
|
111
|
+
export { AudioRingBuffer, type AudioRingBufferOptions } from './AudioRingBuffer.js';
|
|
112
|
+
export { TranscriptDedupe, type TranscriptObservation, type DedupeResult, } from './TranscriptDedupe.js';
|
|
113
|
+
export { VoiceMetricsReporter, type VoiceMetricEvent, type VoiceMetricListener, } from './VoiceMetricsReporter.js';
|
|
114
|
+
export { createVoiceProvidersFromEnv, NoVoiceProvidersAvailableError, type VoiceProviderEnvConfig, type VoiceProviderBundle, } from './env-constructor.js';
|
|
108
115
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGG;AAIH,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAG3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,EACL,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,sBAAsB,EACtB,KAAK,4BAA4B,EACjC,sBAAsB,EACtB,KAAK,4BAA4B,EACjC,wBAAwB,GACzB,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGG;AAIH,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAGzE,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAG1F,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAG3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAG/D,OAAO,EACL,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,sBAAsB,EACtB,KAAK,4BAA4B,EACjC,sBAAsB,EACtB,KAAK,4BAA4B,EACjC,wBAAwB,EACxB,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,GAC9B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,GAC5B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EACtB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,cAAc,EACd,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACpF,OAAO,EACL,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,2BAA2B,EAC3B,8BAA8B,EAC9B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,GACzB,MAAM,sBAAsB,CAAC"}
|
|
@@ -113,5 +113,13 @@ export { VoicePipelineOrchestrator } from './VoicePipelineOrchestrator.js';
|
|
|
113
113
|
// Typed error for barge-in interruptions
|
|
114
114
|
export { VoiceInterruptError } from './VoiceInterruptError.js';
|
|
115
115
|
// Streaming provider implementations
|
|
116
|
-
export { DeepgramStreamingSTT, ElevenLabsStreamingSTT, ElevenLabsStreamingTTS, AgentSessionVoiceAdapter, } from './providers/index.js';
|
|
116
|
+
export { DeepgramStreamingSTT, ElevenLabsStreamingSTT, ElevenLabsStreamingTTS, AgentSessionVoiceAdapter, StreamingSTTChain, StreamingTTSChain, } from './providers/index.js';
|
|
117
|
+
// Resilience primitives
|
|
118
|
+
export { VoicePipelineError, AggregateVoiceError, } from './VoicePipelineError.js';
|
|
119
|
+
export { defaultCapabilities, supportsLanguage, } from './HealthyProvider.js';
|
|
120
|
+
export { CircuitBreaker, } from './CircuitBreaker.js';
|
|
121
|
+
export { AudioRingBuffer } from './AudioRingBuffer.js';
|
|
122
|
+
export { TranscriptDedupe, } from './TranscriptDedupe.js';
|
|
123
|
+
export { VoiceMetricsReporter, } from './VoiceMetricsReporter.js';
|
|
124
|
+
export { createVoiceProvidersFromEnv, NoVoiceProvidersAvailableError, } from './env-constructor.js';
|
|
117
125
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/voice-pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGG;AAEH,wDAAwD;AACxD,8EAA8E;AAC9E,cAAc,YAAY,CAAC;AAE3B,4CAA4C;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE,6CAA6C;AAC7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,2CAA2C;AAC3C,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,oDAAoD;AACpD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAE1F,gDAAgD;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,yCAAyC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,qCAAqC;AACrC,OAAO,EACL,oBAAoB,EAEpB,sBAAsB,EAEtB,sBAAsB,EAEtB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/voice-pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGG;AAEH,wDAAwD;AACxD,8EAA8E;AAC9E,cAAc,YAAY,CAAC;AAE3B,4CAA4C;AAC5C,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE,6CAA6C;AAC7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,2CAA2C;AAC3C,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,oDAAoD;AACpD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAE1F,gDAAgD;AAChD,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,yCAAyC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,qCAAqC;AACrC,OAAO,EACL,oBAAoB,EAEpB,sBAAsB,EAEtB,sBAAsB,EAEtB,wBAAwB,EACxB,iBAAiB,EAKjB,iBAAiB,GAKlB,MAAM,sBAAsB,CAAC;AAE9B,wBAAwB;AACxB,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GAGpB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAIL,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,cAAc,GAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAA+B,MAAM,sBAAsB,CAAC;AACpF,OAAO,EACL,gBAAgB,GAGjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,GAGrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,2BAA2B,EAC3B,8BAA8B,GAG/B,MAAM,sBAAsB,CAAC"}
|
|
@@ -24,6 +24,16 @@
|
|
|
24
24
|
* @see https://developers.deepgram.com/docs/streaming
|
|
25
25
|
*/
|
|
26
26
|
import type { IStreamingSTT, StreamingSTTSession, StreamingSTTConfig } from '../types.js';
|
|
27
|
+
import { type HealthyProvider, type HealthCheckResult, type ProviderCapabilities } from '../HealthyProvider.js';
|
|
28
|
+
/**
|
|
29
|
+
* Shape of the injected health probe used for deterministic tests.
|
|
30
|
+
* Default implementation hits Deepgram's /v1/projects endpoint.
|
|
31
|
+
*/
|
|
32
|
+
export type VoiceHealthProbe = (apiKey: string) => Promise<{
|
|
33
|
+
ok: boolean;
|
|
34
|
+
status: number;
|
|
35
|
+
latencyMs: number;
|
|
36
|
+
}>;
|
|
27
37
|
/**
|
|
28
38
|
* Configuration for the {@link DeepgramStreamingSTT} provider.
|
|
29
39
|
*/
|
|
@@ -40,6 +50,15 @@ export interface DeepgramStreamingSTTConfig {
|
|
|
40
50
|
* @default 'nova-2'
|
|
41
51
|
*/
|
|
42
52
|
model?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Chain priority. Lower values are tried first.
|
|
55
|
+
* @default 10
|
|
56
|
+
*/
|
|
57
|
+
priority?: number;
|
|
58
|
+
/** Optional capability overrides. Merged into defaultCapabilities(). */
|
|
59
|
+
capabilities?: Partial<ProviderCapabilities>;
|
|
60
|
+
/** Injectable health probe for tests. Defaults to Deepgram /v1/projects. */
|
|
61
|
+
healthProbe?: VoiceHealthProbe;
|
|
43
62
|
}
|
|
44
63
|
/**
|
|
45
64
|
* Streaming STT provider that creates Deepgram WebSocket sessions.
|
|
@@ -55,12 +74,16 @@ export interface DeepgramStreamingSTTConfig {
|
|
|
55
74
|
* session.on('transcript', (event) => console.log(event.text));
|
|
56
75
|
* ```
|
|
57
76
|
*/
|
|
58
|
-
export declare class DeepgramStreamingSTT implements IStreamingSTT {
|
|
77
|
+
export declare class DeepgramStreamingSTT implements IStreamingSTT, HealthyProvider {
|
|
59
78
|
private readonly config;
|
|
60
79
|
readonly providerId = "deepgram-streaming";
|
|
61
80
|
readonly isStreaming = true;
|
|
81
|
+
readonly priority: number;
|
|
82
|
+
readonly capabilities: ProviderCapabilities;
|
|
62
83
|
private readonly keyPool;
|
|
84
|
+
private readonly healthProbe;
|
|
63
85
|
constructor(config: DeepgramStreamingSTTConfig);
|
|
86
|
+
healthCheck(): Promise<HealthCheckResult>;
|
|
64
87
|
/**
|
|
65
88
|
* Create a new streaming STT session connected to Deepgram.
|
|
66
89
|
* Each session gets a fresh key from the round-robin pool.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeepgramStreamingSTT.d.ts","sourceRoot":"","sources":["../../../src/voice-pipeline/providers/DeepgramStreamingSTT.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAInB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"DeepgramStreamingSTT.d.ts","sourceRoot":"","sources":["../../../src/voice-pipeline/providers/DeepgramStreamingSTT.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAInB,MAAM,aAAa,CAAC;AACrB,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EAC1B,MAAM,uBAAuB,CAAC;AAG/B;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,MAAM,EAAE,MAAM,KACX,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAmBjE;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,wEAAwE;IACxE,YAAY,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAE7C,4EAA4E;IAC5E,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC;AAmQD;;;;;;;;;;;;;GAaG;AACH,qBAAa,oBAAqB,YAAW,aAAa,EAAE,eAAe;IAQ7D,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPnC,QAAQ,CAAC,UAAU,wBAAwB;IAC3C,QAAQ,CAAC,WAAW,QAAQ;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAAC;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmB;gBAElB,MAAM,EAAE,0BAA0B;IAazD,WAAW,IAAI,OAAO,CAAC,iBAAiB,CAAC;IA6B/C;;;OAGG;IACG,YAAY,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAM9E"}
|
|
@@ -26,6 +26,21 @@
|
|
|
26
26
|
import { EventEmitter } from 'node:events';
|
|
27
27
|
import WebSocket from 'ws';
|
|
28
28
|
import { ApiKeyPool } from '../../core/providers/ApiKeyPool.js';
|
|
29
|
+
import { defaultCapabilities, } from '../HealthyProvider.js';
|
|
30
|
+
import { VoicePipelineError } from '../VoicePipelineError.js';
|
|
31
|
+
async function defaultDeepgramProbe(apiKey) {
|
|
32
|
+
const start = Date.now();
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch('https://api.deepgram.com/v1/projects', {
|
|
35
|
+
headers: { Authorization: `Token ${apiKey}` },
|
|
36
|
+
signal: AbortSignal.timeout(1000),
|
|
37
|
+
});
|
|
38
|
+
return { ok: res.ok, status: res.status, latencyMs: Date.now() - start };
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
29
44
|
// ---------------------------------------------------------------------------
|
|
30
45
|
// Session Implementation
|
|
31
46
|
// ---------------------------------------------------------------------------
|
|
@@ -229,6 +244,42 @@ export class DeepgramStreamingSTT {
|
|
|
229
244
|
this.providerId = 'deepgram-streaming';
|
|
230
245
|
this.isStreaming = true;
|
|
231
246
|
this.keyPool = new ApiKeyPool(config.apiKey);
|
|
247
|
+
this.priority = config.priority ?? 10;
|
|
248
|
+
this.capabilities = defaultCapabilities({
|
|
249
|
+
languages: ['*'],
|
|
250
|
+
streaming: true,
|
|
251
|
+
costTier: 'standard',
|
|
252
|
+
latencyClass: 'realtime',
|
|
253
|
+
...(config.capabilities ?? {}),
|
|
254
|
+
});
|
|
255
|
+
this.healthProbe = config.healthProbe ?? defaultDeepgramProbe;
|
|
256
|
+
}
|
|
257
|
+
async healthCheck() {
|
|
258
|
+
if (!this.keyPool.hasKeys) {
|
|
259
|
+
return { ok: false, error: { class: 'auth', message: 'no api key available' } };
|
|
260
|
+
}
|
|
261
|
+
const key = this.keyPool.next();
|
|
262
|
+
try {
|
|
263
|
+
const res = await this.healthProbe(key);
|
|
264
|
+
if (res.ok)
|
|
265
|
+
return { ok: true, latencyMs: res.latencyMs };
|
|
266
|
+
const classified = VoicePipelineError.classifyError(new Error(`HTTP ${res.status}`), { kind: 'stt', provider: this.providerId });
|
|
267
|
+
return {
|
|
268
|
+
ok: false,
|
|
269
|
+
latencyMs: res.latencyMs,
|
|
270
|
+
error: { class: classified.errorClass, message: `HTTP ${res.status}` },
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
const classified = VoicePipelineError.classifyError(err, {
|
|
275
|
+
kind: 'stt',
|
|
276
|
+
provider: this.providerId,
|
|
277
|
+
});
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
error: { class: classified.errorClass, message: classified.message },
|
|
281
|
+
};
|
|
282
|
+
}
|
|
232
283
|
}
|
|
233
284
|
/**
|
|
234
285
|
* Create a new streaming STT session connected to Deepgram.
|