@voice-kit/core 0.1.2 → 0.1.3
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/index.cjs +2137 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1466 -4
- package/dist/index.d.ts +1466 -4
- package/dist/index.js +2102 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -31
- package/dist/audio.cjs +0 -533
- package/dist/audio.cjs.map +0 -1
- package/dist/audio.d.cts +0 -260
- package/dist/audio.d.ts +0 -260
- package/dist/audio.js +0 -514
- package/dist/audio.js.map +0 -1
- package/dist/compliance.cjs +0 -343
- package/dist/compliance.cjs.map +0 -1
- package/dist/compliance.d.cts +0 -163
- package/dist/compliance.d.ts +0 -163
- package/dist/compliance.js +0 -335
- package/dist/compliance.js.map +0 -1
- package/dist/errors.cjs +0 -284
- package/dist/errors.cjs.map +0 -1
- package/dist/errors.d.cts +0 -100
- package/dist/errors.d.ts +0 -100
- package/dist/errors.js +0 -262
- package/dist/errors.js.map +0 -1
- package/dist/index-D3KfRXMP.d.cts +0 -319
- package/dist/index-D3KfRXMP.d.ts +0 -319
- package/dist/memory.cjs +0 -121
- package/dist/memory.cjs.map +0 -1
- package/dist/memory.d.cts +0 -29
- package/dist/memory.d.ts +0 -29
- package/dist/memory.js +0 -115
- package/dist/memory.js.map +0 -1
- package/dist/observability.cjs +0 -229
- package/dist/observability.cjs.map +0 -1
- package/dist/observability.d.cts +0 -122
- package/dist/observability.d.ts +0 -122
- package/dist/observability.js +0 -222
- package/dist/observability.js.map +0 -1
- package/dist/stt.cjs +0 -828
- package/dist/stt.cjs.map +0 -1
- package/dist/stt.d.cts +0 -308
- package/dist/stt.d.ts +0 -308
- package/dist/stt.js +0 -815
- package/dist/stt.js.map +0 -1
- package/dist/telephony.errors-BQYr6-vl.d.cts +0 -80
- package/dist/telephony.errors-C0-nScrF.d.ts +0 -80
- package/dist/tts.cjs +0 -429
- package/dist/tts.cjs.map +0 -1
- package/dist/tts.d.cts +0 -151
- package/dist/tts.d.ts +0 -151
- package/dist/tts.js +0 -418
- package/dist/tts.js.map +0 -1
package/dist/index-D3KfRXMP.d.ts
DELETED
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
import * as ai from 'ai';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @voice-kit/core — Type definitions
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* A single word with timing information from an STT provider.
|
|
8
|
-
*/
|
|
9
|
-
interface WordTimestamp {
|
|
10
|
-
word: string;
|
|
11
|
-
startMs: number;
|
|
12
|
-
endMs: number;
|
|
13
|
-
confidence: number;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* The result of a speech-to-text transcription, either streaming partial
|
|
17
|
-
* or final. `isFinal` distinguishes the two.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* for await (const result of stt.transcribeStream(audioIterable)) {
|
|
22
|
-
* if (result.isFinal) console.log('Final:', result.transcript)
|
|
23
|
-
* }
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
interface STTResult {
|
|
27
|
-
/** The transcribed text. May be a partial result if `isFinal` is false. */
|
|
28
|
-
transcript: string;
|
|
29
|
-
/** Whether this is the final result for this utterance. */
|
|
30
|
-
isFinal: boolean;
|
|
31
|
-
/** Confidence score from the provider, 0–1. */
|
|
32
|
-
confidence: number;
|
|
33
|
-
/** BCP-47 language tag, e.g. 'hi-IN', 'en-IN'. */
|
|
34
|
-
language: string;
|
|
35
|
-
/** True if a mid-sentence language switch was detected (e.g. Hinglish). */
|
|
36
|
-
languageSwitchDetected: boolean;
|
|
37
|
-
/** Word-level timestamps if supported by the provider. */
|
|
38
|
-
words?: WordTimestamp[];
|
|
39
|
-
/** Time from audio start to this result being emitted, in ms. */
|
|
40
|
-
latencyMs: number;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Configuration for STT provider instantiation.
|
|
44
|
-
*/
|
|
45
|
-
interface STTConfig {
|
|
46
|
-
/** BCP-47 language code. Defaults to 'en-IN'. */
|
|
47
|
-
language?: string;
|
|
48
|
-
/** Additional languages to detect for code-switching. */
|
|
49
|
-
alternateLanguages?: string[];
|
|
50
|
-
/** API key. Falls back to provider-specific env var if omitted. */
|
|
51
|
-
apiKey?: string;
|
|
52
|
-
/** Custom model name. Provider-specific. */
|
|
53
|
-
model?: string;
|
|
54
|
-
/** Enable word-level timestamps. Default false. */
|
|
55
|
-
wordTimestamps?: boolean;
|
|
56
|
-
/** Enable interim / partial results. Default true. */
|
|
57
|
-
interimResults?: boolean;
|
|
58
|
-
/** Deepgram-specific: smart formatting. Default true. */
|
|
59
|
-
smartFormat?: boolean;
|
|
60
|
-
/** Sarvam-specific: region hint. */
|
|
61
|
-
region?: string;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* The STTProvider interface. Obtained via `createSTT()` — never instantiate
|
|
65
|
-
* provider classes directly.
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```ts
|
|
69
|
-
* const stt = createSTT('deepgram', { language: 'en-IN' })
|
|
70
|
-
* for await (const result of stt.transcribeStream(audioStream)) {
|
|
71
|
-
* console.log(result.transcript)
|
|
72
|
-
* }
|
|
73
|
-
* ```
|
|
74
|
-
*/
|
|
75
|
-
interface STTProvider {
|
|
76
|
-
/** Stream audio in, stream STTResults out. Primary realtime path. */
|
|
77
|
-
transcribeStream(audio: AsyncIterable<Buffer>): AsyncIterable<STTResult>;
|
|
78
|
-
/** Batch transcription for recordings. Returns single final result. */
|
|
79
|
-
transcribeBatch(audio: Buffer): Promise<STTResult>;
|
|
80
|
-
/** Whether this provider supports streaming (all except Whisper). */
|
|
81
|
-
readonly supportsStreaming: boolean;
|
|
82
|
-
/** BCP-47 codes this provider can handle. */
|
|
83
|
-
readonly supportedLanguages: string[];
|
|
84
|
-
/** Human-readable provider name for logging. */
|
|
85
|
-
readonly name: string;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Configuration for TTS provider instantiation.
|
|
89
|
-
*/
|
|
90
|
-
interface TTSConfig {
|
|
91
|
-
/** Voice identifier. Provider-specific. */
|
|
92
|
-
voiceId?: string;
|
|
93
|
-
/** Output sample rate. Defaults to provider native rate. */
|
|
94
|
-
sampleRate?: number;
|
|
95
|
-
/** Speaking speed multiplier. Default 1.0. */
|
|
96
|
-
speed?: number;
|
|
97
|
-
/** Pitch adjustment. Provider-specific. */
|
|
98
|
-
pitch?: number;
|
|
99
|
-
/** API key. Falls back to provider-specific env var if omitted. */
|
|
100
|
-
apiKey?: string;
|
|
101
|
-
/** ElevenLabs-specific: model ID. */
|
|
102
|
-
modelId?: string;
|
|
103
|
-
/** Cartesia-specific: emotion control. */
|
|
104
|
-
emotion?: string;
|
|
105
|
-
/** Sarvam-specific: target language for Indic voices. */
|
|
106
|
-
targetLanguage?: string;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* The TTSProvider interface. Obtained via `createTTS()` — never instantiate
|
|
110
|
-
* provider classes directly.
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* ```ts
|
|
114
|
-
* const tts = createTTS('elevenlabs', { voiceId: 'your-voice-id' })
|
|
115
|
-
* for await (const chunk of tts.synthesizeStream('Hello, how can I help?')) {
|
|
116
|
-
* socket.write(chunk)
|
|
117
|
-
* }
|
|
118
|
-
* ```
|
|
119
|
-
*/
|
|
120
|
-
interface TTSProvider {
|
|
121
|
-
/** Stream synthesis — preferred for realtime. First chunk < 300ms. */
|
|
122
|
-
synthesizeStream(text: string, config?: TTSConfig): AsyncIterable<Buffer>;
|
|
123
|
-
/** Synthesize full audio — for pre-recorded prompts or caching. */
|
|
124
|
-
synthesizeFull(text: string, config?: TTSConfig): Promise<Buffer>;
|
|
125
|
-
/** Native output sample rate of this provider in Hz. */
|
|
126
|
-
readonly outputSampleRate: number;
|
|
127
|
-
/** Native output format before any resampling. */
|
|
128
|
-
readonly outputFormat: 'pcm' | 'mulaw' | 'opus' | 'mp3';
|
|
129
|
-
/** Human-readable provider name for logging. */
|
|
130
|
-
readonly name: string;
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* A frame of audio classified by the VAD engine.
|
|
134
|
-
* Developers subscribe to these events — never to raw VAD API.
|
|
135
|
-
*/
|
|
136
|
-
interface VoiceFrame {
|
|
137
|
-
/** Event type. */
|
|
138
|
-
type: 'speech_start' | 'speech_end' | 'speech';
|
|
139
|
-
/** VAD confidence 0–1. */
|
|
140
|
-
confidence: number;
|
|
141
|
-
/** Raw PCM audio bytes for this frame. */
|
|
142
|
-
audioBuffer: Buffer;
|
|
143
|
-
/** Duration of audio in this frame, in ms. */
|
|
144
|
-
durationMs: number;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Configuration for the VAD engine.
|
|
148
|
-
*/
|
|
149
|
-
interface VADConfig {
|
|
150
|
-
/** Activation threshold 0–1. Default 0.6. */
|
|
151
|
-
threshold?: number;
|
|
152
|
-
/** Consecutive positive frames before speech_start. Default 3. */
|
|
153
|
-
positiveSpeechFrames?: number;
|
|
154
|
-
/** Consecutive negative frames before speech_end. Default 5. */
|
|
155
|
-
negativeSpeechFrames?: number;
|
|
156
|
-
/** Debounce window in ms to prevent rapid flip-flop. Default 150. */
|
|
157
|
-
debounceMs?: number;
|
|
158
|
-
/** Input sample rate. Auto-set by AudioPipeline — do not override. */
|
|
159
|
-
sampleRate?: number;
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Configuration for call memory (LRU-backed sliding window of turns).
|
|
163
|
-
*/
|
|
164
|
-
interface CallMemoryConfig {
|
|
165
|
-
/** Maximum number of turns to retain. Default 20. */
|
|
166
|
-
maxTurns?: number;
|
|
167
|
-
/** Maximum bytes of conversation history to retain. Default 512KB. */
|
|
168
|
-
maxBytes?: number;
|
|
169
|
-
/** TTL for the entire call memory entry in ms. Default 30 minutes. */
|
|
170
|
-
ttlMs?: number;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* In-process LRU-backed call memory. Obtained via `createCallMemory()`.
|
|
174
|
-
*
|
|
175
|
-
* @example
|
|
176
|
-
* ```ts
|
|
177
|
-
* const memory = createCallMemory({ maxTurns: 20 })
|
|
178
|
-
* memory.addTurn(callId, { role: 'user', content: 'Hello' })
|
|
179
|
-
* const history = memory.getTurns(callId)
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
interface CallMemory {
|
|
183
|
-
addTurn(callId: string, message: ai.ModelMessage): void;
|
|
184
|
-
getTurns(callId: string): ai.ModelMessage[];
|
|
185
|
-
clearCall(callId: string): void;
|
|
186
|
-
getTokenEstimate(callId: string): number;
|
|
187
|
-
/** Truncate oldest turns to stay within budget. */
|
|
188
|
-
trimToTokenBudget(callId: string, maxTokens: number): void;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Type of call for TRAI DND classification.
|
|
192
|
-
*/
|
|
193
|
-
type CallPurpose = 'TRANSACTIONAL' | 'PROMOTIONAL' | 'SERVICE' | 'EMERGENCY';
|
|
194
|
-
/**
|
|
195
|
-
* TRAI DNC check parameters.
|
|
196
|
-
*/
|
|
197
|
-
interface DNCCheckParams {
|
|
198
|
-
/** E.164 format phone number, validated via libphonenumber-js. */
|
|
199
|
-
to: string;
|
|
200
|
-
/** Purpose category for TRAI classification. */
|
|
201
|
-
purpose: CallPurpose;
|
|
202
|
-
/** Scheduled call time. Defaults to now. */
|
|
203
|
-
scheduledAt?: Date;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Result of a TRAI DNC check.
|
|
207
|
-
*/
|
|
208
|
-
interface DNCCheckResult {
|
|
209
|
-
/** Whether the call is permitted. */
|
|
210
|
-
allowed: boolean;
|
|
211
|
-
/** Human-readable reason if not allowed. */
|
|
212
|
-
reason?: string;
|
|
213
|
-
/** When this result was fetched (from LRU cache). */
|
|
214
|
-
cachedAt?: Date;
|
|
215
|
-
/** Whether result came from local LRU cache. */
|
|
216
|
-
fromCache: boolean;
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Consent record stored for TRAI compliance.
|
|
220
|
-
*/
|
|
221
|
-
interface ConsentRecord {
|
|
222
|
-
phoneNumber: string;
|
|
223
|
-
consentedAt: Date;
|
|
224
|
-
/** Channel through which consent was obtained. */
|
|
225
|
-
channel: 'voice' | 'sms' | 'web' | 'ivr';
|
|
226
|
-
/** Call purpose consent was given for. */
|
|
227
|
-
purpose: CallPurpose;
|
|
228
|
-
/** Optional reference ID (e.g. recording URL). */
|
|
229
|
-
referenceId?: string;
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* TRAI compliance configuration.
|
|
233
|
-
*/
|
|
234
|
-
interface TRAIConfig {
|
|
235
|
-
/** Disable TRAI checks entirely. Default false. */
|
|
236
|
-
disabled?: boolean;
|
|
237
|
-
/** Calling timezone override. Default 'Asia/Kolkata'. */
|
|
238
|
-
timezone?: string;
|
|
239
|
-
/** Override calling hours start (24h). Default 9. */
|
|
240
|
-
callingHoursStart?: number;
|
|
241
|
-
/** Override calling hours end (24h). Default 21. */
|
|
242
|
-
callingHoursEnd?: number;
|
|
243
|
-
/** Custom DNC API endpoint. Default: mock endpoint (must be replaced in production). */
|
|
244
|
-
dncApiEndpoint?: string;
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Aggregated metrics for a completed or in-progress call.
|
|
248
|
-
*/
|
|
249
|
-
interface CallMetricsSummary {
|
|
250
|
-
callId: string;
|
|
251
|
-
sttFirstByteMs: number[];
|
|
252
|
-
ttsFirstByteMs: number[];
|
|
253
|
-
llmFirstTokenMs: number[];
|
|
254
|
-
turnLatencyMs: number[];
|
|
255
|
-
interruptionCount: number;
|
|
256
|
-
interruptionPositions: number[];
|
|
257
|
-
tokenCost: {
|
|
258
|
-
model: string;
|
|
259
|
-
inputTokens: number;
|
|
260
|
-
outputTokens: number;
|
|
261
|
-
estimatedUsdCost: number;
|
|
262
|
-
}[];
|
|
263
|
-
avgTurnLatencyMs: number;
|
|
264
|
-
p95TurnLatencyMs: number;
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Error severity level.
|
|
268
|
-
*/
|
|
269
|
-
type ErrorSeverity = 'low' | 'medium' | 'high' | 'critical';
|
|
270
|
-
/**
|
|
271
|
-
* Base error context shared by all VoiceKit errors.
|
|
272
|
-
*/
|
|
273
|
-
interface VoiceKitErrorContext {
|
|
274
|
-
/** Error code for programmatic handling. */
|
|
275
|
-
code: string;
|
|
276
|
-
/** Associated call ID if applicable. */
|
|
277
|
-
callId?: string;
|
|
278
|
-
/** The provider that threw (e.g. 'deepgram', 'elevenlabs'). */
|
|
279
|
-
provider?: string;
|
|
280
|
-
/** Whether this error is safe to retry. */
|
|
281
|
-
retryable: boolean;
|
|
282
|
-
/** Severity for alerting/logging. */
|
|
283
|
-
severity: ErrorSeverity;
|
|
284
|
-
/** Original upstream error if wrapping. */
|
|
285
|
-
cause?: unknown;
|
|
286
|
-
}
|
|
287
|
-
/**
|
|
288
|
-
* Create an STT provider instance. This is the ONLY public API for STT.
|
|
289
|
-
* Never instantiate provider classes directly.
|
|
290
|
-
*
|
|
291
|
-
* @example
|
|
292
|
-
* ```ts
|
|
293
|
-
* const stt = createSTT('deepgram', { language: 'hi-IN' })
|
|
294
|
-
* const stt2 = createSTT('sarvam', { language: 'ta-IN' })
|
|
295
|
-
* ```
|
|
296
|
-
*/
|
|
297
|
-
declare function createSTT(provider: 'deepgram' | 'whisper' | 'assemblyai' | 'sarvam', config?: STTConfig): STTProvider;
|
|
298
|
-
/**
|
|
299
|
-
* Create a TTS provider instance. This is the ONLY public API for TTS.
|
|
300
|
-
* Never instantiate provider classes directly.
|
|
301
|
-
*
|
|
302
|
-
* @example
|
|
303
|
-
* ```ts
|
|
304
|
-
* const tts = createTTS('elevenlabs', { voiceId: 'your-voice-id' })
|
|
305
|
-
* const tts2 = createTTS('sarvam', { targetLanguage: 'hi-IN' })
|
|
306
|
-
* ```
|
|
307
|
-
*/
|
|
308
|
-
declare function createTTS(provider: 'elevenlabs' | 'cartesia' | 'sarvam', config?: TTSConfig): TTSProvider;
|
|
309
|
-
/**
|
|
310
|
-
* Create an LRU-backed call memory instance.
|
|
311
|
-
*
|
|
312
|
-
* @example
|
|
313
|
-
* ```ts
|
|
314
|
-
* const memory = createCallMemory({ maxTurns: 20, maxBytes: 512_000 })
|
|
315
|
-
* ```
|
|
316
|
-
*/
|
|
317
|
-
declare function createCallMemory(config?: CallMemoryConfig): CallMemory;
|
|
318
|
-
|
|
319
|
-
export { type CallMemoryConfig as C, type DNCCheckParams as D, type ErrorSeverity as E, type STTProvider as S, type TRAIConfig as T, type VADConfig as V, type WordTimestamp as W, type CallMemory as a, type DNCCheckResult as b, type ConsentRecord as c, type CallMetricsSummary as d, type TTSProvider as e, type TTSConfig as f, type VoiceFrame as g, type STTConfig as h, type STTResult as i, type CallPurpose as j, type VoiceKitErrorContext as k, createCallMemory as l, createSTT as m, createTTS as n };
|
package/dist/memory.cjs
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var lruCache = require('lru-cache');
|
|
4
|
-
var pino = require('pino');
|
|
5
|
-
|
|
6
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
-
|
|
8
|
-
var pino__default = /*#__PURE__*/_interopDefault(pino);
|
|
9
|
-
|
|
10
|
-
// src/memory/lru-cache/index.ts
|
|
11
|
-
var logger = pino__default.default({ name: "@voice-kit/core:memory" });
|
|
12
|
-
var DEFAULTS = {
|
|
13
|
-
maxTurns: 20,
|
|
14
|
-
maxBytes: 512e3,
|
|
15
|
-
// 512KB
|
|
16
|
-
ttlMs: 30 * 6e4
|
|
17
|
-
// 30 minutes
|
|
18
|
-
};
|
|
19
|
-
function estimateTokens(messages) {
|
|
20
|
-
let chars = 0;
|
|
21
|
-
for (const msg of messages) {
|
|
22
|
-
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
23
|
-
chars += content.length;
|
|
24
|
-
}
|
|
25
|
-
return Math.ceil(chars / 4);
|
|
26
|
-
}
|
|
27
|
-
function estimateBytes(messages) {
|
|
28
|
-
return JSON.stringify(messages).length;
|
|
29
|
-
}
|
|
30
|
-
var LRUCallMemory = class {
|
|
31
|
-
cache;
|
|
32
|
-
config;
|
|
33
|
-
constructor(config) {
|
|
34
|
-
this.config = config;
|
|
35
|
-
this.cache = new lruCache.LRUCache({
|
|
36
|
-
max: 1e3,
|
|
37
|
-
// max concurrent calls in memory
|
|
38
|
-
ttl: config.ttlMs,
|
|
39
|
-
updateAgeOnGet: true
|
|
40
|
-
// reset TTL on access (active calls stay warm)
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Add a turn to the call's conversation window.
|
|
45
|
-
* Automatically trims oldest turns when maxTurns or maxBytes is exceeded.
|
|
46
|
-
*
|
|
47
|
-
* @param callId The call identifier
|
|
48
|
-
* @param message ModelMessage to append
|
|
49
|
-
*/
|
|
50
|
-
addTurn(callId, message) {
|
|
51
|
-
const existing = this.cache.get(callId) ?? [];
|
|
52
|
-
const updated = [...existing, message];
|
|
53
|
-
const trimmed = updated.length > this.config.maxTurns ? updated.slice(updated.length - this.config.maxTurns) : updated;
|
|
54
|
-
let bytesTrimmed = trimmed;
|
|
55
|
-
while (bytesTrimmed.length > 1 && estimateBytes(bytesTrimmed) > this.config.maxBytes) {
|
|
56
|
-
bytesTrimmed = bytesTrimmed.slice(1);
|
|
57
|
-
}
|
|
58
|
-
this.cache.set(callId, bytesTrimmed);
|
|
59
|
-
logger.debug(
|
|
60
|
-
{ callId, turns: bytesTrimmed.length, bytes: estimateBytes(bytesTrimmed) },
|
|
61
|
-
"Memory: turn added"
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Get all turns for a call.
|
|
66
|
-
*
|
|
67
|
-
* @param callId The call identifier
|
|
68
|
-
* @returns Array of ModelMessage (empty if call not found)
|
|
69
|
-
*/
|
|
70
|
-
getTurns(callId) {
|
|
71
|
-
return this.cache.get(callId) ?? [];
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Clear all turns for a call. Call this on call.ended to free memory.
|
|
75
|
-
*
|
|
76
|
-
* @param callId The call identifier
|
|
77
|
-
*/
|
|
78
|
-
clearCall(callId) {
|
|
79
|
-
this.cache.delete(callId);
|
|
80
|
-
logger.debug({ callId }, "Memory: call cleared");
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Estimate the number of LLM tokens used by a call's history.
|
|
84
|
-
*
|
|
85
|
-
* @param callId The call identifier
|
|
86
|
-
*/
|
|
87
|
-
getTokenEstimate(callId) {
|
|
88
|
-
const messages = this.cache.get(callId) ?? [];
|
|
89
|
-
return estimateTokens(messages);
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Trim oldest turns to stay within a token budget.
|
|
93
|
-
* Called by VoiceAgent before each LLM call to prevent context overflow.
|
|
94
|
-
*
|
|
95
|
-
* @param callId The call identifier
|
|
96
|
-
* @param maxTokens Maximum tokens to retain
|
|
97
|
-
*/
|
|
98
|
-
trimToTokenBudget(callId, maxTokens) {
|
|
99
|
-
let messages = this.cache.get(callId) ?? [];
|
|
100
|
-
while (messages.length > 1 && estimateTokens(messages) > maxTokens) {
|
|
101
|
-
messages = messages.slice(1);
|
|
102
|
-
}
|
|
103
|
-
this.cache.set(callId, messages);
|
|
104
|
-
logger.debug(
|
|
105
|
-
{ callId, turns: messages.length, estimatedTokens: estimateTokens(messages) },
|
|
106
|
-
"Memory: trimmed to token budget"
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
function createCallMemory(config) {
|
|
111
|
-
const merged = {
|
|
112
|
-
maxTurns: config?.maxTurns ?? DEFAULTS.maxTurns,
|
|
113
|
-
maxBytes: config?.maxBytes ?? DEFAULTS.maxBytes,
|
|
114
|
-
ttlMs: config?.ttlMs ?? DEFAULTS.ttlMs
|
|
115
|
-
};
|
|
116
|
-
return new LRUCallMemory(merged);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
exports.createCallMemory = createCallMemory;
|
|
120
|
-
//# sourceMappingURL=memory.cjs.map
|
|
121
|
-
//# sourceMappingURL=memory.cjs.map
|
package/dist/memory.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/memory/lru-cache/index.ts"],"names":["pino","LRUCache"],"mappings":"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","file":"memory.cjs","sourcesContent":["/**\r\n * @voice-kit/core — LRU-backed call memory\r\n *\r\n * Provides a sliding window of conversation turns per call.\r\n * Uses lru-cache for bounded in-process storage — no Redis, no DB.\r\n * Every cache has explicit max size and TTL to prevent unbounded growth.\r\n */\r\n\r\nimport { LRUCache } from 'lru-cache'\r\nimport type { ModelMessage } from 'ai'\r\nimport type { CallMemory, CallMemoryConfig } from '../../types'\r\nimport pino from 'pino'\r\n\r\nconst logger = pino({ name: '@voice-kit/core:memory' })\r\n\r\nconst DEFAULTS: Required<CallMemoryConfig> = {\r\n maxTurns: 20,\r\n maxBytes: 512_000, // 512KB\r\n ttlMs: 30 * 60_000, // 30 minutes\r\n}\r\n\r\n/** Rough token estimate: 1 token ≈ 4 chars */\r\nfunction estimateTokens(messages: ModelMessage[]): number {\r\n let chars = 0\r\n for (const msg of messages) {\r\n const content = typeof msg.content === 'string'\r\n ? msg.content\r\n : JSON.stringify(msg.content)\r\n chars += content.length\r\n }\r\n return Math.ceil(chars / 4)\r\n}\r\n\r\n/** Rough byte size of messages array */\r\nfunction estimateBytes(messages: ModelMessage[]): number {\r\n return JSON.stringify(messages).length\r\n}\r\n\r\n/**\r\n * LRU-backed call memory. Maintains a sliding window of ModelMessage[]\r\n * per callId with automatic eviction.\r\n *\r\n * @internal — obtained via createCallMemory()\r\n */\r\nclass LRUCallMemory implements CallMemory {\r\n private readonly cache: LRUCache<string, ModelMessage[]>\r\n private readonly config: Required<CallMemoryConfig>\r\n\r\n constructor(config: Required<CallMemoryConfig>) {\r\n this.config = config\r\n this.cache = new LRUCache<string, ModelMessage[]>({\r\n max: 1_000, // max concurrent calls in memory\r\n ttl: config.ttlMs,\r\n updateAgeOnGet: true, // reset TTL on access (active calls stay warm)\r\n })\r\n }\r\n\r\n /**\r\n * Add a turn to the call's conversation window.\r\n * Automatically trims oldest turns when maxTurns or maxBytes is exceeded.\r\n *\r\n * @param callId The call identifier\r\n * @param message ModelMessage to append\r\n */\r\n addTurn(callId: string, message: ModelMessage): void {\r\n const existing = this.cache.get(callId) ?? []\r\n const updated = [...existing, message]\r\n\r\n // Trim to maxTurns\r\n const trimmed = updated.length > this.config.maxTurns\r\n ? updated.slice(updated.length - this.config.maxTurns)\r\n : updated\r\n\r\n // Trim to maxBytes\r\n let bytesTrimmed = trimmed\r\n while (bytesTrimmed.length > 1 && estimateBytes(bytesTrimmed) > this.config.maxBytes) {\r\n bytesTrimmed = bytesTrimmed.slice(1) // Remove oldest turn\r\n }\r\n\r\n this.cache.set(callId, bytesTrimmed)\r\n\r\n logger.debug(\r\n { callId, turns: bytesTrimmed.length, bytes: estimateBytes(bytesTrimmed) },\r\n 'Memory: turn added'\r\n )\r\n }\r\n\r\n /**\r\n * Get all turns for a call.\r\n *\r\n * @param callId The call identifier\r\n * @returns Array of ModelMessage (empty if call not found)\r\n */\r\n getTurns(callId: string): ModelMessage[] {\r\n return this.cache.get(callId) ?? []\r\n }\r\n\r\n /**\r\n * Clear all turns for a call. Call this on call.ended to free memory.\r\n *\r\n * @param callId The call identifier\r\n */\r\n clearCall(callId: string): void {\r\n this.cache.delete(callId)\r\n logger.debug({ callId }, 'Memory: call cleared')\r\n }\r\n\r\n /**\r\n * Estimate the number of LLM tokens used by a call's history.\r\n *\r\n * @param callId The call identifier\r\n */\r\n getTokenEstimate(callId: string): number {\r\n const messages = this.cache.get(callId) ?? []\r\n return estimateTokens(messages)\r\n }\r\n\r\n /**\r\n * Trim oldest turns to stay within a token budget.\r\n * Called by VoiceAgent before each LLM call to prevent context overflow.\r\n *\r\n * @param callId The call identifier\r\n * @param maxTokens Maximum tokens to retain\r\n */\r\n trimToTokenBudget(callId: string, maxTokens: number): void {\r\n let messages = this.cache.get(callId) ?? []\r\n\r\n while (messages.length > 1 && estimateTokens(messages) > maxTokens) {\r\n messages = messages.slice(1)\r\n }\r\n\r\n this.cache.set(callId, messages)\r\n\r\n logger.debug(\r\n { callId, turns: messages.length, estimatedTokens: estimateTokens(messages) },\r\n 'Memory: trimmed to token budget'\r\n )\r\n }\r\n}\r\n\r\n/**\r\n * Create an LRU-backed call memory instance.\r\n * This is the ONLY in-process memory system in the SDK.\r\n *\r\n * @param config Memory configuration\r\n *\r\n * @example\r\n * ```ts\r\n * // Default: 20 turns, 512KB, 30min TTL\r\n * const memory = createCallMemory()\r\n *\r\n * // Custom\r\n * const memory = createCallMemory({ maxTurns: 30, maxBytes: 1_000_000 })\r\n * ```\r\n */\r\nexport function createCallMemory(config?: CallMemoryConfig): CallMemory {\r\n const merged: Required<CallMemoryConfig> = {\r\n maxTurns: config?.maxTurns ?? DEFAULTS.maxTurns,\r\n maxBytes: config?.maxBytes ?? DEFAULTS.maxBytes,\r\n ttlMs: config?.ttlMs ?? DEFAULTS.ttlMs,\r\n }\r\n return new LRUCallMemory(merged)\r\n}"]}
|
package/dist/memory.d.cts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { C as CallMemoryConfig, a as CallMemory } from './index-D3KfRXMP.cjs';
|
|
2
|
-
import 'ai';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @voice-kit/core — LRU-backed call memory
|
|
6
|
-
*
|
|
7
|
-
* Provides a sliding window of conversation turns per call.
|
|
8
|
-
* Uses lru-cache for bounded in-process storage — no Redis, no DB.
|
|
9
|
-
* Every cache has explicit max size and TTL to prevent unbounded growth.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Create an LRU-backed call memory instance.
|
|
14
|
-
* This is the ONLY in-process memory system in the SDK.
|
|
15
|
-
*
|
|
16
|
-
* @param config Memory configuration
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```ts
|
|
20
|
-
* // Default: 20 turns, 512KB, 30min TTL
|
|
21
|
-
* const memory = createCallMemory()
|
|
22
|
-
*
|
|
23
|
-
* // Custom
|
|
24
|
-
* const memory = createCallMemory({ maxTurns: 30, maxBytes: 1_000_000 })
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
declare function createCallMemory(config?: CallMemoryConfig): CallMemory;
|
|
28
|
-
|
|
29
|
-
export { createCallMemory };
|
package/dist/memory.d.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { C as CallMemoryConfig, a as CallMemory } from './index-D3KfRXMP.js';
|
|
2
|
-
import 'ai';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @voice-kit/core — LRU-backed call memory
|
|
6
|
-
*
|
|
7
|
-
* Provides a sliding window of conversation turns per call.
|
|
8
|
-
* Uses lru-cache for bounded in-process storage — no Redis, no DB.
|
|
9
|
-
* Every cache has explicit max size and TTL to prevent unbounded growth.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Create an LRU-backed call memory instance.
|
|
14
|
-
* This is the ONLY in-process memory system in the SDK.
|
|
15
|
-
*
|
|
16
|
-
* @param config Memory configuration
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```ts
|
|
20
|
-
* // Default: 20 turns, 512KB, 30min TTL
|
|
21
|
-
* const memory = createCallMemory()
|
|
22
|
-
*
|
|
23
|
-
* // Custom
|
|
24
|
-
* const memory = createCallMemory({ maxTurns: 30, maxBytes: 1_000_000 })
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
|
-
declare function createCallMemory(config?: CallMemoryConfig): CallMemory;
|
|
28
|
-
|
|
29
|
-
export { createCallMemory };
|
package/dist/memory.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { LRUCache } from 'lru-cache';
|
|
2
|
-
import pino from 'pino';
|
|
3
|
-
|
|
4
|
-
// src/memory/lru-cache/index.ts
|
|
5
|
-
var logger = pino({ name: "@voice-kit/core:memory" });
|
|
6
|
-
var DEFAULTS = {
|
|
7
|
-
maxTurns: 20,
|
|
8
|
-
maxBytes: 512e3,
|
|
9
|
-
// 512KB
|
|
10
|
-
ttlMs: 30 * 6e4
|
|
11
|
-
// 30 minutes
|
|
12
|
-
};
|
|
13
|
-
function estimateTokens(messages) {
|
|
14
|
-
let chars = 0;
|
|
15
|
-
for (const msg of messages) {
|
|
16
|
-
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
17
|
-
chars += content.length;
|
|
18
|
-
}
|
|
19
|
-
return Math.ceil(chars / 4);
|
|
20
|
-
}
|
|
21
|
-
function estimateBytes(messages) {
|
|
22
|
-
return JSON.stringify(messages).length;
|
|
23
|
-
}
|
|
24
|
-
var LRUCallMemory = class {
|
|
25
|
-
cache;
|
|
26
|
-
config;
|
|
27
|
-
constructor(config) {
|
|
28
|
-
this.config = config;
|
|
29
|
-
this.cache = new LRUCache({
|
|
30
|
-
max: 1e3,
|
|
31
|
-
// max concurrent calls in memory
|
|
32
|
-
ttl: config.ttlMs,
|
|
33
|
-
updateAgeOnGet: true
|
|
34
|
-
// reset TTL on access (active calls stay warm)
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Add a turn to the call's conversation window.
|
|
39
|
-
* Automatically trims oldest turns when maxTurns or maxBytes is exceeded.
|
|
40
|
-
*
|
|
41
|
-
* @param callId The call identifier
|
|
42
|
-
* @param message ModelMessage to append
|
|
43
|
-
*/
|
|
44
|
-
addTurn(callId, message) {
|
|
45
|
-
const existing = this.cache.get(callId) ?? [];
|
|
46
|
-
const updated = [...existing, message];
|
|
47
|
-
const trimmed = updated.length > this.config.maxTurns ? updated.slice(updated.length - this.config.maxTurns) : updated;
|
|
48
|
-
let bytesTrimmed = trimmed;
|
|
49
|
-
while (bytesTrimmed.length > 1 && estimateBytes(bytesTrimmed) > this.config.maxBytes) {
|
|
50
|
-
bytesTrimmed = bytesTrimmed.slice(1);
|
|
51
|
-
}
|
|
52
|
-
this.cache.set(callId, bytesTrimmed);
|
|
53
|
-
logger.debug(
|
|
54
|
-
{ callId, turns: bytesTrimmed.length, bytes: estimateBytes(bytesTrimmed) },
|
|
55
|
-
"Memory: turn added"
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Get all turns for a call.
|
|
60
|
-
*
|
|
61
|
-
* @param callId The call identifier
|
|
62
|
-
* @returns Array of ModelMessage (empty if call not found)
|
|
63
|
-
*/
|
|
64
|
-
getTurns(callId) {
|
|
65
|
-
return this.cache.get(callId) ?? [];
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Clear all turns for a call. Call this on call.ended to free memory.
|
|
69
|
-
*
|
|
70
|
-
* @param callId The call identifier
|
|
71
|
-
*/
|
|
72
|
-
clearCall(callId) {
|
|
73
|
-
this.cache.delete(callId);
|
|
74
|
-
logger.debug({ callId }, "Memory: call cleared");
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Estimate the number of LLM tokens used by a call's history.
|
|
78
|
-
*
|
|
79
|
-
* @param callId The call identifier
|
|
80
|
-
*/
|
|
81
|
-
getTokenEstimate(callId) {
|
|
82
|
-
const messages = this.cache.get(callId) ?? [];
|
|
83
|
-
return estimateTokens(messages);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Trim oldest turns to stay within a token budget.
|
|
87
|
-
* Called by VoiceAgent before each LLM call to prevent context overflow.
|
|
88
|
-
*
|
|
89
|
-
* @param callId The call identifier
|
|
90
|
-
* @param maxTokens Maximum tokens to retain
|
|
91
|
-
*/
|
|
92
|
-
trimToTokenBudget(callId, maxTokens) {
|
|
93
|
-
let messages = this.cache.get(callId) ?? [];
|
|
94
|
-
while (messages.length > 1 && estimateTokens(messages) > maxTokens) {
|
|
95
|
-
messages = messages.slice(1);
|
|
96
|
-
}
|
|
97
|
-
this.cache.set(callId, messages);
|
|
98
|
-
logger.debug(
|
|
99
|
-
{ callId, turns: messages.length, estimatedTokens: estimateTokens(messages) },
|
|
100
|
-
"Memory: trimmed to token budget"
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
function createCallMemory(config) {
|
|
105
|
-
const merged = {
|
|
106
|
-
maxTurns: config?.maxTurns ?? DEFAULTS.maxTurns,
|
|
107
|
-
maxBytes: config?.maxBytes ?? DEFAULTS.maxBytes,
|
|
108
|
-
ttlMs: config?.ttlMs ?? DEFAULTS.ttlMs
|
|
109
|
-
};
|
|
110
|
-
return new LRUCallMemory(merged);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
export { createCallMemory };
|
|
114
|
-
//# sourceMappingURL=memory.js.map
|
|
115
|
-
//# sourceMappingURL=memory.js.map
|
package/dist/memory.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/memory/lru-cache/index.ts"],"names":[],"mappings":"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","file":"memory.js","sourcesContent":["/**\r\n * @voice-kit/core — LRU-backed call memory\r\n *\r\n * Provides a sliding window of conversation turns per call.\r\n * Uses lru-cache for bounded in-process storage — no Redis, no DB.\r\n * Every cache has explicit max size and TTL to prevent unbounded growth.\r\n */\r\n\r\nimport { LRUCache } from 'lru-cache'\r\nimport type { ModelMessage } from 'ai'\r\nimport type { CallMemory, CallMemoryConfig } from '../../types'\r\nimport pino from 'pino'\r\n\r\nconst logger = pino({ name: '@voice-kit/core:memory' })\r\n\r\nconst DEFAULTS: Required<CallMemoryConfig> = {\r\n maxTurns: 20,\r\n maxBytes: 512_000, // 512KB\r\n ttlMs: 30 * 60_000, // 30 minutes\r\n}\r\n\r\n/** Rough token estimate: 1 token ≈ 4 chars */\r\nfunction estimateTokens(messages: ModelMessage[]): number {\r\n let chars = 0\r\n for (const msg of messages) {\r\n const content = typeof msg.content === 'string'\r\n ? msg.content\r\n : JSON.stringify(msg.content)\r\n chars += content.length\r\n }\r\n return Math.ceil(chars / 4)\r\n}\r\n\r\n/** Rough byte size of messages array */\r\nfunction estimateBytes(messages: ModelMessage[]): number {\r\n return JSON.stringify(messages).length\r\n}\r\n\r\n/**\r\n * LRU-backed call memory. Maintains a sliding window of ModelMessage[]\r\n * per callId with automatic eviction.\r\n *\r\n * @internal — obtained via createCallMemory()\r\n */\r\nclass LRUCallMemory implements CallMemory {\r\n private readonly cache: LRUCache<string, ModelMessage[]>\r\n private readonly config: Required<CallMemoryConfig>\r\n\r\n constructor(config: Required<CallMemoryConfig>) {\r\n this.config = config\r\n this.cache = new LRUCache<string, ModelMessage[]>({\r\n max: 1_000, // max concurrent calls in memory\r\n ttl: config.ttlMs,\r\n updateAgeOnGet: true, // reset TTL on access (active calls stay warm)\r\n })\r\n }\r\n\r\n /**\r\n * Add a turn to the call's conversation window.\r\n * Automatically trims oldest turns when maxTurns or maxBytes is exceeded.\r\n *\r\n * @param callId The call identifier\r\n * @param message ModelMessage to append\r\n */\r\n addTurn(callId: string, message: ModelMessage): void {\r\n const existing = this.cache.get(callId) ?? []\r\n const updated = [...existing, message]\r\n\r\n // Trim to maxTurns\r\n const trimmed = updated.length > this.config.maxTurns\r\n ? updated.slice(updated.length - this.config.maxTurns)\r\n : updated\r\n\r\n // Trim to maxBytes\r\n let bytesTrimmed = trimmed\r\n while (bytesTrimmed.length > 1 && estimateBytes(bytesTrimmed) > this.config.maxBytes) {\r\n bytesTrimmed = bytesTrimmed.slice(1) // Remove oldest turn\r\n }\r\n\r\n this.cache.set(callId, bytesTrimmed)\r\n\r\n logger.debug(\r\n { callId, turns: bytesTrimmed.length, bytes: estimateBytes(bytesTrimmed) },\r\n 'Memory: turn added'\r\n )\r\n }\r\n\r\n /**\r\n * Get all turns for a call.\r\n *\r\n * @param callId The call identifier\r\n * @returns Array of ModelMessage (empty if call not found)\r\n */\r\n getTurns(callId: string): ModelMessage[] {\r\n return this.cache.get(callId) ?? []\r\n }\r\n\r\n /**\r\n * Clear all turns for a call. Call this on call.ended to free memory.\r\n *\r\n * @param callId The call identifier\r\n */\r\n clearCall(callId: string): void {\r\n this.cache.delete(callId)\r\n logger.debug({ callId }, 'Memory: call cleared')\r\n }\r\n\r\n /**\r\n * Estimate the number of LLM tokens used by a call's history.\r\n *\r\n * @param callId The call identifier\r\n */\r\n getTokenEstimate(callId: string): number {\r\n const messages = this.cache.get(callId) ?? []\r\n return estimateTokens(messages)\r\n }\r\n\r\n /**\r\n * Trim oldest turns to stay within a token budget.\r\n * Called by VoiceAgent before each LLM call to prevent context overflow.\r\n *\r\n * @param callId The call identifier\r\n * @param maxTokens Maximum tokens to retain\r\n */\r\n trimToTokenBudget(callId: string, maxTokens: number): void {\r\n let messages = this.cache.get(callId) ?? []\r\n\r\n while (messages.length > 1 && estimateTokens(messages) > maxTokens) {\r\n messages = messages.slice(1)\r\n }\r\n\r\n this.cache.set(callId, messages)\r\n\r\n logger.debug(\r\n { callId, turns: messages.length, estimatedTokens: estimateTokens(messages) },\r\n 'Memory: trimmed to token budget'\r\n )\r\n }\r\n}\r\n\r\n/**\r\n * Create an LRU-backed call memory instance.\r\n * This is the ONLY in-process memory system in the SDK.\r\n *\r\n * @param config Memory configuration\r\n *\r\n * @example\r\n * ```ts\r\n * // Default: 20 turns, 512KB, 30min TTL\r\n * const memory = createCallMemory()\r\n *\r\n * // Custom\r\n * const memory = createCallMemory({ maxTurns: 30, maxBytes: 1_000_000 })\r\n * ```\r\n */\r\nexport function createCallMemory(config?: CallMemoryConfig): CallMemory {\r\n const merged: Required<CallMemoryConfig> = {\r\n maxTurns: config?.maxTurns ?? DEFAULTS.maxTurns,\r\n maxBytes: config?.maxBytes ?? DEFAULTS.maxBytes,\r\n ttlMs: config?.ttlMs ?? DEFAULTS.ttlMs,\r\n }\r\n return new LRUCallMemory(merged)\r\n}"]}
|