@absolutejs/voice 0.0.22-beta.541 → 0.0.22-beta.543
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/core/cachedTTS.d.ts +30 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +89 -12
- package/dist/testing/index.js +8 -4
- package/package.json +1 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { TTSAdapter, TTSAdapterOpenOptions } from './types';
|
|
2
|
+
export type CachedTTSOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* Return a stable cache key for an utterance whose synthesized audio should
|
|
5
|
+
* be rendered once and replayed verbatim on later calls (typically a
|
|
6
|
+
* greeting / fixed prompt), or `null`/`undefined` to synthesize it live every
|
|
7
|
+
* time (dynamic turn replies).
|
|
8
|
+
*
|
|
9
|
+
* The key must encode everything that affects the audio — the text, the
|
|
10
|
+
* voice, the model, and the output format. Because the cache is
|
|
11
|
+
* content-addressed, editing any of those naturally produces a new key, so a
|
|
12
|
+
* stale rendering is never replayed: the first call after a change re-renders
|
|
13
|
+
* (and re-caches) while the old entry is simply orphaned.
|
|
14
|
+
*/
|
|
15
|
+
keyFor: (text: string, openOptions: TTSAdapterOpenOptions) => string | null | undefined;
|
|
16
|
+
/** Max distinct utterances to retain (LRU by insertion). Default 32. */
|
|
17
|
+
maxEntries?: number;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Wrap a TTS adapter so selected utterances are synthesized once and replayed
|
|
21
|
+
* from memory on subsequent `send()`s — eliminating provider latency for fixed
|
|
22
|
+
* prompts like a call greeting. Utterances are selected (and keyed) by
|
|
23
|
+
* `options.keyFor`; everything else passes straight through to the inner
|
|
24
|
+
* adapter, so dynamic replies are unaffected.
|
|
25
|
+
*
|
|
26
|
+
* The cache lives for the lifetime of the wrapper (one per adapter), so it is
|
|
27
|
+
* shared across every session/call the adapter serves. Warm it ahead of the
|
|
28
|
+
* first call by opening a session and `send()`ing the cacheable text once.
|
|
29
|
+
*/
|
|
30
|
+
export declare const createCachedTTS: (inner: TTSAdapter, options: CachedTTSOptions) => TTSAdapter<TTSAdapterOpenOptions>;
|
package/dist/index.d.ts
CHANGED
|
@@ -159,6 +159,7 @@ export { computePcmDurationMs, createVoiceMemoryRecordingStore, createVoiceWavRe
|
|
|
159
159
|
export type { EncodeStereoWavInput, InterleavePcmInput, StoredVoiceRecordingArtifact, VoiceRecordingArtifact, VoiceRecordingChannel, VoiceRecordingEncoder, VoiceRecordingEncoderInput, VoiceRecordingEncoderResult, VoiceRecordingStore, } from "./core/recordingStore";
|
|
160
160
|
export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace, } from "./core/assistantMemory";
|
|
161
161
|
export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, createVoiceProviderOrchestrationProfile, resolveVoiceProviderRoutingPolicyPreset, createVoiceProviderRouter, } from "./core/modelAdapters";
|
|
162
|
+
export { createCachedTTS } from "./core/cachedTTS";
|
|
162
163
|
export { createOpenAIVoiceTTS } from "./core/openaiTTS";
|
|
163
164
|
export { createVoiceProviderHealthHTMLHandler, createVoiceProviderHealthJSONHandler, createVoiceProviderHealthRoutes, renderVoiceProviderHealthHTML, summarizeVoiceProviderHealth, } from "./core/providerHealth";
|
|
164
165
|
export { createVoiceProviderCapabilityHTMLHandler, createVoiceProviderCapabilityJSONHandler, createVoiceProviderCapabilityRoutes, renderVoiceProviderCapabilityHTML, summarizeVoiceProviderCapabilities, } from "./core/providerCapabilities";
|
|
@@ -220,6 +221,7 @@ export type { VoiceSimulationSuiteAssertionInput, VoiceSimulationSuiteAssertionR
|
|
|
220
221
|
export type { VoiceWorkflowContract, VoiceWorkflowContractDefinition, VoiceWorkflowContractField, VoiceWorkflowContractFieldMatch, VoiceWorkflowContractPresetName, VoiceWorkflowContractPresetOptions, VoiceWorkflowContractTracePayload, VoiceWorkflowContractValidation, VoiceWorkflowContractValidationIssue, VoiceWorkflowOutcome, } from "./core/workflowContract";
|
|
221
222
|
export type { VoiceSessionListHTMLHandlerOptions, VoiceSessionListItem, VoiceSessionListOptions, VoiceSessionListRoutesOptions, VoiceSessionListStatus, VoiceProviderFallbackRecoverySummary, VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn, } from "./core/sessionReplay";
|
|
222
223
|
export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderOrchestrationProfile, VoiceProviderOrchestrationProfileOptions, VoiceProviderOrchestrationResolvedSurface, VoiceProviderOrchestrationSurface, VoiceProviderRouterPolicy, VoiceProviderRouterPolicyPreset, VoiceProviderRouterPolicyWeights, VoiceProviderRouterProviderHealth, VoiceProviderRouterProviderProfile, VoiceProviderRouterStrategy, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions, } from "./core/modelAdapters";
|
|
224
|
+
export type { CachedTTSOptions } from "./core/cachedTTS";
|
|
223
225
|
export type { OpenAIVoiceTTSOptions, OpenAIVoiceTTSVoice, } from "./core/openaiTTS";
|
|
224
226
|
export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions, } from "./core/providerHealth";
|
|
225
227
|
export type { VoiceProviderCapabilityDefinition, VoiceProviderCapabilityHandlerOptions, VoiceProviderCapabilityHTMLHandlerOptions, VoiceProviderCapabilityKind, VoiceProviderCapabilityOptions, VoiceProviderCapabilityReport, VoiceProviderCapabilityRoutesOptions, VoiceProviderCapabilitySummary, } from "./core/providerCapabilities";
|
package/dist/index.js
CHANGED
|
@@ -24261,6 +24261,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
|
|
|
24261
24261
|
audioConditioning,
|
|
24262
24262
|
context: options.context,
|
|
24263
24263
|
costTelemetry: options.costTelemetry,
|
|
24264
|
+
greeting: options.greeting,
|
|
24264
24265
|
id: bridgeState.sessionId,
|
|
24265
24266
|
languageStrategy: options.languageStrategy,
|
|
24266
24267
|
lexicon,
|
|
@@ -24277,6 +24278,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
|
|
|
24277
24278
|
tts: options.tts,
|
|
24278
24279
|
turnDetection
|
|
24279
24280
|
});
|
|
24281
|
+
await sessionHandle.connect(voiceSocket);
|
|
24280
24282
|
return sessionHandle;
|
|
24281
24283
|
};
|
|
24282
24284
|
return {
|
|
@@ -24429,12 +24431,14 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
24429
24431
|
});
|
|
24430
24432
|
}).ws(streamPath, {
|
|
24431
24433
|
close: async (ws, _code, reason) => {
|
|
24432
|
-
const
|
|
24433
|
-
bridges.
|
|
24434
|
+
const key = ws.raw ?? ws;
|
|
24435
|
+
const bridge = bridges.get(key);
|
|
24436
|
+
bridges.delete(key);
|
|
24434
24437
|
await bridge?.close(reason);
|
|
24435
24438
|
},
|
|
24436
24439
|
message: async (ws, raw) => {
|
|
24437
|
-
|
|
24440
|
+
const key = ws.raw ?? ws;
|
|
24441
|
+
let bridge = bridges.get(key);
|
|
24438
24442
|
if (!bridge) {
|
|
24439
24443
|
bridge = createTwilioMediaStreamBridge({
|
|
24440
24444
|
close: (code, reason) => {
|
|
@@ -24444,7 +24448,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
24444
24448
|
ws.send(data);
|
|
24445
24449
|
}
|
|
24446
24450
|
}, options);
|
|
24447
|
-
bridges.set(
|
|
24451
|
+
bridges.set(key, bridge);
|
|
24448
24452
|
}
|
|
24449
24453
|
await bridge.handleMessage(raw);
|
|
24450
24454
|
}
|
|
@@ -25088,8 +25092,9 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
25088
25092
|
});
|
|
25089
25093
|
}).ws(streamPath, {
|
|
25090
25094
|
close: async (ws, _code, reason) => {
|
|
25091
|
-
const
|
|
25092
|
-
bridges.
|
|
25095
|
+
const key = ws.raw ?? ws;
|
|
25096
|
+
const bridge = bridges.get(key);
|
|
25097
|
+
bridges.delete(key);
|
|
25093
25098
|
await bridge?.close(reason);
|
|
25094
25099
|
},
|
|
25095
25100
|
message: async (ws, raw) => {
|
|
@@ -25097,7 +25102,8 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
25097
25102
|
ws.close(1011, "Plivo media bridge is not configured.");
|
|
25098
25103
|
return;
|
|
25099
25104
|
}
|
|
25100
|
-
|
|
25105
|
+
const key = ws.raw ?? ws;
|
|
25106
|
+
let bridge = bridges.get(key);
|
|
25101
25107
|
if (!bridge) {
|
|
25102
25108
|
bridge = createPlivoMediaStreamBridge({
|
|
25103
25109
|
close: (code, reason) => {
|
|
@@ -25107,7 +25113,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
25107
25113
|
ws.send(data);
|
|
25108
25114
|
}
|
|
25109
25115
|
}, options.bridge);
|
|
25110
|
-
bridges.set(
|
|
25116
|
+
bridges.set(key, bridge);
|
|
25111
25117
|
}
|
|
25112
25118
|
await bridge.handleMessage(raw);
|
|
25113
25119
|
}
|
|
@@ -25702,8 +25708,9 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
25702
25708
|
});
|
|
25703
25709
|
}).ws(streamPath, {
|
|
25704
25710
|
close: async (ws, _code, reason) => {
|
|
25705
|
-
const
|
|
25706
|
-
bridges.
|
|
25711
|
+
const key = ws.raw ?? ws;
|
|
25712
|
+
const bridge = bridges.get(key);
|
|
25713
|
+
bridges.delete(key);
|
|
25707
25714
|
await bridge?.close(reason);
|
|
25708
25715
|
},
|
|
25709
25716
|
message: async (ws, raw) => {
|
|
@@ -25711,7 +25718,8 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
25711
25718
|
ws.close(1011, "Telnyx media bridge is not configured.");
|
|
25712
25719
|
return;
|
|
25713
25720
|
}
|
|
25714
|
-
|
|
25721
|
+
const key = ws.raw ?? ws;
|
|
25722
|
+
let bridge = bridges.get(key);
|
|
25715
25723
|
if (!bridge) {
|
|
25716
25724
|
bridge = createTelnyxMediaStreamBridge({
|
|
25717
25725
|
close: (code, reason) => {
|
|
@@ -25721,7 +25729,7 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
25721
25729
|
ws.send(data);
|
|
25722
25730
|
}
|
|
25723
25731
|
}, options.bridge);
|
|
25724
|
-
bridges.set(
|
|
25732
|
+
bridges.set(key, bridge);
|
|
25725
25733
|
}
|
|
25726
25734
|
await bridge.handleMessage(raw);
|
|
25727
25735
|
}
|
|
@@ -44990,6 +44998,74 @@ var createGeminiVoiceAssistantModel = (options) => {
|
|
|
44990
44998
|
}
|
|
44991
44999
|
};
|
|
44992
45000
|
};
|
|
45001
|
+
// src/core/cachedTTS.ts
|
|
45002
|
+
var DEFAULT_MAX_ENTRIES = 32;
|
|
45003
|
+
var createCachedTTS = (inner, options) => {
|
|
45004
|
+
const maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
45005
|
+
const cache = new Map;
|
|
45006
|
+
const remember = (key, events) => {
|
|
45007
|
+
cache.delete(key);
|
|
45008
|
+
cache.set(key, events);
|
|
45009
|
+
while (cache.size > maxEntries) {
|
|
45010
|
+
const oldest = cache.keys().next().value;
|
|
45011
|
+
if (oldest === undefined) {
|
|
45012
|
+
break;
|
|
45013
|
+
}
|
|
45014
|
+
cache.delete(oldest);
|
|
45015
|
+
}
|
|
45016
|
+
};
|
|
45017
|
+
return {
|
|
45018
|
+
kind: "tts",
|
|
45019
|
+
open: async (openOptions) => {
|
|
45020
|
+
const session = await inner.open(openOptions);
|
|
45021
|
+
const audioHandlers = new Set;
|
|
45022
|
+
let capture = null;
|
|
45023
|
+
session.on("audio", (event) => {
|
|
45024
|
+
if (capture) {
|
|
45025
|
+
capture.push(event);
|
|
45026
|
+
}
|
|
45027
|
+
});
|
|
45028
|
+
return {
|
|
45029
|
+
cancel: async (reason) => {
|
|
45030
|
+
if (session.cancel) {
|
|
45031
|
+
await session.cancel(reason);
|
|
45032
|
+
}
|
|
45033
|
+
},
|
|
45034
|
+
close: (reason) => session.close(reason),
|
|
45035
|
+
on: (event, handler) => {
|
|
45036
|
+
if (event === "audio") {
|
|
45037
|
+
audioHandlers.add(handler);
|
|
45038
|
+
}
|
|
45039
|
+
return session.on(event, handler);
|
|
45040
|
+
},
|
|
45041
|
+
send: async (text) => {
|
|
45042
|
+
const key = options.keyFor(text, openOptions);
|
|
45043
|
+
if (key === null || key === undefined) {
|
|
45044
|
+
await session.send(text);
|
|
45045
|
+
return;
|
|
45046
|
+
}
|
|
45047
|
+
const cached = cache.get(key);
|
|
45048
|
+
if (cached) {
|
|
45049
|
+
for (const event of cached) {
|
|
45050
|
+
const replay = {
|
|
45051
|
+
...event,
|
|
45052
|
+
receivedAt: Date.now()
|
|
45053
|
+
};
|
|
45054
|
+
for (const handler of audioHandlers) {
|
|
45055
|
+
await Promise.resolve(handler(replay));
|
|
45056
|
+
}
|
|
45057
|
+
}
|
|
45058
|
+
return;
|
|
45059
|
+
}
|
|
45060
|
+
capture = [];
|
|
45061
|
+
await session.send(text);
|
|
45062
|
+
remember(key, capture);
|
|
45063
|
+
capture = null;
|
|
45064
|
+
}
|
|
45065
|
+
};
|
|
45066
|
+
}
|
|
45067
|
+
};
|
|
45068
|
+
};
|
|
44993
45069
|
// src/core/openaiTTS.ts
|
|
44994
45070
|
var OPENAI_PCM24_FORMAT = {
|
|
44995
45071
|
channels: 1,
|
|
@@ -52363,6 +52439,7 @@ export {
|
|
|
52363
52439
|
createDomainPhraseHints,
|
|
52364
52440
|
createDomainLexicon,
|
|
52365
52441
|
createCoturnIceServers,
|
|
52442
|
+
createCachedTTS,
|
|
52366
52443
|
createAnthropicVoiceAssistantModel,
|
|
52367
52444
|
createAIVoiceModel,
|
|
52368
52445
|
conditionAudioChunk,
|
package/dist/testing/index.js
CHANGED
|
@@ -12939,6 +12939,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
|
|
|
12939
12939
|
audioConditioning,
|
|
12940
12940
|
context: options.context,
|
|
12941
12941
|
costTelemetry: options.costTelemetry,
|
|
12942
|
+
greeting: options.greeting,
|
|
12942
12943
|
id: bridgeState.sessionId,
|
|
12943
12944
|
languageStrategy: options.languageStrategy,
|
|
12944
12945
|
lexicon,
|
|
@@ -12955,6 +12956,7 @@ var createTwilioMediaStreamBridge = (socket, options) => {
|
|
|
12955
12956
|
tts: options.tts,
|
|
12956
12957
|
turnDetection
|
|
12957
12958
|
});
|
|
12959
|
+
await sessionHandle.connect(voiceSocket);
|
|
12958
12960
|
return sessionHandle;
|
|
12959
12961
|
};
|
|
12960
12962
|
return {
|
|
@@ -13107,12 +13109,14 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
13107
13109
|
});
|
|
13108
13110
|
}).ws(streamPath, {
|
|
13109
13111
|
close: async (ws, _code, reason) => {
|
|
13110
|
-
const
|
|
13111
|
-
bridges.
|
|
13112
|
+
const key = ws.raw ?? ws;
|
|
13113
|
+
const bridge = bridges.get(key);
|
|
13114
|
+
bridges.delete(key);
|
|
13112
13115
|
await bridge?.close(reason);
|
|
13113
13116
|
},
|
|
13114
13117
|
message: async (ws, raw) => {
|
|
13115
|
-
|
|
13118
|
+
const key = ws.raw ?? ws;
|
|
13119
|
+
let bridge = bridges.get(key);
|
|
13116
13120
|
if (!bridge) {
|
|
13117
13121
|
bridge = createTwilioMediaStreamBridge({
|
|
13118
13122
|
close: (code, reason) => {
|
|
@@ -13122,7 +13126,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
13122
13126
|
ws.send(data);
|
|
13123
13127
|
}
|
|
13124
13128
|
}, options);
|
|
13125
|
-
bridges.set(
|
|
13129
|
+
bridges.set(key, bridge);
|
|
13126
13130
|
}
|
|
13127
13131
|
await bridge.handleMessage(raw);
|
|
13128
13132
|
}
|