vocal-stack 1.0.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/LICENSE +21 -0
- package/README.md +269 -0
- package/dist/flow/index.cjs +571 -0
- package/dist/flow/index.cjs.map +1 -0
- package/dist/flow/index.d.cts +337 -0
- package/dist/flow/index.d.ts +337 -0
- package/dist/flow/index.js +559 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/index.cjs +1026 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +1003 -0
- package/dist/index.js.map +1 -0
- package/dist/monitor/index.cjs +291 -0
- package/dist/monitor/index.cjs.map +1 -0
- package/dist/monitor/index.d.cts +122 -0
- package/dist/monitor/index.d.ts +122 -0
- package/dist/monitor/index.js +286 -0
- package/dist/monitor/index.js.map +1 -0
- package/dist/sanitizer/index.cjs +190 -0
- package/dist/sanitizer/index.cjs.map +1 -0
- package/dist/sanitizer/index.d.cts +83 -0
- package/dist/sanitizer/index.d.ts +83 -0
- package/dist/sanitizer/index.js +186 -0
- package/dist/sanitizer/index.js.map +1 -0
- package/package.json +90 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for flow control
|
|
3
|
+
*/
|
|
4
|
+
interface FlowConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Stall threshold in milliseconds
|
|
7
|
+
* @default 700
|
|
8
|
+
*/
|
|
9
|
+
readonly stallThresholdMs?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Filler phrases to inject
|
|
12
|
+
* @default ['um', 'let me think', 'hmm']
|
|
13
|
+
*/
|
|
14
|
+
readonly fillerPhrases?: readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* Whether to enable filler injection
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
readonly enableFillers?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Maximum number of fillers to inject per response
|
|
22
|
+
* @default 3
|
|
23
|
+
*/
|
|
24
|
+
readonly maxFillersPerResponse?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Callback when filler is injected
|
|
27
|
+
*/
|
|
28
|
+
readonly onFillerInjected?: (filler: string) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Callback when stall is detected
|
|
31
|
+
*/
|
|
32
|
+
readonly onStallDetected?: (durationMs: number) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Callback when first chunk is emitted
|
|
35
|
+
*/
|
|
36
|
+
readonly onFirstChunk?: () => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Conversation states
|
|
40
|
+
*/
|
|
41
|
+
declare enum ConversationState {
|
|
42
|
+
IDLE = "idle",
|
|
43
|
+
WAITING = "waiting",
|
|
44
|
+
SPEAKING = "speaking",
|
|
45
|
+
INTERRUPTED = "interrupted"
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Statistics tracked by flow controller
|
|
49
|
+
*/
|
|
50
|
+
interface FlowStats {
|
|
51
|
+
readonly fillersInjected: number;
|
|
52
|
+
readonly stallsDetected: number;
|
|
53
|
+
readonly chunksProcessed: number;
|
|
54
|
+
readonly firstChunkTime: number | null;
|
|
55
|
+
readonly totalDurationMs: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Flow events for low-level API
|
|
59
|
+
*/
|
|
60
|
+
type FlowEvent = {
|
|
61
|
+
type: 'stall-detected';
|
|
62
|
+
durationMs: number;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'filler-injected';
|
|
65
|
+
filler: string;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'first-chunk';
|
|
68
|
+
chunk: string;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'state-change';
|
|
71
|
+
from: ConversationState;
|
|
72
|
+
to: ConversationState;
|
|
73
|
+
} | {
|
|
74
|
+
type: 'interrupted';
|
|
75
|
+
} | {
|
|
76
|
+
type: 'chunk-processed';
|
|
77
|
+
chunk: string;
|
|
78
|
+
} | {
|
|
79
|
+
type: 'completed';
|
|
80
|
+
stats: FlowStats;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Event listener for flow events
|
|
84
|
+
*/
|
|
85
|
+
type FlowEventListener = (event: FlowEvent) => void;
|
|
86
|
+
/**
|
|
87
|
+
* Configuration for low-level FlowManager
|
|
88
|
+
*/
|
|
89
|
+
interface FlowManagerConfig {
|
|
90
|
+
/**
|
|
91
|
+
* Stall threshold in milliseconds
|
|
92
|
+
* @default 700
|
|
93
|
+
*/
|
|
94
|
+
readonly stallThresholdMs?: number;
|
|
95
|
+
/**
|
|
96
|
+
* Filler phrases to inject
|
|
97
|
+
* @default ['um', 'let me think', 'hmm']
|
|
98
|
+
*/
|
|
99
|
+
readonly fillerPhrases?: readonly string[];
|
|
100
|
+
/**
|
|
101
|
+
* Whether to enable filler injection
|
|
102
|
+
* @default true
|
|
103
|
+
*/
|
|
104
|
+
readonly enableFillers?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Maximum number of fillers to inject per response
|
|
107
|
+
* @default 3
|
|
108
|
+
*/
|
|
109
|
+
readonly maxFillersPerResponse?: number;
|
|
110
|
+
/**
|
|
111
|
+
* Buffer size for barge-in scenarios
|
|
112
|
+
* @default 10
|
|
113
|
+
*/
|
|
114
|
+
readonly bufferSize?: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* High-level stream wrapper for flow control
|
|
119
|
+
*/
|
|
120
|
+
declare class FlowController {
|
|
121
|
+
private readonly config;
|
|
122
|
+
private readonly stateMachine;
|
|
123
|
+
private readonly stallDetector;
|
|
124
|
+
private readonly fillerInjector;
|
|
125
|
+
private readonly bufferManager;
|
|
126
|
+
private firstChunkEmitted;
|
|
127
|
+
private stats;
|
|
128
|
+
private startTime;
|
|
129
|
+
constructor(config?: FlowConfig);
|
|
130
|
+
/**
|
|
131
|
+
* Wrap an async iterable with flow control
|
|
132
|
+
*/
|
|
133
|
+
wrap(input: AsyncIterable<string>): AsyncIterable<string>;
|
|
134
|
+
/**
|
|
135
|
+
* Interrupt the current flow (for barge-in)
|
|
136
|
+
*/
|
|
137
|
+
interrupt(): void;
|
|
138
|
+
/**
|
|
139
|
+
* Get current conversation state
|
|
140
|
+
*/
|
|
141
|
+
getState(): ConversationState;
|
|
142
|
+
/**
|
|
143
|
+
* Get flow statistics
|
|
144
|
+
*/
|
|
145
|
+
getStats(): FlowStats;
|
|
146
|
+
/**
|
|
147
|
+
* Get buffered chunks (for advanced barge-in scenarios)
|
|
148
|
+
*/
|
|
149
|
+
getBufferedChunks(): readonly string[];
|
|
150
|
+
private handleStall;
|
|
151
|
+
private reset;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Convenience function to create and use flow controller
|
|
155
|
+
*/
|
|
156
|
+
declare function withFlowControl(input: AsyncIterable<string>, config?: FlowConfig): AsyncIterable<string>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Low-level event-based flow manager
|
|
160
|
+
*/
|
|
161
|
+
declare class FlowManager {
|
|
162
|
+
private readonly config;
|
|
163
|
+
private readonly stateMachine;
|
|
164
|
+
private readonly stallDetector;
|
|
165
|
+
private readonly fillerInjector;
|
|
166
|
+
private readonly bufferManager;
|
|
167
|
+
private readonly listeners;
|
|
168
|
+
private firstChunkEmitted;
|
|
169
|
+
private stats;
|
|
170
|
+
private startTime;
|
|
171
|
+
private stateChangeUnsubscribe;
|
|
172
|
+
constructor(config?: FlowManagerConfig);
|
|
173
|
+
/**
|
|
174
|
+
* Add event listener
|
|
175
|
+
*/
|
|
176
|
+
on(listener: FlowEventListener): () => void;
|
|
177
|
+
/**
|
|
178
|
+
* Start flow tracking
|
|
179
|
+
*/
|
|
180
|
+
start(): void;
|
|
181
|
+
/**
|
|
182
|
+
* Process a chunk from the stream
|
|
183
|
+
*/
|
|
184
|
+
processChunk(chunk: string): void;
|
|
185
|
+
/**
|
|
186
|
+
* Complete the flow
|
|
187
|
+
*/
|
|
188
|
+
complete(): void;
|
|
189
|
+
/**
|
|
190
|
+
* Interrupt the flow (for barge-in)
|
|
191
|
+
*/
|
|
192
|
+
interrupt(): void;
|
|
193
|
+
/**
|
|
194
|
+
* Get current conversation state
|
|
195
|
+
*/
|
|
196
|
+
getState(): ConversationState;
|
|
197
|
+
/**
|
|
198
|
+
* Get flow statistics
|
|
199
|
+
*/
|
|
200
|
+
getStats(): FlowStats;
|
|
201
|
+
/**
|
|
202
|
+
* Get buffered chunks
|
|
203
|
+
*/
|
|
204
|
+
getBufferedChunks(): readonly string[];
|
|
205
|
+
private handleStall;
|
|
206
|
+
private emit;
|
|
207
|
+
private reset;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Conversation state machine
|
|
212
|
+
*/
|
|
213
|
+
declare class ConversationStateMachine {
|
|
214
|
+
private currentState;
|
|
215
|
+
private readonly listeners;
|
|
216
|
+
/**
|
|
217
|
+
* Get current state
|
|
218
|
+
*/
|
|
219
|
+
getState(): ConversationState;
|
|
220
|
+
/**
|
|
221
|
+
* Attempt to transition to new state
|
|
222
|
+
*/
|
|
223
|
+
transition(to: ConversationState): boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Add state change listener
|
|
226
|
+
*/
|
|
227
|
+
onStateChange(listener: (from: ConversationState, to: ConversationState) => void): () => void;
|
|
228
|
+
/**
|
|
229
|
+
* Reset to IDLE
|
|
230
|
+
*/
|
|
231
|
+
reset(): void;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Default stall threshold in milliseconds
|
|
236
|
+
* Based on human perception of silence: 500-1000ms feels like a pause
|
|
237
|
+
* Most LLM APIs stream chunks every 50-200ms when active
|
|
238
|
+
*/
|
|
239
|
+
declare const DEFAULT_STALL_THRESHOLD_MS = 700;
|
|
240
|
+
/**
|
|
241
|
+
* Default filler phrases to inject during stalls
|
|
242
|
+
*/
|
|
243
|
+
declare const DEFAULT_FILLER_PHRASES: string[];
|
|
244
|
+
/**
|
|
245
|
+
* Default maximum fillers per response
|
|
246
|
+
* Prevents over-use of filler words
|
|
247
|
+
*/
|
|
248
|
+
declare const DEFAULT_MAX_FILLERS_PER_RESPONSE = 3;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Detects stream stalls based on timing
|
|
252
|
+
*/
|
|
253
|
+
declare class StallDetector {
|
|
254
|
+
private lastChunkTime;
|
|
255
|
+
private stallTimer;
|
|
256
|
+
private readonly thresholdMs;
|
|
257
|
+
private readonly onStall;
|
|
258
|
+
constructor(thresholdMs: number, onStall: (durationMs: number) => void);
|
|
259
|
+
/**
|
|
260
|
+
* Notify detector that a chunk was received
|
|
261
|
+
*/
|
|
262
|
+
notifyChunk(): void;
|
|
263
|
+
/**
|
|
264
|
+
* Start monitoring for stalls
|
|
265
|
+
*/
|
|
266
|
+
start(): void;
|
|
267
|
+
/**
|
|
268
|
+
* Stop monitoring
|
|
269
|
+
*/
|
|
270
|
+
stop(): void;
|
|
271
|
+
private scheduleStallCheck;
|
|
272
|
+
private clearTimer;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Manages filler phrase injection
|
|
277
|
+
*/
|
|
278
|
+
declare class FillerInjector {
|
|
279
|
+
private readonly phrases;
|
|
280
|
+
private readonly maxFillers;
|
|
281
|
+
private fillersUsed;
|
|
282
|
+
private lastFillerIndex;
|
|
283
|
+
constructor(phrases: readonly string[], maxFillers: number);
|
|
284
|
+
/**
|
|
285
|
+
* Get next filler phrase (returns null if limit reached)
|
|
286
|
+
*/
|
|
287
|
+
getFiller(): string | null;
|
|
288
|
+
/**
|
|
289
|
+
* Reset filler state
|
|
290
|
+
*/
|
|
291
|
+
reset(): void;
|
|
292
|
+
/**
|
|
293
|
+
* Check if more fillers can be injected
|
|
294
|
+
*/
|
|
295
|
+
canInjectMore(): boolean;
|
|
296
|
+
/**
|
|
297
|
+
* Get count of fillers used
|
|
298
|
+
*/
|
|
299
|
+
getUsedCount(): number;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Buffer manager for barge-in scenarios
|
|
304
|
+
*/
|
|
305
|
+
declare class BufferManager {
|
|
306
|
+
private buffer;
|
|
307
|
+
private readonly maxSize;
|
|
308
|
+
private head;
|
|
309
|
+
private size;
|
|
310
|
+
constructor(maxSize?: number);
|
|
311
|
+
/**
|
|
312
|
+
* Add chunk to buffer
|
|
313
|
+
*/
|
|
314
|
+
add(chunk: string): void;
|
|
315
|
+
/**
|
|
316
|
+
* Get all buffered chunks in order
|
|
317
|
+
*/
|
|
318
|
+
getAll(): readonly string[];
|
|
319
|
+
/**
|
|
320
|
+
* Clear all buffered chunks
|
|
321
|
+
*/
|
|
322
|
+
clear(): void;
|
|
323
|
+
/**
|
|
324
|
+
* Get current buffer size
|
|
325
|
+
*/
|
|
326
|
+
getSize(): number;
|
|
327
|
+
/**
|
|
328
|
+
* Check if buffer is empty
|
|
329
|
+
*/
|
|
330
|
+
isEmpty(): boolean;
|
|
331
|
+
/**
|
|
332
|
+
* Check if buffer is full
|
|
333
|
+
*/
|
|
334
|
+
isFull(): boolean;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export { BufferManager, ConversationState, ConversationStateMachine, DEFAULT_FILLER_PHRASES, DEFAULT_MAX_FILLERS_PER_RESPONSE, DEFAULT_STALL_THRESHOLD_MS, FillerInjector, type FlowConfig, FlowController, type FlowEvent, type FlowEventListener, FlowManager, type FlowManagerConfig, type FlowStats, StallDetector, withFlowControl };
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for flow control
|
|
3
|
+
*/
|
|
4
|
+
interface FlowConfig {
|
|
5
|
+
/**
|
|
6
|
+
* Stall threshold in milliseconds
|
|
7
|
+
* @default 700
|
|
8
|
+
*/
|
|
9
|
+
readonly stallThresholdMs?: number;
|
|
10
|
+
/**
|
|
11
|
+
* Filler phrases to inject
|
|
12
|
+
* @default ['um', 'let me think', 'hmm']
|
|
13
|
+
*/
|
|
14
|
+
readonly fillerPhrases?: readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* Whether to enable filler injection
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
readonly enableFillers?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Maximum number of fillers to inject per response
|
|
22
|
+
* @default 3
|
|
23
|
+
*/
|
|
24
|
+
readonly maxFillersPerResponse?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Callback when filler is injected
|
|
27
|
+
*/
|
|
28
|
+
readonly onFillerInjected?: (filler: string) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Callback when stall is detected
|
|
31
|
+
*/
|
|
32
|
+
readonly onStallDetected?: (durationMs: number) => void;
|
|
33
|
+
/**
|
|
34
|
+
* Callback when first chunk is emitted
|
|
35
|
+
*/
|
|
36
|
+
readonly onFirstChunk?: () => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Conversation states
|
|
40
|
+
*/
|
|
41
|
+
declare enum ConversationState {
|
|
42
|
+
IDLE = "idle",
|
|
43
|
+
WAITING = "waiting",
|
|
44
|
+
SPEAKING = "speaking",
|
|
45
|
+
INTERRUPTED = "interrupted"
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Statistics tracked by flow controller
|
|
49
|
+
*/
|
|
50
|
+
interface FlowStats {
|
|
51
|
+
readonly fillersInjected: number;
|
|
52
|
+
readonly stallsDetected: number;
|
|
53
|
+
readonly chunksProcessed: number;
|
|
54
|
+
readonly firstChunkTime: number | null;
|
|
55
|
+
readonly totalDurationMs: number;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Flow events for low-level API
|
|
59
|
+
*/
|
|
60
|
+
type FlowEvent = {
|
|
61
|
+
type: 'stall-detected';
|
|
62
|
+
durationMs: number;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'filler-injected';
|
|
65
|
+
filler: string;
|
|
66
|
+
} | {
|
|
67
|
+
type: 'first-chunk';
|
|
68
|
+
chunk: string;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'state-change';
|
|
71
|
+
from: ConversationState;
|
|
72
|
+
to: ConversationState;
|
|
73
|
+
} | {
|
|
74
|
+
type: 'interrupted';
|
|
75
|
+
} | {
|
|
76
|
+
type: 'chunk-processed';
|
|
77
|
+
chunk: string;
|
|
78
|
+
} | {
|
|
79
|
+
type: 'completed';
|
|
80
|
+
stats: FlowStats;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Event listener for flow events
|
|
84
|
+
*/
|
|
85
|
+
type FlowEventListener = (event: FlowEvent) => void;
|
|
86
|
+
/**
|
|
87
|
+
* Configuration for low-level FlowManager
|
|
88
|
+
*/
|
|
89
|
+
interface FlowManagerConfig {
|
|
90
|
+
/**
|
|
91
|
+
* Stall threshold in milliseconds
|
|
92
|
+
* @default 700
|
|
93
|
+
*/
|
|
94
|
+
readonly stallThresholdMs?: number;
|
|
95
|
+
/**
|
|
96
|
+
* Filler phrases to inject
|
|
97
|
+
* @default ['um', 'let me think', 'hmm']
|
|
98
|
+
*/
|
|
99
|
+
readonly fillerPhrases?: readonly string[];
|
|
100
|
+
/**
|
|
101
|
+
* Whether to enable filler injection
|
|
102
|
+
* @default true
|
|
103
|
+
*/
|
|
104
|
+
readonly enableFillers?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Maximum number of fillers to inject per response
|
|
107
|
+
* @default 3
|
|
108
|
+
*/
|
|
109
|
+
readonly maxFillersPerResponse?: number;
|
|
110
|
+
/**
|
|
111
|
+
* Buffer size for barge-in scenarios
|
|
112
|
+
* @default 10
|
|
113
|
+
*/
|
|
114
|
+
readonly bufferSize?: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* High-level stream wrapper for flow control
|
|
119
|
+
*/
|
|
120
|
+
declare class FlowController {
|
|
121
|
+
private readonly config;
|
|
122
|
+
private readonly stateMachine;
|
|
123
|
+
private readonly stallDetector;
|
|
124
|
+
private readonly fillerInjector;
|
|
125
|
+
private readonly bufferManager;
|
|
126
|
+
private firstChunkEmitted;
|
|
127
|
+
private stats;
|
|
128
|
+
private startTime;
|
|
129
|
+
constructor(config?: FlowConfig);
|
|
130
|
+
/**
|
|
131
|
+
* Wrap an async iterable with flow control
|
|
132
|
+
*/
|
|
133
|
+
wrap(input: AsyncIterable<string>): AsyncIterable<string>;
|
|
134
|
+
/**
|
|
135
|
+
* Interrupt the current flow (for barge-in)
|
|
136
|
+
*/
|
|
137
|
+
interrupt(): void;
|
|
138
|
+
/**
|
|
139
|
+
* Get current conversation state
|
|
140
|
+
*/
|
|
141
|
+
getState(): ConversationState;
|
|
142
|
+
/**
|
|
143
|
+
* Get flow statistics
|
|
144
|
+
*/
|
|
145
|
+
getStats(): FlowStats;
|
|
146
|
+
/**
|
|
147
|
+
* Get buffered chunks (for advanced barge-in scenarios)
|
|
148
|
+
*/
|
|
149
|
+
getBufferedChunks(): readonly string[];
|
|
150
|
+
private handleStall;
|
|
151
|
+
private reset;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Convenience function to create and use flow controller
|
|
155
|
+
*/
|
|
156
|
+
declare function withFlowControl(input: AsyncIterable<string>, config?: FlowConfig): AsyncIterable<string>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Low-level event-based flow manager
|
|
160
|
+
*/
|
|
161
|
+
declare class FlowManager {
|
|
162
|
+
private readonly config;
|
|
163
|
+
private readonly stateMachine;
|
|
164
|
+
private readonly stallDetector;
|
|
165
|
+
private readonly fillerInjector;
|
|
166
|
+
private readonly bufferManager;
|
|
167
|
+
private readonly listeners;
|
|
168
|
+
private firstChunkEmitted;
|
|
169
|
+
private stats;
|
|
170
|
+
private startTime;
|
|
171
|
+
private stateChangeUnsubscribe;
|
|
172
|
+
constructor(config?: FlowManagerConfig);
|
|
173
|
+
/**
|
|
174
|
+
* Add event listener
|
|
175
|
+
*/
|
|
176
|
+
on(listener: FlowEventListener): () => void;
|
|
177
|
+
/**
|
|
178
|
+
* Start flow tracking
|
|
179
|
+
*/
|
|
180
|
+
start(): void;
|
|
181
|
+
/**
|
|
182
|
+
* Process a chunk from the stream
|
|
183
|
+
*/
|
|
184
|
+
processChunk(chunk: string): void;
|
|
185
|
+
/**
|
|
186
|
+
* Complete the flow
|
|
187
|
+
*/
|
|
188
|
+
complete(): void;
|
|
189
|
+
/**
|
|
190
|
+
* Interrupt the flow (for barge-in)
|
|
191
|
+
*/
|
|
192
|
+
interrupt(): void;
|
|
193
|
+
/**
|
|
194
|
+
* Get current conversation state
|
|
195
|
+
*/
|
|
196
|
+
getState(): ConversationState;
|
|
197
|
+
/**
|
|
198
|
+
* Get flow statistics
|
|
199
|
+
*/
|
|
200
|
+
getStats(): FlowStats;
|
|
201
|
+
/**
|
|
202
|
+
* Get buffered chunks
|
|
203
|
+
*/
|
|
204
|
+
getBufferedChunks(): readonly string[];
|
|
205
|
+
private handleStall;
|
|
206
|
+
private emit;
|
|
207
|
+
private reset;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Conversation state machine
|
|
212
|
+
*/
|
|
213
|
+
declare class ConversationStateMachine {
|
|
214
|
+
private currentState;
|
|
215
|
+
private readonly listeners;
|
|
216
|
+
/**
|
|
217
|
+
* Get current state
|
|
218
|
+
*/
|
|
219
|
+
getState(): ConversationState;
|
|
220
|
+
/**
|
|
221
|
+
* Attempt to transition to new state
|
|
222
|
+
*/
|
|
223
|
+
transition(to: ConversationState): boolean;
|
|
224
|
+
/**
|
|
225
|
+
* Add state change listener
|
|
226
|
+
*/
|
|
227
|
+
onStateChange(listener: (from: ConversationState, to: ConversationState) => void): () => void;
|
|
228
|
+
/**
|
|
229
|
+
* Reset to IDLE
|
|
230
|
+
*/
|
|
231
|
+
reset(): void;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Default stall threshold in milliseconds
|
|
236
|
+
* Based on human perception of silence: 500-1000ms feels like a pause
|
|
237
|
+
* Most LLM APIs stream chunks every 50-200ms when active
|
|
238
|
+
*/
|
|
239
|
+
declare const DEFAULT_STALL_THRESHOLD_MS = 700;
|
|
240
|
+
/**
|
|
241
|
+
* Default filler phrases to inject during stalls
|
|
242
|
+
*/
|
|
243
|
+
declare const DEFAULT_FILLER_PHRASES: string[];
|
|
244
|
+
/**
|
|
245
|
+
* Default maximum fillers per response
|
|
246
|
+
* Prevents over-use of filler words
|
|
247
|
+
*/
|
|
248
|
+
declare const DEFAULT_MAX_FILLERS_PER_RESPONSE = 3;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Detects stream stalls based on timing
|
|
252
|
+
*/
|
|
253
|
+
declare class StallDetector {
|
|
254
|
+
private lastChunkTime;
|
|
255
|
+
private stallTimer;
|
|
256
|
+
private readonly thresholdMs;
|
|
257
|
+
private readonly onStall;
|
|
258
|
+
constructor(thresholdMs: number, onStall: (durationMs: number) => void);
|
|
259
|
+
/**
|
|
260
|
+
* Notify detector that a chunk was received
|
|
261
|
+
*/
|
|
262
|
+
notifyChunk(): void;
|
|
263
|
+
/**
|
|
264
|
+
* Start monitoring for stalls
|
|
265
|
+
*/
|
|
266
|
+
start(): void;
|
|
267
|
+
/**
|
|
268
|
+
* Stop monitoring
|
|
269
|
+
*/
|
|
270
|
+
stop(): void;
|
|
271
|
+
private scheduleStallCheck;
|
|
272
|
+
private clearTimer;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Manages filler phrase injection
|
|
277
|
+
*/
|
|
278
|
+
declare class FillerInjector {
|
|
279
|
+
private readonly phrases;
|
|
280
|
+
private readonly maxFillers;
|
|
281
|
+
private fillersUsed;
|
|
282
|
+
private lastFillerIndex;
|
|
283
|
+
constructor(phrases: readonly string[], maxFillers: number);
|
|
284
|
+
/**
|
|
285
|
+
* Get next filler phrase (returns null if limit reached)
|
|
286
|
+
*/
|
|
287
|
+
getFiller(): string | null;
|
|
288
|
+
/**
|
|
289
|
+
* Reset filler state
|
|
290
|
+
*/
|
|
291
|
+
reset(): void;
|
|
292
|
+
/**
|
|
293
|
+
* Check if more fillers can be injected
|
|
294
|
+
*/
|
|
295
|
+
canInjectMore(): boolean;
|
|
296
|
+
/**
|
|
297
|
+
* Get count of fillers used
|
|
298
|
+
*/
|
|
299
|
+
getUsedCount(): number;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Buffer manager for barge-in scenarios
|
|
304
|
+
*/
|
|
305
|
+
declare class BufferManager {
|
|
306
|
+
private buffer;
|
|
307
|
+
private readonly maxSize;
|
|
308
|
+
private head;
|
|
309
|
+
private size;
|
|
310
|
+
constructor(maxSize?: number);
|
|
311
|
+
/**
|
|
312
|
+
* Add chunk to buffer
|
|
313
|
+
*/
|
|
314
|
+
add(chunk: string): void;
|
|
315
|
+
/**
|
|
316
|
+
* Get all buffered chunks in order
|
|
317
|
+
*/
|
|
318
|
+
getAll(): readonly string[];
|
|
319
|
+
/**
|
|
320
|
+
* Clear all buffered chunks
|
|
321
|
+
*/
|
|
322
|
+
clear(): void;
|
|
323
|
+
/**
|
|
324
|
+
* Get current buffer size
|
|
325
|
+
*/
|
|
326
|
+
getSize(): number;
|
|
327
|
+
/**
|
|
328
|
+
* Check if buffer is empty
|
|
329
|
+
*/
|
|
330
|
+
isEmpty(): boolean;
|
|
331
|
+
/**
|
|
332
|
+
* Check if buffer is full
|
|
333
|
+
*/
|
|
334
|
+
isFull(): boolean;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export { BufferManager, ConversationState, ConversationStateMachine, DEFAULT_FILLER_PHRASES, DEFAULT_MAX_FILLERS_PER_RESPONSE, DEFAULT_STALL_THRESHOLD_MS, FillerInjector, type FlowConfig, FlowController, type FlowEvent, type FlowEventListener, FlowManager, type FlowManagerConfig, type FlowStats, StallDetector, withFlowControl };
|