@absolutejs/voice 0.0.22-beta.500 → 0.0.22-beta.501
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 +6 -0
- package/dist/angular/index.js +605 -264
- package/dist/angular/voice-cost-dashboard.service.d.ts +15 -0
- package/dist/angular/voice-live-call-viewer.service.d.ts +16 -0
- package/dist/angular/voice-replay-timeline.service.d.ts +13 -0
- package/dist/svelte/createVoiceCostDashboard.d.ts +13 -0
- package/dist/svelte/createVoiceLiveCallViewer.d.ts +26 -0
- package/dist/svelte/createVoiceReplayTimeline.d.ts +13 -0
- package/dist/svelte/index.d.ts +6 -0
- package/dist/svelte/index.js +376 -0
- package/dist/vue/VoiceCostDashboard.d.ts +57 -0
- package/dist/vue/VoiceLiveCallViewer.d.ts +35 -0
- package/dist/vue/VoiceReplayTimeline.d.ts +17 -0
- package/dist/vue/index.d.ts +3 -0
- package/dist/vue/index.js +570 -12
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StoredVoiceTraceEvent } from "../trace";
|
|
2
|
+
import { type VoiceCostDashboardOptions, type VoiceCostDashboardReport } from "../client/costDashboard";
|
|
3
|
+
export type VoiceCostDashboardServiceOptions = VoiceCostDashboardOptions & {
|
|
4
|
+
currency?: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare class VoiceCostDashboardService {
|
|
8
|
+
build(options: VoiceCostDashboardServiceOptions): {
|
|
9
|
+
currency: string;
|
|
10
|
+
report: import("@angular/core").Signal<VoiceCostDashboardReport>;
|
|
11
|
+
setEvents: (next: ReadonlyArray<StoredVoiceTraceEvent>) => void;
|
|
12
|
+
setFilters: (next: Partial<typeof options>) => void;
|
|
13
|
+
title: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CreateLiveCallViewerOptions, type LiveCallViewState, type LiveCallViewer } from "../client/liveCallViewer";
|
|
2
|
+
export type VoiceLiveCallViewerServiceOptions = CreateLiveCallViewerOptions & {
|
|
3
|
+
title?: string;
|
|
4
|
+
viewer?: LiveCallViewer;
|
|
5
|
+
};
|
|
6
|
+
export declare class VoiceLiveCallViewerService {
|
|
7
|
+
build(options: VoiceLiveCallViewerServiceOptions): {
|
|
8
|
+
noteAgentAudio: (at?: number) => void;
|
|
9
|
+
notePartial: (text: string, at?: number) => void;
|
|
10
|
+
noteTranscript: (text: string, at?: number) => void;
|
|
11
|
+
reset: (sessionId: string, startedAt?: number) => void;
|
|
12
|
+
state: import("@angular/core").Signal<LiveCallViewState>;
|
|
13
|
+
stop: () => void;
|
|
14
|
+
title: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { VoiceCallReviewArtifact } from "../testing/review";
|
|
2
|
+
import { type ReplayTimelineReport } from "../client/replayTimeline";
|
|
3
|
+
export type VoiceReplayTimelineServiceOptions = {
|
|
4
|
+
artifact: VoiceCallReviewArtifact;
|
|
5
|
+
title?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare class VoiceReplayTimelineService {
|
|
8
|
+
build(options: VoiceReplayTimelineServiceOptions): {
|
|
9
|
+
report: import("@angular/core").Signal<ReplayTimelineReport>;
|
|
10
|
+
setArtifact: (next: VoiceCallReviewArtifact) => void;
|
|
11
|
+
title: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type VoiceCostDashboardOptions, type VoiceCostDashboardReport } from "../client/costDashboard";
|
|
2
|
+
export type CreateVoiceCostDashboardOptions = VoiceCostDashboardOptions & {
|
|
3
|
+
currency?: string;
|
|
4
|
+
title?: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const renderVoiceCostDashboardHTML: (report: VoiceCostDashboardReport, options?: {
|
|
7
|
+
currency?: string;
|
|
8
|
+
title?: string;
|
|
9
|
+
}) => string;
|
|
10
|
+
export declare const createVoiceCostDashboard: (options: CreateVoiceCostDashboardOptions) => {
|
|
11
|
+
getHTML: () => string;
|
|
12
|
+
getReport: () => VoiceCostDashboardReport;
|
|
13
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type CreateLiveCallViewerOptions, type LiveCallViewState } from "../client/liveCallViewer";
|
|
2
|
+
export declare const renderVoiceLiveCallViewerHTML: (state: LiveCallViewState, options?: {
|
|
3
|
+
title?: string;
|
|
4
|
+
}) => string;
|
|
5
|
+
export type CreateVoiceLiveCallViewerSvelteOptions = CreateLiveCallViewerOptions & {
|
|
6
|
+
title?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const createVoiceLiveCallViewer: (options: CreateVoiceLiveCallViewerSvelteOptions) => {
|
|
9
|
+
getHTML: () => string;
|
|
10
|
+
title: string | undefined;
|
|
11
|
+
applyControl: (control: {
|
|
12
|
+
reason?: string;
|
|
13
|
+
type: string;
|
|
14
|
+
}) => void;
|
|
15
|
+
applyEvent: (event: import("..").LiveCallTimelineEvent) => void;
|
|
16
|
+
applyMonitorEvent: (event: {
|
|
17
|
+
payload: Record<string, unknown>;
|
|
18
|
+
type: string;
|
|
19
|
+
}) => void;
|
|
20
|
+
getState: () => LiveCallViewState;
|
|
21
|
+
noteAgentAudio: (at?: number) => void;
|
|
22
|
+
notePartial: (text: string, at?: number) => void;
|
|
23
|
+
noteTranscript: (text: string, at?: number) => void;
|
|
24
|
+
reset: (sessionId: string, startedAt?: number) => void;
|
|
25
|
+
subscribe: (subscriber: () => void) => () => void;
|
|
26
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { VoiceCallReviewArtifact } from "../testing/review";
|
|
2
|
+
import { type ReplayTimelineReport } from "../client/replayTimeline";
|
|
3
|
+
export declare const renderVoiceReplayTimelineHTML: (report: ReplayTimelineReport, options?: {
|
|
4
|
+
title?: string;
|
|
5
|
+
}) => string;
|
|
6
|
+
export type CreateVoiceReplayTimelineOptions = {
|
|
7
|
+
artifact: VoiceCallReviewArtifact;
|
|
8
|
+
title?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const createVoiceReplayTimeline: (options: CreateVoiceReplayTimelineOptions) => {
|
|
11
|
+
getHTML: () => string;
|
|
12
|
+
getReport: () => ReplayTimelineReport;
|
|
13
|
+
};
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export { createVoiceCampaignDialerProof } from "./createVoiceCampaignDialerProof";
|
|
2
2
|
export { createVoiceWidget } from "./createVoiceWidget";
|
|
3
3
|
export type { CreateVoiceWidgetOptions, VoiceWidgetLabels, VoiceWidgetTheme, VoiceWidgetViewModel, } from "./createVoiceWidget";
|
|
4
|
+
export { createVoiceCostDashboard, renderVoiceCostDashboardHTML, } from "./createVoiceCostDashboard";
|
|
5
|
+
export type { CreateVoiceCostDashboardOptions } from "./createVoiceCostDashboard";
|
|
6
|
+
export { createVoiceLiveCallViewer, renderVoiceLiveCallViewerHTML, } from "./createVoiceLiveCallViewer";
|
|
7
|
+
export type { CreateVoiceLiveCallViewerSvelteOptions } from "./createVoiceLiveCallViewer";
|
|
8
|
+
export { createVoiceReplayTimeline, renderVoiceReplayTimelineHTML, } from "./createVoiceReplayTimeline";
|
|
9
|
+
export type { CreateVoiceReplayTimelineOptions } from "./createVoiceReplayTimeline";
|
|
4
10
|
export { createVoiceDeliveryRuntime } from "./createVoiceDeliveryRuntime";
|
|
5
11
|
export { createVoiceOpsActionCenter } from "./createVoiceOpsActionCenter";
|
|
6
12
|
export { createVoiceLiveOps } from "./createVoiceLiveOps";
|
package/dist/svelte/index.js
CHANGED
|
@@ -1737,6 +1737,376 @@ var createVoiceWidget = (path, options = {}) => {
|
|
|
1737
1737
|
unmute: () => controller.startRecording()
|
|
1738
1738
|
};
|
|
1739
1739
|
};
|
|
1740
|
+
// src/client/costDashboard.ts
|
|
1741
|
+
var padTwo = (value) => String(value).padStart(2, "0");
|
|
1742
|
+
var formatBucketKey = (epochMs, bucketBy) => {
|
|
1743
|
+
const date = new Date(epochMs);
|
|
1744
|
+
const y = date.getUTCFullYear();
|
|
1745
|
+
const m = padTwo(date.getUTCMonth() + 1);
|
|
1746
|
+
const d = padTwo(date.getUTCDate());
|
|
1747
|
+
const h = padTwo(date.getUTCHours());
|
|
1748
|
+
if (bucketBy === "month")
|
|
1749
|
+
return `${y}-${m}`;
|
|
1750
|
+
if (bucketBy === "day")
|
|
1751
|
+
return `${y}-${m}-${d}`;
|
|
1752
|
+
return `${y}-${m}-${d}T${h}`;
|
|
1753
|
+
};
|
|
1754
|
+
var isCostBreakdown = (value) => Boolean(value) && typeof value === "object" && typeof value.totalUsd === "number";
|
|
1755
|
+
var emptyBucket = (bucketKey) => ({
|
|
1756
|
+
bucketKey,
|
|
1757
|
+
callCount: 0,
|
|
1758
|
+
llmUsd: 0,
|
|
1759
|
+
sttUsd: 0,
|
|
1760
|
+
telephonyMinutes: 0,
|
|
1761
|
+
telephonyUsd: 0,
|
|
1762
|
+
totalUsd: 0,
|
|
1763
|
+
ttsUsd: 0
|
|
1764
|
+
});
|
|
1765
|
+
var accumulate = (bucket, payload) => {
|
|
1766
|
+
bucket.callCount += 1;
|
|
1767
|
+
bucket.llmUsd += payload.llm.usd;
|
|
1768
|
+
bucket.sttUsd += payload.stt.usd;
|
|
1769
|
+
bucket.ttsUsd += payload.tts.usd;
|
|
1770
|
+
bucket.telephonyUsd += payload.telephony.usd;
|
|
1771
|
+
bucket.telephonyMinutes += payload.telephony.minutes;
|
|
1772
|
+
bucket.totalUsd += payload.totalUsd;
|
|
1773
|
+
};
|
|
1774
|
+
var roundCurrency = (bucket) => {
|
|
1775
|
+
bucket.llmUsd = Math.round(bucket.llmUsd * 1e6) / 1e6;
|
|
1776
|
+
bucket.sttUsd = Math.round(bucket.sttUsd * 1e6) / 1e6;
|
|
1777
|
+
bucket.ttsUsd = Math.round(bucket.ttsUsd * 1e6) / 1e6;
|
|
1778
|
+
bucket.telephonyUsd = Math.round(bucket.telephonyUsd * 1e6) / 1e6;
|
|
1779
|
+
bucket.totalUsd = Math.round(bucket.totalUsd * 1e6) / 1e6;
|
|
1780
|
+
};
|
|
1781
|
+
var buildVoiceCostDashboardReport = (options) => {
|
|
1782
|
+
const bucketBy = options.bucketBy ?? "day";
|
|
1783
|
+
const fromMs = options.fromMs ?? Number.NEGATIVE_INFINITY;
|
|
1784
|
+
const toMs = options.toMs ?? Number.POSITIVE_INFINITY;
|
|
1785
|
+
const buckets = new Map;
|
|
1786
|
+
const grandTotal = emptyBucket("total");
|
|
1787
|
+
let minMs = Number.POSITIVE_INFINITY;
|
|
1788
|
+
let maxMs = Number.NEGATIVE_INFINITY;
|
|
1789
|
+
for (const event of options.events) {
|
|
1790
|
+
if (event.type !== "cost.ready")
|
|
1791
|
+
continue;
|
|
1792
|
+
if (event.at < fromMs || event.at > toMs)
|
|
1793
|
+
continue;
|
|
1794
|
+
if (!isCostBreakdown(event.payload))
|
|
1795
|
+
continue;
|
|
1796
|
+
minMs = Math.min(minMs, event.at);
|
|
1797
|
+
maxMs = Math.max(maxMs, event.at);
|
|
1798
|
+
const bucketKey = formatBucketKey(event.at, bucketBy);
|
|
1799
|
+
let bucket = buckets.get(bucketKey);
|
|
1800
|
+
if (!bucket) {
|
|
1801
|
+
bucket = emptyBucket(bucketKey);
|
|
1802
|
+
buckets.set(bucketKey, bucket);
|
|
1803
|
+
}
|
|
1804
|
+
accumulate(bucket, event.payload);
|
|
1805
|
+
accumulate(grandTotal, event.payload);
|
|
1806
|
+
}
|
|
1807
|
+
for (const bucket of buckets.values()) {
|
|
1808
|
+
roundCurrency(bucket);
|
|
1809
|
+
}
|
|
1810
|
+
roundCurrency(grandTotal);
|
|
1811
|
+
return {
|
|
1812
|
+
buckets: Array.from(buckets.values()).sort((a, b) => a.bucketKey.localeCompare(b.bucketKey)),
|
|
1813
|
+
generatedAt: Date.now(),
|
|
1814
|
+
grandTotal,
|
|
1815
|
+
windowEndMs: Number.isFinite(maxMs) ? maxMs : 0,
|
|
1816
|
+
windowStartMs: Number.isFinite(minMs) ? minMs : 0
|
|
1817
|
+
};
|
|
1818
|
+
};
|
|
1819
|
+
|
|
1820
|
+
// src/svelte/createVoiceCostDashboard.ts
|
|
1821
|
+
var escape = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1822
|
+
var formatUsd = (value, currency = "USD") => new Intl.NumberFormat("en-US", {
|
|
1823
|
+
currency,
|
|
1824
|
+
maximumFractionDigits: 4,
|
|
1825
|
+
minimumFractionDigits: 2,
|
|
1826
|
+
style: "currency"
|
|
1827
|
+
}).format(value);
|
|
1828
|
+
var formatInteger = (value) => new Intl.NumberFormat("en-US").format(value);
|
|
1829
|
+
var renderVoiceCostDashboardHTML = (report, options = {}) => {
|
|
1830
|
+
const currency = options.currency ?? "USD";
|
|
1831
|
+
const title = options.title ?? "Voice cost dashboard";
|
|
1832
|
+
const rowFor = (bucketKey, callCount, llmUsd, ttsUsd, sttUsd, telephonyUsd, totalUsd, isTotal) => `<tr data-bucket-key="${escape(bucketKey)}" style="${isTotal ? "border-top:2px solid rgba(255,255,255,0.15);font-weight:600;" : ""}">
|
|
1833
|
+
<td style="padding:8px 12px;">${escape(bucketKey)}</td>
|
|
1834
|
+
<td style="padding:8px 12px;text-align:right;">${formatInteger(callCount)}</td>
|
|
1835
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(llmUsd, currency)}</td>
|
|
1836
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(ttsUsd, currency)}</td>
|
|
1837
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(sttUsd, currency)}</td>
|
|
1838
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(telephonyUsd, currency)}</td>
|
|
1839
|
+
<td style="padding:8px 12px;text-align:right;">${formatUsd(totalUsd, currency)}</td>
|
|
1840
|
+
</tr>`;
|
|
1841
|
+
const bodyRows = report.buckets.map((bucket) => rowFor(bucket.bucketKey, bucket.callCount, bucket.llmUsd, bucket.ttsUsd, bucket.sttUsd, bucket.telephonyUsd, bucket.totalUsd, false)).join("");
|
|
1842
|
+
const totalRow = rowFor("total", report.grandTotal.callCount, report.grandTotal.llmUsd, report.grandTotal.ttsUsd, report.grandTotal.sttUsd, report.grandTotal.telephonyUsd, report.grandTotal.totalUsd, true);
|
|
1843
|
+
return `<section aria-label="voice-cost-dashboard" class="absolute-voice-cost-dashboard" style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
1844
|
+
<header style="align-items:baseline;display:flex;gap:12px;margin-bottom:12px;">
|
|
1845
|
+
<strong style="font-size:16px;">${escape(title)}</strong>
|
|
1846
|
+
<span style="font-size:13px;opacity:0.7;">${report.buckets.length} buckets \xB7 grand total ${formatUsd(report.grandTotal.totalUsd, currency)}</span>
|
|
1847
|
+
</header>
|
|
1848
|
+
<table style="border-collapse:collapse;font-size:13px;width:100%;">
|
|
1849
|
+
<thead><tr style="opacity:0.7;text-align:left;">
|
|
1850
|
+
<th style="padding:8px 12px;">Bucket</th>
|
|
1851
|
+
<th style="padding:8px 12px;text-align:right;">Calls</th>
|
|
1852
|
+
<th style="padding:8px 12px;text-align:right;">LLM</th>
|
|
1853
|
+
<th style="padding:8px 12px;text-align:right;">TTS</th>
|
|
1854
|
+
<th style="padding:8px 12px;text-align:right;">STT</th>
|
|
1855
|
+
<th style="padding:8px 12px;text-align:right;">Tel.</th>
|
|
1856
|
+
<th style="padding:8px 12px;text-align:right;">Total</th>
|
|
1857
|
+
</tr></thead>
|
|
1858
|
+
<tbody>${bodyRows}${totalRow}</tbody>
|
|
1859
|
+
</table>
|
|
1860
|
+
</section>`;
|
|
1861
|
+
};
|
|
1862
|
+
var createVoiceCostDashboard = (options) => {
|
|
1863
|
+
const buildReport = () => buildVoiceCostDashboardReport(options);
|
|
1864
|
+
return {
|
|
1865
|
+
getHTML: () => renderVoiceCostDashboardHTML(buildReport(), {
|
|
1866
|
+
currency: options.currency,
|
|
1867
|
+
title: options.title
|
|
1868
|
+
}),
|
|
1869
|
+
getReport: buildReport
|
|
1870
|
+
};
|
|
1871
|
+
};
|
|
1872
|
+
// src/client/liveCallViewer.ts
|
|
1873
|
+
var EVENT_BUFFER_LIMIT = 200;
|
|
1874
|
+
var createLiveCallViewer = (options) => {
|
|
1875
|
+
const bufferLimit = options.bufferLimit ?? EVENT_BUFFER_LIMIT;
|
|
1876
|
+
const subscribers = new Set;
|
|
1877
|
+
let state = {
|
|
1878
|
+
agentState: "idle",
|
|
1879
|
+
callDurationMs: 0,
|
|
1880
|
+
events: [],
|
|
1881
|
+
isConnected: true,
|
|
1882
|
+
isLiveListening: true,
|
|
1883
|
+
partialTranscript: "",
|
|
1884
|
+
sessionId: options.sessionId
|
|
1885
|
+
};
|
|
1886
|
+
const startedAt = options.startedAt ?? Date.now();
|
|
1887
|
+
const notify = () => {
|
|
1888
|
+
for (const subscriber of subscribers)
|
|
1889
|
+
subscriber();
|
|
1890
|
+
};
|
|
1891
|
+
const update = (next) => {
|
|
1892
|
+
state = { ...state, ...next };
|
|
1893
|
+
state.callDurationMs = Math.max(0, Date.now() - startedAt);
|
|
1894
|
+
state.agentState = deriveVoiceAgentUIState({
|
|
1895
|
+
hasActivePartial: state.partialTranscript.length > 0,
|
|
1896
|
+
isConnected: state.isConnected,
|
|
1897
|
+
isPlaying: false,
|
|
1898
|
+
isRecording: state.isLiveListening,
|
|
1899
|
+
lastAssistantAt: state.lastAssistantAt,
|
|
1900
|
+
lastTranscriptAt: state.lastTranscriptAt
|
|
1901
|
+
});
|
|
1902
|
+
notify();
|
|
1903
|
+
};
|
|
1904
|
+
const pushEvent = (event) => {
|
|
1905
|
+
const next = state.events.concat(event);
|
|
1906
|
+
if (next.length > bufferLimit) {
|
|
1907
|
+
next.splice(0, next.length - bufferLimit);
|
|
1908
|
+
}
|
|
1909
|
+
update({ events: next });
|
|
1910
|
+
};
|
|
1911
|
+
return {
|
|
1912
|
+
applyControl: (control) => {
|
|
1913
|
+
pushEvent({
|
|
1914
|
+
at: Date.now(),
|
|
1915
|
+
detail: control.reason,
|
|
1916
|
+
kind: "lifecycle",
|
|
1917
|
+
title: `control:${control.type}`
|
|
1918
|
+
});
|
|
1919
|
+
},
|
|
1920
|
+
applyEvent: pushEvent,
|
|
1921
|
+
applyMonitorEvent: ({ payload, type }) => {
|
|
1922
|
+
pushEvent({
|
|
1923
|
+
at: Date.now(),
|
|
1924
|
+
detail: JSON.stringify(payload).slice(0, 240),
|
|
1925
|
+
kind: type === "call.lifecycle" ? "lifecycle" : "lifecycle",
|
|
1926
|
+
title: type
|
|
1927
|
+
});
|
|
1928
|
+
},
|
|
1929
|
+
getState: () => state,
|
|
1930
|
+
noteAgentAudio: (at) => {
|
|
1931
|
+
const ts = at ?? Date.now();
|
|
1932
|
+
update({ lastAssistantAt: ts });
|
|
1933
|
+
pushEvent({
|
|
1934
|
+
at: ts,
|
|
1935
|
+
kind: "agent_audio",
|
|
1936
|
+
title: "Agent audio frame"
|
|
1937
|
+
});
|
|
1938
|
+
},
|
|
1939
|
+
notePartial: (text, at) => {
|
|
1940
|
+
update({ partialTranscript: text });
|
|
1941
|
+
if (text) {
|
|
1942
|
+
pushEvent({
|
|
1943
|
+
at: at ?? Date.now(),
|
|
1944
|
+
detail: text,
|
|
1945
|
+
kind: "transcript",
|
|
1946
|
+
title: "Partial"
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
},
|
|
1950
|
+
noteTranscript: (text, at) => {
|
|
1951
|
+
const ts = at ?? Date.now();
|
|
1952
|
+
update({ lastTranscriptAt: ts, partialTranscript: "" });
|
|
1953
|
+
pushEvent({
|
|
1954
|
+
at: ts,
|
|
1955
|
+
detail: text,
|
|
1956
|
+
kind: "transcript",
|
|
1957
|
+
title: "Final transcript"
|
|
1958
|
+
});
|
|
1959
|
+
},
|
|
1960
|
+
reset: (sessionId, startedAtOverride) => {
|
|
1961
|
+
state = {
|
|
1962
|
+
agentState: "idle",
|
|
1963
|
+
callDurationMs: 0,
|
|
1964
|
+
events: [],
|
|
1965
|
+
isConnected: true,
|
|
1966
|
+
isLiveListening: true,
|
|
1967
|
+
partialTranscript: "",
|
|
1968
|
+
sessionId
|
|
1969
|
+
};
|
|
1970
|
+
if (typeof startedAtOverride === "number") {}
|
|
1971
|
+
notify();
|
|
1972
|
+
},
|
|
1973
|
+
subscribe: (subscriber) => {
|
|
1974
|
+
subscribers.add(subscriber);
|
|
1975
|
+
return () => subscribers.delete(subscriber);
|
|
1976
|
+
}
|
|
1977
|
+
};
|
|
1978
|
+
};
|
|
1979
|
+
|
|
1980
|
+
// src/svelte/createVoiceLiveCallViewer.ts
|
|
1981
|
+
var escape2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1982
|
+
var CATEGORY_COLOR = {
|
|
1983
|
+
agent_audio: "#3b82f6",
|
|
1984
|
+
agent_text: "#3b82f6",
|
|
1985
|
+
lifecycle: "#94a3b8",
|
|
1986
|
+
tool: "#f59e0b",
|
|
1987
|
+
transcript: "#10b981"
|
|
1988
|
+
};
|
|
1989
|
+
var formatRelative = (ms) => {
|
|
1990
|
+
const seconds = Math.max(0, Math.floor(ms / 1000));
|
|
1991
|
+
const minutes = Math.floor(seconds / 60);
|
|
1992
|
+
const remaining = seconds % 60;
|
|
1993
|
+
return `${String(minutes).padStart(2, "0")}:${String(remaining).padStart(2, "0")}`;
|
|
1994
|
+
};
|
|
1995
|
+
var renderVoiceLiveCallViewerHTML = (state, options = {}) => {
|
|
1996
|
+
const title = options.title ?? "Live call";
|
|
1997
|
+
const firstAt = state.events[0]?.at ?? Date.now();
|
|
1998
|
+
const items = state.events.map((event, index) => `<li key="${event.at}-${index}" style="align-items:center;border-left:3px solid ${CATEGORY_COLOR[event.kind] ?? "#94a3b8"};display:flex;font-size:13px;gap:12px;padding-left:12px;">
|
|
1999
|
+
<span style="color:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;width:60px;">${formatRelative(event.at - firstAt)}</span>
|
|
2000
|
+
<strong style="font-size:13px;">${escape2(event.title)}</strong>
|
|
2001
|
+
${event.detail ? `<span style="opacity:0.85;">${escape2(event.detail)}</span>` : ""}
|
|
2002
|
+
</li>`).join("");
|
|
2003
|
+
return `<section aria-label="voice-live-call-viewer" class="absolute-voice-live-call-viewer" data-agent-state="${state.agentState}" style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
2004
|
+
<header style="align-items:center;display:flex;gap:12px;margin-bottom:12px;">
|
|
2005
|
+
<strong style="font-size:16px;">${escape2(title)}</strong>
|
|
2006
|
+
<span style="background:rgba(59,130,246,0.18);border-radius:999px;font-size:11px;padding:3px 10px;text-transform:uppercase;">${state.agentState}</span>
|
|
2007
|
+
<span style="font-size:13px;margin-left:auto;opacity:0.7;">${escape2(state.sessionId)} \xB7 ${formatRelative(state.callDurationMs)}</span>
|
|
2008
|
+
</header>
|
|
2009
|
+
${state.partialTranscript ? `<p style="background:rgba(16,185,129,0.12);border-radius:12px;font-size:13px;margin:0 0 12px;opacity:0.95;padding:10px 12px;">\u201C${escape2(state.partialTranscript)}\u201D</p>` : ""}
|
|
2010
|
+
<ol style="display:flex;flex-direction:column;gap:6px;list-style:none;margin:0;max-height:320px;overflow-y:auto;padding:0;">${items}</ol>
|
|
2011
|
+
</section>`;
|
|
2012
|
+
};
|
|
2013
|
+
var createVoiceLiveCallViewer = (options) => {
|
|
2014
|
+
const viewer = createLiveCallViewer(options);
|
|
2015
|
+
return {
|
|
2016
|
+
...viewer,
|
|
2017
|
+
getHTML: () => renderVoiceLiveCallViewerHTML(viewer.getState(), {
|
|
2018
|
+
title: options.title
|
|
2019
|
+
}),
|
|
2020
|
+
title: options.title
|
|
2021
|
+
};
|
|
2022
|
+
};
|
|
2023
|
+
// src/client/replayTimeline.ts
|
|
2024
|
+
var categorize = (entry) => {
|
|
2025
|
+
const event = entry.event.toLowerCase();
|
|
2026
|
+
if (event.startsWith("stt.") || event.includes("user"))
|
|
2027
|
+
return "user";
|
|
2028
|
+
if (event.startsWith("tts.") || event.includes("assistant") || event.includes("agent"))
|
|
2029
|
+
return "agent";
|
|
2030
|
+
if (event.startsWith("tool.") || event.includes("tool"))
|
|
2031
|
+
return "tool";
|
|
2032
|
+
return "lifecycle";
|
|
2033
|
+
};
|
|
2034
|
+
var buildReplayTimelineReport = (input) => {
|
|
2035
|
+
const events = [];
|
|
2036
|
+
let summaryAgentTurns = 0;
|
|
2037
|
+
let summaryUserTurns = 0;
|
|
2038
|
+
let summaryToolCalls = 0;
|
|
2039
|
+
for (const entry of input.artifact.timeline ?? []) {
|
|
2040
|
+
const category = categorize(entry);
|
|
2041
|
+
if (category === "user")
|
|
2042
|
+
summaryUserTurns += 1;
|
|
2043
|
+
if (category === "agent")
|
|
2044
|
+
summaryAgentTurns += 1;
|
|
2045
|
+
if (category === "tool")
|
|
2046
|
+
summaryToolCalls += 1;
|
|
2047
|
+
events.push({
|
|
2048
|
+
at: entry.atMs,
|
|
2049
|
+
category,
|
|
2050
|
+
detail: entry.text ?? entry.reason,
|
|
2051
|
+
durationMs: entry.chunkDurationMs,
|
|
2052
|
+
label: entry.event
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
events.sort((a, b) => a.at - b.at);
|
|
2056
|
+
const first = events[0]?.at ?? 0;
|
|
2057
|
+
const last = events.at(-1)?.at ?? first;
|
|
2058
|
+
return {
|
|
2059
|
+
duration: last - first,
|
|
2060
|
+
events,
|
|
2061
|
+
metadata: {
|
|
2062
|
+
artifactId: input.artifact.id ?? "",
|
|
2063
|
+
title: input.artifact.title
|
|
2064
|
+
},
|
|
2065
|
+
startedAt: first,
|
|
2066
|
+
summary: {
|
|
2067
|
+
agentTurns: summaryAgentTurns,
|
|
2068
|
+
toolCalls: summaryToolCalls,
|
|
2069
|
+
userTurns: summaryUserTurns
|
|
2070
|
+
}
|
|
2071
|
+
};
|
|
2072
|
+
};
|
|
2073
|
+
|
|
2074
|
+
// src/svelte/createVoiceReplayTimeline.ts
|
|
2075
|
+
var escape3 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2076
|
+
var CATEGORY_COLOR2 = {
|
|
2077
|
+
agent: "#3b82f6",
|
|
2078
|
+
lifecycle: "#94a3b8",
|
|
2079
|
+
tool: "#f59e0b",
|
|
2080
|
+
user: "#10b981"
|
|
2081
|
+
};
|
|
2082
|
+
var formatRelative2 = (ms) => {
|
|
2083
|
+
const seconds = Math.max(0, Math.floor(ms / 1000));
|
|
2084
|
+
const minutes = Math.floor(seconds / 60);
|
|
2085
|
+
const remaining = seconds % 60;
|
|
2086
|
+
return `${String(minutes).padStart(2, "0")}:${String(remaining).padStart(2, "0")}`;
|
|
2087
|
+
};
|
|
2088
|
+
var renderVoiceReplayTimelineHTML = (report, options = {}) => {
|
|
2089
|
+
const title = options.title ?? report.metadata.title ?? "Replay";
|
|
2090
|
+
const items = report.events.map((event, index) => `<li key="${event.at}-${index}" style="align-items:center;border-left:3px solid ${CATEGORY_COLOR2[event.category]};display:flex;font-size:13px;gap:12px;padding-left:12px;">
|
|
2091
|
+
<span style="color:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;width:60px;">${formatRelative2(event.at - report.startedAt)}</span>
|
|
2092
|
+
<strong style="font-size:13px;">${escape3(event.label)}</strong>
|
|
2093
|
+
${event.detail ? `<span style="opacity:0.85;">${escape3(event.detail)}</span>` : ""}
|
|
2094
|
+
</li>`).join("");
|
|
2095
|
+
return `<section aria-label="voice-replay-timeline" class="absolute-voice-replay-timeline" style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
2096
|
+
<header style="align-items:baseline;display:flex;gap:12px;margin-bottom:12px;">
|
|
2097
|
+
<strong style="font-size:16px;">${escape3(title)}</strong>
|
|
2098
|
+
<span style="font-size:13px;opacity:0.7;">${report.events.length} events \xB7 ${report.summary.userTurns} user \xB7 ${report.summary.agentTurns} agent \xB7 ${report.summary.toolCalls} tool</span>
|
|
2099
|
+
</header>
|
|
2100
|
+
<ol style="display:flex;flex-direction:column;gap:6px;list-style:none;margin:0;padding:0;">${items}</ol>
|
|
2101
|
+
</section>`;
|
|
2102
|
+
};
|
|
2103
|
+
var createVoiceReplayTimeline = (options) => {
|
|
2104
|
+
const buildReport = () => buildReplayTimelineReport({ artifact: options.artifact });
|
|
2105
|
+
return {
|
|
2106
|
+
getHTML: () => renderVoiceReplayTimelineHTML(buildReport(), { title: options.title }),
|
|
2107
|
+
getReport: buildReport
|
|
2108
|
+
};
|
|
2109
|
+
};
|
|
1740
2110
|
// src/client/deliveryRuntime.ts
|
|
1741
2111
|
var getDefaultActionPath = (path, action, options) => {
|
|
1742
2112
|
if (action === "tick") {
|
|
@@ -7008,6 +7378,9 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
|
|
|
7008
7378
|
// src/svelte/createVoiceWorkflowStatus.ts
|
|
7009
7379
|
var createVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => createVoiceWorkflowStatusStore(path, options);
|
|
7010
7380
|
export {
|
|
7381
|
+
renderVoiceReplayTimelineHTML,
|
|
7382
|
+
renderVoiceLiveCallViewerHTML,
|
|
7383
|
+
renderVoiceCostDashboardHTML,
|
|
7011
7384
|
createVoiceWorkflowStatus,
|
|
7012
7385
|
createVoiceWidget,
|
|
7013
7386
|
createVoiceTurnQuality,
|
|
@@ -7017,6 +7390,7 @@ export {
|
|
|
7017
7390
|
createVoiceSessionSnapshot,
|
|
7018
7391
|
createVoiceSessionObservability,
|
|
7019
7392
|
createVoiceRoutingStatus,
|
|
7393
|
+
createVoiceReplayTimeline,
|
|
7020
7394
|
createVoiceReconnectProfileEvidence,
|
|
7021
7395
|
createVoiceReadinessFailures,
|
|
7022
7396
|
createVoiceProviderStatus,
|
|
@@ -7029,7 +7403,9 @@ export {
|
|
|
7029
7403
|
createVoiceOpsStatus,
|
|
7030
7404
|
createVoiceOpsActionCenter,
|
|
7031
7405
|
createVoiceLiveOps,
|
|
7406
|
+
createVoiceLiveCallViewer,
|
|
7032
7407
|
createVoiceDeliveryRuntime,
|
|
7408
|
+
createVoiceCostDashboard,
|
|
7033
7409
|
createVoiceController,
|
|
7034
7410
|
createVoiceCampaignDialerProof,
|
|
7035
7411
|
createVoiceCallDebugger,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type PropType } from "vue";
|
|
2
|
+
import type { StoredVoiceTraceEvent } from "../trace";
|
|
3
|
+
import { type VoiceCostDashboardOptions } from "../client/costDashboard";
|
|
4
|
+
export declare const VoiceCostDashboard: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
5
|
+
bucketBy: {
|
|
6
|
+
default: VoiceCostDashboardOptions["bucketBy"];
|
|
7
|
+
type: PropType<VoiceCostDashboardOptions["bucketBy"]>;
|
|
8
|
+
};
|
|
9
|
+
currency: {
|
|
10
|
+
default: string;
|
|
11
|
+
type: StringConstructor;
|
|
12
|
+
};
|
|
13
|
+
emptyMessage: {
|
|
14
|
+
default: string;
|
|
15
|
+
type: StringConstructor;
|
|
16
|
+
};
|
|
17
|
+
events: {
|
|
18
|
+
required: true;
|
|
19
|
+
type: PropType<ReadonlyArray<StoredVoiceTraceEvent>>;
|
|
20
|
+
};
|
|
21
|
+
fromMs: NumberConstructor;
|
|
22
|
+
title: {
|
|
23
|
+
default: string;
|
|
24
|
+
type: StringConstructor;
|
|
25
|
+
};
|
|
26
|
+
toMs: NumberConstructor;
|
|
27
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
30
|
+
bucketBy: {
|
|
31
|
+
default: VoiceCostDashboardOptions["bucketBy"];
|
|
32
|
+
type: PropType<VoiceCostDashboardOptions["bucketBy"]>;
|
|
33
|
+
};
|
|
34
|
+
currency: {
|
|
35
|
+
default: string;
|
|
36
|
+
type: StringConstructor;
|
|
37
|
+
};
|
|
38
|
+
emptyMessage: {
|
|
39
|
+
default: string;
|
|
40
|
+
type: StringConstructor;
|
|
41
|
+
};
|
|
42
|
+
events: {
|
|
43
|
+
required: true;
|
|
44
|
+
type: PropType<ReadonlyArray<StoredVoiceTraceEvent>>;
|
|
45
|
+
};
|
|
46
|
+
fromMs: NumberConstructor;
|
|
47
|
+
title: {
|
|
48
|
+
default: string;
|
|
49
|
+
type: StringConstructor;
|
|
50
|
+
};
|
|
51
|
+
toMs: NumberConstructor;
|
|
52
|
+
}>> & Readonly<{}>, {
|
|
53
|
+
title: string;
|
|
54
|
+
bucketBy: "day" | "hour" | "month" | undefined;
|
|
55
|
+
currency: string;
|
|
56
|
+
emptyMessage: string;
|
|
57
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type PropType } from "vue";
|
|
2
|
+
import { type LiveCallViewer } from "../client/liveCallViewer";
|
|
3
|
+
export declare const VoiceLiveCallViewer: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
4
|
+
sessionId: {
|
|
5
|
+
default: string;
|
|
6
|
+
type: StringConstructor;
|
|
7
|
+
};
|
|
8
|
+
title: {
|
|
9
|
+
default: string;
|
|
10
|
+
type: StringConstructor;
|
|
11
|
+
};
|
|
12
|
+
viewer: {
|
|
13
|
+
default: undefined;
|
|
14
|
+
type: PropType<LiveCallViewer | undefined>;
|
|
15
|
+
};
|
|
16
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
17
|
+
[key: string]: any;
|
|
18
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
19
|
+
sessionId: {
|
|
20
|
+
default: string;
|
|
21
|
+
type: StringConstructor;
|
|
22
|
+
};
|
|
23
|
+
title: {
|
|
24
|
+
default: string;
|
|
25
|
+
type: StringConstructor;
|
|
26
|
+
};
|
|
27
|
+
viewer: {
|
|
28
|
+
default: undefined;
|
|
29
|
+
type: PropType<LiveCallViewer | undefined>;
|
|
30
|
+
};
|
|
31
|
+
}>> & Readonly<{}>, {
|
|
32
|
+
title: string;
|
|
33
|
+
sessionId: string;
|
|
34
|
+
viewer: LiveCallViewer | undefined;
|
|
35
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type PropType } from "vue";
|
|
2
|
+
import type { VoiceCallReviewArtifact } from "../testing/review";
|
|
3
|
+
export declare const VoiceReplayTimeline: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
4
|
+
artifact: {
|
|
5
|
+
required: true;
|
|
6
|
+
type: PropType<VoiceCallReviewArtifact>;
|
|
7
|
+
};
|
|
8
|
+
title: StringConstructor;
|
|
9
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
10
|
+
[key: string]: any;
|
|
11
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
12
|
+
artifact: {
|
|
13
|
+
required: true;
|
|
14
|
+
type: PropType<VoiceCallReviewArtifact>;
|
|
15
|
+
};
|
|
16
|
+
title: StringConstructor;
|
|
17
|
+
}>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -32,6 +32,9 @@ export { useVoiceCampaignDialerProof } from "./useVoiceCampaignDialerProof";
|
|
|
32
32
|
export { useVoiceStream } from "./useVoiceStream";
|
|
33
33
|
export { useVoiceController } from "./useVoiceController";
|
|
34
34
|
export { VoiceWidget } from "./VoiceWidget";
|
|
35
|
+
export { VoiceCostDashboard } from "./VoiceCostDashboard";
|
|
36
|
+
export { VoiceLiveCallViewer } from "./VoiceLiveCallViewer";
|
|
37
|
+
export { VoiceReplayTimeline } from "./VoiceReplayTimeline";
|
|
35
38
|
export type { VoiceWidgetLabels, VoiceWidgetTheme, } from "./VoiceWidget";
|
|
36
39
|
export { useVoiceProviderStatus } from "./useVoiceProviderStatus";
|
|
37
40
|
export { useVoiceProviderCapabilities } from "./useVoiceProviderCapabilities";
|