@absolutejs/voice 0.0.22-beta.102 → 0.0.22-beta.103
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 +135 -60
- package/dist/liveLatency.d.ts +78 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from '.
|
|
|
11
11
|
export { createVoiceToolIdempotencyKey, createVoiceToolRuntime } from './toolRuntime';
|
|
12
12
|
export { createVoiceToolContract, createVoiceToolContractHTMLHandler, createVoiceToolContractJSONHandler, createVoiceToolContractRoutes, createVoiceToolRuntimeContractDefaults, renderVoiceToolContractHTML, runVoiceToolContractSuite, runVoiceToolContract } from './toolContract';
|
|
13
13
|
export { createVoiceTurnLatencyHTMLHandler, createVoiceTurnLatencyJSONHandler, createVoiceTurnLatencyRoutes, renderVoiceTurnLatencyHTML, summarizeVoiceTurnLatency } from './turnLatency';
|
|
14
|
+
export { createVoiceLiveLatencyRoutes, renderVoiceLiveLatencyHTML, summarizeVoiceLiveLatency } from './liveLatency';
|
|
14
15
|
export { createVoiceTurnQualityHTMLHandler, createVoiceTurnQualityJSONHandler, createVoiceTurnQualityRoutes, renderVoiceTurnQualityHTML, summarizeVoiceTurnQuality } from './turnQuality';
|
|
15
16
|
export { createVoiceOutcomeContractHTMLHandler, createVoiceOutcomeContractJSONHandler, createVoiceOutcomeContractRoutes, renderVoiceOutcomeContractHTML, runVoiceOutcomeContractSuite } from './outcomeContract';
|
|
16
17
|
export { applyVoiceTelephonyOutcome, createMemoryVoiceTelephonyWebhookIdempotencyStore, createVoiceTelephonyOutcomePolicy, createVoiceTelephonyWebhookHandler, createVoiceTelephonyWebhookRoutes, parseVoiceTelephonyWebhookEvent, resolveVoiceTelephonyOutcome, signVoiceTwilioWebhook, verifyVoiceTwilioWebhookSignature, voiceTelephonyOutcomeToRouteResult } from './telephonyOutcome';
|
|
@@ -63,6 +64,7 @@ export type { OpenAIVoiceTTSOptions, OpenAIVoiceTTSVoice } from './openaiTTS';
|
|
|
63
64
|
export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
|
|
64
65
|
export type { VoiceProviderCapabilityDefinition, VoiceProviderCapabilityHandlerOptions, VoiceProviderCapabilityHTMLHandlerOptions, VoiceProviderCapabilityKind, VoiceProviderCapabilityOptions, VoiceProviderCapabilityReport, VoiceProviderCapabilityRoutesOptions, VoiceProviderCapabilitySummary } from './providerCapabilities';
|
|
65
66
|
export type { VoiceTurnLatencyHTMLHandlerOptions, VoiceTurnLatencyItem, VoiceTurnLatencyOptions, VoiceTurnLatencyReport, VoiceTurnLatencyRoutesOptions, VoiceTurnLatencyStage, VoiceTurnLatencyStatus } from './turnLatency';
|
|
67
|
+
export type { VoiceLiveLatencyOptions, VoiceLiveLatencyReport, VoiceLiveLatencyRoutesOptions, VoiceLiveLatencySample, VoiceLiveLatencyStatus } from './liveLatency';
|
|
66
68
|
export type { VoiceTurnQualityHTMLHandlerOptions, VoiceTurnQualityItem, VoiceTurnQualityOptions, VoiceTurnQualityReport, VoiceTurnQualityRoutesOptions, VoiceTurnQualityStatus } from './turnQuality';
|
|
67
69
|
export type { VoiceOutcomeContractDefinition, VoiceOutcomeContractHTMLHandlerOptions, VoiceOutcomeContractIssue, VoiceOutcomeContractOptions, VoiceOutcomeContractReport, VoiceOutcomeContractRoutesOptions, VoiceOutcomeContractStatus, VoiceOutcomeContractSuiteReport } from './outcomeContract';
|
|
68
70
|
export type { VoiceTelephonyOutcomeAction, VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomePolicy, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyOutcomeRouteResult, VoiceTelephonyOutcomeStatusDecision, VoiceTelephonyWebhookDecision, VoiceTelephonyWebhookHandlerOptions, VoiceTelephonyWebhookIdempotencyStore, VoiceTelephonyWebhookParseInput, VoiceTelephonyWebhookProvider, VoiceTelephonyWebhookRoutesOptions, VoiceTelephonyWebhookVerificationResult, StoredVoiceTelephonyWebhookDecision } from './telephonyOutcome';
|
package/dist/index.js
CHANGED
|
@@ -11407,10 +11407,82 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
11407
11407
|
}
|
|
11408
11408
|
return routes;
|
|
11409
11409
|
};
|
|
11410
|
-
// src/
|
|
11410
|
+
// src/liveLatency.ts
|
|
11411
11411
|
import { Elysia as Elysia19 } from "elysia";
|
|
11412
|
-
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
11413
11412
|
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11413
|
+
var percentile = (values, percentileValue) => {
|
|
11414
|
+
if (values.length === 0) {
|
|
11415
|
+
return;
|
|
11416
|
+
}
|
|
11417
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
11418
|
+
const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(percentileValue / 100 * sorted.length) - 1));
|
|
11419
|
+
return Math.round(sorted[index] ?? 0);
|
|
11420
|
+
};
|
|
11421
|
+
var getLatency = (payload) => typeof payload.latencyMs === "number" ? payload.latencyMs : typeof payload.elapsedMs === "number" ? payload.elapsedMs : undefined;
|
|
11422
|
+
var summarizeVoiceLiveLatency = async (options) => {
|
|
11423
|
+
const warnAfterMs = options.warnAfterMs ?? 1800;
|
|
11424
|
+
const failAfterMs = options.failAfterMs ?? 3200;
|
|
11425
|
+
const events = await options.store.list({
|
|
11426
|
+
limit: options.limit ?? 100,
|
|
11427
|
+
type: "client.live_latency"
|
|
11428
|
+
});
|
|
11429
|
+
const recent = events.map((event) => {
|
|
11430
|
+
const latencyMs = getLatency(event.payload);
|
|
11431
|
+
if (latencyMs === undefined) {
|
|
11432
|
+
return;
|
|
11433
|
+
}
|
|
11434
|
+
return {
|
|
11435
|
+
at: event.at,
|
|
11436
|
+
latencyMs,
|
|
11437
|
+
sessionId: event.sessionId,
|
|
11438
|
+
status: typeof event.payload.status === "string" ? event.payload.status : undefined,
|
|
11439
|
+
traceId: event.traceId
|
|
11440
|
+
};
|
|
11441
|
+
}).filter((sample) => Boolean(sample)).sort((left, right) => right.at - left.at);
|
|
11442
|
+
const latencies = recent.map((sample) => sample.latencyMs);
|
|
11443
|
+
const failed = latencies.filter((value) => value > failAfterMs).length;
|
|
11444
|
+
const warnings = latencies.filter((value) => value > warnAfterMs && value <= failAfterMs).length;
|
|
11445
|
+
return {
|
|
11446
|
+
averageLatencyMs: latencies.length > 0 ? Math.round(latencies.reduce((total, value) => total + value, 0) / latencies.length) : undefined,
|
|
11447
|
+
checkedAt: Date.now(),
|
|
11448
|
+
failed,
|
|
11449
|
+
p50LatencyMs: percentile(latencies, 50),
|
|
11450
|
+
p95LatencyMs: percentile(latencies, 95),
|
|
11451
|
+
recent,
|
|
11452
|
+
status: latencies.length === 0 ? "empty" : failed > 0 ? "fail" : warnings > 0 ? "warn" : "pass",
|
|
11453
|
+
total: latencies.length,
|
|
11454
|
+
warnings
|
|
11455
|
+
};
|
|
11456
|
+
};
|
|
11457
|
+
var formatMs3 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
11458
|
+
var renderVoiceLiveLatencyHTML = (report, options = {}) => {
|
|
11459
|
+
const title = options.title ?? "Voice Live Latency";
|
|
11460
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml20(sample.sessionId)}</td><td>${escapeHtml20(formatMs3(sample.latencyMs))}</td><td>${escapeHtml20(sample.status ?? "unknown")}</td><td>${escapeHtml20(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
11461
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(245,158,11,.1));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.warn,.empty{color:#fbbf24}.fail{color:#fca5a5}.metrics{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:18px 0}.metrics article,table{background:#141922;border:1px solid #26313d;border-radius:18px}.metrics article{padding:16px}.metrics span{color:#a8b0b8}.metrics strong{display:block;font-size:2rem;margin-top:.25rem}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #26313d;padding:12px;text-align:left}@media(max-width:760px){main{padding:20px}}</style></head><body><main><section class="hero"><p class="eyebrow">Browser proof</p><h1>${escapeHtml20(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml20(report.status)}">Status: ${escapeHtml20(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml20(formatMs3(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml20(formatMs3(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml20(formatMs3(report.averageLatencyMs))}</strong></article><article><span>Samples</span><strong>${String(report.total)}</strong></article></section></section><table><thead><tr><th>Session</th><th>Latency</th><th>Status</th><th>Measured</th></tr></thead><tbody>${rows || '<tr><td colspan="4">No live latency samples yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
11462
|
+
};
|
|
11463
|
+
var createVoiceLiveLatencyRoutes = (options) => {
|
|
11464
|
+
const path = options.path ?? "/api/live-latency";
|
|
11465
|
+
const htmlPath = options.htmlPath === undefined ? "/live-latency" : options.htmlPath;
|
|
11466
|
+
const routes = new Elysia19({
|
|
11467
|
+
name: options.name ?? "absolutejs-voice-live-latency"
|
|
11468
|
+
}).get(path, () => summarizeVoiceLiveLatency(options));
|
|
11469
|
+
if (htmlPath) {
|
|
11470
|
+
routes.get(htmlPath, async () => {
|
|
11471
|
+
const report = await summarizeVoiceLiveLatency(options);
|
|
11472
|
+
return new Response(renderVoiceLiveLatencyHTML(report, options), {
|
|
11473
|
+
headers: {
|
|
11474
|
+
"content-type": "text/html; charset=utf-8",
|
|
11475
|
+
...options.headers
|
|
11476
|
+
}
|
|
11477
|
+
});
|
|
11478
|
+
});
|
|
11479
|
+
}
|
|
11480
|
+
return routes;
|
|
11481
|
+
};
|
|
11482
|
+
// src/turnQuality.ts
|
|
11483
|
+
import { Elysia as Elysia20 } from "elysia";
|
|
11484
|
+
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
11485
|
+
var escapeHtml21 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11414
11486
|
var getTurnLatencyMs = (turn) => {
|
|
11415
11487
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
11416
11488
|
if (firstTranscriptAt === undefined) {
|
|
@@ -11481,24 +11553,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
11481
11553
|
};
|
|
11482
11554
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
11483
11555
|
const title = options.title ?? "Voice Turn Quality";
|
|
11484
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
11556
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml21(turn.status)}">
|
|
11485
11557
|
<div class="turn-header">
|
|
11486
11558
|
<div>
|
|
11487
|
-
<p class="eyebrow">${
|
|
11488
|
-
<h2>${
|
|
11559
|
+
<p class="eyebrow">${escapeHtml21(turn.sessionId)} \xB7 ${escapeHtml21(turn.turnId)}</p>
|
|
11560
|
+
<h2>${escapeHtml21(turn.text || "Empty turn")}</h2>
|
|
11489
11561
|
</div>
|
|
11490
|
-
<strong>${
|
|
11562
|
+
<strong>${escapeHtml21(turn.status)}</strong>
|
|
11491
11563
|
</div>
|
|
11492
11564
|
<dl>
|
|
11493
|
-
<div><dt>Source</dt><dd>${
|
|
11565
|
+
<div><dt>Source</dt><dd>${escapeHtml21(turn.source ?? "unknown")}</dd></div>
|
|
11494
11566
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
11495
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
11496
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
11567
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml21(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
11568
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml21(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
11497
11569
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
11498
11570
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
11499
11571
|
</dl>
|
|
11500
11572
|
</article>`).join("");
|
|
11501
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
11573
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml21(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(251,191,36,.16),rgba(34,197,94,.1))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.turn-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.unknown{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.turn-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Realtime STT Debugging</p><h1>${escapeHtml21(title)}</h1><div class="summary"><span class="pill ${escapeHtml21(report.status)}">${escapeHtml21(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span><span class="pill">${String(report.sessions)} sessions</span></div></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
11502
11574
|
};
|
|
11503
11575
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
11504
11576
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -11515,7 +11587,7 @@ var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
|
11515
11587
|
var createVoiceTurnQualityRoutes = (options) => {
|
|
11516
11588
|
const path = options.path ?? "/api/turn-quality";
|
|
11517
11589
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
11518
|
-
const routes = new
|
|
11590
|
+
const routes = new Elysia20({
|
|
11519
11591
|
name: options.name ?? "absolutejs-voice-turn-quality"
|
|
11520
11592
|
}).get(path, createVoiceTurnQualityJSONHandler(options));
|
|
11521
11593
|
if (htmlPath) {
|
|
@@ -11524,8 +11596,8 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
11524
11596
|
return routes;
|
|
11525
11597
|
};
|
|
11526
11598
|
// src/outcomeContract.ts
|
|
11527
|
-
import { Elysia as
|
|
11528
|
-
var
|
|
11599
|
+
import { Elysia as Elysia21 } from "elysia";
|
|
11600
|
+
var escapeHtml22 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11529
11601
|
var getPayloadString = (event, key) => typeof event.payload[key] === "string" ? event.payload[key] : undefined;
|
|
11530
11602
|
var toList = async (input) => Array.isArray(input) ? input : await input?.list() ?? [];
|
|
11531
11603
|
var hydrateSessions = async (input) => {
|
|
@@ -11633,9 +11705,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
11633
11705
|
const contracts = report.contracts.map((contract) => `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
11634
11706
|
<div class="contract-header">
|
|
11635
11707
|
<div>
|
|
11636
|
-
<p class="eyebrow">${
|
|
11637
|
-
<h2>${
|
|
11638
|
-
${contract.description ? `<p>${
|
|
11708
|
+
<p class="eyebrow">${escapeHtml22(contract.contractId)}</p>
|
|
11709
|
+
<h2>${escapeHtml22(contract.label ?? contract.contractId)}</h2>
|
|
11710
|
+
${contract.description ? `<p>${escapeHtml22(contract.description)}</p>` : ""}
|
|
11639
11711
|
</div>
|
|
11640
11712
|
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
11641
11713
|
</div>
|
|
@@ -11646,9 +11718,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
11646
11718
|
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
11647
11719
|
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
11648
11720
|
</div>
|
|
11649
|
-
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${
|
|
11721
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml22(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
11650
11722
|
</section>`).join("");
|
|
11651
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
11723
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml22(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(14,165,233,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary,.grid{display:flex;flex-wrap:wrap;gap:10px}.pill,.grid span{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}li{margin:8px 0}@media(max-width:800px){main{padding:18px}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Business Outcome Verification</p><h1>${escapeHtml22(title)}</h1><div class="summary"><span class="pill ${report.status}">${report.status}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No outcome contracts configured.</p></section>'}</main></body></html>`;
|
|
11652
11724
|
};
|
|
11653
11725
|
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
11654
11726
|
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
@@ -11664,7 +11736,7 @@ var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
|
11664
11736
|
var createVoiceOutcomeContractRoutes = (options) => {
|
|
11665
11737
|
const path = options.path ?? "/api/outcome-contracts";
|
|
11666
11738
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
11667
|
-
const routes = new
|
|
11739
|
+
const routes = new Elysia21({
|
|
11668
11740
|
name: options.name ?? "absolutejs-voice-outcome-contracts"
|
|
11669
11741
|
}).get(path, createVoiceOutcomeContractJSONHandler(options));
|
|
11670
11742
|
if (htmlPath) {
|
|
@@ -11673,7 +11745,7 @@ var createVoiceOutcomeContractRoutes = (options) => {
|
|
|
11673
11745
|
return routes;
|
|
11674
11746
|
};
|
|
11675
11747
|
// src/telephonyOutcome.ts
|
|
11676
|
-
import { Elysia as
|
|
11748
|
+
import { Elysia as Elysia22 } from "elysia";
|
|
11677
11749
|
var DEFAULT_COMPLETED_STATUSES = [
|
|
11678
11750
|
"answered",
|
|
11679
11751
|
"completed",
|
|
@@ -12320,7 +12392,7 @@ var createVoiceTelephonyWebhookHandler = (options = {}) => async (input) => {
|
|
|
12320
12392
|
var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
12321
12393
|
const path = options.path ?? "/api/voice/telephony/webhook";
|
|
12322
12394
|
const handler = createVoiceTelephonyWebhookHandler(options);
|
|
12323
|
-
return new
|
|
12395
|
+
return new Elysia22({
|
|
12324
12396
|
name: options.name ?? "absolutejs-voice-telephony-webhooks"
|
|
12325
12397
|
}).post(path, async ({ query, request }) => {
|
|
12326
12398
|
try {
|
|
@@ -14588,7 +14660,7 @@ var createVoiceMemoryStore = () => {
|
|
|
14588
14660
|
return { get, getOrCreate, list, remove, set };
|
|
14589
14661
|
};
|
|
14590
14662
|
// src/opsWebhook.ts
|
|
14591
|
-
import { Elysia as
|
|
14663
|
+
import { Elysia as Elysia23 } from "elysia";
|
|
14592
14664
|
var toHex5 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
14593
14665
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
14594
14666
|
const encoder = new TextEncoder;
|
|
@@ -14718,7 +14790,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
14718
14790
|
};
|
|
14719
14791
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
14720
14792
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
14721
|
-
return new
|
|
14793
|
+
return new Elysia23().post(path, async ({ body, request, set }) => {
|
|
14722
14794
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
14723
14795
|
if (options.signingSecret) {
|
|
14724
14796
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -16447,7 +16519,7 @@ var createVoiceSTTRoutingCorrectionHandler = (mode = "generic") => {
|
|
|
16447
16519
|
};
|
|
16448
16520
|
// src/telephony/twilio.ts
|
|
16449
16521
|
import { Buffer as Buffer3 } from "buffer";
|
|
16450
|
-
import { Elysia as
|
|
16522
|
+
import { Elysia as Elysia24 } from "elysia";
|
|
16451
16523
|
var TWILIO_MULAW_SAMPLE_RATE = 8000;
|
|
16452
16524
|
var VOICE_PCM_SAMPLE_RATE = 16000;
|
|
16453
16525
|
var escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -16477,7 +16549,7 @@ var resolveTwilioStreamParameters = async (parameters, input) => {
|
|
|
16477
16549
|
return parameters;
|
|
16478
16550
|
};
|
|
16479
16551
|
var joinUrlPath = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
16480
|
-
var
|
|
16552
|
+
var escapeHtml23 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16481
16553
|
var getWebhookVerificationUrl = (webhook, input) => {
|
|
16482
16554
|
if (!webhook?.verificationUrl) {
|
|
16483
16555
|
return;
|
|
@@ -16520,23 +16592,23 @@ var buildTwilioVoiceSetupStatus = async (options, input) => {
|
|
|
16520
16592
|
};
|
|
16521
16593
|
var renderTwilioVoiceSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16522
16594
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio setup</p>
|
|
16523
|
-
<h1>${
|
|
16595
|
+
<h1>${escapeHtml23(title)}</h1>
|
|
16524
16596
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
16525
16597
|
<section>
|
|
16526
16598
|
<h2>URLs</h2>
|
|
16527
16599
|
<ul>
|
|
16528
|
-
<li><strong>TwiML:</strong> <code>${
|
|
16529
|
-
<li><strong>Media stream:</strong> <code>${
|
|
16530
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
16600
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml23(status.urls.twiml)}</code></li>
|
|
16601
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml23(status.urls.stream)}</code></li>
|
|
16602
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml23(status.urls.webhook)}</code></li>
|
|
16531
16603
|
</ul>
|
|
16532
16604
|
</section>
|
|
16533
16605
|
<section>
|
|
16534
16606
|
<h2>Signing</h2>
|
|
16535
16607
|
<p>Mode: <code>${status.signing.mode}</code></p>
|
|
16536
|
-
${status.signing.verificationUrl ? `<p>Verification URL: <code>${
|
|
16608
|
+
${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml23(status.signing.verificationUrl)}</code></p>` : ""}
|
|
16537
16609
|
</section>
|
|
16538
|
-
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
16539
|
-
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
16610
|
+
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml23(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
16611
|
+
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml23(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
16540
16612
|
</main>`;
|
|
16541
16613
|
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
16542
16614
|
var createSmokeCheck = (name, status, message, details) => ({
|
|
@@ -16547,20 +16619,20 @@ var createSmokeCheck = (name, status, message, details) => ({
|
|
|
16547
16619
|
});
|
|
16548
16620
|
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16549
16621
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
16550
|
-
<h1>${
|
|
16622
|
+
<h1>${escapeHtml23(title)}</h1>
|
|
16551
16623
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
16552
16624
|
<section>
|
|
16553
16625
|
<h2>Checks</h2>
|
|
16554
16626
|
<ul>
|
|
16555
|
-
${report.checks.map((check) => `<li><strong>${
|
|
16627
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml23(check.name)}</strong>: ${escapeHtml23(check.status)}${check.message ? ` - ${escapeHtml23(check.message)}` : ""}</li>`).join("")}
|
|
16556
16628
|
</ul>
|
|
16557
16629
|
</section>
|
|
16558
16630
|
<section>
|
|
16559
16631
|
<h2>Observed URLs</h2>
|
|
16560
16632
|
<ul>
|
|
16561
|
-
<li><strong>TwiML:</strong> <code>${
|
|
16562
|
-
<li><strong>Stream:</strong> <code>${
|
|
16563
|
-
<li><strong>Webhook:</strong> <code>${
|
|
16633
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml23(report.setup.urls.twiml)}</code></li>
|
|
16634
|
+
<li><strong>Stream:</strong> <code>${escapeHtml23(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
16635
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml23(report.setup.urls.webhook)}</code></li>
|
|
16564
16636
|
</ul>
|
|
16565
16637
|
</section>
|
|
16566
16638
|
</main>`;
|
|
@@ -17020,7 +17092,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
17020
17092
|
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/twilio/smoke";
|
|
17021
17093
|
const bridges = new WeakMap;
|
|
17022
17094
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
17023
|
-
const app = new
|
|
17095
|
+
const app = new Elysia24({
|
|
17024
17096
|
name: options.name ?? "absolutejs-voice-twilio"
|
|
17025
17097
|
}).get(twimlPath, async ({ query, request }) => {
|
|
17026
17098
|
const streamUrl = await resolveTwilioStreamUrl(options, {
|
|
@@ -17156,9 +17228,9 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
17156
17228
|
};
|
|
17157
17229
|
// src/telephony/telnyx.ts
|
|
17158
17230
|
import { Buffer as Buffer4 } from "buffer";
|
|
17159
|
-
import { Elysia as
|
|
17231
|
+
import { Elysia as Elysia25 } from "elysia";
|
|
17160
17232
|
var escapeXml3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17161
|
-
var
|
|
17233
|
+
var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17162
17234
|
var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
17163
17235
|
var resolveRequestOrigin2 = (request) => {
|
|
17164
17236
|
const url = new URL(request.url);
|
|
@@ -17359,21 +17431,21 @@ var buildTelnyxVoiceSetupStatus = async (options, input) => {
|
|
|
17359
17431
|
};
|
|
17360
17432
|
var renderTelnyxSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
17361
17433
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx setup</p>
|
|
17362
|
-
<h1>${
|
|
17434
|
+
<h1>${escapeHtml24(title)}</h1>
|
|
17363
17435
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
17364
17436
|
<ul>
|
|
17365
|
-
<li><strong>TeXML:</strong> <code>${
|
|
17366
|
-
<li><strong>Media stream:</strong> <code>${
|
|
17367
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
17437
|
+
<li><strong>TeXML:</strong> <code>${escapeHtml24(status.urls.texml)}</code></li>
|
|
17438
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml24(status.urls.stream)}</code></li>
|
|
17439
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml24(status.urls.webhook)}</code></li>
|
|
17368
17440
|
</ul>
|
|
17369
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
17370
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
17441
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml24(name)}</code></li>`).join("")}</ul>` : ""}
|
|
17442
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml24(warning)}</li>`).join("")}</ul>` : ""}
|
|
17371
17443
|
</main>`;
|
|
17372
17444
|
var renderTelnyxSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
17373
17445
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx smoke test</p>
|
|
17374
|
-
<h1>${
|
|
17446
|
+
<h1>${escapeHtml24(title)}</h1>
|
|
17375
17447
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
17376
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
17448
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml24(check.name)}</strong>: ${escapeHtml24(check.status)}${check.message ? ` - ${escapeHtml24(check.message)}` : ""}</li>`).join("")}</ul>
|
|
17377
17449
|
</main>`;
|
|
17378
17450
|
var runTelnyxSmokeTest = async (input) => {
|
|
17379
17451
|
const setup = await buildTelnyxVoiceSetupStatus(input.options, input);
|
|
@@ -17467,7 +17539,7 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
17467
17539
|
publicKey: options.webhook?.publicKey,
|
|
17468
17540
|
toleranceSeconds: options.webhook?.toleranceSeconds
|
|
17469
17541
|
}) : undefined);
|
|
17470
|
-
const app = new
|
|
17542
|
+
const app = new Elysia25({
|
|
17471
17543
|
name: options.name ?? "absolutejs-voice-telnyx"
|
|
17472
17544
|
}).get(texmlPath, async ({ query, request }) => {
|
|
17473
17545
|
const streamUrl = await resolveTelnyxStreamUrl(options, {
|
|
@@ -17577,9 +17649,9 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
17577
17649
|
};
|
|
17578
17650
|
// src/telephony/plivo.ts
|
|
17579
17651
|
import { Buffer as Buffer5 } from "buffer";
|
|
17580
|
-
import { Elysia as
|
|
17652
|
+
import { Elysia as Elysia26 } from "elysia";
|
|
17581
17653
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17582
|
-
var
|
|
17654
|
+
var escapeHtml25 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17583
17655
|
var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
17584
17656
|
var resolveRequestOrigin3 = (request) => {
|
|
17585
17657
|
const url = new URL(request.url);
|
|
@@ -17830,21 +17902,21 @@ var buildPlivoVoiceSetupStatus = async (options, input) => {
|
|
|
17830
17902
|
};
|
|
17831
17903
|
var renderPlivoSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
17832
17904
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo setup</p>
|
|
17833
|
-
<h1>${
|
|
17905
|
+
<h1>${escapeHtml25(title)}</h1>
|
|
17834
17906
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
17835
17907
|
<ul>
|
|
17836
|
-
<li><strong>Answer XML:</strong> <code>${
|
|
17837
|
-
<li><strong>Audio stream:</strong> <code>${
|
|
17838
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
17908
|
+
<li><strong>Answer XML:</strong> <code>${escapeHtml25(status.urls.answer)}</code></li>
|
|
17909
|
+
<li><strong>Audio stream:</strong> <code>${escapeHtml25(status.urls.stream)}</code></li>
|
|
17910
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml25(status.urls.webhook)}</code></li>
|
|
17839
17911
|
</ul>
|
|
17840
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
17841
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
17912
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml25(name)}</code></li>`).join("")}</ul>` : ""}
|
|
17913
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml25(warning)}</li>`).join("")}</ul>` : ""}
|
|
17842
17914
|
</main>`;
|
|
17843
17915
|
var renderPlivoSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
17844
17916
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo smoke test</p>
|
|
17845
|
-
<h1>${
|
|
17917
|
+
<h1>${escapeHtml25(title)}</h1>
|
|
17846
17918
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
17847
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
17919
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml25(check.name)}</strong>: ${escapeHtml25(check.status)}${check.message ? ` - ${escapeHtml25(check.message)}` : ""}</li>`).join("")}</ul>
|
|
17848
17920
|
</main>`;
|
|
17849
17921
|
var runPlivoSmokeTest = async (input) => {
|
|
17850
17922
|
const setup = await buildPlivoVoiceSetupStatus(input.options, input);
|
|
@@ -17939,7 +18011,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
17939
18011
|
request: input.request
|
|
17940
18012
|
}) : verificationUrl ?? input.request.url
|
|
17941
18013
|
}) : undefined);
|
|
17942
|
-
const app = new
|
|
18014
|
+
const app = new Elysia26({
|
|
17943
18015
|
name: options.name ?? "absolutejs-voice-plivo"
|
|
17944
18016
|
}).get(answerPath, async ({ query, request }) => {
|
|
17945
18017
|
const streamUrl = await resolvePlivoStreamUrl(options, {
|
|
@@ -18125,6 +18197,7 @@ export {
|
|
|
18125
18197
|
summarizeVoiceOpsTasks,
|
|
18126
18198
|
summarizeVoiceOpsTaskQueue,
|
|
18127
18199
|
summarizeVoiceOpsTaskAnalytics,
|
|
18200
|
+
summarizeVoiceLiveLatency,
|
|
18128
18201
|
summarizeVoiceIntegrationEvents,
|
|
18129
18202
|
summarizeVoiceHandoffHealth,
|
|
18130
18203
|
summarizeVoiceHandoffDeliveries,
|
|
@@ -18177,6 +18250,7 @@ export {
|
|
|
18177
18250
|
renderVoiceProductionReadinessHTML,
|
|
18178
18251
|
renderVoiceOutcomeContractHTML,
|
|
18179
18252
|
renderVoiceOpsConsoleHTML,
|
|
18253
|
+
renderVoiceLiveLatencyHTML,
|
|
18180
18254
|
renderVoiceHandoffHealthHTML,
|
|
18181
18255
|
renderVoiceEvalHTML,
|
|
18182
18256
|
renderVoiceEvalBaselineHTML,
|
|
@@ -18314,6 +18388,7 @@ export {
|
|
|
18314
18388
|
createVoiceMemoryStore,
|
|
18315
18389
|
createVoiceMemoryHandoffDeliveryStore,
|
|
18316
18390
|
createVoiceMemoryAssistantMemoryStore,
|
|
18391
|
+
createVoiceLiveLatencyRoutes,
|
|
18317
18392
|
createVoiceLinearIssueUpdateSink,
|
|
18318
18393
|
createVoiceLinearIssueSyncSinks,
|
|
18319
18394
|
createVoiceLinearIssueSink,
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
2
|
+
import type { VoiceTraceEventStore } from './trace';
|
|
3
|
+
export type VoiceLiveLatencyStatus = 'empty' | 'fail' | 'pass' | 'warn';
|
|
4
|
+
export type VoiceLiveLatencySample = {
|
|
5
|
+
at: number;
|
|
6
|
+
latencyMs: number;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
status?: string;
|
|
9
|
+
traceId?: string;
|
|
10
|
+
};
|
|
11
|
+
export type VoiceLiveLatencyReport = {
|
|
12
|
+
averageLatencyMs?: number;
|
|
13
|
+
checkedAt: number;
|
|
14
|
+
failed: number;
|
|
15
|
+
p50LatencyMs?: number;
|
|
16
|
+
p95LatencyMs?: number;
|
|
17
|
+
recent: VoiceLiveLatencySample[];
|
|
18
|
+
status: VoiceLiveLatencyStatus;
|
|
19
|
+
total: number;
|
|
20
|
+
warnings: number;
|
|
21
|
+
};
|
|
22
|
+
export type VoiceLiveLatencyOptions = {
|
|
23
|
+
failAfterMs?: number;
|
|
24
|
+
limit?: number;
|
|
25
|
+
store: VoiceTraceEventStore;
|
|
26
|
+
warnAfterMs?: number;
|
|
27
|
+
};
|
|
28
|
+
export type VoiceLiveLatencyRoutesOptions = VoiceLiveLatencyOptions & {
|
|
29
|
+
headers?: HeadersInit;
|
|
30
|
+
htmlPath?: false | string;
|
|
31
|
+
name?: string;
|
|
32
|
+
path?: string;
|
|
33
|
+
title?: string;
|
|
34
|
+
};
|
|
35
|
+
export declare const summarizeVoiceLiveLatency: (options: VoiceLiveLatencyOptions) => Promise<VoiceLiveLatencyReport>;
|
|
36
|
+
export declare const renderVoiceLiveLatencyHTML: (report: VoiceLiveLatencyReport, options?: {
|
|
37
|
+
title?: string;
|
|
38
|
+
}) => string;
|
|
39
|
+
export declare const createVoiceLiveLatencyRoutes: (options: VoiceLiveLatencyRoutesOptions) => Elysia<"", {
|
|
40
|
+
decorator: {};
|
|
41
|
+
store: {};
|
|
42
|
+
derive: {};
|
|
43
|
+
resolve: {};
|
|
44
|
+
}, {
|
|
45
|
+
typebox: {};
|
|
46
|
+
error: {};
|
|
47
|
+
}, {
|
|
48
|
+
schema: {};
|
|
49
|
+
standaloneSchema: {};
|
|
50
|
+
macro: {};
|
|
51
|
+
macroFn: {};
|
|
52
|
+
parser: {};
|
|
53
|
+
response: {};
|
|
54
|
+
}, {
|
|
55
|
+
[x: string]: {
|
|
56
|
+
get: {
|
|
57
|
+
body: unknown;
|
|
58
|
+
params: {};
|
|
59
|
+
query: unknown;
|
|
60
|
+
headers: unknown;
|
|
61
|
+
response: {
|
|
62
|
+
200: VoiceLiveLatencyReport;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
}, {
|
|
67
|
+
derive: {};
|
|
68
|
+
resolve: {};
|
|
69
|
+
schema: {};
|
|
70
|
+
standaloneSchema: {};
|
|
71
|
+
response: {};
|
|
72
|
+
}, {
|
|
73
|
+
derive: {};
|
|
74
|
+
resolve: {};
|
|
75
|
+
schema: {};
|
|
76
|
+
standaloneSchema: {};
|
|
77
|
+
response: {};
|
|
78
|
+
}>;
|