@kuralle-syrinx/core 4.4.0 → 4.5.0
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/audio/alaw.d.ts +3 -0
- package/dist/audio/alaw.d.ts.map +1 -0
- package/dist/audio/alaw.js +53 -0
- package/dist/audio/alaw.js.map +1 -0
- package/dist/audio/g722.d.ts +39 -0
- package/dist/audio/g722.d.ts.map +1 -0
- package/dist/audio/g722.js +286 -0
- package/dist/audio/g722.js.map +1 -0
- package/dist/audio/index.d.ts +7 -0
- package/dist/audio/index.d.ts.map +1 -0
- package/dist/audio/index.js +8 -0
- package/dist/audio/index.js.map +1 -0
- package/dist/audio/loudness.d.ts +13 -0
- package/dist/audio/loudness.d.ts.map +1 -0
- package/dist/audio/loudness.js +65 -0
- package/dist/audio/loudness.js.map +1 -0
- package/dist/audio/mulaw.d.ts +3 -0
- package/dist/audio/mulaw.d.ts.map +1 -0
- package/dist/audio/mulaw.js +39 -0
- package/dist/audio/mulaw.js.map +1 -0
- package/dist/audio/pcm.d.ts +5 -0
- package/dist/audio/pcm.d.ts.map +1 -0
- package/dist/audio/pcm.js +40 -0
- package/dist/audio/pcm.js.map +1 -0
- package/dist/audio/resample.d.ts +12 -0
- package/dist/audio/resample.d.ts.map +1 -0
- package/dist/audio/resample.js +157 -0
- package/dist/audio/resample.js.map +1 -0
- package/dist/audio-envelope.d.ts +23 -0
- package/dist/audio-envelope.d.ts.map +1 -0
- package/dist/audio-envelope.js +114 -0
- package/dist/audio-envelope.js.map +1 -0
- package/dist/confidence-to-wait.d.ts +12 -0
- package/dist/confidence-to-wait.d.ts.map +1 -0
- package/dist/confidence-to-wait.js +18 -0
- package/dist/confidence-to-wait.js.map +1 -0
- package/dist/conversation-event.d.ts +19 -0
- package/dist/conversation-event.d.ts.map +1 -0
- package/dist/conversation-event.js +44 -0
- package/dist/conversation-event.js.map +1 -0
- package/dist/error-handler.d.ts +35 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +136 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/idle-timeout.d.ts +69 -0
- package/dist/idle-timeout.d.ts.map +1 -0
- package/dist/idle-timeout.js +149 -0
- package/dist/idle-timeout.js.map +1 -0
- package/dist/incremental-unit.d.ts +17 -0
- package/dist/incremental-unit.d.ts.map +1 -0
- package/dist/incremental-unit.js +5 -0
- package/dist/incremental-unit.js.map +1 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/init-chain.d.ts +35 -0
- package/dist/init-chain.d.ts.map +1 -0
- package/dist/init-chain.js +102 -0
- package/dist/init-chain.js.map +1 -0
- package/dist/init-stage-order.d.ts +5 -0
- package/dist/init-stage-order.d.ts.map +1 -0
- package/dist/init-stage-order.js +74 -0
- package/dist/init-stage-order.js.map +1 -0
- package/dist/interaction-coordinator.d.ts +45 -0
- package/dist/interaction-coordinator.d.ts.map +1 -0
- package/dist/interaction-coordinator.js +197 -0
- package/dist/interaction-coordinator.js.map +1 -0
- package/dist/interaction-policy.d.ts +103 -0
- package/dist/interaction-policy.d.ts.map +1 -0
- package/dist/interaction-policy.js +6 -0
- package/dist/interaction-policy.js.map +1 -0
- package/dist/iu-ledger.d.ts +39 -0
- package/dist/iu-ledger.d.ts.map +1 -0
- package/dist/iu-ledger.js +90 -0
- package/dist/iu-ledger.js.map +1 -0
- package/dist/latency-filler-fixtures.d.ts +7 -0
- package/dist/latency-filler-fixtures.d.ts.map +1 -0
- package/dist/latency-filler-fixtures.js +10 -0
- package/dist/latency-filler-fixtures.js.map +1 -0
- package/dist/latency-filler.d.ts +29 -0
- package/dist/latency-filler.d.ts.map +1 -0
- package/dist/latency-filler.js +102 -0
- package/dist/latency-filler.js.map +1 -0
- package/dist/mode-switcher.d.ts +24 -0
- package/dist/mode-switcher.d.ts.map +1 -0
- package/dist/mode-switcher.js +83 -0
- package/dist/mode-switcher.js.map +1 -0
- package/dist/observability-observer.d.ts +36 -0
- package/dist/observability-observer.d.ts.map +1 -0
- package/dist/observability-observer.js +141 -0
- package/dist/observability-observer.js.map +1 -0
- package/dist/observability.d.ts +68 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +70 -0
- package/dist/observability.js.map +1 -0
- package/dist/packet-factories.d.ts +58 -0
- package/dist/packet-factories.d.ts.map +1 -0
- package/dist/packet-factories.js +181 -0
- package/dist/packet-factories.js.map +1 -0
- package/dist/packets.d.ts +532 -0
- package/dist/packets.d.ts.map +1 -0
- package/dist/packets.js +51 -0
- package/dist/packets.js.map +1 -0
- package/dist/pipeline-bus.d.ts +92 -0
- package/dist/pipeline-bus.d.ts.map +1 -0
- package/dist/pipeline-bus.js +294 -0
- package/dist/pipeline-bus.js.map +1 -0
- package/dist/plugin-contract.d.ts +73 -0
- package/dist/plugin-contract.d.ts.map +1 -0
- package/dist/plugin-contract.js +30 -0
- package/dist/plugin-contract.js.map +1 -0
- package/dist/policies/defer.d.ts +11 -0
- package/dist/policies/defer.d.ts.map +1 -0
- package/dist/policies/defer.js +13 -0
- package/dist/policies/defer.js.map +1 -0
- package/dist/policies/rule-based.d.ts +35 -0
- package/dist/policies/rule-based.d.ts.map +1 -0
- package/dist/policies/rule-based.js +130 -0
- package/dist/policies/rule-based.js.map +1 -0
- package/dist/pricing.d.ts +34 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +108 -0
- package/dist/pricing.js.map +1 -0
- package/dist/primary-speaker-fixtures.d.ts +12 -0
- package/dist/primary-speaker-fixtures.d.ts.map +1 -0
- package/dist/primary-speaker-fixtures.js +37 -0
- package/dist/primary-speaker-fixtures.js.map +1 -0
- package/dist/primary-speaker-gate.d.ts +42 -0
- package/dist/primary-speaker-gate.d.ts.map +1 -0
- package/dist/primary-speaker-gate.js +178 -0
- package/dist/primary-speaker-gate.js.map +1 -0
- package/dist/provider-fallback.d.ts +28 -0
- package/dist/provider-fallback.d.ts.map +1 -0
- package/dist/provider-fallback.js +72 -0
- package/dist/provider-fallback.js.map +1 -0
- package/dist/reasoner-hedge.d.ts +21 -0
- package/dist/reasoner-hedge.d.ts.map +1 -0
- package/dist/reasoner-hedge.js +170 -0
- package/dist/reasoner-hedge.js.map +1 -0
- package/dist/reasoner-route.d.ts +24 -0
- package/dist/reasoner-route.d.ts.map +1 -0
- package/dist/reasoner-route.js +87 -0
- package/dist/reasoner-route.js.map +1 -0
- package/dist/reasoner-session-store.d.ts +18 -0
- package/dist/reasoner-session-store.d.ts.map +1 -0
- package/dist/reasoner-session-store.js +21 -0
- package/dist/reasoner-session-store.js.map +1 -0
- package/dist/reasoner.d.ts +97 -0
- package/dist/reasoner.d.ts.map +1 -0
- package/dist/reasoner.js +9 -0
- package/dist/reasoner.js.map +1 -0
- package/dist/retry.d.ts +31 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +83 -0
- package/dist/retry.js.map +1 -0
- package/dist/scheduler.d.ts +11 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +20 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/spend-cap.d.ts +27 -0
- package/dist/spend-cap.d.ts.map +1 -0
- package/dist/spend-cap.js +38 -0
- package/dist/spend-cap.js.map +1 -0
- package/dist/tts-playout-clock.d.ts +43 -0
- package/dist/tts-playout-clock.d.ts.map +1 -0
- package/dist/tts-playout-clock.js +125 -0
- package/dist/tts-playout-clock.js.map +1 -0
- package/dist/turn-arbiter.d.ts +41 -0
- package/dist/turn-arbiter.d.ts.map +1 -0
- package/dist/turn-arbiter.js +259 -0
- package/dist/turn-arbiter.js.map +1 -0
- package/dist/voice-agent-session-util.d.ts +60 -0
- package/dist/voice-agent-session-util.d.ts.map +1 -0
- package/dist/voice-agent-session-util.js +204 -0
- package/dist/voice-agent-session-util.js.map +1 -0
- package/dist/voice-agent-session.d.ts +435 -0
- package/dist/voice-agent-session.d.ts.map +1 -0
- package/dist/voice-agent-session.js +1597 -0
- package/dist/voice-agent-session.js.map +1 -0
- package/dist/voice-text.d.ts +46 -0
- package/dist/voice-text.d.ts.map +1 -0
- package/dist/voice-text.js +209 -0
- package/dist/voice-text.js.map +1 -0
- package/package.json +19 -6
- package/src/audio/alaw.test.ts +87 -0
- package/src/audio/audio.test.ts +285 -0
- package/src/audio/g722.test.ts +149 -0
- package/src/audio/loudness.test.ts +58 -0
- package/src/audio-envelope.test.ts +167 -0
- package/src/confidence-to-wait.test.ts +21 -0
- package/src/error-handler.test.ts +56 -0
- package/src/hedge-throwing-backend.test.ts +46 -0
- package/src/interaction-coordinator.test.ts +529 -0
- package/src/iu-ledger.test.ts +276 -0
- package/src/latency-filler.test.ts +62 -0
- package/src/observability-observer.test.ts +248 -0
- package/src/observability.test.ts +94 -0
- package/src/packet-factories.test.ts +126 -0
- package/src/pipeline-bus.g10.test.ts +145 -0
- package/src/pipeline-bus.test.ts +211 -0
- package/src/policies/defer.test.ts +86 -0
- package/src/policies/rule-based.test.ts +334 -0
- package/src/pricing.test.ts +226 -0
- package/src/primary-speaker-gate.test.ts +150 -0
- package/src/provider-fallback.test.ts +87 -0
- package/src/reasoner-hedge.test.ts +361 -0
- package/src/reasoner-route.test.ts +248 -0
- package/src/reasoner.test.ts +69 -0
- package/src/retry.test.ts +83 -0
- package/src/route-throwing-spec.test.ts +44 -0
- package/src/spend-cap.test.ts +114 -0
- package/src/tts-playout-clock.test.ts +125 -0
- package/src/turn-arbiter.characterization.test.ts +477 -0
- package/src/turn-arbiter.test.ts +762 -0
- package/src/voice-agent-session.test.ts +4165 -0
- package/src/voice-text.test.ts +156 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
import { TurnArbiter } from "../turn-arbiter.js";
|
|
3
|
+
export class RuleBasedInteractionPolicy {
|
|
4
|
+
arbiter;
|
|
5
|
+
ttsPlayout;
|
|
6
|
+
backchannelConfig;
|
|
7
|
+
pending = [];
|
|
8
|
+
bargeInAudioConsumed = false;
|
|
9
|
+
delegateGapOpen = false;
|
|
10
|
+
delegateCuePlayed = false;
|
|
11
|
+
userSpeaking = false;
|
|
12
|
+
constructor(deps) {
|
|
13
|
+
this.ttsPlayout = deps.ttsPlayout;
|
|
14
|
+
this.backchannelConfig = deps.backchannel;
|
|
15
|
+
this.arbiter = new TurnArbiter({
|
|
16
|
+
bus: deps.bus,
|
|
17
|
+
primarySpeakerGate: deps.primarySpeakerGate,
|
|
18
|
+
ttsPlayout: deps.ttsPlayout,
|
|
19
|
+
minInterruptionMs: deps.minInterruptionMs,
|
|
20
|
+
pauseThenResolveBargeIn: deps.pauseThenResolveBargeIn,
|
|
21
|
+
onInterrupt: (id) => this.pending.push({ kind: "interrupt", interruptedContextId: id }),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
observe(obs) {
|
|
25
|
+
switch (obs.kind) {
|
|
26
|
+
case "vad_speech_started":
|
|
27
|
+
this.userSpeaking = true;
|
|
28
|
+
if (obs.interruptedContextId)
|
|
29
|
+
this.arbiter.onSpeechStarted({
|
|
30
|
+
kind: "vad.speech_started",
|
|
31
|
+
contextId: obs.contextId,
|
|
32
|
+
timestampMs: obs.timestampMs,
|
|
33
|
+
confidence: obs.confidence,
|
|
34
|
+
}, obs.interruptedContextId);
|
|
35
|
+
break;
|
|
36
|
+
case "vad_speech_activity":
|
|
37
|
+
this.arbiter.onSpeechActivity({
|
|
38
|
+
kind: "vad.speech_activity",
|
|
39
|
+
contextId: obs.contextId,
|
|
40
|
+
timestampMs: obs.timestampMs,
|
|
41
|
+
isAsync: true,
|
|
42
|
+
});
|
|
43
|
+
break;
|
|
44
|
+
case "vad_speech_ended":
|
|
45
|
+
this.userSpeaking = false;
|
|
46
|
+
this.arbiter.onSpeechEnded({
|
|
47
|
+
kind: "vad.speech_ended",
|
|
48
|
+
contextId: obs.contextId,
|
|
49
|
+
timestampMs: obs.timestampMs,
|
|
50
|
+
}, obs.hasActiveTts);
|
|
51
|
+
break;
|
|
52
|
+
case "vad_barge_in_audio":
|
|
53
|
+
this.bargeInAudioConsumed = this.arbiter.observeBargeInAudio({
|
|
54
|
+
kind: "vad.audio",
|
|
55
|
+
contextId: obs.contextId,
|
|
56
|
+
timestampMs: obs.timestampMs,
|
|
57
|
+
audio: obs.audio,
|
|
58
|
+
});
|
|
59
|
+
break;
|
|
60
|
+
case "stt_partial":
|
|
61
|
+
this.arbiter.noteInterimEvidence(obs.text);
|
|
62
|
+
if (obs.interruptedContextId) {
|
|
63
|
+
this.arbiter.onProviderSttEvidence(obs.contextId, obs.timestampMs, obs.interruptedContextId);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
case "stt_final":
|
|
67
|
+
this.arbiter.noteInterimEvidence(obs.text, obs.confidence);
|
|
68
|
+
if (obs.interruptedContextId) {
|
|
69
|
+
this.arbiter.onProviderSttEvidence(obs.contextId, obs.timestampMs, obs.interruptedContextId);
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case "delegate_state":
|
|
73
|
+
this.pending.push(...this.handleDelegateState(obs));
|
|
74
|
+
break;
|
|
75
|
+
default:
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
if (this.pending.length === 0)
|
|
79
|
+
return [];
|
|
80
|
+
const out = this.pending;
|
|
81
|
+
this.pending = [];
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
handleDelegateState(obs) {
|
|
85
|
+
const phase = obs.toolCallPhase;
|
|
86
|
+
if (!phase)
|
|
87
|
+
return [];
|
|
88
|
+
switch (phase) {
|
|
89
|
+
case "started":
|
|
90
|
+
this.delegateGapOpen = true;
|
|
91
|
+
this.delegateCuePlayed = false;
|
|
92
|
+
return [];
|
|
93
|
+
case "delayed": {
|
|
94
|
+
if (!this.delegateGapOpen || this.delegateCuePlayed)
|
|
95
|
+
return [];
|
|
96
|
+
if (this.depsTtsActive())
|
|
97
|
+
return [];
|
|
98
|
+
if (this.userSpeaking)
|
|
99
|
+
return [];
|
|
100
|
+
this.delegateCuePlayed = true;
|
|
101
|
+
if (this.backchannelConfig?.enabled === false)
|
|
102
|
+
return [];
|
|
103
|
+
const cue = this.backchannelConfig?.cues?.[0] ?? "mm_hmm";
|
|
104
|
+
return [{ kind: "backchannel", cue }];
|
|
105
|
+
}
|
|
106
|
+
case "complete":
|
|
107
|
+
case "failed":
|
|
108
|
+
this.delegateGapOpen = false;
|
|
109
|
+
this.delegateCuePlayed = false;
|
|
110
|
+
return [];
|
|
111
|
+
default:
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
depsTtsActive() {
|
|
116
|
+
return this.ttsPlayout.activeContexts().length > 0;
|
|
117
|
+
}
|
|
118
|
+
reset(_contextId) {
|
|
119
|
+
this.delegateGapOpen = false;
|
|
120
|
+
this.delegateCuePlayed = false;
|
|
121
|
+
this.userSpeaking = false;
|
|
122
|
+
this.arbiter.clear();
|
|
123
|
+
}
|
|
124
|
+
takeBargeInAudioConsumed() {
|
|
125
|
+
const consumed = this.bargeInAudioConsumed;
|
|
126
|
+
this.bargeInAudioConsumed = false;
|
|
127
|
+
return consumed;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=rule-based.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-based.js","sourceRoot":"","sources":["../../src/policies/rule-based.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { UsageRecordedPacket } from "./packets.js";
|
|
2
|
+
export interface SttPrice {
|
|
3
|
+
readonly usdPerAudioSecond: number;
|
|
4
|
+
}
|
|
5
|
+
export interface LlmPrice {
|
|
6
|
+
readonly usdPer1MInputTokens: number;
|
|
7
|
+
readonly usdPer1MOutputTokens: number;
|
|
8
|
+
readonly usdPer1MCachedInputTokens?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface TtsPrice {
|
|
11
|
+
readonly usdPer1MCharacters: number;
|
|
12
|
+
}
|
|
13
|
+
export interface PriceCatalog {
|
|
14
|
+
readonly source: string;
|
|
15
|
+
readonly version: string;
|
|
16
|
+
/** Keys: `provider/model` (e.g. `deepgram/nova-3`). */
|
|
17
|
+
readonly stt: Readonly<Record<string, SttPrice>>;
|
|
18
|
+
readonly llm: Readonly<Record<string, LlmPrice>>;
|
|
19
|
+
readonly tts: Readonly<Record<string, TtsPrice>>;
|
|
20
|
+
}
|
|
21
|
+
export type CostResult = {
|
|
22
|
+
readonly usd: number;
|
|
23
|
+
readonly unpriced?: undefined;
|
|
24
|
+
} | {
|
|
25
|
+
readonly usd: null;
|
|
26
|
+
readonly unpriced: true;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Public list prices stamped into DEFAULT_PRICE_CATALOG.source.
|
|
30
|
+
* STT $/audio-second from $/min ÷ 60. TTS is $/1M characters. LLM is $/1M tokens.
|
|
31
|
+
*/
|
|
32
|
+
export declare const DEFAULT_PRICE_CATALOG: PriceCatalog;
|
|
33
|
+
export declare function costOf(usage: UsageRecordedPacket, catalog: PriceCatalog): CostResult;
|
|
34
|
+
//# sourceMappingURL=pricing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;CAC7C;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjD,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACjD,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;CAClD;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAA;CAAE,CAAC;AAOpD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,YAuDnC,CAAC;AAEF,wBAAgB,MAAM,CAAC,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,YAAY,GAAG,UAAU,CAmCpF"}
|
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Versioned price catalog: usage.recorded quantities → USD.
|
|
4
|
+
// Unknown provider/model keys yield unpriced (never silent 0 for real providers).
|
|
5
|
+
// Declared local/self-hosted models are explicitly zero-cost.
|
|
6
|
+
function catalogKey(provider, model) {
|
|
7
|
+
if (!provider || !model)
|
|
8
|
+
return null;
|
|
9
|
+
return `${provider}/${model}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Public list prices stamped into DEFAULT_PRICE_CATALOG.source.
|
|
13
|
+
* STT $/audio-second from $/min ÷ 60. TTS is $/1M characters. LLM is $/1M tokens.
|
|
14
|
+
*/
|
|
15
|
+
export const DEFAULT_PRICE_CATALOG = {
|
|
16
|
+
source: "voice-prices@3 | deepgram.com/pricing PAYG Nova-3 streaming monolingual $0.0077/min → $0.000128333/s; " +
|
|
17
|
+
"deepgram.com/pricing Aura-2 TTS $0.030/1k chars → $30/1M; " +
|
|
18
|
+
"cartesia.ai/pricing + docs.cartesia.ai/pricing ~1 credit/char, PAYG ~$50/1M chars (cloudtalk.io cartesia-pricing 2026); " +
|
|
19
|
+
"openai tts-1 $15/1M chars (openai.com api pricing); gpt-4o-mini-tts text $0.60/1M (community/azure listed text input rate); " +
|
|
20
|
+
"openai GPT-4.1-mini $0.40/$1.60 per 1M in/out (developers.openai.com/api/docs/pricing; Azure GPT-4.1-mini-2025-04-14 Global); " +
|
|
21
|
+
"cloud.google.com/speech-to-text/pricing V2 standard recognition $0.016/min → $0.000266667/s (latest_long); " +
|
|
22
|
+
"ai.google.dev/pricing Gemini 2.5 Flash TTS preview text $0.50/1M chars (Gemini API TTS text input rate, 2026); " +
|
|
23
|
+
"elevenlabs.io/pricing/api Flash/Turbo TTS $0.05/1k chars → $50/1M; Multilingual v2/v3 $0.10/1k → $100/1M; " +
|
|
24
|
+
"Scribe v2 Realtime $0.39/hour → $0.000108333/s; " +
|
|
25
|
+
"grok/stt + grok/* TTS + epsilon/epsilon-tts intentionally ABSENT (no public list price → costOf unpriced, never silent $0)",
|
|
26
|
+
version: "3",
|
|
27
|
+
stt: {
|
|
28
|
+
"deepgram/nova-3": { usdPerAudioSecond: 0.0077 / 60 },
|
|
29
|
+
"deepgram/nova-2": { usdPerAudioSecond: 0.0077 / 60 },
|
|
30
|
+
"deepgram/flux-general-en": { usdPerAudioSecond: 0.0077 / 60 },
|
|
31
|
+
// Google Cloud Speech-to-Text V2 standard streaming recognition list rate.
|
|
32
|
+
"google/latest_long": { usdPerAudioSecond: 0.016 / 60 },
|
|
33
|
+
"google/latest_short": { usdPerAudioSecond: 0.016 / 60 },
|
|
34
|
+
"google/chirp_2": { usdPerAudioSecond: 0.016 / 60 },
|
|
35
|
+
// ElevenLabs Scribe v2 Realtime — public list $0.39/hour.
|
|
36
|
+
"elevenlabs/scribe_v2_realtime": { usdPerAudioSecond: 0.39 / 3600 },
|
|
37
|
+
"local/whisper": { usdPerAudioSecond: 0 },
|
|
38
|
+
// grok/stt: no public list price — omit so costOf → unpriced
|
|
39
|
+
},
|
|
40
|
+
llm: {
|
|
41
|
+
"openai/gpt-4.1-mini": {
|
|
42
|
+
usdPer1MInputTokens: 0.4,
|
|
43
|
+
usdPer1MOutputTokens: 1.6,
|
|
44
|
+
usdPer1MCachedInputTokens: 0.1,
|
|
45
|
+
},
|
|
46
|
+
"local/llm": {
|
|
47
|
+
usdPer1MInputTokens: 0,
|
|
48
|
+
usdPer1MOutputTokens: 0,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
tts: {
|
|
52
|
+
"cartesia/sonic-3": { usdPer1MCharacters: 50 },
|
|
53
|
+
"openai/gpt-4o-mini-tts": { usdPer1MCharacters: 0.6 },
|
|
54
|
+
"openai/tts-1": { usdPer1MCharacters: 15 },
|
|
55
|
+
"deepgram/aura-2-thalia-en": { usdPer1MCharacters: 30 },
|
|
56
|
+
// Gemini TTS preview models — Gemini API TTS text input list rate.
|
|
57
|
+
"gemini/gemini-3.1-flash-tts-preview": { usdPer1MCharacters: 0.5 },
|
|
58
|
+
"gemini/gemini-2.5-flash-preview-tts": { usdPer1MCharacters: 0.5 },
|
|
59
|
+
"gemini/gemini-2.5-pro-preview-tts": { usdPer1MCharacters: 0.5 },
|
|
60
|
+
// ElevenLabs public list — Flash/Turbo $0.05/1k, Multilingual $0.10/1k.
|
|
61
|
+
"elevenlabs/eleven_flash_v2_5": { usdPer1MCharacters: 50 },
|
|
62
|
+
"elevenlabs/eleven_flash_v2": { usdPer1MCharacters: 50 },
|
|
63
|
+
"elevenlabs/eleven_turbo_v2_5": { usdPer1MCharacters: 50 },
|
|
64
|
+
"elevenlabs/eleven_turbo_v2": { usdPer1MCharacters: 50 },
|
|
65
|
+
"elevenlabs/eleven_multilingual_v2": { usdPer1MCharacters: 100 },
|
|
66
|
+
"local/tts": { usdPer1MCharacters: 0 },
|
|
67
|
+
// grok/* voice keys + epsilon/epsilon-tts: no public list — omit → unpriced
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
export function costOf(usage, catalog) {
|
|
71
|
+
const key = catalogKey(usage.provider, usage.model);
|
|
72
|
+
if (!key)
|
|
73
|
+
return { usd: null, unpriced: true };
|
|
74
|
+
switch (usage.stage) {
|
|
75
|
+
case "stt": {
|
|
76
|
+
const price = catalog.stt[key];
|
|
77
|
+
if (!price)
|
|
78
|
+
return { usd: null, unpriced: true };
|
|
79
|
+
const seconds = usage.audioSeconds ?? 0;
|
|
80
|
+
return { usd: seconds * price.usdPerAudioSecond };
|
|
81
|
+
}
|
|
82
|
+
case "tts": {
|
|
83
|
+
const price = catalog.tts[key];
|
|
84
|
+
if (!price)
|
|
85
|
+
return { usd: null, unpriced: true };
|
|
86
|
+
const characters = usage.characters ?? 0;
|
|
87
|
+
return { usd: (characters / 1_000_000) * price.usdPer1MCharacters };
|
|
88
|
+
}
|
|
89
|
+
case "llm": {
|
|
90
|
+
const price = catalog.llm[key];
|
|
91
|
+
if (!price)
|
|
92
|
+
return { usd: null, unpriced: true };
|
|
93
|
+
const input = usage.inputTokens ?? 0;
|
|
94
|
+
const output = usage.outputTokens ?? 0;
|
|
95
|
+
const cached = usage.cachedInputTokens ?? 0;
|
|
96
|
+
const cachedRate = price.usdPer1MCachedInputTokens ?? price.usdPer1MInputTokens;
|
|
97
|
+
// Prefer charging cached tokens at the cached rate when present; non-cached input is the remainder.
|
|
98
|
+
const nonCachedInput = Math.max(0, input - cached);
|
|
99
|
+
const usd = (nonCachedInput / 1_000_000) * price.usdPer1MInputTokens +
|
|
100
|
+
(cached / 1_000_000) * cachedRate +
|
|
101
|
+
(output / 1_000_000) * price.usdPer1MOutputTokens;
|
|
102
|
+
return { usd };
|
|
103
|
+
}
|
|
104
|
+
default:
|
|
105
|
+
return { usd: null, unpriced: true };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function synthesizeTonePcm16(options: {
|
|
2
|
+
frequencyHz: number;
|
|
3
|
+
durationMs: number;
|
|
4
|
+
sampleRateHz?: number;
|
|
5
|
+
amplitude?: number;
|
|
6
|
+
phaseRad?: number;
|
|
7
|
+
}): Uint8Array;
|
|
8
|
+
export declare function mixPcm16(chunks: Uint8Array[], weights: number[]): Uint8Array;
|
|
9
|
+
export declare const PRIMARY_SPEAKER_TONE_HZ = 280;
|
|
10
|
+
export declare const BYSTANDER_SPEAKER_TONE_HZ = 2100;
|
|
11
|
+
export declare const ASSISTANT_ECHO_TONE_HZ = 520;
|
|
12
|
+
//# sourceMappingURL=primary-speaker-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-speaker-fixtures.d.ts","sourceRoot":"","sources":["../src/primary-speaker-fixtures.ts"],"names":[],"mappings":"AAMA,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,UAAU,CAYb;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,UAAU,CAc5E;AAED,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C,eAAO,MAAM,sBAAsB,MAAM,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// Synthetic PCM fixtures for primary-speaker gate tests.
|
|
4
|
+
import { pcm16BytesToSamples, pcm16SamplesToBytes } from "./audio/pcm.js";
|
|
5
|
+
export function synthesizeTonePcm16(options) {
|
|
6
|
+
const sampleRateHz = options.sampleRateHz ?? 16000;
|
|
7
|
+
const amplitude = options.amplitude ?? 0.35;
|
|
8
|
+
const phaseRad = options.phaseRad ?? 0;
|
|
9
|
+
const sampleCount = Math.max(1, Math.round((options.durationMs * sampleRateHz) / 1000));
|
|
10
|
+
const samples = new Int16Array(sampleCount);
|
|
11
|
+
const omega = (2 * Math.PI * options.frequencyHz) / sampleRateHz;
|
|
12
|
+
for (let i = 0; i < sampleCount; i += 1) {
|
|
13
|
+
const value = Math.sin(omega * i + phaseRad) * amplitude * 32767;
|
|
14
|
+
samples[i] = Math.max(-32768, Math.min(32767, Math.round(value)));
|
|
15
|
+
}
|
|
16
|
+
return pcm16SamplesToBytes(samples);
|
|
17
|
+
}
|
|
18
|
+
export function mixPcm16(chunks, weights) {
|
|
19
|
+
if (chunks.length === 0)
|
|
20
|
+
return new Uint8Array(0);
|
|
21
|
+
const maxLen = Math.max(...chunks.map((c) => c.byteLength));
|
|
22
|
+
const out = new Int16Array(maxLen / 2);
|
|
23
|
+
for (let c = 0; c < chunks.length; c += 1) {
|
|
24
|
+
const chunk = chunks[c];
|
|
25
|
+
const weight = weights[c] ?? 1;
|
|
26
|
+
const samples = pcm16BytesToSamples(chunk);
|
|
27
|
+
for (let i = 0; i < samples.length; i += 1) {
|
|
28
|
+
const mixed = (out[i] ?? 0) + samples[i] * weight;
|
|
29
|
+
out[i] = Math.max(-32768, Math.min(32767, Math.round(mixed)));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return pcm16SamplesToBytes(out);
|
|
33
|
+
}
|
|
34
|
+
export const PRIMARY_SPEAKER_TONE_HZ = 280;
|
|
35
|
+
export const BYSTANDER_SPEAKER_TONE_HZ = 2100;
|
|
36
|
+
export const ASSISTANT_ECHO_TONE_HZ = 520;
|
|
37
|
+
//# sourceMappingURL=primary-speaker-fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-speaker-fixtures.js","sourceRoot":"","sources":["../src/primary-speaker-fixtures.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,EAAE;AACF,yDAAyD;AAEzD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1E,MAAM,UAAU,mBAAmB,CAAC,OAMnC;IACC,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,KAAK,CAAC;IACnD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,UAAU,GAAG,YAAY,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IACxF,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,YAAY,CAAC;IACjE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,WAAW,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,SAAS,GAAG,KAAK,CAAC;QACjE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAoB,EAAE,OAAiB;IAC9D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;QACzB,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAE,GAAG,MAAM,CAAC;YACnD,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAC3C,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAC9C,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface SpeakerFingerprint {
|
|
2
|
+
readonly bands: readonly number[];
|
|
3
|
+
readonly rms: number;
|
|
4
|
+
readonly zcr: number;
|
|
5
|
+
}
|
|
6
|
+
export interface PrimarySpeakerGateConfig {
|
|
7
|
+
readonly enabled?: boolean;
|
|
8
|
+
readonly similarityThreshold?: number;
|
|
9
|
+
readonly echoDominanceMargin?: number;
|
|
10
|
+
readonly onDecision?: (decision: PrimarySpeakerGateDecision) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface PrimarySpeakerGateDecision {
|
|
13
|
+
readonly contextId: string;
|
|
14
|
+
readonly signal: "primary_speaker" | "echo_rejected";
|
|
15
|
+
readonly accepted: boolean;
|
|
16
|
+
readonly frameCount: number;
|
|
17
|
+
readonly primaryHits: number;
|
|
18
|
+
readonly echoRejectedFrames: number;
|
|
19
|
+
}
|
|
20
|
+
export declare class PrimarySpeakerGate {
|
|
21
|
+
private readonly enabled;
|
|
22
|
+
private readonly similarityThreshold;
|
|
23
|
+
private readonly echoDominanceMargin;
|
|
24
|
+
private readonly onDecision?;
|
|
25
|
+
private profile;
|
|
26
|
+
private enrollFrames;
|
|
27
|
+
private bargeInFrames;
|
|
28
|
+
private assistantProfile;
|
|
29
|
+
constructor(config?: PrimarySpeakerGateConfig);
|
|
30
|
+
isEnabled(): boolean;
|
|
31
|
+
hasProfile(): boolean;
|
|
32
|
+
enrollUserTurnChunk(pcm: Uint8Array): void;
|
|
33
|
+
lockProfileFromFirstTurn(): void;
|
|
34
|
+
beginBargeInWindow(): void;
|
|
35
|
+
observeBargeInChunk(pcm: Uint8Array): void;
|
|
36
|
+
observeAssistantPlayout(pcm: Uint8Array): void;
|
|
37
|
+
shouldCommitBargeIn(contextId?: string): boolean;
|
|
38
|
+
resetBargeInWindow(): void;
|
|
39
|
+
}
|
|
40
|
+
export declare function extractSpeakerFingerprint(pcm: Uint8Array): SpeakerFingerprint | null;
|
|
41
|
+
export declare function fingerprintSimilarity(a: SpeakerFingerprint, b: SpeakerFingerprint): number;
|
|
42
|
+
//# sourceMappingURL=primary-speaker-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-speaker-gate.d.ts","sourceRoot":"","sources":["../src/primary-speaker-gate.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,0BAA0B,KAAK,IAAI,CAAC;CACtE;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAyC;IACrE,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,gBAAgB,CAAmC;gBAE/C,MAAM,GAAE,wBAA6B;IAOjD,SAAS,IAAI,OAAO;IAIpB,UAAU,IAAI,OAAO;IAIrB,mBAAmB,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAM1C,wBAAwB,IAAI,IAAI;IAMhC,kBAAkB,IAAI,IAAI;IAI1B,mBAAmB,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAM1C,uBAAuB,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAO9C,mBAAmB,CAAC,SAAS,SAAK,GAAG,OAAO;IA0B5C,kBAAkB,IAAI,IAAI;CAG3B;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,UAAU,GAAG,kBAAkB,GAAG,IAAI,CA6BpF;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,kBAAkB,EAAE,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAmB1F"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
//
|
|
3
|
+
// VE-02 / G23 — lightweight primary-speaker gate for barge-in (FireRedChat pVAD direction).
|
|
4
|
+
// Locks a spectral fingerprint from the first user turn; gates sustained barge-in on match.
|
|
5
|
+
import { pcm16BytesToSamples } from "./audio/pcm.js";
|
|
6
|
+
const SAMPLE_RATE_HZ = 16000;
|
|
7
|
+
const MIN_SAMPLES = 320;
|
|
8
|
+
const BAND_HZ = [150, 300, 600, 1200, 2400, 4800];
|
|
9
|
+
export class PrimarySpeakerGate {
|
|
10
|
+
enabled;
|
|
11
|
+
similarityThreshold;
|
|
12
|
+
echoDominanceMargin;
|
|
13
|
+
onDecision;
|
|
14
|
+
profile = null;
|
|
15
|
+
enrollFrames = [];
|
|
16
|
+
bargeInFrames = [];
|
|
17
|
+
assistantProfile = null;
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
this.enabled = config.enabled !== false;
|
|
20
|
+
this.similarityThreshold = config.similarityThreshold ?? 0.72;
|
|
21
|
+
this.echoDominanceMargin = config.echoDominanceMargin ?? 0.12;
|
|
22
|
+
this.onDecision = config.onDecision;
|
|
23
|
+
}
|
|
24
|
+
isEnabled() {
|
|
25
|
+
return this.enabled;
|
|
26
|
+
}
|
|
27
|
+
hasProfile() {
|
|
28
|
+
return this.profile !== null;
|
|
29
|
+
}
|
|
30
|
+
enrollUserTurnChunk(pcm) {
|
|
31
|
+
if (!this.enabled || this.profile !== null)
|
|
32
|
+
return;
|
|
33
|
+
const frame = extractSpeakerFingerprint(pcm);
|
|
34
|
+
if (frame)
|
|
35
|
+
this.enrollFrames.push(frame);
|
|
36
|
+
}
|
|
37
|
+
lockProfileFromFirstTurn() {
|
|
38
|
+
if (!this.enabled || this.profile !== null || this.enrollFrames.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
this.profile = averageFingerprints(this.enrollFrames);
|
|
41
|
+
this.enrollFrames = [];
|
|
42
|
+
}
|
|
43
|
+
beginBargeInWindow() {
|
|
44
|
+
this.bargeInFrames = [];
|
|
45
|
+
}
|
|
46
|
+
observeBargeInChunk(pcm) {
|
|
47
|
+
if (!this.enabled)
|
|
48
|
+
return;
|
|
49
|
+
const frame = extractSpeakerFingerprint(pcm);
|
|
50
|
+
if (frame)
|
|
51
|
+
this.bargeInFrames.push(frame);
|
|
52
|
+
}
|
|
53
|
+
observeAssistantPlayout(pcm) {
|
|
54
|
+
if (!this.enabled)
|
|
55
|
+
return;
|
|
56
|
+
const frame = extractSpeakerFingerprint(pcm);
|
|
57
|
+
if (!frame)
|
|
58
|
+
return;
|
|
59
|
+
this.assistantProfile = frame;
|
|
60
|
+
}
|
|
61
|
+
shouldCommitBargeIn(contextId = "") {
|
|
62
|
+
if (!this.enabled || this.profile === null)
|
|
63
|
+
return true;
|
|
64
|
+
if (this.bargeInFrames.length === 0)
|
|
65
|
+
return false;
|
|
66
|
+
let primaryHits = 0;
|
|
67
|
+
let echoRejectedFrames = 0;
|
|
68
|
+
for (const frame of this.bargeInFrames) {
|
|
69
|
+
const primarySim = fingerprintSimilarity(frame, this.profile);
|
|
70
|
+
if (this.assistantProfile !== null) {
|
|
71
|
+
const echoSim = fingerprintSimilarity(frame, this.assistantProfile);
|
|
72
|
+
if (echoSim >= primarySim + this.echoDominanceMargin) {
|
|
73
|
+
echoRejectedFrames += 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (primarySim >= this.similarityThreshold)
|
|
78
|
+
primaryHits += 1;
|
|
79
|
+
}
|
|
80
|
+
const required = Math.max(1, Math.ceil(this.bargeInFrames.length * 0.45));
|
|
81
|
+
const accepted = primaryHits >= required;
|
|
82
|
+
const decision = { contextId, accepted, frameCount: this.bargeInFrames.length, primaryHits, echoRejectedFrames };
|
|
83
|
+
if (echoRejectedFrames > 0)
|
|
84
|
+
this.onDecision?.({ signal: "echo_rejected", ...decision });
|
|
85
|
+
this.onDecision?.({ signal: "primary_speaker", ...decision });
|
|
86
|
+
return accepted;
|
|
87
|
+
}
|
|
88
|
+
resetBargeInWindow() {
|
|
89
|
+
this.bargeInFrames = [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export function extractSpeakerFingerprint(pcm) {
|
|
93
|
+
if (pcm.byteLength < MIN_SAMPLES * 2)
|
|
94
|
+
return null;
|
|
95
|
+
let samples;
|
|
96
|
+
try {
|
|
97
|
+
samples = pcm16BytesToSamples(pcm);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
if (samples.length < MIN_SAMPLES)
|
|
103
|
+
return null;
|
|
104
|
+
const window = samples.length > 512 ? samples.subarray(0, 512) : samples;
|
|
105
|
+
const bands = BAND_HZ.map((hz) => goertzelMagnitude(window, hz, SAMPLE_RATE_HZ));
|
|
106
|
+
const maxBand = Math.max(...bands, 1e-9);
|
|
107
|
+
const normalizedBands = bands.map((b) => b / maxBand);
|
|
108
|
+
let sumSq = 0;
|
|
109
|
+
let crossings = 0;
|
|
110
|
+
let prev = window[0] >= 0;
|
|
111
|
+
for (let i = 0; i < window.length; i += 1) {
|
|
112
|
+
const s = window[i];
|
|
113
|
+
sumSq += s * s;
|
|
114
|
+
const positive = s >= 0;
|
|
115
|
+
if (i > 0 && positive !== prev)
|
|
116
|
+
crossings += 1;
|
|
117
|
+
prev = positive;
|
|
118
|
+
}
|
|
119
|
+
const rms = Math.sqrt(sumSq / window.length) / 32768;
|
|
120
|
+
const zcr = crossings / window.length;
|
|
121
|
+
return { bands: normalizedBands, rms, zcr };
|
|
122
|
+
}
|
|
123
|
+
export function fingerprintSimilarity(a, b) {
|
|
124
|
+
let dot = 0;
|
|
125
|
+
let normA = 0;
|
|
126
|
+
let normB = 0;
|
|
127
|
+
for (let i = 0; i < a.bands.length; i += 1) {
|
|
128
|
+
const av = a.bands[i] ?? 0;
|
|
129
|
+
const bv = b.bands[i] ?? 0;
|
|
130
|
+
dot += av * bv;
|
|
131
|
+
normA += av * av;
|
|
132
|
+
normB += bv * bv;
|
|
133
|
+
}
|
|
134
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
135
|
+
if (denom <= 0)
|
|
136
|
+
return 0;
|
|
137
|
+
const spectral = dot / denom;
|
|
138
|
+
const rmsDelta = Math.abs(a.rms - b.rms);
|
|
139
|
+
const zcrDelta = Math.abs(a.zcr - b.zcr);
|
|
140
|
+
const timbrePenalty = Math.min(1, rmsDelta * 4 + zcrDelta * 2);
|
|
141
|
+
return Math.max(0, spectral * (1 - timbrePenalty * 0.35));
|
|
142
|
+
}
|
|
143
|
+
function averageFingerprints(frames) {
|
|
144
|
+
const bandCount = frames[0].bands.length;
|
|
145
|
+
const bands = new Array(bandCount).fill(0);
|
|
146
|
+
let rms = 0;
|
|
147
|
+
let zcr = 0;
|
|
148
|
+
for (const frame of frames) {
|
|
149
|
+
rms += frame.rms;
|
|
150
|
+
zcr += frame.zcr;
|
|
151
|
+
for (let i = 0; i < bandCount; i += 1) {
|
|
152
|
+
bands[i] = (bands[i] ?? 0) + (frame.bands[i] ?? 0);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const n = frames.length;
|
|
156
|
+
return {
|
|
157
|
+
bands: bands.map((b) => b / n),
|
|
158
|
+
rms: rms / n,
|
|
159
|
+
zcr: zcr / n,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function goertzelMagnitude(samples, targetHz, sampleRateHz) {
|
|
163
|
+
const omega = (2 * Math.PI * targetHz) / sampleRateHz;
|
|
164
|
+
const coeff = 2 * Math.cos(omega);
|
|
165
|
+
let s0 = 0;
|
|
166
|
+
let s1 = 0;
|
|
167
|
+
let s2 = 0;
|
|
168
|
+
for (let i = 0; i < samples.length; i += 1) {
|
|
169
|
+
const x = samples[i] / 32768;
|
|
170
|
+
s0 = x + coeff * s1 - s2;
|
|
171
|
+
s2 = s1;
|
|
172
|
+
s1 = s0;
|
|
173
|
+
}
|
|
174
|
+
const real = s1 - s2 * Math.cos(omega);
|
|
175
|
+
const imag = s2 * Math.sin(omega);
|
|
176
|
+
return Math.sqrt(real * real + imag * imag);
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=primary-speaker-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"primary-speaker-gate.js","sourceRoot":"","sources":["../src/primary-speaker-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type PipelineBus } from "./pipeline-bus.js";
|
|
2
|
+
import { type Scheduler } from "./scheduler.js";
|
|
3
|
+
export interface FallbackProvider<TReq, TResp> {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
send(req: TReq, signal: AbortSignal): Promise<TResp>;
|
|
6
|
+
healthProbe(signal: AbortSignal): Promise<boolean>;
|
|
7
|
+
}
|
|
8
|
+
export interface ProviderFallbackOptions {
|
|
9
|
+
readonly bus: PipelineBus;
|
|
10
|
+
readonly contextId: string;
|
|
11
|
+
readonly attemptTimeoutMs: number;
|
|
12
|
+
readonly recoveryProbeIntervalMs: number;
|
|
13
|
+
readonly scheduler?: Scheduler;
|
|
14
|
+
}
|
|
15
|
+
export declare class ProviderFallback<TReq, TResp> {
|
|
16
|
+
private readonly providers;
|
|
17
|
+
private readonly opts;
|
|
18
|
+
private readonly unavailable;
|
|
19
|
+
private readonly recoveryTimers;
|
|
20
|
+
private readonly scheduler;
|
|
21
|
+
constructor(providers: readonly FallbackProvider<TReq, TResp>[], opts: ProviderFallbackOptions);
|
|
22
|
+
send(req: TReq): Promise<TResp>;
|
|
23
|
+
close(): void;
|
|
24
|
+
private markUnavailable;
|
|
25
|
+
private scheduleRecoveryProbe;
|
|
26
|
+
private metric;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=provider-fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-fallback.d.ts","sourceRoot":"","sources":["../src/provider-fallback.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE5D,OAAO,EAAkB,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhE,MAAM,WAAW,gBAAgB,CAAC,IAAI,EAAE,KAAK;IAC3C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACrD,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;CAChC;AAED,qBAAa,gBAAgB,CAAC,IAAI,EAAE,KAAK;IAMrC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IANvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAqB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAGnB,SAAS,EAAE,SAAS,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EACnD,IAAI,EAAE,uBAAuB;IAK1C,IAAI,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;IAcrC,KAAK,IAAI,IAAI;IAMb,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,qBAAqB;IAoB7B,OAAO,CAAC,MAAM;CAGf"}
|