@absolutejs/voice 0.0.22-beta.487 → 0.0.22-beta.489
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/backchannel.d.ts +18 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +152 -0
- package/dist/ivrPlan.d.ts +40 -0
- package/package.json +1 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type VoiceBackchannelCue = {
|
|
2
|
+
audioUrl?: string;
|
|
3
|
+
metadata?: Record<string, unknown>;
|
|
4
|
+
text?: string;
|
|
5
|
+
};
|
|
6
|
+
export type VoiceBackchannelDriverOptions = {
|
|
7
|
+
cues?: ReadonlyArray<VoiceBackchannelCue>;
|
|
8
|
+
cueIntervalMs?: number;
|
|
9
|
+
cueIndex?: (index: number) => number;
|
|
10
|
+
minSpeechMs?: number;
|
|
11
|
+
onCue: (cue: VoiceBackchannelCue) => Promise<void> | void;
|
|
12
|
+
};
|
|
13
|
+
export type VoiceBackchannelDriver = {
|
|
14
|
+
noteSpeech: (timestampMs?: number) => void;
|
|
15
|
+
noteSilence: (timestampMs?: number) => void;
|
|
16
|
+
reset: () => void;
|
|
17
|
+
};
|
|
18
|
+
export declare const createVoiceBackchannelDriver: (options: VoiceBackchannelDriverOptions) => VoiceBackchannelDriver;
|
package/dist/index.d.ts
CHANGED
|
@@ -81,6 +81,10 @@ export { describeVoiceAssistantMode, resolveVoiceAssistantMode, } from "./assist
|
|
|
81
81
|
export type { VoiceAssistantMode, VoiceAssistantModality, VoiceAssistantModeDescriptor, VoiceSemanticVADConfig, } from "./assistantMode";
|
|
82
82
|
export { createPunctuationSemanticTurnDetector, createRegexSemanticTurnDetector, } from "./semanticTurn";
|
|
83
83
|
export { VOICE_WEBHOOK_SIGNATURE_HEADER, VOICE_WEBHOOK_TIMESTAMP_HEADER, extractVoiceWebhookSignatureFromHeaders, signVoiceWebhookBody, verifyVoiceWebhookSignature, } from "./webhookVerification";
|
|
84
|
+
export { createVoiceBackchannelDriver } from "./backchannel";
|
|
85
|
+
export type { VoiceBackchannelCue, VoiceBackchannelDriver, VoiceBackchannelDriverOptions, } from "./backchannel";
|
|
86
|
+
export { createVoiceIVRSession, describeVoiceIVRPlan, evaluateVoiceIVRPlan, } from "./ivrPlan";
|
|
87
|
+
export type { VoiceIVRBranch, VoiceIVRDecision, VoiceIVRInput, VoiceIVRMatch, VoiceIVRPlan, VoiceIVRSession, } from "./ivrPlan";
|
|
84
88
|
export { VOICE_CALLER_MEMORY_KEY, buildVoiceCallerMemoryNamespace, createVoiceCallerMemoryNamespace, summarizeVoiceCallerTranscript, } from "./callerMemory";
|
|
85
89
|
export type { CreateVoiceCallerMemoryNamespaceOptions, SummarizeVoiceCallerTranscriptOptions, VoiceCallerIdentity, VoiceCallerMemoryCompletion, VoiceCallerMemorySnapshot, VoiceCallerMemorySummarizerInput, } from "./callerMemory";
|
|
86
90
|
export { aggregateVoiceTurnLatencySpans, buildOTELSpanId, buildOTELTraceId, buildVoiceOTELPayload, createVoiceOTELHTTPExporter, } from "./otelExporter";
|
package/dist/index.js
CHANGED
|
@@ -35475,6 +35475,154 @@ var extractVoiceWebhookSignatureFromHeaders = (headers) => {
|
|
|
35475
35475
|
timestamp: get(VOICE_WEBHOOK_TIMESTAMP_HEADER)
|
|
35476
35476
|
};
|
|
35477
35477
|
};
|
|
35478
|
+
// src/backchannel.ts
|
|
35479
|
+
var DEFAULT_CUES = [
|
|
35480
|
+
{ text: "mm-hmm" },
|
|
35481
|
+
{ text: "I see" },
|
|
35482
|
+
{ text: "right" },
|
|
35483
|
+
{ text: "go on" }
|
|
35484
|
+
];
|
|
35485
|
+
var createVoiceBackchannelDriver = (options) => {
|
|
35486
|
+
const cues = options.cues ?? DEFAULT_CUES;
|
|
35487
|
+
const minSpeechMs = options.minSpeechMs ?? 2500;
|
|
35488
|
+
const cueIntervalMs = options.cueIntervalMs ?? 2500;
|
|
35489
|
+
const cueIndexFn = options.cueIndex ?? ((index) => index % Math.max(cues.length, 1));
|
|
35490
|
+
let speechStartedAt;
|
|
35491
|
+
let lastCueAt;
|
|
35492
|
+
let cueCount = 0;
|
|
35493
|
+
let firing = false;
|
|
35494
|
+
const tryFire = async (now) => {
|
|
35495
|
+
if (firing || cues.length === 0) {
|
|
35496
|
+
return;
|
|
35497
|
+
}
|
|
35498
|
+
if (speechStartedAt === undefined) {
|
|
35499
|
+
return;
|
|
35500
|
+
}
|
|
35501
|
+
const elapsed = now - speechStartedAt;
|
|
35502
|
+
if (elapsed < minSpeechMs) {
|
|
35503
|
+
return;
|
|
35504
|
+
}
|
|
35505
|
+
if (lastCueAt !== undefined && now - lastCueAt < cueIntervalMs) {
|
|
35506
|
+
return;
|
|
35507
|
+
}
|
|
35508
|
+
const cue = cues[cueIndexFn(cueCount)];
|
|
35509
|
+
if (!cue) {
|
|
35510
|
+
return;
|
|
35511
|
+
}
|
|
35512
|
+
firing = true;
|
|
35513
|
+
try {
|
|
35514
|
+
await options.onCue(cue);
|
|
35515
|
+
} finally {
|
|
35516
|
+
firing = false;
|
|
35517
|
+
lastCueAt = now;
|
|
35518
|
+
cueCount += 1;
|
|
35519
|
+
}
|
|
35520
|
+
};
|
|
35521
|
+
return {
|
|
35522
|
+
noteSpeech: (timestampMs) => {
|
|
35523
|
+
const now = timestampMs ?? Date.now();
|
|
35524
|
+
if (speechStartedAt === undefined) {
|
|
35525
|
+
speechStartedAt = now;
|
|
35526
|
+
}
|
|
35527
|
+
tryFire(now);
|
|
35528
|
+
},
|
|
35529
|
+
noteSilence: (timestampMs) => {
|
|
35530
|
+
const now = timestampMs ?? Date.now();
|
|
35531
|
+
if (lastCueAt !== undefined && now - lastCueAt > cueIntervalMs * 2) {
|
|
35532
|
+
speechStartedAt = undefined;
|
|
35533
|
+
}
|
|
35534
|
+
},
|
|
35535
|
+
reset: () => {
|
|
35536
|
+
speechStartedAt = undefined;
|
|
35537
|
+
lastCueAt = undefined;
|
|
35538
|
+
cueCount = 0;
|
|
35539
|
+
}
|
|
35540
|
+
};
|
|
35541
|
+
};
|
|
35542
|
+
// src/ivrPlan.ts
|
|
35543
|
+
var speechMatchesPattern = (pattern, speech) => {
|
|
35544
|
+
const normalized = speech.toLowerCase().trim();
|
|
35545
|
+
if (pattern instanceof RegExp) {
|
|
35546
|
+
return pattern.test(normalized);
|
|
35547
|
+
}
|
|
35548
|
+
const target = pattern.toLowerCase().trim();
|
|
35549
|
+
if (!target) {
|
|
35550
|
+
return false;
|
|
35551
|
+
}
|
|
35552
|
+
return normalized.includes(target);
|
|
35553
|
+
};
|
|
35554
|
+
var digitsMatchPattern = (pattern, input) => {
|
|
35555
|
+
return input === pattern;
|
|
35556
|
+
};
|
|
35557
|
+
var branchMatches = (branch, input) => {
|
|
35558
|
+
const matcher = branch.match;
|
|
35559
|
+
if (matcher.digit && input.digits) {
|
|
35560
|
+
if (input.digits === matcher.digit) {
|
|
35561
|
+
return true;
|
|
35562
|
+
}
|
|
35563
|
+
}
|
|
35564
|
+
if (matcher.digits && input.digits) {
|
|
35565
|
+
if (digitsMatchPattern(matcher.digits, input.digits)) {
|
|
35566
|
+
return true;
|
|
35567
|
+
}
|
|
35568
|
+
}
|
|
35569
|
+
if (matcher.speech && input.speech) {
|
|
35570
|
+
if (speechMatchesPattern(matcher.speech, input.speech)) {
|
|
35571
|
+
return true;
|
|
35572
|
+
}
|
|
35573
|
+
}
|
|
35574
|
+
return false;
|
|
35575
|
+
};
|
|
35576
|
+
var evaluateVoiceIVRPlan = (plan, input) => {
|
|
35577
|
+
if (!input.digits && !input.speech) {
|
|
35578
|
+
return { reason: "timeout" };
|
|
35579
|
+
}
|
|
35580
|
+
for (const branch of plan.branches) {
|
|
35581
|
+
if (branchMatches(branch, input)) {
|
|
35582
|
+
return { branch, reason: "matched" };
|
|
35583
|
+
}
|
|
35584
|
+
}
|
|
35585
|
+
if (plan.fallbackBranchId) {
|
|
35586
|
+
const fallback = plan.branches.find((branch) => branch.id === plan.fallbackBranchId);
|
|
35587
|
+
if (fallback) {
|
|
35588
|
+
return { branch: fallback, reason: "fallback" };
|
|
35589
|
+
}
|
|
35590
|
+
}
|
|
35591
|
+
return { reason: "no-match" };
|
|
35592
|
+
};
|
|
35593
|
+
var createVoiceIVRSession = (plan) => {
|
|
35594
|
+
const maxAttempts = plan.maxAttempts ?? 3;
|
|
35595
|
+
let attempts = 0;
|
|
35596
|
+
return {
|
|
35597
|
+
attempt: () => attempts,
|
|
35598
|
+
decide: (input) => {
|
|
35599
|
+
attempts += 1;
|
|
35600
|
+
return evaluateVoiceIVRPlan(plan, input);
|
|
35601
|
+
},
|
|
35602
|
+
exhausted: () => attempts >= maxAttempts,
|
|
35603
|
+
reset: () => {
|
|
35604
|
+
attempts = 0;
|
|
35605
|
+
}
|
|
35606
|
+
};
|
|
35607
|
+
};
|
|
35608
|
+
var describeVoiceIVRPlan = (plan) => {
|
|
35609
|
+
const lines = [plan.greeting];
|
|
35610
|
+
for (const branch of plan.branches) {
|
|
35611
|
+
const triggers = [];
|
|
35612
|
+
if (branch.match.digit) {
|
|
35613
|
+
triggers.push(`press ${branch.match.digit}`);
|
|
35614
|
+
}
|
|
35615
|
+
if (branch.match.digits) {
|
|
35616
|
+
triggers.push(`press ${branch.match.digits}`);
|
|
35617
|
+
}
|
|
35618
|
+
if (branch.match.speech) {
|
|
35619
|
+
triggers.push(`say "${branch.match.speech instanceof RegExp ? branch.match.speech.source : branch.match.speech}"`);
|
|
35620
|
+
}
|
|
35621
|
+
lines.push(`- ${branch.label}: ${triggers.join(" or ")}`);
|
|
35622
|
+
}
|
|
35623
|
+
return lines.join(`
|
|
35624
|
+
`);
|
|
35625
|
+
};
|
|
35478
35626
|
// src/callerMemory.ts
|
|
35479
35627
|
var VOICE_CALLER_MEMORY_KEY = "caller-memory-snapshot";
|
|
35480
35628
|
var normalizeIdentifier = (value) => value.trim().replace(/[^a-zA-Z0-9+@._-]/g, "-").toLowerCase();
|
|
@@ -46553,6 +46701,7 @@ export {
|
|
|
46553
46701
|
evaluateVoiceMediaPipelineEvidence,
|
|
46554
46702
|
evaluateVoiceLiveOpsEvidence,
|
|
46555
46703
|
evaluateVoiceLiveOpsControlEvidence,
|
|
46704
|
+
evaluateVoiceIVRPlan,
|
|
46556
46705
|
evaluateVoiceGuardrailPolicy,
|
|
46557
46706
|
evaluateVoiceDataControlEvidence,
|
|
46558
46707
|
evaluateVoiceCompetitiveCoverage,
|
|
@@ -46562,6 +46711,7 @@ export {
|
|
|
46562
46711
|
evaluateVoiceAgentSquadContractEvidence,
|
|
46563
46712
|
encodeTwilioMulawBase64,
|
|
46564
46713
|
encodePcmAsWav,
|
|
46714
|
+
describeVoiceIVRPlan,
|
|
46565
46715
|
describeVoiceAssistantMode,
|
|
46566
46716
|
deliverVoiceTraceEventsToSinks,
|
|
46567
46717
|
deliverVoiceObservabilityExport,
|
|
@@ -46807,6 +46957,7 @@ export {
|
|
|
46807
46957
|
createVoiceIncidentBundleRoutes,
|
|
46808
46958
|
createVoiceInMemoryRealCallProfileRecoveryJobStore,
|
|
46809
46959
|
createVoiceInMemoryMonitorRegistry,
|
|
46960
|
+
createVoiceIVRSession,
|
|
46810
46961
|
createVoiceHubSpotTaskUpdateSink,
|
|
46811
46962
|
createVoiceHubSpotTaskSyncSinks,
|
|
46812
46963
|
createVoiceHubSpotTaskSink,
|
|
@@ -46872,6 +47023,7 @@ export {
|
|
|
46872
47023
|
createVoiceBrowserMediaRoutes,
|
|
46873
47024
|
createVoiceBrowserCallProfileRoutes,
|
|
46874
47025
|
createVoiceBargeInRoutes,
|
|
47026
|
+
createVoiceBackchannelDriver,
|
|
46875
47027
|
createVoiceAuditTrailRoutes,
|
|
46876
47028
|
createVoiceAuditSinkStore,
|
|
46877
47029
|
createVoiceAuditSinkDeliveryWorkerLoop,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type VoiceIVRMatch = {
|
|
2
|
+
digit?: string;
|
|
3
|
+
digits?: string;
|
|
4
|
+
speech?: string | RegExp;
|
|
5
|
+
};
|
|
6
|
+
export type VoiceIVRBranch = {
|
|
7
|
+
assistantId?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
match: VoiceIVRMatch;
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
target?: string;
|
|
14
|
+
};
|
|
15
|
+
export type VoiceIVRPlan = {
|
|
16
|
+
branches: readonly VoiceIVRBranch[];
|
|
17
|
+
fallbackBranchId?: string;
|
|
18
|
+
greeting: string;
|
|
19
|
+
maxAttempts?: number;
|
|
20
|
+
noMatchPrompt?: string;
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
timeoutPrompt?: string;
|
|
23
|
+
};
|
|
24
|
+
export type VoiceIVRInput = {
|
|
25
|
+
digits?: string;
|
|
26
|
+
speech?: string;
|
|
27
|
+
};
|
|
28
|
+
export type VoiceIVRDecision = {
|
|
29
|
+
branch?: VoiceIVRBranch;
|
|
30
|
+
reason: "matched" | "no-match" | "fallback" | "timeout";
|
|
31
|
+
};
|
|
32
|
+
export declare const evaluateVoiceIVRPlan: (plan: VoiceIVRPlan, input: VoiceIVRInput) => VoiceIVRDecision;
|
|
33
|
+
export type VoiceIVRSession = {
|
|
34
|
+
attempt: () => number;
|
|
35
|
+
decide: (input: VoiceIVRInput) => VoiceIVRDecision;
|
|
36
|
+
exhausted: () => boolean;
|
|
37
|
+
reset: () => void;
|
|
38
|
+
};
|
|
39
|
+
export declare const createVoiceIVRSession: (plan: VoiceIVRPlan) => VoiceIVRSession;
|
|
40
|
+
export declare const describeVoiceIVRPlan: (plan: VoiceIVRPlan) => string;
|