@absolutejs/voice 0.0.22-beta.57 → 0.0.22-beta.59
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 +130 -6
- package/dist/angular/voice-routing-status.service.d.ts +11 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +176 -0
- package/dist/client/routingStatus.d.ts +19 -0
- package/dist/client/routingStatusWidget.d.ts +28 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +24 -0
- package/dist/react/VoiceRoutingStatus.d.ts +6 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +265 -16
- package/dist/react/useVoiceRoutingStatus.d.ts +8 -0
- package/dist/resilienceRoutes.d.ts +11 -0
- package/dist/svelte/createVoiceRoutingStatus.d.ts +10 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +181 -0
- package/dist/vue/VoiceRoutingStatus.d.ts +51 -0
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.js +300 -35
- package/dist/vue/useVoiceRoutingStatus.d.ts +8 -0
- package/package.json +1 -1
package/dist/react/index.js
CHANGED
|
@@ -387,9 +387,256 @@ var VoiceOpsStatus = ({
|
|
|
387
387
|
]
|
|
388
388
|
}, undefined, true, undefined, this);
|
|
389
389
|
};
|
|
390
|
-
// src/react/
|
|
390
|
+
// src/react/useVoiceRoutingStatus.tsx
|
|
391
391
|
import { useEffect as useEffect2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
392
392
|
|
|
393
|
+
// src/client/routingStatus.ts
|
|
394
|
+
var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
|
|
395
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
396
|
+
const response = await fetchImpl(path);
|
|
397
|
+
if (!response.ok) {
|
|
398
|
+
throw new Error(`Voice routing status failed: HTTP ${response.status}`);
|
|
399
|
+
}
|
|
400
|
+
return await response.json();
|
|
401
|
+
};
|
|
402
|
+
var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {}) => {
|
|
403
|
+
const listeners = new Set;
|
|
404
|
+
let closed = false;
|
|
405
|
+
let timer;
|
|
406
|
+
let snapshot = {
|
|
407
|
+
decision: null,
|
|
408
|
+
error: null,
|
|
409
|
+
isLoading: false
|
|
410
|
+
};
|
|
411
|
+
const emit = () => {
|
|
412
|
+
for (const listener of listeners) {
|
|
413
|
+
listener();
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const refresh = async () => {
|
|
417
|
+
if (closed) {
|
|
418
|
+
return snapshot.decision;
|
|
419
|
+
}
|
|
420
|
+
snapshot = {
|
|
421
|
+
...snapshot,
|
|
422
|
+
error: null,
|
|
423
|
+
isLoading: true
|
|
424
|
+
};
|
|
425
|
+
emit();
|
|
426
|
+
try {
|
|
427
|
+
const decision = await fetchVoiceRoutingStatus(path, options);
|
|
428
|
+
snapshot = {
|
|
429
|
+
decision,
|
|
430
|
+
error: null,
|
|
431
|
+
isLoading: false,
|
|
432
|
+
updatedAt: Date.now()
|
|
433
|
+
};
|
|
434
|
+
emit();
|
|
435
|
+
return decision;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
snapshot = {
|
|
438
|
+
...snapshot,
|
|
439
|
+
error: error instanceof Error ? error.message : String(error),
|
|
440
|
+
isLoading: false
|
|
441
|
+
};
|
|
442
|
+
emit();
|
|
443
|
+
throw error;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
const close = () => {
|
|
447
|
+
closed = true;
|
|
448
|
+
if (timer) {
|
|
449
|
+
clearInterval(timer);
|
|
450
|
+
timer = undefined;
|
|
451
|
+
}
|
|
452
|
+
listeners.clear();
|
|
453
|
+
};
|
|
454
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
455
|
+
timer = setInterval(() => {
|
|
456
|
+
refresh().catch(() => {});
|
|
457
|
+
}, options.intervalMs);
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
close,
|
|
461
|
+
getServerSnapshot: () => snapshot,
|
|
462
|
+
getSnapshot: () => snapshot,
|
|
463
|
+
refresh,
|
|
464
|
+
subscribe: (listener) => {
|
|
465
|
+
listeners.add(listener);
|
|
466
|
+
return () => {
|
|
467
|
+
listeners.delete(listener);
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/react/useVoiceRoutingStatus.tsx
|
|
474
|
+
var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
|
|
475
|
+
const storeRef = useRef2(null);
|
|
476
|
+
if (!storeRef.current) {
|
|
477
|
+
storeRef.current = createVoiceRoutingStatusStore(path, options);
|
|
478
|
+
}
|
|
479
|
+
const store = storeRef.current;
|
|
480
|
+
useEffect2(() => {
|
|
481
|
+
store.refresh().catch(() => {});
|
|
482
|
+
return () => store.close();
|
|
483
|
+
}, [store]);
|
|
484
|
+
return {
|
|
485
|
+
...useSyncExternalStore2(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
486
|
+
refresh: store.refresh
|
|
487
|
+
};
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// src/client/routingStatusWidget.ts
|
|
491
|
+
var DEFAULT_TITLE2 = "Voice Routing";
|
|
492
|
+
var DEFAULT_DESCRIPTION2 = "Latest provider routing decision from the self-hosted trace store.";
|
|
493
|
+
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
494
|
+
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
495
|
+
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
496
|
+
const decision = snapshot.decision;
|
|
497
|
+
const rows = decision ? [
|
|
498
|
+
{ label: "Kind", value: decision.kind.toUpperCase() },
|
|
499
|
+
{ label: "Policy", value: formatValue(decision.routing, "Unknown") },
|
|
500
|
+
{ label: "Provider", value: formatValue(decision.provider, "Unknown") },
|
|
501
|
+
{
|
|
502
|
+
label: "Selected",
|
|
503
|
+
value: formatValue(decision.selectedProvider, "Unknown")
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
label: "Fallback",
|
|
507
|
+
value: formatValue(decision.fallbackProvider)
|
|
508
|
+
},
|
|
509
|
+
{ label: "Status", value: formatValue(decision.status, "unknown") },
|
|
510
|
+
{
|
|
511
|
+
label: "Latency budget",
|
|
512
|
+
value: typeof decision.latencyBudgetMs === "number" ? `${decision.latencyBudgetMs}ms` : "None"
|
|
513
|
+
}
|
|
514
|
+
] : [];
|
|
515
|
+
return {
|
|
516
|
+
decision,
|
|
517
|
+
description: options.description ?? DEFAULT_DESCRIPTION2,
|
|
518
|
+
error: snapshot.error,
|
|
519
|
+
isLoading: snapshot.isLoading,
|
|
520
|
+
label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
|
|
521
|
+
rows,
|
|
522
|
+
status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
523
|
+
title: options.title ?? DEFAULT_TITLE2,
|
|
524
|
+
updatedAt: snapshot.updatedAt
|
|
525
|
+
};
|
|
526
|
+
};
|
|
527
|
+
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
528
|
+
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
529
|
+
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
530
|
+
<span>${escapeHtml2(row.label)}</span>
|
|
531
|
+
<strong>${escapeHtml2(row.value)}</strong>
|
|
532
|
+
</div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
|
|
533
|
+
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml2(model.status)}">
|
|
534
|
+
<header class="absolute-voice-routing-status__header">
|
|
535
|
+
<span class="absolute-voice-routing-status__eyebrow">${escapeHtml2(model.title)}</span>
|
|
536
|
+
<strong class="absolute-voice-routing-status__label">${escapeHtml2(model.label)}</strong>
|
|
537
|
+
</header>
|
|
538
|
+
<p class="absolute-voice-routing-status__description">${escapeHtml2(model.description)}</p>
|
|
539
|
+
${rows}
|
|
540
|
+
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml2(model.error)}</p>` : ""}
|
|
541
|
+
</section>`;
|
|
542
|
+
};
|
|
543
|
+
var getVoiceRoutingStatusCSS = () => `.absolute-voice-routing-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-routing-status--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-routing-status__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-routing-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-routing-status__label{font-size:24px;line-height:1}.absolute-voice-routing-status__description{color:#514733;margin:12px 0 0}.absolute-voice-routing-status__grid{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:14px}.absolute-voice-routing-status__grid div{background:#fff;border:1px solid #eee4d2;border-radius:14px;padding:10px 12px}.absolute-voice-routing-status__grid span{color:#655944;display:block;font-size:12px;margin-bottom:4px}.absolute-voice-routing-status__grid strong{overflow-wrap:anywhere}.absolute-voice-routing-status__empty{color:#655944;margin:14px 0 0}.absolute-voice-routing-status__error{color:#9f1239;font-weight:700}`;
|
|
544
|
+
var mountVoiceRoutingStatus = (element, path = "/api/routing/latest", options = {}) => {
|
|
545
|
+
const store = createVoiceRoutingStatusStore(path, options);
|
|
546
|
+
const render = () => {
|
|
547
|
+
element.innerHTML = renderVoiceRoutingStatusHTML(store.getSnapshot(), options);
|
|
548
|
+
};
|
|
549
|
+
const unsubscribe = store.subscribe(render);
|
|
550
|
+
render();
|
|
551
|
+
store.refresh().catch(() => {});
|
|
552
|
+
return {
|
|
553
|
+
close: () => {
|
|
554
|
+
unsubscribe();
|
|
555
|
+
store.close();
|
|
556
|
+
},
|
|
557
|
+
refresh: store.refresh
|
|
558
|
+
};
|
|
559
|
+
};
|
|
560
|
+
var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status") => {
|
|
561
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
customElements.define(tagName, class AbsoluteVoiceRoutingStatusElement extends HTMLElement {
|
|
565
|
+
mounted;
|
|
566
|
+
connectedCallback() {
|
|
567
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
568
|
+
this.mounted = mountVoiceRoutingStatus(this, this.getAttribute("path") ?? "/api/routing/latest", {
|
|
569
|
+
description: this.getAttribute("description") ?? undefined,
|
|
570
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
571
|
+
title: this.getAttribute("title") ?? undefined
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
disconnectedCallback() {
|
|
575
|
+
this.mounted?.close();
|
|
576
|
+
this.mounted = undefined;
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// src/react/VoiceRoutingStatus.tsx
|
|
582
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
583
|
+
var VoiceRoutingStatus = ({
|
|
584
|
+
className,
|
|
585
|
+
path = "/api/routing/latest",
|
|
586
|
+
...options
|
|
587
|
+
}) => {
|
|
588
|
+
const snapshot = useVoiceRoutingStatus(path, options);
|
|
589
|
+
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
590
|
+
return /* @__PURE__ */ jsxDEV2("section", {
|
|
591
|
+
className: [
|
|
592
|
+
"absolute-voice-routing-status",
|
|
593
|
+
`absolute-voice-routing-status--${model.status}`,
|
|
594
|
+
className
|
|
595
|
+
].filter(Boolean).join(" "),
|
|
596
|
+
children: [
|
|
597
|
+
/* @__PURE__ */ jsxDEV2("header", {
|
|
598
|
+
className: "absolute-voice-routing-status__header",
|
|
599
|
+
children: [
|
|
600
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
601
|
+
className: "absolute-voice-routing-status__eyebrow",
|
|
602
|
+
children: model.title
|
|
603
|
+
}, undefined, false, undefined, this),
|
|
604
|
+
/* @__PURE__ */ jsxDEV2("strong", {
|
|
605
|
+
className: "absolute-voice-routing-status__label",
|
|
606
|
+
children: model.label
|
|
607
|
+
}, undefined, false, undefined, this)
|
|
608
|
+
]
|
|
609
|
+
}, undefined, true, undefined, this),
|
|
610
|
+
/* @__PURE__ */ jsxDEV2("p", {
|
|
611
|
+
className: "absolute-voice-routing-status__description",
|
|
612
|
+
children: model.description
|
|
613
|
+
}, undefined, false, undefined, this),
|
|
614
|
+
model.rows.length ? /* @__PURE__ */ jsxDEV2("div", {
|
|
615
|
+
className: "absolute-voice-routing-status__grid",
|
|
616
|
+
children: model.rows.map((row) => /* @__PURE__ */ jsxDEV2("div", {
|
|
617
|
+
children: [
|
|
618
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
619
|
+
children: row.label
|
|
620
|
+
}, undefined, false, undefined, this),
|
|
621
|
+
/* @__PURE__ */ jsxDEV2("strong", {
|
|
622
|
+
children: row.value
|
|
623
|
+
}, undefined, false, undefined, this)
|
|
624
|
+
]
|
|
625
|
+
}, row.label, true, undefined, this))
|
|
626
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV2("p", {
|
|
627
|
+
className: "absolute-voice-routing-status__empty",
|
|
628
|
+
children: "Start a voice session to see the selected provider."
|
|
629
|
+
}, undefined, false, undefined, this),
|
|
630
|
+
model.error ? /* @__PURE__ */ jsxDEV2("p", {
|
|
631
|
+
className: "absolute-voice-routing-status__error",
|
|
632
|
+
children: model.error
|
|
633
|
+
}, undefined, false, undefined, this) : null
|
|
634
|
+
]
|
|
635
|
+
}, undefined, true, undefined, this);
|
|
636
|
+
};
|
|
637
|
+
// src/react/useVoiceStream.tsx
|
|
638
|
+
import { useEffect as useEffect3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
639
|
+
|
|
393
640
|
// src/client/actions.ts
|
|
394
641
|
var normalizeErrorMessage = (value) => {
|
|
395
642
|
if (typeof value === "string" && value.trim()) {
|
|
@@ -916,13 +1163,13 @@ var EMPTY_SNAPSHOT = {
|
|
|
916
1163
|
turns: []
|
|
917
1164
|
};
|
|
918
1165
|
var useVoiceStream = (path, options = {}) => {
|
|
919
|
-
const streamRef =
|
|
1166
|
+
const streamRef = useRef3(null);
|
|
920
1167
|
if (!streamRef.current) {
|
|
921
1168
|
streamRef.current = createVoiceStream(path, options);
|
|
922
1169
|
}
|
|
923
1170
|
const stream = streamRef.current;
|
|
924
|
-
|
|
925
|
-
const snapshot =
|
|
1171
|
+
useEffect3(() => () => stream.close(), [stream]);
|
|
1172
|
+
const snapshot = useSyncExternalStore3(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
|
|
926
1173
|
return {
|
|
927
1174
|
...snapshot,
|
|
928
1175
|
callControl: (message) => stream.callControl(message),
|
|
@@ -932,7 +1179,7 @@ var useVoiceStream = (path, options = {}) => {
|
|
|
932
1179
|
};
|
|
933
1180
|
};
|
|
934
1181
|
// src/react/useVoiceController.tsx
|
|
935
|
-
import { useEffect as
|
|
1182
|
+
import { useEffect as useEffect4, useRef as useRef4, useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
936
1183
|
|
|
937
1184
|
// src/client/htmx.ts
|
|
938
1185
|
var DEFAULT_EVENT_NAME = "voice-refresh";
|
|
@@ -1579,13 +1826,13 @@ var EMPTY_SNAPSHOT2 = {
|
|
|
1579
1826
|
turns: []
|
|
1580
1827
|
};
|
|
1581
1828
|
var useVoiceController = (path, options = {}) => {
|
|
1582
|
-
const controllerRef =
|
|
1829
|
+
const controllerRef = useRef4(null);
|
|
1583
1830
|
if (!controllerRef.current) {
|
|
1584
1831
|
controllerRef.current = createVoiceController(path, options);
|
|
1585
1832
|
}
|
|
1586
1833
|
const controller = controllerRef.current;
|
|
1587
|
-
|
|
1588
|
-
const snapshot =
|
|
1834
|
+
useEffect4(() => () => controller.close(), [controller]);
|
|
1835
|
+
const snapshot = useSyncExternalStore4(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
|
|
1589
1836
|
return {
|
|
1590
1837
|
...snapshot,
|
|
1591
1838
|
bindHTMX: controller.bindHTMX,
|
|
@@ -1599,7 +1846,7 @@ var useVoiceController = (path, options = {}) => {
|
|
|
1599
1846
|
};
|
|
1600
1847
|
};
|
|
1601
1848
|
// src/react/useVoiceProviderStatus.tsx
|
|
1602
|
-
import { useEffect as
|
|
1849
|
+
import { useEffect as useEffect5, useRef as useRef5, useSyncExternalStore as useSyncExternalStore5 } from "react";
|
|
1603
1850
|
|
|
1604
1851
|
// src/client/providerStatus.ts
|
|
1605
1852
|
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
@@ -1683,22 +1930,22 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
|
|
|
1683
1930
|
|
|
1684
1931
|
// src/react/useVoiceProviderStatus.tsx
|
|
1685
1932
|
var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
1686
|
-
const storeRef =
|
|
1933
|
+
const storeRef = useRef5(null);
|
|
1687
1934
|
if (!storeRef.current) {
|
|
1688
1935
|
storeRef.current = createVoiceProviderStatusStore(path, options);
|
|
1689
1936
|
}
|
|
1690
1937
|
const store = storeRef.current;
|
|
1691
|
-
|
|
1938
|
+
useEffect5(() => {
|
|
1692
1939
|
store.refresh().catch(() => {});
|
|
1693
1940
|
return () => store.close();
|
|
1694
1941
|
}, [store]);
|
|
1695
1942
|
return {
|
|
1696
|
-
...
|
|
1943
|
+
...useSyncExternalStore5(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
1697
1944
|
refresh: store.refresh
|
|
1698
1945
|
};
|
|
1699
1946
|
};
|
|
1700
1947
|
// src/react/useVoiceWorkflowStatus.tsx
|
|
1701
|
-
import { useEffect as
|
|
1948
|
+
import { useEffect as useEffect6, useRef as useRef6, useSyncExternalStore as useSyncExternalStore6 } from "react";
|
|
1702
1949
|
|
|
1703
1950
|
// src/client/workflowStatus.ts
|
|
1704
1951
|
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
@@ -1781,25 +2028,27 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
|
|
|
1781
2028
|
|
|
1782
2029
|
// src/react/useVoiceWorkflowStatus.tsx
|
|
1783
2030
|
var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
|
|
1784
|
-
const storeRef =
|
|
2031
|
+
const storeRef = useRef6(null);
|
|
1785
2032
|
if (!storeRef.current) {
|
|
1786
2033
|
storeRef.current = createVoiceWorkflowStatusStore(path, options);
|
|
1787
2034
|
}
|
|
1788
2035
|
const store = storeRef.current;
|
|
1789
|
-
|
|
2036
|
+
useEffect6(() => {
|
|
1790
2037
|
store.refresh().catch(() => {});
|
|
1791
2038
|
return () => store.close();
|
|
1792
2039
|
}, [store]);
|
|
1793
2040
|
return {
|
|
1794
|
-
...
|
|
2041
|
+
...useSyncExternalStore6(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
1795
2042
|
refresh: store.refresh
|
|
1796
2043
|
};
|
|
1797
2044
|
};
|
|
1798
2045
|
export {
|
|
1799
2046
|
useVoiceWorkflowStatus,
|
|
1800
2047
|
useVoiceStream,
|
|
2048
|
+
useVoiceRoutingStatus,
|
|
1801
2049
|
useVoiceProviderStatus,
|
|
1802
2050
|
useVoiceController,
|
|
1803
2051
|
useVoiceAppKitStatus,
|
|
2052
|
+
VoiceRoutingStatus,
|
|
1804
2053
|
VoiceOpsStatus
|
|
1805
2054
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type VoiceRoutingStatusClientOptions } from '../client/routingStatus';
|
|
2
|
+
export declare const useVoiceRoutingStatus: (path?: string, options?: VoiceRoutingStatusClientOptions) => {
|
|
3
|
+
refresh: () => Promise<import("..").VoiceRoutingEvent | null>;
|
|
4
|
+
decision: import("..").VoiceRoutingDecisionSummary | null;
|
|
5
|
+
error: string | null;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
updatedAt?: number;
|
|
8
|
+
};
|
|
@@ -13,12 +13,21 @@ export type VoiceRoutingEvent = {
|
|
|
13
13
|
latencyBudgetMs?: number;
|
|
14
14
|
operation?: string;
|
|
15
15
|
provider?: string;
|
|
16
|
+
routing?: string;
|
|
16
17
|
selectedProvider?: string;
|
|
17
18
|
sessionId: string;
|
|
18
19
|
status?: string;
|
|
20
|
+
suppressionRemainingMs?: number;
|
|
19
21
|
timedOut: boolean;
|
|
20
22
|
turnId?: string;
|
|
21
23
|
};
|
|
24
|
+
export type VoiceRoutingDecisionSummary = VoiceRoutingEvent;
|
|
25
|
+
export type VoiceRoutingDecisionSummaryOptions = {
|
|
26
|
+
kind?: VoiceRoutingEventKind;
|
|
27
|
+
limit?: number;
|
|
28
|
+
sessionId?: string;
|
|
29
|
+
store: VoiceTraceEventStore;
|
|
30
|
+
};
|
|
22
31
|
export type VoiceResilienceLink = {
|
|
23
32
|
href: string;
|
|
24
33
|
label: string;
|
|
@@ -63,6 +72,8 @@ export type VoiceResilienceRoutesOptions = {
|
|
|
63
72
|
ttsSimulation?: VoiceResilienceIOSimulator<string>;
|
|
64
73
|
};
|
|
65
74
|
export declare const listVoiceRoutingEvents: (events: StoredVoiceTraceEvent[]) => VoiceRoutingEvent[];
|
|
75
|
+
export declare const summarizeVoiceRoutingDecision: (events: StoredVoiceTraceEvent[], options?: Omit<VoiceRoutingDecisionSummaryOptions, "store">) => VoiceRoutingDecisionSummary | null;
|
|
76
|
+
export declare const createVoiceRoutingDecisionSummary: (options: VoiceRoutingDecisionSummaryOptions) => Promise<VoiceRoutingDecisionSummary | null>;
|
|
66
77
|
export declare const renderVoiceResilienceHTML: (input: VoiceResiliencePageData) => string;
|
|
67
78
|
export declare const createVoiceResilienceRoutes: (options: VoiceResilienceRoutesOptions) => Elysia<"", {
|
|
68
79
|
decorator: {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type VoiceRoutingStatusWidgetOptions } from '../client/routingStatusWidget';
|
|
2
|
+
export declare const createVoiceRoutingStatus: (path?: string, options?: VoiceRoutingStatusWidgetOptions) => {
|
|
3
|
+
getHTML: () => string;
|
|
4
|
+
getViewModel: () => import("../client").VoiceRoutingStatusViewModel;
|
|
5
|
+
close: () => void;
|
|
6
|
+
getServerSnapshot: () => import("../client").VoiceRoutingStatusSnapshot;
|
|
7
|
+
getSnapshot: () => import("../client").VoiceRoutingStatusSnapshot;
|
|
8
|
+
refresh: () => Promise<import("..").VoiceRoutingEvent | null>;
|
|
9
|
+
subscribe: (listener: () => void) => () => void;
|
|
10
|
+
};
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -2,5 +2,6 @@ export { createVoiceAppKitStatus } from './createVoiceAppKitStatus';
|
|
|
2
2
|
export { createVoiceOpsStatus } from './createVoiceOpsStatus';
|
|
3
3
|
export { createVoiceStream } from './createVoiceStream';
|
|
4
4
|
export { createVoiceProviderStatus } from './createVoiceProviderStatus';
|
|
5
|
+
export { createVoiceRoutingStatus } from './createVoiceRoutingStatus';
|
|
5
6
|
export { createVoiceWorkflowStatus } from './createVoiceWorkflowStatus';
|
|
6
7
|
export { createVoiceController } from '../client/controller';
|
package/dist/svelte/index.js
CHANGED
|
@@ -884,6 +884,186 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
|
|
|
884
884
|
|
|
885
885
|
// src/svelte/createVoiceProviderStatus.ts
|
|
886
886
|
var createVoiceProviderStatus = (path = "/api/provider-status", options = {}) => createVoiceProviderStatusStore(path, options);
|
|
887
|
+
// src/client/routingStatus.ts
|
|
888
|
+
var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
|
|
889
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
890
|
+
const response = await fetchImpl(path);
|
|
891
|
+
if (!response.ok) {
|
|
892
|
+
throw new Error(`Voice routing status failed: HTTP ${response.status}`);
|
|
893
|
+
}
|
|
894
|
+
return await response.json();
|
|
895
|
+
};
|
|
896
|
+
var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {}) => {
|
|
897
|
+
const listeners = new Set;
|
|
898
|
+
let closed = false;
|
|
899
|
+
let timer;
|
|
900
|
+
let snapshot = {
|
|
901
|
+
decision: null,
|
|
902
|
+
error: null,
|
|
903
|
+
isLoading: false
|
|
904
|
+
};
|
|
905
|
+
const emit = () => {
|
|
906
|
+
for (const listener of listeners) {
|
|
907
|
+
listener();
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
const refresh = async () => {
|
|
911
|
+
if (closed) {
|
|
912
|
+
return snapshot.decision;
|
|
913
|
+
}
|
|
914
|
+
snapshot = {
|
|
915
|
+
...snapshot,
|
|
916
|
+
error: null,
|
|
917
|
+
isLoading: true
|
|
918
|
+
};
|
|
919
|
+
emit();
|
|
920
|
+
try {
|
|
921
|
+
const decision = await fetchVoiceRoutingStatus(path, options);
|
|
922
|
+
snapshot = {
|
|
923
|
+
decision,
|
|
924
|
+
error: null,
|
|
925
|
+
isLoading: false,
|
|
926
|
+
updatedAt: Date.now()
|
|
927
|
+
};
|
|
928
|
+
emit();
|
|
929
|
+
return decision;
|
|
930
|
+
} catch (error) {
|
|
931
|
+
snapshot = {
|
|
932
|
+
...snapshot,
|
|
933
|
+
error: error instanceof Error ? error.message : String(error),
|
|
934
|
+
isLoading: false
|
|
935
|
+
};
|
|
936
|
+
emit();
|
|
937
|
+
throw error;
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
const close = () => {
|
|
941
|
+
closed = true;
|
|
942
|
+
if (timer) {
|
|
943
|
+
clearInterval(timer);
|
|
944
|
+
timer = undefined;
|
|
945
|
+
}
|
|
946
|
+
listeners.clear();
|
|
947
|
+
};
|
|
948
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
949
|
+
timer = setInterval(() => {
|
|
950
|
+
refresh().catch(() => {});
|
|
951
|
+
}, options.intervalMs);
|
|
952
|
+
}
|
|
953
|
+
return {
|
|
954
|
+
close,
|
|
955
|
+
getServerSnapshot: () => snapshot,
|
|
956
|
+
getSnapshot: () => snapshot,
|
|
957
|
+
refresh,
|
|
958
|
+
subscribe: (listener) => {
|
|
959
|
+
listeners.add(listener);
|
|
960
|
+
return () => {
|
|
961
|
+
listeners.delete(listener);
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
// src/client/routingStatusWidget.ts
|
|
968
|
+
var DEFAULT_TITLE2 = "Voice Routing";
|
|
969
|
+
var DEFAULT_DESCRIPTION2 = "Latest provider routing decision from the self-hosted trace store.";
|
|
970
|
+
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
971
|
+
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
972
|
+
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
973
|
+
const decision = snapshot.decision;
|
|
974
|
+
const rows = decision ? [
|
|
975
|
+
{ label: "Kind", value: decision.kind.toUpperCase() },
|
|
976
|
+
{ label: "Policy", value: formatValue(decision.routing, "Unknown") },
|
|
977
|
+
{ label: "Provider", value: formatValue(decision.provider, "Unknown") },
|
|
978
|
+
{
|
|
979
|
+
label: "Selected",
|
|
980
|
+
value: formatValue(decision.selectedProvider, "Unknown")
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
label: "Fallback",
|
|
984
|
+
value: formatValue(decision.fallbackProvider)
|
|
985
|
+
},
|
|
986
|
+
{ label: "Status", value: formatValue(decision.status, "unknown") },
|
|
987
|
+
{
|
|
988
|
+
label: "Latency budget",
|
|
989
|
+
value: typeof decision.latencyBudgetMs === "number" ? `${decision.latencyBudgetMs}ms` : "None"
|
|
990
|
+
}
|
|
991
|
+
] : [];
|
|
992
|
+
return {
|
|
993
|
+
decision,
|
|
994
|
+
description: options.description ?? DEFAULT_DESCRIPTION2,
|
|
995
|
+
error: snapshot.error,
|
|
996
|
+
isLoading: snapshot.isLoading,
|
|
997
|
+
label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
|
|
998
|
+
rows,
|
|
999
|
+
status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1000
|
+
title: options.title ?? DEFAULT_TITLE2,
|
|
1001
|
+
updatedAt: snapshot.updatedAt
|
|
1002
|
+
};
|
|
1003
|
+
};
|
|
1004
|
+
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
1005
|
+
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
1006
|
+
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
1007
|
+
<span>${escapeHtml2(row.label)}</span>
|
|
1008
|
+
<strong>${escapeHtml2(row.value)}</strong>
|
|
1009
|
+
</div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
|
|
1010
|
+
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml2(model.status)}">
|
|
1011
|
+
<header class="absolute-voice-routing-status__header">
|
|
1012
|
+
<span class="absolute-voice-routing-status__eyebrow">${escapeHtml2(model.title)}</span>
|
|
1013
|
+
<strong class="absolute-voice-routing-status__label">${escapeHtml2(model.label)}</strong>
|
|
1014
|
+
</header>
|
|
1015
|
+
<p class="absolute-voice-routing-status__description">${escapeHtml2(model.description)}</p>
|
|
1016
|
+
${rows}
|
|
1017
|
+
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml2(model.error)}</p>` : ""}
|
|
1018
|
+
</section>`;
|
|
1019
|
+
};
|
|
1020
|
+
var getVoiceRoutingStatusCSS = () => `.absolute-voice-routing-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-routing-status--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-routing-status__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-routing-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-routing-status__label{font-size:24px;line-height:1}.absolute-voice-routing-status__description{color:#514733;margin:12px 0 0}.absolute-voice-routing-status__grid{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:14px}.absolute-voice-routing-status__grid div{background:#fff;border:1px solid #eee4d2;border-radius:14px;padding:10px 12px}.absolute-voice-routing-status__grid span{color:#655944;display:block;font-size:12px;margin-bottom:4px}.absolute-voice-routing-status__grid strong{overflow-wrap:anywhere}.absolute-voice-routing-status__empty{color:#655944;margin:14px 0 0}.absolute-voice-routing-status__error{color:#9f1239;font-weight:700}`;
|
|
1021
|
+
var mountVoiceRoutingStatus = (element, path = "/api/routing/latest", options = {}) => {
|
|
1022
|
+
const store = createVoiceRoutingStatusStore(path, options);
|
|
1023
|
+
const render = () => {
|
|
1024
|
+
element.innerHTML = renderVoiceRoutingStatusHTML(store.getSnapshot(), options);
|
|
1025
|
+
};
|
|
1026
|
+
const unsubscribe = store.subscribe(render);
|
|
1027
|
+
render();
|
|
1028
|
+
store.refresh().catch(() => {});
|
|
1029
|
+
return {
|
|
1030
|
+
close: () => {
|
|
1031
|
+
unsubscribe();
|
|
1032
|
+
store.close();
|
|
1033
|
+
},
|
|
1034
|
+
refresh: store.refresh
|
|
1035
|
+
};
|
|
1036
|
+
};
|
|
1037
|
+
var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status") => {
|
|
1038
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
customElements.define(tagName, class AbsoluteVoiceRoutingStatusElement extends HTMLElement {
|
|
1042
|
+
mounted;
|
|
1043
|
+
connectedCallback() {
|
|
1044
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
1045
|
+
this.mounted = mountVoiceRoutingStatus(this, this.getAttribute("path") ?? "/api/routing/latest", {
|
|
1046
|
+
description: this.getAttribute("description") ?? undefined,
|
|
1047
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
1048
|
+
title: this.getAttribute("title") ?? undefined
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
disconnectedCallback() {
|
|
1052
|
+
this.mounted?.close();
|
|
1053
|
+
this.mounted = undefined;
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
// src/svelte/createVoiceRoutingStatus.ts
|
|
1059
|
+
var createVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
|
|
1060
|
+
const store = createVoiceRoutingStatusStore(path, options);
|
|
1061
|
+
return {
|
|
1062
|
+
...store,
|
|
1063
|
+
getHTML: () => renderVoiceRoutingStatusHTML(store.getSnapshot(), options),
|
|
1064
|
+
getViewModel: () => createVoiceRoutingStatusViewModel(store.getSnapshot(), options)
|
|
1065
|
+
};
|
|
1066
|
+
};
|
|
887
1067
|
// src/client/workflowStatus.ts
|
|
888
1068
|
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
889
1069
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -1597,6 +1777,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1597
1777
|
export {
|
|
1598
1778
|
createVoiceWorkflowStatus,
|
|
1599
1779
|
createVoiceStream2 as createVoiceStream,
|
|
1780
|
+
createVoiceRoutingStatus,
|
|
1600
1781
|
createVoiceProviderStatus,
|
|
1601
1782
|
createVoiceOpsStatus,
|
|
1602
1783
|
createVoiceController,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export declare const VoiceRoutingStatus: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
|
|
2
|
+
class: {
|
|
3
|
+
default: string;
|
|
4
|
+
type: StringConstructor;
|
|
5
|
+
};
|
|
6
|
+
description: {
|
|
7
|
+
default: undefined;
|
|
8
|
+
type: StringConstructor;
|
|
9
|
+
};
|
|
10
|
+
intervalMs: {
|
|
11
|
+
default: number;
|
|
12
|
+
type: NumberConstructor;
|
|
13
|
+
};
|
|
14
|
+
path: {
|
|
15
|
+
default: string;
|
|
16
|
+
type: StringConstructor;
|
|
17
|
+
};
|
|
18
|
+
title: {
|
|
19
|
+
default: undefined;
|
|
20
|
+
type: StringConstructor;
|
|
21
|
+
};
|
|
22
|
+
}>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
|
25
|
+
class: {
|
|
26
|
+
default: string;
|
|
27
|
+
type: StringConstructor;
|
|
28
|
+
};
|
|
29
|
+
description: {
|
|
30
|
+
default: undefined;
|
|
31
|
+
type: StringConstructor;
|
|
32
|
+
};
|
|
33
|
+
intervalMs: {
|
|
34
|
+
default: number;
|
|
35
|
+
type: NumberConstructor;
|
|
36
|
+
};
|
|
37
|
+
path: {
|
|
38
|
+
default: string;
|
|
39
|
+
type: StringConstructor;
|
|
40
|
+
};
|
|
41
|
+
title: {
|
|
42
|
+
default: undefined;
|
|
43
|
+
type: StringConstructor;
|
|
44
|
+
};
|
|
45
|
+
}>> & Readonly<{}>, {
|
|
46
|
+
description: string;
|
|
47
|
+
title: string;
|
|
48
|
+
path: string;
|
|
49
|
+
intervalMs: number;
|
|
50
|
+
class: string;
|
|
51
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|