@absolutejs/voice 0.0.22-beta.329 → 0.0.22-beta.330
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.d.ts +2 -0
- package/dist/index.js +366 -0
- package/dist/testing/index.js +2551 -39
- package/dist/testing/telephony.d.ts +25 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -109,6 +109,7 @@ export { conditionAudioChunk, resolveAudioConditioningConfig } from './audioCond
|
|
|
109
109
|
export { resolveVoiceRuntimePreset } from './presets';
|
|
110
110
|
export { resolveTurnDetectionConfig, TURN_PROFILE_DEFAULTS } from './turnProfiles';
|
|
111
111
|
export { createVoiceCallReviewFromLiveTelephonyReport, createVoiceCallReviewRecorder, renderVoiceCallReviewHTML, renderVoiceCallReviewMarkdown } from './testing/review';
|
|
112
|
+
export { getDefaultVoiceTelephonyBenchmarkScenarios, runVoiceTelephonyBenchmark, runVoiceTelephonyBenchmarkScenario, runVoiceTelephonyMediaOperationsSmoke, summarizeVoiceTelephonyBenchmark } from './testing/telephony';
|
|
112
113
|
export type { VoiceCampaign, VoiceCampaignAttempt, VoiceCampaignAttemptResultInput, VoiceCampaignAttemptStatus, VoiceCampaignCreateInput, VoiceCampaignDialer, VoiceCampaignDialerInput, VoiceCampaignDialerResult, VoiceCampaignProofOptions, VoiceCampaignProofReport, VoiceCampaignReadinessAssertionInput, VoiceCampaignReadinessAssertionReport, VoiceCampaignReadinessCheck, VoiceCampaignReadinessProofOptions, VoiceCampaignReadinessProofReport, VoiceCampaignRecipient, VoiceCampaignRecipientImportIssue, VoiceCampaignRecipientImportIssueCode, VoiceCampaignRecipientImportOptions, VoiceCampaignRecipientImportResult, VoiceCampaignRecipientImportRow, VoiceCampaignRecipientInput, VoiceCampaignRecipientStatus, VoiceCampaignRecord, VoiceCampaignRoutesOptions, VoiceCampaignRuntime, VoiceCampaignRuntimeOptions, VoiceCampaignRateLimit, VoiceCampaignRetryPolicy, VoiceCampaignSchedule, VoiceCampaignStatus, VoiceCampaignStore, VoiceCampaignSummary, VoiceCampaignTimeWindow, VoiceCampaignTickResult } from './campaign';
|
|
113
114
|
export type { VoiceCampaignDialerProofAssertionInput, VoiceCampaignDialerProofAssertionReport, VoiceCampaignDialerProofCarrierRequest, VoiceCampaignDialerProofOptions, VoiceCampaignDialerProofProvider, VoiceCampaignDialerProofProviderResult, VoiceCampaignDialerProofReport, VoiceCampaignDialerProofStatus, VoicePlivoCampaignDialerOptions, VoiceTelnyxCampaignDialerOptions, VoiceTwilioCampaignDialerOptions } from './campaignDialers';
|
|
114
115
|
export type { VoiceBargeInReport, VoiceBargeInRoutesOptions } from './bargeInRoutes';
|
|
@@ -162,6 +163,7 @@ export type { VoiceOpsWebhookEnvelope, VoiceOpsWebhookEntity, VoiceOpsWebhookLin
|
|
|
162
163
|
export type { VoiceHandoffDelivery, VoiceHandoffDeliveryRecord, VoiceHandoffDeliveryRecordInput, VoiceHandoffFanoutResult, VoiceQueuedHandoffDeliveryOptions, VoiceTwilioRedirectHandoffAdapterOptions, VoiceWebhookHandoffAdapterOptions } from './handoff';
|
|
163
164
|
export type { VoiceHandoffHealthDelivery, VoiceHandoffHealthEvent, VoiceHandoffHealthHTMLHandlerOptions, VoiceHandoffHealthRoutesOptions, VoiceHandoffHealthStatus, VoiceHandoffHealthSummary, VoiceHandoffHealthSummaryOptions } from './handoffHealth';
|
|
164
165
|
export type { StoredVoiceCallReviewArtifact, VoiceCallReviewArtifact, VoiceCallReviewConfig, VoiceCallReviewPostCallSummary, VoiceCallReviewRecorder, VoiceCallReviewRecorderOptions, VoiceCallReviewStore, VoiceCallReviewSummary, VoiceCallReviewTimelineEvent } from './testing/review';
|
|
166
|
+
export type { VoiceTelephonyBenchmarkReport, VoiceTelephonyBenchmarkScenario, VoiceTelephonyBenchmarkScenarioResult, VoiceTelephonyBenchmarkSummary, VoiceTelephonyMediaOperationsSmokeOptions, VoiceTelephonyMediaOperationsSmokeReport } from './testing/telephony';
|
|
165
167
|
export type { StoredVoiceAuditEvent, VoiceAuditActor, VoiceAuditEvent, VoiceAuditEventFilter, VoiceAuditEventStore, VoiceAuditEventType, VoiceAuditLogger, VoiceAuditOutcome, VoiceAuditResource, VoiceHandoffAuditEventInput, VoiceOperatorAuditEventInput, VoiceProviderAuditEventInput, VoiceRetentionAuditEventInput, VoiceToolAuditEventInput } from './audit';
|
|
166
168
|
export type { VoiceAuditTrailOptions, VoiceAuditTrailReport, VoiceAuditTrailRoutesOptions, VoiceAuditTrailSummary } from './auditRoutes';
|
|
167
169
|
export type { VoiceAuditExport } from './auditExport';
|
package/dist/index.js
CHANGED
|
@@ -35510,6 +35510,367 @@ var createVoiceSTTRoutingCorrectionHandler = (mode = "generic") => {
|
|
|
35510
35510
|
}
|
|
35511
35511
|
return createPhraseHintCorrectionHandler();
|
|
35512
35512
|
};
|
|
35513
|
+
// src/testing/telephony.ts
|
|
35514
|
+
var DEFAULT_PCM16_FORMAT = {
|
|
35515
|
+
channels: 1,
|
|
35516
|
+
container: "raw",
|
|
35517
|
+
encoding: "pcm_s16le",
|
|
35518
|
+
sampleRateHz: 16000
|
|
35519
|
+
};
|
|
35520
|
+
var DEFAULT_SCENARIOS = [
|
|
35521
|
+
{
|
|
35522
|
+
expectClear: false,
|
|
35523
|
+
expectMark: true,
|
|
35524
|
+
expectOutboundMedia: true,
|
|
35525
|
+
id: "telephony-turn",
|
|
35526
|
+
title: "Telephony bridge streams assistant audio and mark"
|
|
35527
|
+
},
|
|
35528
|
+
{
|
|
35529
|
+
expectClear: true,
|
|
35530
|
+
expectMark: true,
|
|
35531
|
+
expectOutboundMedia: true,
|
|
35532
|
+
id: "telephony-barge-in",
|
|
35533
|
+
secondInboundDelayMs: 5,
|
|
35534
|
+
title: "Telephony bridge clears queued outbound audio on barge-in"
|
|
35535
|
+
},
|
|
35536
|
+
{
|
|
35537
|
+
expectClear: true,
|
|
35538
|
+
expectMark: true,
|
|
35539
|
+
expectOutboundMedia: true,
|
|
35540
|
+
id: "telephony-streaming",
|
|
35541
|
+
secondInboundDelayMs: 8,
|
|
35542
|
+
title: "Telephony bridge keeps streaming chunks and still clears on re-entry",
|
|
35543
|
+
ttsChunkCount: 3,
|
|
35544
|
+
ttsChunkDelayMs: 4
|
|
35545
|
+
}
|
|
35546
|
+
];
|
|
35547
|
+
var waitFor = async (check, timeoutMs, intervalMs = 5) => {
|
|
35548
|
+
const deadline = Date.now() + timeoutMs;
|
|
35549
|
+
while (Date.now() < deadline) {
|
|
35550
|
+
if (check()) {
|
|
35551
|
+
return true;
|
|
35552
|
+
}
|
|
35553
|
+
await Bun.sleep(intervalMs);
|
|
35554
|
+
}
|
|
35555
|
+
return check();
|
|
35556
|
+
};
|
|
35557
|
+
var toUint8Array2 = (audio) => audio instanceof Uint8Array ? audio : audio instanceof ArrayBuffer ? new Uint8Array(audio) : new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
|
|
35558
|
+
var createFakeSTTAdapter = (inputSpy, sttDelayMs) => ({
|
|
35559
|
+
kind: "stt",
|
|
35560
|
+
open: (_options) => {
|
|
35561
|
+
const listeners = {
|
|
35562
|
+
close: new Set,
|
|
35563
|
+
endOfTurn: new Set,
|
|
35564
|
+
error: new Set,
|
|
35565
|
+
final: new Set,
|
|
35566
|
+
partial: new Set
|
|
35567
|
+
};
|
|
35568
|
+
let delivered = false;
|
|
35569
|
+
return {
|
|
35570
|
+
close: async () => {
|
|
35571
|
+
for (const handler of listeners.close) {
|
|
35572
|
+
handler({ type: "close" });
|
|
35573
|
+
}
|
|
35574
|
+
},
|
|
35575
|
+
on: (event, handler) => {
|
|
35576
|
+
listeners[event].add(handler);
|
|
35577
|
+
return () => {
|
|
35578
|
+
listeners[event].delete(handler);
|
|
35579
|
+
};
|
|
35580
|
+
},
|
|
35581
|
+
send: async (audio) => {
|
|
35582
|
+
inputSpy.push(toUint8Array2(audio));
|
|
35583
|
+
if (delivered) {
|
|
35584
|
+
return;
|
|
35585
|
+
}
|
|
35586
|
+
delivered = true;
|
|
35587
|
+
if (sttDelayMs > 0) {
|
|
35588
|
+
await Bun.sleep(sttDelayMs);
|
|
35589
|
+
}
|
|
35590
|
+
const receivedAt = Date.now();
|
|
35591
|
+
for (const handler of listeners.final) {
|
|
35592
|
+
handler({
|
|
35593
|
+
receivedAt,
|
|
35594
|
+
transcript: {
|
|
35595
|
+
id: "telephony-benchmark-final",
|
|
35596
|
+
isFinal: true,
|
|
35597
|
+
text: "hello from twilio"
|
|
35598
|
+
},
|
|
35599
|
+
type: "final"
|
|
35600
|
+
});
|
|
35601
|
+
}
|
|
35602
|
+
for (const handler of listeners.endOfTurn) {
|
|
35603
|
+
handler({
|
|
35604
|
+
receivedAt,
|
|
35605
|
+
reason: "vendor",
|
|
35606
|
+
type: "endOfTurn"
|
|
35607
|
+
});
|
|
35608
|
+
}
|
|
35609
|
+
}
|
|
35610
|
+
};
|
|
35611
|
+
}
|
|
35612
|
+
});
|
|
35613
|
+
var createFakeTTSAdapter = (chunkCount, chunkDelayMs) => ({
|
|
35614
|
+
kind: "tts",
|
|
35615
|
+
open: () => {
|
|
35616
|
+
const listeners = {
|
|
35617
|
+
audio: new Set,
|
|
35618
|
+
close: new Set,
|
|
35619
|
+
error: new Set
|
|
35620
|
+
};
|
|
35621
|
+
return {
|
|
35622
|
+
close: async () => {
|
|
35623
|
+
for (const handler of listeners.close) {
|
|
35624
|
+
handler({ type: "close" });
|
|
35625
|
+
}
|
|
35626
|
+
},
|
|
35627
|
+
on: (event, handler) => {
|
|
35628
|
+
listeners[event].add(handler);
|
|
35629
|
+
return () => {
|
|
35630
|
+
listeners[event].delete(handler);
|
|
35631
|
+
};
|
|
35632
|
+
},
|
|
35633
|
+
send: async () => {
|
|
35634
|
+
for (let index = 0;index < chunkCount; index += 1) {
|
|
35635
|
+
if (chunkDelayMs > 0) {
|
|
35636
|
+
await Bun.sleep(chunkDelayMs);
|
|
35637
|
+
}
|
|
35638
|
+
const chunk = new Uint8Array(320);
|
|
35639
|
+
for (let byteIndex = 0;byteIndex < chunk.length; byteIndex += 2) {
|
|
35640
|
+
chunk[byteIndex] = 255;
|
|
35641
|
+
chunk[byteIndex + 1] = 31;
|
|
35642
|
+
}
|
|
35643
|
+
for (const handler of listeners.audio) {
|
|
35644
|
+
handler({
|
|
35645
|
+
chunk,
|
|
35646
|
+
format: DEFAULT_PCM16_FORMAT,
|
|
35647
|
+
receivedAt: Date.now(),
|
|
35648
|
+
type: "audio"
|
|
35649
|
+
});
|
|
35650
|
+
}
|
|
35651
|
+
}
|
|
35652
|
+
}
|
|
35653
|
+
};
|
|
35654
|
+
}
|
|
35655
|
+
});
|
|
35656
|
+
var defaultOperationsRecordHref = ({
|
|
35657
|
+
sessionId
|
|
35658
|
+
}) => `/voice-operations/${encodeURIComponent(sessionId)}`;
|
|
35659
|
+
var resolveOperationsRecordHref = (href, input) => typeof href === "function" ? href(input) : href === undefined ? defaultOperationsRecordHref(input) : href;
|
|
35660
|
+
var runVoiceTelephonyMediaOperationsSmoke = async (options = {}) => {
|
|
35661
|
+
const timeoutMs = options.timeoutMs ?? 5000;
|
|
35662
|
+
const sessionId = options.sessionId ?? `telephony-media-ops-${Date.now()}`;
|
|
35663
|
+
const streamSid = options.streamSid ?? "telephony-media-ops-stream";
|
|
35664
|
+
const callSid = options.callSid ?? "telephony-media-ops-call";
|
|
35665
|
+
const scenarioId = options.scenarioId ?? "telephony-media-operations-smoke";
|
|
35666
|
+
const trace = options.store ?? createVoiceMemoryTraceEventStore();
|
|
35667
|
+
const sentEvents = [];
|
|
35668
|
+
const receivedAudio = [];
|
|
35669
|
+
const bridge = createTwilioMediaStreamBridge({
|
|
35670
|
+
close: () => {},
|
|
35671
|
+
send: (data) => {
|
|
35672
|
+
sentEvents.push(JSON.parse(data));
|
|
35673
|
+
}
|
|
35674
|
+
}, {
|
|
35675
|
+
context: {},
|
|
35676
|
+
onComplete: async () => {},
|
|
35677
|
+
onTurn: async () => ({
|
|
35678
|
+
assistantText: "Confirmed. Two way carrier media is visible."
|
|
35679
|
+
}),
|
|
35680
|
+
session: createVoiceMemoryStore(),
|
|
35681
|
+
stt: createFakeSTTAdapter(receivedAudio, 0),
|
|
35682
|
+
trace,
|
|
35683
|
+
tts: createFakeTTSAdapter(1, 0),
|
|
35684
|
+
turnDetection: {
|
|
35685
|
+
transcriptStabilityMs: 0
|
|
35686
|
+
}
|
|
35687
|
+
});
|
|
35688
|
+
const payload = encodeTwilioMulawBase64(new Int16Array([500, -500, 1500, -1500, 2500, -2500]));
|
|
35689
|
+
await bridge.handleMessage({
|
|
35690
|
+
event: "start",
|
|
35691
|
+
start: {
|
|
35692
|
+
callSid,
|
|
35693
|
+
customParameters: {
|
|
35694
|
+
scenarioId,
|
|
35695
|
+
sessionId
|
|
35696
|
+
},
|
|
35697
|
+
streamSid
|
|
35698
|
+
},
|
|
35699
|
+
streamSid
|
|
35700
|
+
});
|
|
35701
|
+
await bridge.handleMessage({
|
|
35702
|
+
event: "media",
|
|
35703
|
+
media: {
|
|
35704
|
+
payload,
|
|
35705
|
+
track: "inbound"
|
|
35706
|
+
},
|
|
35707
|
+
streamSid
|
|
35708
|
+
});
|
|
35709
|
+
await waitFor(() => sentEvents.some((message) => message.event === "media"), timeoutMs);
|
|
35710
|
+
await bridge.handleMessage({
|
|
35711
|
+
event: "media",
|
|
35712
|
+
media: {
|
|
35713
|
+
payload,
|
|
35714
|
+
track: "inbound"
|
|
35715
|
+
},
|
|
35716
|
+
streamSid
|
|
35717
|
+
});
|
|
35718
|
+
await waitFor(() => sentEvents.some((message) => message.event === "clear"), timeoutMs);
|
|
35719
|
+
await bridge.handleMessage({
|
|
35720
|
+
event: "stop",
|
|
35721
|
+
stop: {
|
|
35722
|
+
callSid
|
|
35723
|
+
},
|
|
35724
|
+
streamSid
|
|
35725
|
+
});
|
|
35726
|
+
await bridge.close("telephony-media-operations-smoke-complete");
|
|
35727
|
+
const operationsRecord = await buildVoiceOperationsRecord({
|
|
35728
|
+
sessionId,
|
|
35729
|
+
store: trace
|
|
35730
|
+
});
|
|
35731
|
+
const media = operationsRecord.telephonyMedia;
|
|
35732
|
+
const issues = [
|
|
35733
|
+
media.starts < 1 ? "Missing telephony media start event." : undefined,
|
|
35734
|
+
media.stops < 1 ? "Missing telephony media stop event." : undefined,
|
|
35735
|
+
media.inbound < 2 ? "Expected at least two inbound telephony media events." : undefined,
|
|
35736
|
+
media.outbound < 2 ? "Expected outbound assistant media/control evidence." : undefined,
|
|
35737
|
+
media.media < 3 ? "Expected inbound and outbound telephony media packet evidence." : undefined,
|
|
35738
|
+
media.clears < 1 ? "Missing outbound clear evidence." : undefined,
|
|
35739
|
+
media.audioBytes <= 0 ? "Missing telephony media audio bytes." : undefined,
|
|
35740
|
+
media.carriers.includes("twilio") ? undefined : "Missing Twilio carrier evidence.",
|
|
35741
|
+
media.streamIds.includes(streamSid) ? undefined : "Missing telephony media stream ID evidence."
|
|
35742
|
+
].filter((issue) => typeof issue === "string");
|
|
35743
|
+
return {
|
|
35744
|
+
issues,
|
|
35745
|
+
ok: issues.length === 0,
|
|
35746
|
+
operationsRecord,
|
|
35747
|
+
operationsRecordHref: resolveOperationsRecordHref(options.operationsRecordHref, { sessionId, streamSid }),
|
|
35748
|
+
sentEvents: sentEvents.map((message) => message.event).filter((event) => typeof event === "string"),
|
|
35749
|
+
sessionId,
|
|
35750
|
+
streamSid,
|
|
35751
|
+
telephonyMedia: media
|
|
35752
|
+
};
|
|
35753
|
+
};
|
|
35754
|
+
var getDefaultVoiceTelephonyBenchmarkScenarios = () => DEFAULT_SCENARIOS.map((scenario) => ({ ...scenario }));
|
|
35755
|
+
var runVoiceTelephonyBenchmarkScenario = async (scenario, options = {}) => {
|
|
35756
|
+
const timeoutMs = options.timeoutMs ?? 1000;
|
|
35757
|
+
const sentEvents = [];
|
|
35758
|
+
const receivedAudio = [];
|
|
35759
|
+
const bridge = createTwilioMediaStreamBridge({
|
|
35760
|
+
close: () => {},
|
|
35761
|
+
send: (data) => {
|
|
35762
|
+
sentEvents.push({
|
|
35763
|
+
at: Date.now(),
|
|
35764
|
+
event: JSON.parse(data)
|
|
35765
|
+
});
|
|
35766
|
+
}
|
|
35767
|
+
}, {
|
|
35768
|
+
context: {},
|
|
35769
|
+
onComplete: async () => {},
|
|
35770
|
+
onTurn: async () => ({
|
|
35771
|
+
assistantText: "Copy that."
|
|
35772
|
+
}),
|
|
35773
|
+
session: createVoiceMemoryStore(),
|
|
35774
|
+
stt: createFakeSTTAdapter(receivedAudio, scenario.sttDelayMs ?? 0),
|
|
35775
|
+
tts: createFakeTTSAdapter(scenario.ttsChunkCount ?? 2, scenario.ttsChunkDelayMs ?? 0),
|
|
35776
|
+
turnDetection: {
|
|
35777
|
+
transcriptStabilityMs: 0
|
|
35778
|
+
}
|
|
35779
|
+
});
|
|
35780
|
+
const startedAt = Date.now();
|
|
35781
|
+
let secondInboundAt;
|
|
35782
|
+
try {
|
|
35783
|
+
await bridge.handleMessage({
|
|
35784
|
+
event: "start",
|
|
35785
|
+
start: {
|
|
35786
|
+
callSid: "CA-benchmark",
|
|
35787
|
+
customParameters: {
|
|
35788
|
+
scenarioId: scenario.id,
|
|
35789
|
+
sessionId: `phone-${scenario.id}`
|
|
35790
|
+
},
|
|
35791
|
+
streamSid: "MZ-benchmark"
|
|
35792
|
+
},
|
|
35793
|
+
streamSid: "MZ-benchmark"
|
|
35794
|
+
});
|
|
35795
|
+
await bridge.handleMessage({
|
|
35796
|
+
event: "media",
|
|
35797
|
+
media: {
|
|
35798
|
+
payload: encodeTwilioMulawBase64(new Int16Array([500, -500, 1500, -1500, 2500, -2500])),
|
|
35799
|
+
track: "inbound"
|
|
35800
|
+
},
|
|
35801
|
+
streamSid: "MZ-benchmark"
|
|
35802
|
+
});
|
|
35803
|
+
const sawOutboundMedia = await waitFor(() => sentEvents.some((entry) => entry.event.event === "media"), timeoutMs);
|
|
35804
|
+
if (scenario.expectClear) {
|
|
35805
|
+
if (scenario.secondInboundDelayMs) {
|
|
35806
|
+
await Bun.sleep(scenario.secondInboundDelayMs);
|
|
35807
|
+
}
|
|
35808
|
+
secondInboundAt = Date.now();
|
|
35809
|
+
await bridge.handleMessage({
|
|
35810
|
+
event: "media",
|
|
35811
|
+
media: {
|
|
35812
|
+
payload: encodeTwilioMulawBase64(new Int16Array([200, -200, 200, -200])),
|
|
35813
|
+
track: "inbound"
|
|
35814
|
+
},
|
|
35815
|
+
streamSid: "MZ-benchmark"
|
|
35816
|
+
});
|
|
35817
|
+
}
|
|
35818
|
+
await waitFor(() => (!scenario.expectOutboundMedia || sawOutboundMedia) && (!scenario.expectMark || sentEvents.some((entry) => entry.event.event === "mark")) && (!scenario.expectClear || sentEvents.some((entry) => entry.event.event === "clear")), timeoutMs);
|
|
35819
|
+
} finally {
|
|
35820
|
+
await bridge.close("telephony-benchmark");
|
|
35821
|
+
}
|
|
35822
|
+
const outboundMediaEvents = sentEvents.filter((entry) => entry.event.event === "media");
|
|
35823
|
+
const markEvents = sentEvents.filter((entry) => entry.event.event === "mark");
|
|
35824
|
+
const clearEvents = sentEvents.filter((entry) => entry.event.event === "clear");
|
|
35825
|
+
const firstOutboundMediaAt = outboundMediaEvents[0]?.at;
|
|
35826
|
+
const firstMarkAt = markEvents[0]?.at;
|
|
35827
|
+
const firstClearAt = clearEvents[0]?.at;
|
|
35828
|
+
const passes = (!scenario.expectOutboundMedia || outboundMediaEvents.length > 0) && (!scenario.expectMark || markEvents.length > 0) && (!scenario.expectClear || clearEvents.length > 0);
|
|
35829
|
+
return {
|
|
35830
|
+
clearCount: clearEvents.length,
|
|
35831
|
+
clearLatencyMs: secondInboundAt !== undefined && firstClearAt !== undefined ? firstClearAt - secondInboundAt : undefined,
|
|
35832
|
+
elapsedMs: Date.now() - startedAt,
|
|
35833
|
+
expectClear: scenario.expectClear,
|
|
35834
|
+
expectMark: scenario.expectMark,
|
|
35835
|
+
expectOutboundMedia: scenario.expectOutboundMedia,
|
|
35836
|
+
fixtureId: scenario.id,
|
|
35837
|
+
firstOutboundMediaLatencyMs: firstOutboundMediaAt !== undefined ? firstOutboundMediaAt - startedAt : undefined,
|
|
35838
|
+
markCount: markEvents.length,
|
|
35839
|
+
markLatencyMs: firstMarkAt !== undefined ? firstMarkAt - startedAt : undefined,
|
|
35840
|
+
outboundMediaCount: outboundMediaEvents.length,
|
|
35841
|
+
passes,
|
|
35842
|
+
receivedAudioBytes: receivedAudio.reduce((sum, chunk) => sum + chunk.byteLength, 0),
|
|
35843
|
+
title: scenario.title
|
|
35844
|
+
};
|
|
35845
|
+
};
|
|
35846
|
+
var summarizeVoiceTelephonyBenchmark = (fixtures) => {
|
|
35847
|
+
const scenarioCount = fixtures.length;
|
|
35848
|
+
const firstMediaSamples = fixtures.filter((fixture) => typeof fixture.firstOutboundMediaLatencyMs === "number");
|
|
35849
|
+
const markSamples = fixtures.filter((fixture) => typeof fixture.markLatencyMs === "number");
|
|
35850
|
+
const clearSamples = fixtures.filter((fixture) => typeof fixture.clearLatencyMs === "number");
|
|
35851
|
+
const passCount = fixtures.filter((fixture) => fixture.passes).length;
|
|
35852
|
+
return {
|
|
35853
|
+
averageClearLatencyMs: clearSamples.length > 0 ? clearSamples.reduce((sum, fixture) => sum + fixture.clearLatencyMs, 0) / clearSamples.length : undefined,
|
|
35854
|
+
averageElapsedMs: scenarioCount > 0 ? fixtures.reduce((sum, fixture) => sum + fixture.elapsedMs, 0) / scenarioCount : 0,
|
|
35855
|
+
averageFirstOutboundMediaLatencyMs: firstMediaSamples.length > 0 ? firstMediaSamples.reduce((sum, fixture) => sum + fixture.firstOutboundMediaLatencyMs, 0) / firstMediaSamples.length : undefined,
|
|
35856
|
+
averageMarkLatencyMs: markSamples.length > 0 ? markSamples.reduce((sum, fixture) => sum + fixture.markLatencyMs, 0) / markSamples.length : undefined,
|
|
35857
|
+
passCount,
|
|
35858
|
+
passRate: scenarioCount > 0 ? passCount / scenarioCount : 0,
|
|
35859
|
+
scenarioCount,
|
|
35860
|
+
totalOutboundMediaCount: fixtures.reduce((sum, fixture) => sum + fixture.outboundMediaCount, 0)
|
|
35861
|
+
};
|
|
35862
|
+
};
|
|
35863
|
+
var runVoiceTelephonyBenchmark = async (scenarios = getDefaultVoiceTelephonyBenchmarkScenarios(), options = {}) => {
|
|
35864
|
+
const fixtures = [];
|
|
35865
|
+
for (const scenario of scenarios) {
|
|
35866
|
+
fixtures.push(await runVoiceTelephonyBenchmarkScenario(scenario, options));
|
|
35867
|
+
}
|
|
35868
|
+
return {
|
|
35869
|
+
fixtures,
|
|
35870
|
+
generatedAt: Date.now(),
|
|
35871
|
+
summary: summarizeVoiceTelephonyBenchmark(fixtures)
|
|
35872
|
+
};
|
|
35873
|
+
};
|
|
35513
35874
|
// src/telephony/response.ts
|
|
35514
35875
|
var normalizeWhitespace = (value) => value.replace(/\s+/g, " ").trim();
|
|
35515
35876
|
var DEFAULT_MAX_WORDS = 12;
|
|
@@ -35584,6 +35945,7 @@ export {
|
|
|
35584
35945
|
summarizeVoiceTraceTimeline,
|
|
35585
35946
|
summarizeVoiceTraceSinkDeliveries,
|
|
35586
35947
|
summarizeVoiceTrace,
|
|
35948
|
+
summarizeVoiceTelephonyBenchmark,
|
|
35587
35949
|
summarizeVoiceSessions,
|
|
35588
35950
|
summarizeVoiceSessionReplay,
|
|
35589
35951
|
summarizeVoiceRoutingSessions,
|
|
@@ -35616,6 +35978,9 @@ export {
|
|
|
35616
35978
|
saveVoiceIncidentBundleArtifact,
|
|
35617
35979
|
runVoiceToolContractSuite,
|
|
35618
35980
|
runVoiceToolContract,
|
|
35981
|
+
runVoiceTelephonyMediaOperationsSmoke,
|
|
35982
|
+
runVoiceTelephonyBenchmarkScenario,
|
|
35983
|
+
runVoiceTelephonyBenchmark,
|
|
35619
35984
|
runVoiceSimulationSuite,
|
|
35620
35985
|
runVoiceSessionEvals,
|
|
35621
35986
|
runVoiceScenarioFixtureEvals,
|
|
@@ -35759,6 +36124,7 @@ export {
|
|
|
35759
36124
|
getVoiceCampaignDialerProofStatus,
|
|
35760
36125
|
getLatestVoiceTelephonyMediaReport,
|
|
35761
36126
|
getLatestVoiceBrowserMediaReport,
|
|
36127
|
+
getDefaultVoiceTelephonyBenchmarkScenarios,
|
|
35762
36128
|
formatVoiceProofTrendAge,
|
|
35763
36129
|
filterVoiceTraceEvents,
|
|
35764
36130
|
filterVoiceAuditEvents,
|