@absolutejs/voice 0.0.22-beta.90 → 0.0.22-beta.92
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/angular/index.d.ts +1 -0
- package/dist/angular/index.js +144 -20
- package/dist/angular/voice-trace-timeline.service.d.ts +12 -0
- package/dist/appKit.d.ts +3 -1
- package/dist/bargeInRoutes.d.ts +56 -0
- package/dist/client/bargeInMonitor.d.ts +7 -0
- package/dist/client/duplex.d.ts +1 -1
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +274 -5
- package/dist/client/traceTimeline.d.ts +19 -0
- package/dist/client/traceTimelineWidget.d.ts +32 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +307 -213
- package/dist/react/VoiceTraceTimeline.d.ts +6 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +316 -50
- package/dist/react/useVoiceTraceTimeline.d.ts +8 -0
- package/dist/svelte/createVoiceTraceTimeline.d.ts +10 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +194 -16
- package/dist/testing/index.js +30 -5
- package/dist/trace.d.ts +1 -1
- package/dist/types.d.ts +40 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +119 -6
- package/dist/vue/useVoiceTraceTimeline.d.ts +9 -0
- package/package.json +1 -1
package/dist/svelte/index.js
CHANGED
|
@@ -1580,6 +1580,183 @@ var createVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
|
|
|
1580
1580
|
getViewModel: () => createVoiceRoutingStatusViewModel(store.getSnapshot(), options)
|
|
1581
1581
|
};
|
|
1582
1582
|
};
|
|
1583
|
+
// src/client/traceTimeline.ts
|
|
1584
|
+
var fetchVoiceTraceTimeline = async (path = "/api/voice-traces", options = {}) => {
|
|
1585
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1586
|
+
const response = await fetchImpl(path);
|
|
1587
|
+
if (!response.ok) {
|
|
1588
|
+
throw new Error(`Voice trace timeline failed: HTTP ${response.status}`);
|
|
1589
|
+
}
|
|
1590
|
+
return await response.json();
|
|
1591
|
+
};
|
|
1592
|
+
var createVoiceTraceTimelineStore = (path = "/api/voice-traces", options = {}) => {
|
|
1593
|
+
const listeners = new Set;
|
|
1594
|
+
let closed = false;
|
|
1595
|
+
let timer;
|
|
1596
|
+
let snapshot = {
|
|
1597
|
+
error: null,
|
|
1598
|
+
isLoading: false,
|
|
1599
|
+
report: null
|
|
1600
|
+
};
|
|
1601
|
+
const emit = () => {
|
|
1602
|
+
for (const listener of listeners) {
|
|
1603
|
+
listener();
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
const refresh = async () => {
|
|
1607
|
+
if (closed) {
|
|
1608
|
+
return snapshot.report;
|
|
1609
|
+
}
|
|
1610
|
+
snapshot = {
|
|
1611
|
+
...snapshot,
|
|
1612
|
+
error: null,
|
|
1613
|
+
isLoading: true
|
|
1614
|
+
};
|
|
1615
|
+
emit();
|
|
1616
|
+
try {
|
|
1617
|
+
const report = await fetchVoiceTraceTimeline(path, options);
|
|
1618
|
+
snapshot = {
|
|
1619
|
+
error: null,
|
|
1620
|
+
isLoading: false,
|
|
1621
|
+
report,
|
|
1622
|
+
updatedAt: Date.now()
|
|
1623
|
+
};
|
|
1624
|
+
emit();
|
|
1625
|
+
return report;
|
|
1626
|
+
} catch (error) {
|
|
1627
|
+
snapshot = {
|
|
1628
|
+
...snapshot,
|
|
1629
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1630
|
+
isLoading: false
|
|
1631
|
+
};
|
|
1632
|
+
emit();
|
|
1633
|
+
throw error;
|
|
1634
|
+
}
|
|
1635
|
+
};
|
|
1636
|
+
const close = () => {
|
|
1637
|
+
closed = true;
|
|
1638
|
+
if (timer) {
|
|
1639
|
+
clearInterval(timer);
|
|
1640
|
+
timer = undefined;
|
|
1641
|
+
}
|
|
1642
|
+
listeners.clear();
|
|
1643
|
+
};
|
|
1644
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1645
|
+
timer = setInterval(() => {
|
|
1646
|
+
refresh().catch(() => {});
|
|
1647
|
+
}, options.intervalMs);
|
|
1648
|
+
}
|
|
1649
|
+
return {
|
|
1650
|
+
close,
|
|
1651
|
+
getServerSnapshot: () => snapshot,
|
|
1652
|
+
getSnapshot: () => snapshot,
|
|
1653
|
+
refresh,
|
|
1654
|
+
subscribe: (listener) => {
|
|
1655
|
+
listeners.add(listener);
|
|
1656
|
+
return () => {
|
|
1657
|
+
listeners.delete(listener);
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
};
|
|
1662
|
+
|
|
1663
|
+
// src/client/traceTimelineWidget.ts
|
|
1664
|
+
var DEFAULT_TITLE5 = "Voice Traces";
|
|
1665
|
+
var DEFAULT_DESCRIPTION5 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
|
|
1666
|
+
var escapeHtml6 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1667
|
+
var formatMs = (value) => typeof value === "number" ? `${value}ms` : "n/a";
|
|
1668
|
+
var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
|
|
1669
|
+
var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
1670
|
+
const sessions = (snapshot.report?.sessions ?? []).slice(0, options.limit ?? 3).map((session) => ({
|
|
1671
|
+
...session,
|
|
1672
|
+
detailHref: `${options.detailBasePath ?? "/traces"}/${encodeURIComponent(session.sessionId)}`,
|
|
1673
|
+
durationLabel: formatMs(session.summary.callDurationMs),
|
|
1674
|
+
label: `${session.summary.eventCount} events / ${session.summary.turnCount} turns`,
|
|
1675
|
+
providerLabel: formatProviders(session)
|
|
1676
|
+
}));
|
|
1677
|
+
const failed = sessions.filter((session) => session.status === "failed").length;
|
|
1678
|
+
const warnings = sessions.filter((session) => session.status === "warning").length;
|
|
1679
|
+
return {
|
|
1680
|
+
description: options.description ?? DEFAULT_DESCRIPTION5,
|
|
1681
|
+
error: snapshot.error,
|
|
1682
|
+
isLoading: snapshot.isLoading,
|
|
1683
|
+
label: snapshot.error ? "Unavailable" : failed > 0 ? `${failed} failed` : warnings > 0 ? `${warnings} warning` : sessions.length ? `${sessions.length} recent` : snapshot.isLoading ? "Checking" : "No traces yet",
|
|
1684
|
+
sessions,
|
|
1685
|
+
status: snapshot.error ? "error" : failed > 0 ? "failed" : warnings > 0 ? "warning" : sessions.length ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1686
|
+
title: options.title ?? DEFAULT_TITLE5,
|
|
1687
|
+
updatedAt: snapshot.updatedAt
|
|
1688
|
+
};
|
|
1689
|
+
};
|
|
1690
|
+
var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
|
|
1691
|
+
const model = createVoiceTraceTimelineViewModel(snapshot, options);
|
|
1692
|
+
const sessions = model.sessions.length ? `<div class="absolute-voice-trace-timeline__sessions">${model.sessions.map((session) => `<article class="absolute-voice-trace-timeline__session absolute-voice-trace-timeline__session--${escapeHtml6(session.status)}">
|
|
1693
|
+
<header>
|
|
1694
|
+
<strong>${escapeHtml6(session.sessionId)}</strong>
|
|
1695
|
+
<span>${escapeHtml6(session.status)}</span>
|
|
1696
|
+
</header>
|
|
1697
|
+
<p>${escapeHtml6(session.label)} \xB7 ${escapeHtml6(session.durationLabel)} \xB7 ${escapeHtml6(session.providerLabel)}</p>
|
|
1698
|
+
<a href="${escapeHtml6(session.detailHref)}">Open timeline</a>
|
|
1699
|
+
</article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
|
|
1700
|
+
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml6(model.status)}">
|
|
1701
|
+
<header class="absolute-voice-trace-timeline__header">
|
|
1702
|
+
<span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml6(model.title)}</span>
|
|
1703
|
+
<strong class="absolute-voice-trace-timeline__label">${escapeHtml6(model.label)}</strong>
|
|
1704
|
+
</header>
|
|
1705
|
+
<p class="absolute-voice-trace-timeline__description">${escapeHtml6(model.description)}</p>
|
|
1706
|
+
${sessions}
|
|
1707
|
+
${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml6(model.error)}</p>` : ""}
|
|
1708
|
+
</section>`;
|
|
1709
|
+
};
|
|
1710
|
+
var getVoiceTraceTimelineCSS = () => `.absolute-voice-trace-timeline{border:1px solid #bad7d3;border-radius:20px;background:#f3fffb;color:#09201c;padding:18px;box-shadow:0 18px 40px rgba(9,32,28,.12);font-family:inherit}.absolute-voice-trace-timeline--error,.absolute-voice-trace-timeline--failed{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-trace-timeline--warning{border-color:#fbbf24;background:#fffaf0}.absolute-voice-trace-timeline__header,.absolute-voice-trace-timeline__session header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-trace-timeline__eyebrow{color:#17665b;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-trace-timeline__label{font-size:24px;line-height:1}.absolute-voice-trace-timeline__description,.absolute-voice-trace-timeline__session p,.absolute-voice-trace-timeline__empty{color:#35544f}.absolute-voice-trace-timeline__sessions{display:grid;gap:12px;margin-top:14px}.absolute-voice-trace-timeline__session{background:#fff;border:1px solid #cfe7e2;border-radius:16px;padding:14px}.absolute-voice-trace-timeline__session--failed{border-color:#f2a7a7}.absolute-voice-trace-timeline__session--warning{border-color:#fbbf24}.absolute-voice-trace-timeline__session p{margin:10px 0}.absolute-voice-trace-timeline__session a{color:#0f766e;font-weight:800}.absolute-voice-trace-timeline__empty{margin:14px 0 0}.absolute-voice-trace-timeline__error{color:#9f1239;font-weight:700}`;
|
|
1711
|
+
var mountVoiceTraceTimeline = (element, path = "/api/voice-traces", options = {}) => {
|
|
1712
|
+
const store = createVoiceTraceTimelineStore(path, options);
|
|
1713
|
+
const render = () => {
|
|
1714
|
+
element.innerHTML = renderVoiceTraceTimelineWidgetHTML(store.getSnapshot(), options);
|
|
1715
|
+
};
|
|
1716
|
+
const unsubscribe = store.subscribe(render);
|
|
1717
|
+
render();
|
|
1718
|
+
store.refresh().catch(() => {});
|
|
1719
|
+
return {
|
|
1720
|
+
close: () => {
|
|
1721
|
+
unsubscribe();
|
|
1722
|
+
store.close();
|
|
1723
|
+
},
|
|
1724
|
+
refresh: store.refresh
|
|
1725
|
+
};
|
|
1726
|
+
};
|
|
1727
|
+
var defineVoiceTraceTimelineElement = (tagName = "absolute-voice-trace-timeline") => {
|
|
1728
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
1729
|
+
return;
|
|
1730
|
+
}
|
|
1731
|
+
customElements.define(tagName, class AbsoluteVoiceTraceTimelineElement extends HTMLElement {
|
|
1732
|
+
mounted;
|
|
1733
|
+
connectedCallback() {
|
|
1734
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
1735
|
+
const limit = Number(this.getAttribute("limit") ?? 3);
|
|
1736
|
+
this.mounted = mountVoiceTraceTimeline(this, this.getAttribute("path") ?? "/api/voice-traces", {
|
|
1737
|
+
description: this.getAttribute("description") ?? undefined,
|
|
1738
|
+
detailBasePath: this.getAttribute("detail-base-path") ?? undefined,
|
|
1739
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
1740
|
+
limit: Number.isFinite(limit) ? limit : 3,
|
|
1741
|
+
title: this.getAttribute("title") ?? undefined
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
disconnectedCallback() {
|
|
1745
|
+
this.mounted?.close();
|
|
1746
|
+
this.mounted = undefined;
|
|
1747
|
+
}
|
|
1748
|
+
});
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
// src/svelte/createVoiceTraceTimeline.ts
|
|
1752
|
+
var createVoiceTraceTimeline = (path = "/api/voice-traces", options = {}) => {
|
|
1753
|
+
const store = createVoiceTraceTimelineStore(path, options);
|
|
1754
|
+
return {
|
|
1755
|
+
...store,
|
|
1756
|
+
getHTML: () => renderVoiceTraceTimelineWidgetHTML(store.getSnapshot(), options),
|
|
1757
|
+
getViewModel: () => createVoiceTraceTimelineViewModel(store.getSnapshot(), options)
|
|
1758
|
+
};
|
|
1759
|
+
};
|
|
1583
1760
|
// src/client/turnQuality.ts
|
|
1584
1761
|
var fetchVoiceTurnQuality = async (path = "/api/turn-quality", options = {}) => {
|
|
1585
1762
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -1660,9 +1837,9 @@ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) =>
|
|
|
1660
1837
|
};
|
|
1661
1838
|
|
|
1662
1839
|
// src/client/turnQualityWidget.ts
|
|
1663
|
-
var
|
|
1664
|
-
var
|
|
1665
|
-
var
|
|
1840
|
+
var DEFAULT_TITLE6 = "Turn Quality";
|
|
1841
|
+
var DEFAULT_DESCRIPTION6 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
|
|
1842
|
+
var escapeHtml7 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1666
1843
|
var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
|
|
1667
1844
|
var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
|
|
1668
1845
|
var getTurnDetail = (turn) => {
|
|
@@ -1700,37 +1877,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
|
|
|
1700
1877
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
1701
1878
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
1702
1879
|
return {
|
|
1703
|
-
description: options.description ??
|
|
1880
|
+
description: options.description ?? DEFAULT_DESCRIPTION6,
|
|
1704
1881
|
error: snapshot.error,
|
|
1705
1882
|
isLoading: snapshot.isLoading,
|
|
1706
1883
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
|
|
1707
1884
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1708
|
-
title: options.title ??
|
|
1885
|
+
title: options.title ?? DEFAULT_TITLE6,
|
|
1709
1886
|
turns,
|
|
1710
1887
|
updatedAt: snapshot.updatedAt
|
|
1711
1888
|
};
|
|
1712
1889
|
};
|
|
1713
1890
|
var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
|
|
1714
1891
|
const model = createVoiceTurnQualityViewModel(snapshot, options);
|
|
1715
|
-
const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${
|
|
1892
|
+
const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${escapeHtml7(turn.status)}">
|
|
1716
1893
|
<header>
|
|
1717
|
-
<strong>${
|
|
1718
|
-
<span>${
|
|
1894
|
+
<strong>${escapeHtml7(turn.label)}</strong>
|
|
1895
|
+
<span>${escapeHtml7(turn.status)}</span>
|
|
1719
1896
|
</header>
|
|
1720
|
-
<p>${
|
|
1897
|
+
<p>${escapeHtml7(turn.detail)}</p>
|
|
1721
1898
|
<dl>${turn.rows.map((row) => `<div>
|
|
1722
|
-
<dt>${
|
|
1723
|
-
<dd>${
|
|
1899
|
+
<dt>${escapeHtml7(row.label)}</dt>
|
|
1900
|
+
<dd>${escapeHtml7(row.value)}</dd>
|
|
1724
1901
|
</div>`).join("")}</dl>
|
|
1725
1902
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
|
|
1726
|
-
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${
|
|
1903
|
+
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml7(model.status)}">
|
|
1727
1904
|
<header class="absolute-voice-turn-quality__header">
|
|
1728
|
-
<span class="absolute-voice-turn-quality__eyebrow">${
|
|
1729
|
-
<strong class="absolute-voice-turn-quality__label">${
|
|
1905
|
+
<span class="absolute-voice-turn-quality__eyebrow">${escapeHtml7(model.title)}</span>
|
|
1906
|
+
<strong class="absolute-voice-turn-quality__label">${escapeHtml7(model.label)}</strong>
|
|
1730
1907
|
</header>
|
|
1731
|
-
<p class="absolute-voice-turn-quality__description">${
|
|
1908
|
+
<p class="absolute-voice-turn-quality__description">${escapeHtml7(model.description)}</p>
|
|
1732
1909
|
${turns}
|
|
1733
|
-
${model.error ? `<p class="absolute-voice-turn-quality__error">${
|
|
1910
|
+
${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml7(model.error)}</p>` : ""}
|
|
1734
1911
|
</section>`;
|
|
1735
1912
|
};
|
|
1736
1913
|
var getVoiceTurnQualityCSS = () => `.absolute-voice-turn-quality{border:1px solid #e4d1a3;border-radius:20px;background:#fff9eb;color:#17120a;padding:18px;box-shadow:0 18px 40px rgba(73,48,14,.12);font-family:inherit}.absolute-voice-turn-quality--error,.absolute-voice-turn-quality--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-turn-quality__header,.absolute-voice-turn-quality__turn header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-turn-quality__eyebrow{color:#8a5a0a;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-turn-quality__label{font-size:24px;line-height:1}.absolute-voice-turn-quality__description,.absolute-voice-turn-quality__turn p,.absolute-voice-turn-quality__turn dt,.absolute-voice-turn-quality__empty{color:#5a4930}.absolute-voice-turn-quality__turns{display:grid;gap:12px;margin-top:14px}.absolute-voice-turn-quality__turn{background:#fff;border:1px solid #f0dfba;border-radius:16px;padding:14px}.absolute-voice-turn-quality__turn--pass{border-color:#86efac}.absolute-voice-turn-quality__turn--warn,.absolute-voice-turn-quality__turn--unknown{border-color:#fbbf24}.absolute-voice-turn-quality__turn--fail{border-color:#f2a7a7}.absolute-voice-turn-quality__turn p{margin:10px 0}.absolute-voice-turn-quality__turn dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-turn-quality__turn div{background:#fff9eb;border:1px solid #f0dfba;border-radius:12px;padding:8px}.absolute-voice-turn-quality__turn dt{font-size:12px}.absolute-voice-turn-quality__turn dd{font-weight:800;margin:4px 0 0}.absolute-voice-turn-quality__empty{margin:14px 0 0}.absolute-voice-turn-quality__error{color:#9f1239;font-weight:700}`;
|
|
@@ -2493,6 +2670,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
2493
2670
|
export {
|
|
2494
2671
|
createVoiceWorkflowStatus,
|
|
2495
2672
|
createVoiceTurnQuality,
|
|
2673
|
+
createVoiceTraceTimeline,
|
|
2496
2674
|
createVoiceStream2 as createVoiceStream,
|
|
2497
2675
|
createVoiceRoutingStatus,
|
|
2498
2676
|
createVoiceProviderStatus,
|
package/dist/testing/index.js
CHANGED
|
@@ -3063,11 +3063,26 @@ var DEFAULT_INTERRUPT_THRESHOLD = 0.08;
|
|
|
3063
3063
|
var shouldInterruptForLevel = (level, options = {}) => (options.enabled ?? true) && level >= (options.interruptThreshold ?? DEFAULT_INTERRUPT_THRESHOLD);
|
|
3064
3064
|
var bindVoiceBargeIn = (controller, player, options = {}) => {
|
|
3065
3065
|
let lastPartial = controller.partial;
|
|
3066
|
-
const interruptIfPlaying = () => {
|
|
3066
|
+
const interruptIfPlaying = (reason) => {
|
|
3067
3067
|
if (!player.isPlaying || options.enabled === false) {
|
|
3068
|
+
options.monitor?.recordSkipped({
|
|
3069
|
+
reason,
|
|
3070
|
+
sessionId: controller.sessionId
|
|
3071
|
+
});
|
|
3068
3072
|
return;
|
|
3069
3073
|
}
|
|
3070
|
-
|
|
3074
|
+
options.monitor?.recordRequested({
|
|
3075
|
+
reason,
|
|
3076
|
+
sessionId: controller.sessionId
|
|
3077
|
+
});
|
|
3078
|
+
player.interrupt().then(() => {
|
|
3079
|
+
options.monitor?.recordStopped({
|
|
3080
|
+
latencyMs: player.lastInterruptLatencyMs,
|
|
3081
|
+
playbackStopLatencyMs: player.lastPlaybackStopLatencyMs,
|
|
3082
|
+
reason,
|
|
3083
|
+
sessionId: controller.sessionId
|
|
3084
|
+
});
|
|
3085
|
+
});
|
|
3071
3086
|
};
|
|
3072
3087
|
const unsubscribe = controller.subscribe(() => {
|
|
3073
3088
|
if (options.interruptOnPartial === false) {
|
|
@@ -3075,7 +3090,7 @@ var bindVoiceBargeIn = (controller, player, options = {}) => {
|
|
|
3075
3090
|
return;
|
|
3076
3091
|
}
|
|
3077
3092
|
if (!lastPartial && controller.partial) {
|
|
3078
|
-
interruptIfPlaying();
|
|
3093
|
+
interruptIfPlaying("partial-transcript");
|
|
3079
3094
|
}
|
|
3080
3095
|
lastPartial = controller.partial;
|
|
3081
3096
|
});
|
|
@@ -3085,11 +3100,11 @@ var bindVoiceBargeIn = (controller, player, options = {}) => {
|
|
|
3085
3100
|
},
|
|
3086
3101
|
handleLevel: (level) => {
|
|
3087
3102
|
if (shouldInterruptForLevel(level, options)) {
|
|
3088
|
-
interruptIfPlaying();
|
|
3103
|
+
interruptIfPlaying("input-level");
|
|
3089
3104
|
}
|
|
3090
3105
|
},
|
|
3091
3106
|
sendAudio: (audio) => {
|
|
3092
|
-
interruptIfPlaying();
|
|
3107
|
+
interruptIfPlaying("manual-audio");
|
|
3093
3108
|
controller.sendAudio(audio);
|
|
3094
3109
|
}
|
|
3095
3110
|
};
|
|
@@ -3119,7 +3134,17 @@ var createVoiceDuplexController = (path, options = {}) => {
|
|
|
3119
3134
|
audioPlayer,
|
|
3120
3135
|
close,
|
|
3121
3136
|
interruptAssistant: async () => {
|
|
3137
|
+
options.bargeIn?.monitor?.recordRequested({
|
|
3138
|
+
reason: "manual-interrupt",
|
|
3139
|
+
sessionId: controller.sessionId
|
|
3140
|
+
});
|
|
3122
3141
|
await audioPlayer.interrupt();
|
|
3142
|
+
options.bargeIn?.monitor?.recordStopped({
|
|
3143
|
+
latencyMs: audioPlayer.lastInterruptLatencyMs,
|
|
3144
|
+
playbackStopLatencyMs: audioPlayer.lastPlaybackStopLatencyMs,
|
|
3145
|
+
reason: "manual-interrupt",
|
|
3146
|
+
sessionId: controller.sessionId
|
|
3147
|
+
});
|
|
3123
3148
|
},
|
|
3124
3149
|
sendAudio: (audio) => {
|
|
3125
3150
|
bargeInBinding?.sendAudio(audio);
|
package/dist/trace.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn.transcript' | 'workflow.contract';
|
|
1
|
+
export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'client.barge_in' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn.transcript' | 'workflow.contract';
|
|
2
2
|
export type VoiceTraceEvent<TPayload extends Record<string, unknown> = Record<string, unknown>> = {
|
|
3
3
|
at: number;
|
|
4
4
|
id?: string;
|
package/dist/types.d.ts
CHANGED
|
@@ -747,6 +747,46 @@ export type VoiceBargeInOptions = {
|
|
|
747
747
|
enabled?: boolean;
|
|
748
748
|
interruptOnPartial?: boolean;
|
|
749
749
|
interruptThreshold?: number;
|
|
750
|
+
monitor?: VoiceBargeInMonitor;
|
|
751
|
+
};
|
|
752
|
+
export type VoiceBargeInTriggerReason = 'input-level' | 'manual-audio' | 'manual-interrupt' | 'partial-transcript';
|
|
753
|
+
export type VoiceBargeInMonitorEvent = {
|
|
754
|
+
at: number;
|
|
755
|
+
id: string;
|
|
756
|
+
latencyMs?: number;
|
|
757
|
+
playbackStopLatencyMs?: number;
|
|
758
|
+
reason: VoiceBargeInTriggerReason;
|
|
759
|
+
sessionId?: string | null;
|
|
760
|
+
status: 'requested' | 'stopped' | 'skipped';
|
|
761
|
+
thresholdMs?: number;
|
|
762
|
+
};
|
|
763
|
+
export type VoiceBargeInMonitorSnapshot = {
|
|
764
|
+
averageLatencyMs?: number;
|
|
765
|
+
events: VoiceBargeInMonitorEvent[];
|
|
766
|
+
failed: number;
|
|
767
|
+
lastEvent?: VoiceBargeInMonitorEvent;
|
|
768
|
+
passed: number;
|
|
769
|
+
status: 'empty' | 'fail' | 'pass' | 'warn';
|
|
770
|
+
thresholdMs: number;
|
|
771
|
+
total: number;
|
|
772
|
+
};
|
|
773
|
+
export type VoiceBargeInMonitor = {
|
|
774
|
+
getSnapshot: () => VoiceBargeInMonitorSnapshot;
|
|
775
|
+
recordRequested: (input: {
|
|
776
|
+
reason: VoiceBargeInTriggerReason;
|
|
777
|
+
sessionId?: string | null;
|
|
778
|
+
}) => VoiceBargeInMonitorEvent;
|
|
779
|
+
recordSkipped: (input: {
|
|
780
|
+
reason: VoiceBargeInTriggerReason;
|
|
781
|
+
sessionId?: string | null;
|
|
782
|
+
}) => VoiceBargeInMonitorEvent;
|
|
783
|
+
recordStopped: (input: {
|
|
784
|
+
latencyMs?: number;
|
|
785
|
+
playbackStopLatencyMs?: number;
|
|
786
|
+
reason: VoiceBargeInTriggerReason;
|
|
787
|
+
sessionId?: string | null;
|
|
788
|
+
}) => VoiceBargeInMonitorEvent;
|
|
789
|
+
subscribe: (subscriber: () => void) => () => void;
|
|
750
790
|
};
|
|
751
791
|
export type VoiceAudioPlayerOptions = {
|
|
752
792
|
autoStart?: boolean;
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -11,5 +11,6 @@ export { useVoiceProviderStatus } from './useVoiceProviderStatus';
|
|
|
11
11
|
export { useVoiceProviderCapabilities } from './useVoiceProviderCapabilities';
|
|
12
12
|
export { useVoiceProviderSimulationControls } from './useVoiceProviderSimulationControls';
|
|
13
13
|
export { useVoiceRoutingStatus } from './useVoiceRoutingStatus';
|
|
14
|
+
export { useVoiceTraceTimeline } from './useVoiceTraceTimeline';
|
|
14
15
|
export { useVoiceTurnQuality } from './useVoiceTurnQuality';
|
|
15
16
|
export { useVoiceWorkflowStatus } from './useVoiceWorkflowStatus';
|
package/dist/vue/index.js
CHANGED
|
@@ -3053,9 +3053,121 @@ var useVoiceController = (path, options = {}) => {
|
|
|
3053
3053
|
turns
|
|
3054
3054
|
};
|
|
3055
3055
|
};
|
|
3056
|
-
// src/vue/
|
|
3056
|
+
// src/vue/useVoiceTraceTimeline.ts
|
|
3057
3057
|
import { onUnmounted as onUnmounted9, ref as ref7, shallowRef as shallowRef8 } from "vue";
|
|
3058
3058
|
|
|
3059
|
+
// src/client/traceTimeline.ts
|
|
3060
|
+
var fetchVoiceTraceTimeline = async (path = "/api/voice-traces", options = {}) => {
|
|
3061
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
3062
|
+
const response = await fetchImpl(path);
|
|
3063
|
+
if (!response.ok) {
|
|
3064
|
+
throw new Error(`Voice trace timeline failed: HTTP ${response.status}`);
|
|
3065
|
+
}
|
|
3066
|
+
return await response.json();
|
|
3067
|
+
};
|
|
3068
|
+
var createVoiceTraceTimelineStore = (path = "/api/voice-traces", options = {}) => {
|
|
3069
|
+
const listeners = new Set;
|
|
3070
|
+
let closed = false;
|
|
3071
|
+
let timer;
|
|
3072
|
+
let snapshot = {
|
|
3073
|
+
error: null,
|
|
3074
|
+
isLoading: false,
|
|
3075
|
+
report: null
|
|
3076
|
+
};
|
|
3077
|
+
const emit = () => {
|
|
3078
|
+
for (const listener of listeners) {
|
|
3079
|
+
listener();
|
|
3080
|
+
}
|
|
3081
|
+
};
|
|
3082
|
+
const refresh = async () => {
|
|
3083
|
+
if (closed) {
|
|
3084
|
+
return snapshot.report;
|
|
3085
|
+
}
|
|
3086
|
+
snapshot = {
|
|
3087
|
+
...snapshot,
|
|
3088
|
+
error: null,
|
|
3089
|
+
isLoading: true
|
|
3090
|
+
};
|
|
3091
|
+
emit();
|
|
3092
|
+
try {
|
|
3093
|
+
const report = await fetchVoiceTraceTimeline(path, options);
|
|
3094
|
+
snapshot = {
|
|
3095
|
+
error: null,
|
|
3096
|
+
isLoading: false,
|
|
3097
|
+
report,
|
|
3098
|
+
updatedAt: Date.now()
|
|
3099
|
+
};
|
|
3100
|
+
emit();
|
|
3101
|
+
return report;
|
|
3102
|
+
} catch (error) {
|
|
3103
|
+
snapshot = {
|
|
3104
|
+
...snapshot,
|
|
3105
|
+
error: error instanceof Error ? error.message : String(error),
|
|
3106
|
+
isLoading: false
|
|
3107
|
+
};
|
|
3108
|
+
emit();
|
|
3109
|
+
throw error;
|
|
3110
|
+
}
|
|
3111
|
+
};
|
|
3112
|
+
const close = () => {
|
|
3113
|
+
closed = true;
|
|
3114
|
+
if (timer) {
|
|
3115
|
+
clearInterval(timer);
|
|
3116
|
+
timer = undefined;
|
|
3117
|
+
}
|
|
3118
|
+
listeners.clear();
|
|
3119
|
+
};
|
|
3120
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
3121
|
+
timer = setInterval(() => {
|
|
3122
|
+
refresh().catch(() => {});
|
|
3123
|
+
}, options.intervalMs);
|
|
3124
|
+
}
|
|
3125
|
+
return {
|
|
3126
|
+
close,
|
|
3127
|
+
getServerSnapshot: () => snapshot,
|
|
3128
|
+
getSnapshot: () => snapshot,
|
|
3129
|
+
refresh,
|
|
3130
|
+
subscribe: (listener) => {
|
|
3131
|
+
listeners.add(listener);
|
|
3132
|
+
return () => {
|
|
3133
|
+
listeners.delete(listener);
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
};
|
|
3138
|
+
|
|
3139
|
+
// src/vue/useVoiceTraceTimeline.ts
|
|
3140
|
+
var useVoiceTraceTimeline = (path = "/api/voice-traces", options = {}) => {
|
|
3141
|
+
const store = createVoiceTraceTimelineStore(path, options);
|
|
3142
|
+
const error = ref7(null);
|
|
3143
|
+
const isLoading = ref7(false);
|
|
3144
|
+
const report = shallowRef8(null);
|
|
3145
|
+
const updatedAt = ref7(undefined);
|
|
3146
|
+
const sync = () => {
|
|
3147
|
+
const snapshot = store.getSnapshot();
|
|
3148
|
+
error.value = snapshot.error;
|
|
3149
|
+
isLoading.value = snapshot.isLoading;
|
|
3150
|
+
report.value = snapshot.report;
|
|
3151
|
+
updatedAt.value = snapshot.updatedAt;
|
|
3152
|
+
};
|
|
3153
|
+
const unsubscribe = store.subscribe(sync);
|
|
3154
|
+
sync();
|
|
3155
|
+
store.refresh().catch(() => {});
|
|
3156
|
+
onUnmounted9(() => {
|
|
3157
|
+
unsubscribe();
|
|
3158
|
+
store.close();
|
|
3159
|
+
});
|
|
3160
|
+
return {
|
|
3161
|
+
error,
|
|
3162
|
+
isLoading,
|
|
3163
|
+
refresh: store.refresh,
|
|
3164
|
+
report,
|
|
3165
|
+
updatedAt
|
|
3166
|
+
};
|
|
3167
|
+
};
|
|
3168
|
+
// src/vue/useVoiceWorkflowStatus.ts
|
|
3169
|
+
import { onUnmounted as onUnmounted10, ref as ref8, shallowRef as shallowRef9 } from "vue";
|
|
3170
|
+
|
|
3059
3171
|
// src/client/workflowStatus.ts
|
|
3060
3172
|
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
3061
3173
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -3138,10 +3250,10 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
|
|
|
3138
3250
|
// src/vue/useVoiceWorkflowStatus.ts
|
|
3139
3251
|
var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
|
|
3140
3252
|
const store = createVoiceWorkflowStatusStore(path, options);
|
|
3141
|
-
const error =
|
|
3142
|
-
const isLoading =
|
|
3143
|
-
const report =
|
|
3144
|
-
const updatedAt =
|
|
3253
|
+
const error = ref8(null);
|
|
3254
|
+
const isLoading = ref8(false);
|
|
3255
|
+
const report = shallowRef9(undefined);
|
|
3256
|
+
const updatedAt = ref8(undefined);
|
|
3145
3257
|
const sync = () => {
|
|
3146
3258
|
const snapshot = store.getSnapshot();
|
|
3147
3259
|
error.value = snapshot.error;
|
|
@@ -3154,7 +3266,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
|
|
|
3154
3266
|
if (typeof window !== "undefined") {
|
|
3155
3267
|
store.refresh().catch(() => {});
|
|
3156
3268
|
}
|
|
3157
|
-
|
|
3269
|
+
onUnmounted10(() => {
|
|
3158
3270
|
unsubscribe();
|
|
3159
3271
|
store.close();
|
|
3160
3272
|
});
|
|
@@ -3169,6 +3281,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
|
|
|
3169
3281
|
export {
|
|
3170
3282
|
useVoiceWorkflowStatus,
|
|
3171
3283
|
useVoiceTurnQuality,
|
|
3284
|
+
useVoiceTraceTimeline,
|
|
3172
3285
|
useVoiceStream,
|
|
3173
3286
|
useVoiceRoutingStatus,
|
|
3174
3287
|
useVoiceProviderStatus,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type VoiceTraceTimelineClientOptions } from '../client/traceTimeline';
|
|
2
|
+
import type { VoiceTraceTimelineReport } from '../traceTimeline';
|
|
3
|
+
export declare const useVoiceTraceTimeline: (path?: string, options?: VoiceTraceTimelineClientOptions) => {
|
|
4
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
5
|
+
isLoading: import("vue").Ref<boolean, boolean>;
|
|
6
|
+
refresh: () => Promise<VoiceTraceTimelineReport | null>;
|
|
7
|
+
report: import("vue").ShallowRef<VoiceTraceTimelineReport | null, VoiceTraceTimelineReport | null>;
|
|
8
|
+
updatedAt: import("vue").Ref<number | undefined, number | undefined>;
|
|
9
|
+
};
|