@framers/agentos 0.1.108 → 0.1.109
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/voice-pipeline/AcousticEndpointDetector.d.ts +95 -20
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts.map +1 -1
- package/dist/voice-pipeline/AcousticEndpointDetector.js +110 -24
- package/dist/voice-pipeline/AcousticEndpointDetector.js.map +1 -1
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts +66 -15
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts.map +1 -1
- package/dist/voice-pipeline/HardCutBargeinHandler.js +65 -13
- package/dist/voice-pipeline/HardCutBargeinHandler.js.map +1 -1
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts +116 -42
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts.map +1 -1
- package/dist/voice-pipeline/HeuristicEndpointDetector.js +159 -52
- package/dist/voice-pipeline/HeuristicEndpointDetector.js.map +1 -1
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts +89 -24
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts.map +1 -1
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js +74 -20
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js.map +1 -1
- package/dist/voice-pipeline/VoiceInterruptError.d.ts +68 -10
- package/dist/voice-pipeline/VoiceInterruptError.d.ts.map +1 -1
- package/dist/voice-pipeline/VoiceInterruptError.js +53 -6
- package/dist/voice-pipeline/VoiceInterruptError.js.map +1 -1
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts +190 -39
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts.map +1 -1
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js +266 -53
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js.map +1 -1
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts +135 -43
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts.map +1 -1
- package/dist/voice-pipeline/WebSocketStreamTransport.js +109 -47
- package/dist/voice-pipeline/WebSocketStreamTransport.js.map +1 -1
- package/dist/voice-pipeline/index.d.ts +34 -1
- package/dist/voice-pipeline/index.d.ts.map +1 -1
- package/dist/voice-pipeline/index.js +41 -1
- package/dist/voice-pipeline/index.js.map +1 -1
- package/dist/voice-pipeline/types.d.ts +432 -106
- package/dist/voice-pipeline/types.d.ts.map +1 -1
- package/dist/voice-pipeline/types.js +21 -9
- package/dist/voice-pipeline/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,30 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module voice-pipeline/SoftFadeBargeinHandler
|
|
3
3
|
*
|
|
4
|
-
* Implements a three-tier soft-fade barge-in policy
|
|
4
|
+
* Implements a three-tier soft-fade barge-in policy that maps detected speech
|
|
5
|
+
* duration to one of three actions: ignore, pause (with fade-out), or cancel.
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* ## Three-tier logic
|
|
8
|
+
*
|
|
9
|
+
* The handler divides the speech duration axis into three regions:
|
|
10
|
+
*
|
|
11
|
+
* ```
|
|
12
|
+
* 0 ms ignoreMs cancelMs
|
|
13
|
+
* |-------- ignore --------|-------- pause --------|-------- cancel -------->
|
|
14
|
+
* (noise) (fade-out) (hard stop)
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* | Region | Action | Rationale |
|
|
18
|
+
* |---------------------------------|----------|-----------------------------------------------|
|
|
19
|
+
* | `speechDurationMs < ignoreMs` | `ignore` | Too short to be intentional (noise, breath). |
|
|
20
|
+
* | `ignoreMs <= speech < cancelMs` | `pause` | Probably intentional; fade out gracefully. |
|
|
21
|
+
* | `speechDurationMs >= cancelMs` | `cancel` | Definitely intentional; stop immediately. |
|
|
22
|
+
*
|
|
23
|
+
* ## Configurable thresholds
|
|
24
|
+
*
|
|
25
|
+
* - **`ignoreMs`** (default 100 ms): The noise floor. Anything shorter than
|
|
26
|
+
* this is dismissed. Set lower in quiet environments, higher in noisy ones.
|
|
27
|
+
*
|
|
28
|
+
* - **`cancelMs`** (default 2,000 ms): The hard-stop ceiling. By this point,
|
|
29
|
+
* the user has clearly been speaking for a while and wants to take over.
|
|
30
|
+
* The pipeline should stop TTS immediately rather than fading.
|
|
31
|
+
*
|
|
32
|
+
* - **`fadeMs`** (default 200 ms): The duration of the audio fade-out applied
|
|
33
|
+
* during a `'pause'` action. Shorter fades (100 ms) feel snappier; longer
|
|
34
|
+
* fades (300+ ms) feel smoother but delay the user's ability to be heard.
|
|
35
|
+
*
|
|
36
|
+
* ## When to use soft-fade vs hard-cut
|
|
37
|
+
*
|
|
38
|
+
* Soft-fade is preferred when:
|
|
39
|
+
* - The environment is noisy and false barge-in detections are common.
|
|
40
|
+
* - The conversation is measured/educational and abrupt cuts feel jarring.
|
|
41
|
+
* - The TTS voice has long trailing prosody that benefits from a fade.
|
|
42
|
+
*
|
|
43
|
+
* Use {@link HardCutBargeinHandler} when:
|
|
44
|
+
* - The conversation is fast-paced (customer support, command interfaces).
|
|
45
|
+
* - Audio quality is high and false positives are rare.
|
|
46
|
+
* - Minimal interruption latency is critical.
|
|
47
|
+
*
|
|
48
|
+
* @see {@link HardCutBargeinHandler} for the binary hard-cut alternative.
|
|
49
|
+
* @see {@link IBargeinHandler} for the interface contract.
|
|
10
50
|
*/
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Implementation
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
11
54
|
/**
|
|
12
55
|
* Barge-in handler that applies a three-tier soft-fade strategy.
|
|
13
56
|
*
|
|
14
|
-
* The handler
|
|
57
|
+
* The handler is stateless -- each {@link handleBargein} call is evaluated
|
|
58
|
+
* independently with no memory of previous barge-in events.
|
|
15
59
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* | `< ignoreMs` | `ignore` — noise, continue TTS uninterrupted |
|
|
19
|
-
* | `>= ignoreMs < cancelMs` | `pause` with `fadeMs` fade-out |
|
|
20
|
-
* | `>= cancelMs` | `cancel` with `'[interrupted]'` marker |
|
|
60
|
+
* @see {@link IBargeinHandler} for the interface contract.
|
|
61
|
+
* @see {@link HardCutBargeinHandler} for the binary hard-cut alternative.
|
|
21
62
|
*
|
|
22
63
|
* @example
|
|
23
|
-
* ```
|
|
64
|
+
* ```typescript
|
|
24
65
|
* const handler = new SoftFadeBargeinHandler({ ignoreMs: 80, cancelMs: 1500, fadeMs: 150 });
|
|
25
|
-
*
|
|
26
|
-
* handler.handleBargein({ speechDurationMs:
|
|
27
|
-
* handler.handleBargein({ speechDurationMs:
|
|
66
|
+
*
|
|
67
|
+
* handler.handleBargein({ speechDurationMs: 30, ... }); // -> { type: 'ignore' }
|
|
68
|
+
* handler.handleBargein({ speechDurationMs: 500, ... }); // -> { type: 'pause', fadeMs: 150 }
|
|
69
|
+
* handler.handleBargein({ speechDurationMs: 1600, ... }); // -> { type: 'cancel', injectMarker: '[interrupted]' }
|
|
28
70
|
* ```
|
|
29
71
|
*/
|
|
30
72
|
export class SoftFadeBargeinHandler {
|
|
@@ -37,7 +79,7 @@ export class SoftFadeBargeinHandler {
|
|
|
37
79
|
constructor(options = {}) {
|
|
38
80
|
/**
|
|
39
81
|
* The interruption strategy implemented by this handler.
|
|
40
|
-
* Always `'soft-fade'
|
|
82
|
+
* Always `'soft-fade'` -- TTS audio is faded out over a configurable window.
|
|
41
83
|
*/
|
|
42
84
|
this.mode = 'soft-fade';
|
|
43
85
|
this.ignoreMs = options.ignoreMs ?? 100;
|
|
@@ -48,21 +90,33 @@ export class SoftFadeBargeinHandler {
|
|
|
48
90
|
* Evaluate the barge-in context and return the pipeline action.
|
|
49
91
|
*
|
|
50
92
|
* Decision tree (evaluated in order):
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
93
|
+
*
|
|
94
|
+
* 1. `speechDurationMs < ignoreMs` -> `{ type: 'ignore' }`
|
|
95
|
+
* Too short to be intentional. Likely a lip smack, breath, or noise burst.
|
|
96
|
+
*
|
|
97
|
+
* 2. `speechDurationMs >= cancelMs` -> `{ type: 'cancel', injectMarker: '[interrupted]' }`
|
|
98
|
+
* The user has been speaking long enough that they clearly want to take over.
|
|
99
|
+
* Stop TTS immediately and mark the conversation as interrupted.
|
|
100
|
+
*
|
|
101
|
+
* 3. Otherwise (ignoreMs <= speech < cancelMs) -> `{ type: 'pause', fadeMs }`
|
|
102
|
+
* Probably intentional but not yet certain. Fade out TTS gracefully so the
|
|
103
|
+
* user can be heard. If the speech stops, the pipeline can resume playback.
|
|
54
104
|
*
|
|
55
105
|
* @param context - Snapshot of the barge-in state at the moment of detection.
|
|
56
|
-
* @returns The pipeline action to execute.
|
|
106
|
+
* @returns The pipeline action to execute. Always synchronous (no Promise).
|
|
57
107
|
*/
|
|
58
108
|
handleBargein(context) {
|
|
59
109
|
const { speechDurationMs } = context;
|
|
110
|
+
// Tier 1: Noise floor -- dismiss as accidental
|
|
60
111
|
if (speechDurationMs < this.ignoreMs) {
|
|
61
112
|
return { type: 'ignore' };
|
|
62
113
|
}
|
|
114
|
+
// Tier 3: Hard stop -- user has been speaking long enough to be certain.
|
|
115
|
+
// (Evaluated before Tier 2 to avoid the pause action for long speech.)
|
|
63
116
|
if (speechDurationMs >= this.cancelMs) {
|
|
64
117
|
return { type: 'cancel', injectMarker: '[interrupted]' };
|
|
65
118
|
}
|
|
119
|
+
// Tier 2: Fade region -- probably intentional, fade out gracefully.
|
|
66
120
|
return { type: 'pause', fadeMs: this.fadeMs };
|
|
67
121
|
}
|
|
68
122
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SoftFadeBargeinHandler.js","sourceRoot":"","sources":["../../src/voice-pipeline/SoftFadeBargeinHandler.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"SoftFadeBargeinHandler.js","sourceRoot":"","sources":["../../src/voice-pipeline/SoftFadeBargeinHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAqDH,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,sBAAsB;IAyBjC;;;;;OAKG;IACH,YAAY,UAAyC,EAAE;QA9BvD;;;WAGG;QACM,SAAI,GAAG,WAAoB,CAAC;QA2BnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,GAAG,CAAC;QACxC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC;QACzC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC;IACtC,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,OAAuB;QACnC,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC;QAErC,+CAA+C;QAC/C,IAAI,gBAAgB,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC5B,CAAC;QAED,yEAAyE;QACzE,uEAAuE;QACvE,IAAI,gBAAgB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;QAC3D,CAAC;QAED,oEAAoE;QACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAChD,CAAC;CACF"}
|
|
@@ -3,45 +3,103 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Typed error thrown when a voice session is interrupted by user barge-in.
|
|
5
5
|
* Used by VoiceNodeExecutor to catch barge-in AbortSignal and return
|
|
6
|
-
* a structured result instead of propagating
|
|
6
|
+
* a structured result instead of propagating a generic Error.
|
|
7
|
+
*
|
|
8
|
+
* ## Design rationale
|
|
9
|
+
*
|
|
10
|
+
* Using a typed error subclass (rather than a plain Error with a message string)
|
|
11
|
+
* allows consumers to:
|
|
12
|
+
* 1. Catch specifically via `instanceof VoiceInterruptError`.
|
|
13
|
+
* 2. Access structured fields ({@link interruptedText}, {@link userSpeech},
|
|
14
|
+
* {@link playedDurationMs}) without parsing an error message.
|
|
15
|
+
* 3. Distinguish barge-in interruptions from other pipeline errors in
|
|
16
|
+
* catch blocks and error boundaries.
|
|
17
|
+
*
|
|
18
|
+
* The `name` property is set as a class field (not via the prototype) so that
|
|
19
|
+
* it survives serialisation and can be used for `instanceof`-free checks when
|
|
20
|
+
* errors cross process boundaries (e.g. worker threads, RPC).
|
|
7
21
|
*/
|
|
8
22
|
/**
|
|
9
23
|
* Structured error representing a user barge-in interruption during TTS playback.
|
|
10
24
|
*
|
|
11
25
|
* Thrown (or returned as a typed sentinel) when the user speaks over the agent.
|
|
12
|
-
* Consumers can inspect
|
|
13
|
-
* to decide how to resume or branch the conversation graph.
|
|
26
|
+
* Consumers can inspect {@link interruptedText}, {@link userSpeech}, and
|
|
27
|
+
* {@link playedDurationMs} to decide how to resume or branch the conversation graph.
|
|
28
|
+
*
|
|
29
|
+
* @see {@link IBargeinHandler} which triggers the barge-in decision.
|
|
30
|
+
* @see {@link VoicePipelineOrchestrator} which transitions through INTERRUPTING state.
|
|
14
31
|
*
|
|
15
|
-
* @example
|
|
32
|
+
* @example Catching a barge-in interruption
|
|
16
33
|
* ```typescript
|
|
17
34
|
* try {
|
|
18
35
|
* await voiceNode.run(context);
|
|
19
36
|
* } catch (err) {
|
|
20
37
|
* if (err instanceof VoiceInterruptError) {
|
|
21
|
-
* console.log(`
|
|
38
|
+
* console.log(`Agent was saying: "${err.interruptedText}"`);
|
|
39
|
+
* console.log(`User said: "${err.userSpeech}"`);
|
|
40
|
+
* console.log(`Played ${err.playedDurationMs}ms before interruption`);
|
|
41
|
+
* // Resume conversation from the user's barge-in utterance
|
|
22
42
|
* await handleBargein(err.userSpeech);
|
|
43
|
+
* } else {
|
|
44
|
+
* throw err; // Re-throw non-barge-in errors
|
|
23
45
|
* }
|
|
24
46
|
* }
|
|
25
47
|
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example Checking without instanceof (e.g. cross-process)
|
|
50
|
+
* ```typescript
|
|
51
|
+
* if (error.name === 'VoiceInterruptError') {
|
|
52
|
+
* // Safe to access barge-in fields
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
26
55
|
*/
|
|
27
56
|
export declare class VoiceInterruptError extends Error {
|
|
28
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Discriminant name -- always `'VoiceInterruptError'`.
|
|
59
|
+
*
|
|
60
|
+
* Set as a class field (not via prototype) so it:
|
|
61
|
+
* - Survives JSON serialisation/deserialisation.
|
|
62
|
+
* - Can be used for `instanceof`-free type discrimination.
|
|
63
|
+
* - Overrides the default `Error` name in stack traces.
|
|
64
|
+
*/
|
|
29
65
|
readonly name = "VoiceInterruptError";
|
|
30
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* The full text the agent was speaking when interrupted.
|
|
68
|
+
* Corresponds to the cumulative TTS text accumulated by the orchestrator
|
|
69
|
+
* at the point of barge-in detection.
|
|
70
|
+
*/
|
|
31
71
|
readonly interruptedText: string;
|
|
32
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* What the user said that caused the interruption.
|
|
74
|
+
* Typically the partial or final STT transcript captured during the
|
|
75
|
+
* barge-in speech detection.
|
|
76
|
+
*/
|
|
33
77
|
readonly userSpeech: string;
|
|
34
78
|
/**
|
|
35
79
|
* How much of the agent's response was already played (ms).
|
|
36
|
-
* Derived from the cumulative `durationMs` of
|
|
37
|
-
* to the transport before the barge-in was detected.
|
|
80
|
+
* Derived from the cumulative `durationMs` of {@link EncodedAudioChunk}s
|
|
81
|
+
* sent to the transport before the barge-in was detected.
|
|
82
|
+
*
|
|
83
|
+
* Combined with {@link interruptedText}, this allows the consumer to
|
|
84
|
+
* estimate how much of the response the user actually heard.
|
|
38
85
|
*/
|
|
39
86
|
readonly playedDurationMs: number;
|
|
40
87
|
/**
|
|
88
|
+
* Create a new VoiceInterruptError with structured barge-in context.
|
|
89
|
+
*
|
|
41
90
|
* @param context - Barge-in context captured at the moment of interruption.
|
|
42
91
|
* @param context.interruptedText - Full TTS text the agent was speaking.
|
|
43
92
|
* @param context.userSpeech - Transcript of the user's barge-in utterance.
|
|
44
93
|
* @param context.playedDurationMs - Milliseconds of audio already played.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* throw new VoiceInterruptError({
|
|
98
|
+
* interruptedText: 'I was explaining the process of...',
|
|
99
|
+
* userSpeech: 'Wait, go back to the first step.',
|
|
100
|
+
* playedDurationMs: 2300,
|
|
101
|
+
* });
|
|
102
|
+
* ```
|
|
45
103
|
*/
|
|
46
104
|
constructor(context: {
|
|
47
105
|
interruptedText: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceInterruptError.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceInterruptError.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"VoiceInterruptError.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceInterruptError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,yBAAyB;IAEtC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B;;;;;;;OAOG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAElC;;;;;;;;;;;;;;;;OAgBG;gBACS,OAAO,EAAE;QACnB,eAAe,EAAE,MAAM,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;KAC1B;CAMF"}
|
|
@@ -3,37 +3,84 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Typed error thrown when a voice session is interrupted by user barge-in.
|
|
5
5
|
* Used by VoiceNodeExecutor to catch barge-in AbortSignal and return
|
|
6
|
-
* a structured result instead of propagating
|
|
6
|
+
* a structured result instead of propagating a generic Error.
|
|
7
|
+
*
|
|
8
|
+
* ## Design rationale
|
|
9
|
+
*
|
|
10
|
+
* Using a typed error subclass (rather than a plain Error with a message string)
|
|
11
|
+
* allows consumers to:
|
|
12
|
+
* 1. Catch specifically via `instanceof VoiceInterruptError`.
|
|
13
|
+
* 2. Access structured fields ({@link interruptedText}, {@link userSpeech},
|
|
14
|
+
* {@link playedDurationMs}) without parsing an error message.
|
|
15
|
+
* 3. Distinguish barge-in interruptions from other pipeline errors in
|
|
16
|
+
* catch blocks and error boundaries.
|
|
17
|
+
*
|
|
18
|
+
* The `name` property is set as a class field (not via the prototype) so that
|
|
19
|
+
* it survives serialisation and can be used for `instanceof`-free checks when
|
|
20
|
+
* errors cross process boundaries (e.g. worker threads, RPC).
|
|
7
21
|
*/
|
|
8
22
|
/**
|
|
9
23
|
* Structured error representing a user barge-in interruption during TTS playback.
|
|
10
24
|
*
|
|
11
25
|
* Thrown (or returned as a typed sentinel) when the user speaks over the agent.
|
|
12
|
-
* Consumers can inspect
|
|
13
|
-
* to decide how to resume or branch the conversation graph.
|
|
26
|
+
* Consumers can inspect {@link interruptedText}, {@link userSpeech}, and
|
|
27
|
+
* {@link playedDurationMs} to decide how to resume or branch the conversation graph.
|
|
28
|
+
*
|
|
29
|
+
* @see {@link IBargeinHandler} which triggers the barge-in decision.
|
|
30
|
+
* @see {@link VoicePipelineOrchestrator} which transitions through INTERRUPTING state.
|
|
14
31
|
*
|
|
15
|
-
* @example
|
|
32
|
+
* @example Catching a barge-in interruption
|
|
16
33
|
* ```typescript
|
|
17
34
|
* try {
|
|
18
35
|
* await voiceNode.run(context);
|
|
19
36
|
* } catch (err) {
|
|
20
37
|
* if (err instanceof VoiceInterruptError) {
|
|
21
|
-
* console.log(`
|
|
38
|
+
* console.log(`Agent was saying: "${err.interruptedText}"`);
|
|
39
|
+
* console.log(`User said: "${err.userSpeech}"`);
|
|
40
|
+
* console.log(`Played ${err.playedDurationMs}ms before interruption`);
|
|
41
|
+
* // Resume conversation from the user's barge-in utterance
|
|
22
42
|
* await handleBargein(err.userSpeech);
|
|
43
|
+
* } else {
|
|
44
|
+
* throw err; // Re-throw non-barge-in errors
|
|
23
45
|
* }
|
|
24
46
|
* }
|
|
25
47
|
* ```
|
|
48
|
+
*
|
|
49
|
+
* @example Checking without instanceof (e.g. cross-process)
|
|
50
|
+
* ```typescript
|
|
51
|
+
* if (error.name === 'VoiceInterruptError') {
|
|
52
|
+
* // Safe to access barge-in fields
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
26
55
|
*/
|
|
27
56
|
export class VoiceInterruptError extends Error {
|
|
28
57
|
/**
|
|
58
|
+
* Create a new VoiceInterruptError with structured barge-in context.
|
|
59
|
+
*
|
|
29
60
|
* @param context - Barge-in context captured at the moment of interruption.
|
|
30
61
|
* @param context.interruptedText - Full TTS text the agent was speaking.
|
|
31
62
|
* @param context.userSpeech - Transcript of the user's barge-in utterance.
|
|
32
63
|
* @param context.playedDurationMs - Milliseconds of audio already played.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* throw new VoiceInterruptError({
|
|
68
|
+
* interruptedText: 'I was explaining the process of...',
|
|
69
|
+
* userSpeech: 'Wait, go back to the first step.',
|
|
70
|
+
* playedDurationMs: 2300,
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
33
73
|
*/
|
|
34
74
|
constructor(context) {
|
|
35
75
|
super('Voice session interrupted by user');
|
|
36
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Discriminant name -- always `'VoiceInterruptError'`.
|
|
78
|
+
*
|
|
79
|
+
* Set as a class field (not via prototype) so it:
|
|
80
|
+
* - Survives JSON serialisation/deserialisation.
|
|
81
|
+
* - Can be used for `instanceof`-free type discrimination.
|
|
82
|
+
* - Overrides the default `Error` name in stack traces.
|
|
83
|
+
*/
|
|
37
84
|
this.name = 'VoiceInterruptError';
|
|
38
85
|
this.interruptedText = context.interruptedText;
|
|
39
86
|
this.userSpeech = context.userSpeech;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VoiceInterruptError.js","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceInterruptError.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"VoiceInterruptError.js","sourceRoot":"","sources":["../../src/voice-pipeline/VoiceInterruptError.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAmC5C;;;;;;;;;;;;;;;;OAgBG;IACH,YAAY,OAIX;QACC,KAAK,CAAC,mCAAmC,CAAC,CAAC;QAxD7C;;;;;;;WAOG;QACM,SAAI,GAAG,qBAAqB,CAAC;QAiDpC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC;QAC/C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IACnD,CAAC;CACF"}
|