@absolutejs/voice 0.0.22-beta.58 → 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/vue/index.js CHANGED
@@ -374,8 +374,271 @@ var VoiceOpsStatus = defineComponent({
374
374
  };
375
375
  }
376
376
  });
377
- // src/vue/useVoiceStream.ts
377
+ // src/vue/VoiceRoutingStatus.ts
378
+ import { computed, defineComponent as defineComponent2, h as h2 } from "vue";
379
+
380
+ // src/client/routingStatus.ts
381
+ var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
382
+ const fetchImpl = options.fetch ?? globalThis.fetch;
383
+ const response = await fetchImpl(path);
384
+ if (!response.ok) {
385
+ throw new Error(`Voice routing status failed: HTTP ${response.status}`);
386
+ }
387
+ return await response.json();
388
+ };
389
+ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {}) => {
390
+ const listeners = new Set;
391
+ let closed = false;
392
+ let timer;
393
+ let snapshot = {
394
+ decision: null,
395
+ error: null,
396
+ isLoading: false
397
+ };
398
+ const emit = () => {
399
+ for (const listener of listeners) {
400
+ listener();
401
+ }
402
+ };
403
+ const refresh = async () => {
404
+ if (closed) {
405
+ return snapshot.decision;
406
+ }
407
+ snapshot = {
408
+ ...snapshot,
409
+ error: null,
410
+ isLoading: true
411
+ };
412
+ emit();
413
+ try {
414
+ const decision = await fetchVoiceRoutingStatus(path, options);
415
+ snapshot = {
416
+ decision,
417
+ error: null,
418
+ isLoading: false,
419
+ updatedAt: Date.now()
420
+ };
421
+ emit();
422
+ return decision;
423
+ } catch (error) {
424
+ snapshot = {
425
+ ...snapshot,
426
+ error: error instanceof Error ? error.message : String(error),
427
+ isLoading: false
428
+ };
429
+ emit();
430
+ throw error;
431
+ }
432
+ };
433
+ const close = () => {
434
+ closed = true;
435
+ if (timer) {
436
+ clearInterval(timer);
437
+ timer = undefined;
438
+ }
439
+ listeners.clear();
440
+ };
441
+ if (options.intervalMs && options.intervalMs > 0) {
442
+ timer = setInterval(() => {
443
+ refresh().catch(() => {});
444
+ }, options.intervalMs);
445
+ }
446
+ return {
447
+ close,
448
+ getServerSnapshot: () => snapshot,
449
+ getSnapshot: () => snapshot,
450
+ refresh,
451
+ subscribe: (listener) => {
452
+ listeners.add(listener);
453
+ return () => {
454
+ listeners.delete(listener);
455
+ };
456
+ }
457
+ };
458
+ };
459
+
460
+ // src/client/routingStatusWidget.ts
461
+ var DEFAULT_TITLE2 = "Voice Routing";
462
+ var DEFAULT_DESCRIPTION2 = "Latest provider routing decision from the self-hosted trace store.";
463
+ var escapeHtml2 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
464
+ var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
465
+ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
466
+ const decision = snapshot.decision;
467
+ const rows = decision ? [
468
+ { label: "Kind", value: decision.kind.toUpperCase() },
469
+ { label: "Policy", value: formatValue(decision.routing, "Unknown") },
470
+ { label: "Provider", value: formatValue(decision.provider, "Unknown") },
471
+ {
472
+ label: "Selected",
473
+ value: formatValue(decision.selectedProvider, "Unknown")
474
+ },
475
+ {
476
+ label: "Fallback",
477
+ value: formatValue(decision.fallbackProvider)
478
+ },
479
+ { label: "Status", value: formatValue(decision.status, "unknown") },
480
+ {
481
+ label: "Latency budget",
482
+ value: typeof decision.latencyBudgetMs === "number" ? `${decision.latencyBudgetMs}ms` : "None"
483
+ }
484
+ ] : [];
485
+ return {
486
+ decision,
487
+ description: options.description ?? DEFAULT_DESCRIPTION2,
488
+ error: snapshot.error,
489
+ isLoading: snapshot.isLoading,
490
+ label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
491
+ rows,
492
+ status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
493
+ title: options.title ?? DEFAULT_TITLE2,
494
+ updatedAt: snapshot.updatedAt
495
+ };
496
+ };
497
+ var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
498
+ const model = createVoiceRoutingStatusViewModel(snapshot, options);
499
+ const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
500
+ <span>${escapeHtml2(row.label)}</span>
501
+ <strong>${escapeHtml2(row.value)}</strong>
502
+ </div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
503
+ return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml2(model.status)}">
504
+ <header class="absolute-voice-routing-status__header">
505
+ <span class="absolute-voice-routing-status__eyebrow">${escapeHtml2(model.title)}</span>
506
+ <strong class="absolute-voice-routing-status__label">${escapeHtml2(model.label)}</strong>
507
+ </header>
508
+ <p class="absolute-voice-routing-status__description">${escapeHtml2(model.description)}</p>
509
+ ${rows}
510
+ ${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml2(model.error)}</p>` : ""}
511
+ </section>`;
512
+ };
513
+ 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}`;
514
+ var mountVoiceRoutingStatus = (element, path = "/api/routing/latest", options = {}) => {
515
+ const store = createVoiceRoutingStatusStore(path, options);
516
+ const render = () => {
517
+ element.innerHTML = renderVoiceRoutingStatusHTML(store.getSnapshot(), options);
518
+ };
519
+ const unsubscribe = store.subscribe(render);
520
+ render();
521
+ store.refresh().catch(() => {});
522
+ return {
523
+ close: () => {
524
+ unsubscribe();
525
+ store.close();
526
+ },
527
+ refresh: store.refresh
528
+ };
529
+ };
530
+ var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status") => {
531
+ if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
532
+ return;
533
+ }
534
+ customElements.define(tagName, class AbsoluteVoiceRoutingStatusElement extends HTMLElement {
535
+ mounted;
536
+ connectedCallback() {
537
+ const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
538
+ this.mounted = mountVoiceRoutingStatus(this, this.getAttribute("path") ?? "/api/routing/latest", {
539
+ description: this.getAttribute("description") ?? undefined,
540
+ intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
541
+ title: this.getAttribute("title") ?? undefined
542
+ });
543
+ }
544
+ disconnectedCallback() {
545
+ this.mounted?.close();
546
+ this.mounted = undefined;
547
+ }
548
+ });
549
+ };
550
+
551
+ // src/vue/useVoiceRoutingStatus.ts
378
552
  import { onUnmounted as onUnmounted2, ref as ref2, shallowRef as shallowRef2 } from "vue";
553
+ var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
554
+ const store = createVoiceRoutingStatusStore(path, options);
555
+ const decision = shallowRef2(null);
556
+ const error = ref2(null);
557
+ const isLoading = ref2(false);
558
+ const updatedAt = ref2(undefined);
559
+ const sync = () => {
560
+ const snapshot = store.getSnapshot();
561
+ decision.value = snapshot.decision;
562
+ error.value = snapshot.error;
563
+ isLoading.value = snapshot.isLoading;
564
+ updatedAt.value = snapshot.updatedAt;
565
+ };
566
+ const unsubscribe = store.subscribe(sync);
567
+ sync();
568
+ store.refresh().catch(() => {});
569
+ onUnmounted2(() => {
570
+ unsubscribe();
571
+ store.close();
572
+ });
573
+ return {
574
+ decision,
575
+ error,
576
+ isLoading,
577
+ refresh: store.refresh,
578
+ updatedAt
579
+ };
580
+ };
581
+
582
+ // src/vue/VoiceRoutingStatus.ts
583
+ var VoiceRoutingStatus = defineComponent2({
584
+ name: "VoiceRoutingStatus",
585
+ props: {
586
+ class: {
587
+ default: "",
588
+ type: String
589
+ },
590
+ description: {
591
+ default: undefined,
592
+ type: String
593
+ },
594
+ intervalMs: {
595
+ default: 5000,
596
+ type: Number
597
+ },
598
+ path: {
599
+ default: "/api/routing/latest",
600
+ type: String
601
+ },
602
+ title: {
603
+ default: undefined,
604
+ type: String
605
+ }
606
+ },
607
+ setup(props) {
608
+ const options = {
609
+ description: props.description,
610
+ intervalMs: props.intervalMs,
611
+ title: props.title
612
+ };
613
+ const status = useVoiceRoutingStatus(props.path, options);
614
+ const model = computed(() => createVoiceRoutingStatusViewModel({
615
+ decision: status.decision.value,
616
+ error: status.error.value,
617
+ isLoading: status.isLoading.value,
618
+ updatedAt: status.updatedAt.value
619
+ }, options));
620
+ return () => h2("section", {
621
+ class: [
622
+ "absolute-voice-routing-status",
623
+ `absolute-voice-routing-status--${model.value.status}`,
624
+ props.class
625
+ ]
626
+ }, [
627
+ h2("header", { class: "absolute-voice-routing-status__header" }, [
628
+ h2("span", { class: "absolute-voice-routing-status__eyebrow" }, model.value.title),
629
+ h2("strong", { class: "absolute-voice-routing-status__label" }, model.value.label)
630
+ ]),
631
+ h2("p", { class: "absolute-voice-routing-status__description" }, model.value.description),
632
+ model.value.rows.length ? h2("div", { class: "absolute-voice-routing-status__grid" }, model.value.rows.map((row) => h2("div", { key: row.label }, [
633
+ h2("span", row.label),
634
+ h2("strong", row.value)
635
+ ]))) : h2("p", { class: "absolute-voice-routing-status__empty" }, "Start a voice session to see the selected provider."),
636
+ model.value.error ? h2("p", { class: "absolute-voice-routing-status__error" }, model.value.error) : null
637
+ ]);
638
+ }
639
+ });
640
+ // src/vue/useVoiceStream.ts
641
+ import { onUnmounted as onUnmounted3, ref as ref3, shallowRef as shallowRef3 } from "vue";
379
642
 
380
643
  // src/client/actions.ts
381
644
  var normalizeErrorMessage = (value) => {
@@ -893,15 +1156,15 @@ var createVoiceStream = (path, options = {}) => {
893
1156
  // src/vue/useVoiceStream.ts
894
1157
  var useVoiceStream = (path, options = {}) => {
895
1158
  const stream = createVoiceStream(path, options);
896
- const assistantAudio = shallowRef2([]);
897
- const assistantTexts = shallowRef2([]);
898
- const call = shallowRef2(null);
899
- const error = ref2(null);
900
- const isConnected = ref2(false);
901
- const partial = ref2("");
902
- const sessionId = ref2(stream.sessionId);
903
- const status = ref2(stream.status);
904
- const turns = shallowRef2([]);
1159
+ const assistantAudio = shallowRef3([]);
1160
+ const assistantTexts = shallowRef3([]);
1161
+ const call = shallowRef3(null);
1162
+ const error = ref3(null);
1163
+ const isConnected = ref3(false);
1164
+ const partial = ref3("");
1165
+ const sessionId = ref3(stream.sessionId);
1166
+ const status = ref3(stream.status);
1167
+ const turns = shallowRef3([]);
905
1168
  const sync = () => {
906
1169
  assistantAudio.value = [...stream.assistantAudio];
907
1170
  assistantTexts.value = [...stream.assistantTexts];
@@ -919,7 +1182,7 @@ var useVoiceStream = (path, options = {}) => {
919
1182
  unsubscribe();
920
1183
  stream.close();
921
1184
  };
922
- onUnmounted2(destroy);
1185
+ onUnmounted3(destroy);
923
1186
  return {
924
1187
  assistantAudio,
925
1188
  assistantTexts,
@@ -937,7 +1200,7 @@ var useVoiceStream = (path, options = {}) => {
937
1200
  };
938
1201
  };
939
1202
  // src/vue/useVoiceController.ts
940
- import { onUnmounted as onUnmounted3, ref as ref3, shallowRef as shallowRef3 } from "vue";
1203
+ import { onUnmounted as onUnmounted4, ref as ref4, shallowRef as shallowRef4 } from "vue";
941
1204
 
942
1205
  // src/client/htmx.ts
943
1206
  var DEFAULT_EVENT_NAME = "voice-refresh";
@@ -1572,16 +1835,16 @@ var createVoiceController = (path, options = {}) => {
1572
1835
  // src/vue/useVoiceController.ts
1573
1836
  var useVoiceController = (path, options = {}) => {
1574
1837
  const controller = createVoiceController(path, options);
1575
- const assistantAudio = shallowRef3([]);
1576
- const assistantTexts = shallowRef3([]);
1577
- const error = ref3(null);
1578
- const isConnected = ref3(false);
1579
- const isRecording = ref3(false);
1580
- const partial = ref3("");
1581
- const recordingError = ref3(null);
1582
- const sessionId = ref3(controller.sessionId);
1583
- const status = ref3(controller.status);
1584
- const turns = shallowRef3([]);
1838
+ const assistantAudio = shallowRef4([]);
1839
+ const assistantTexts = shallowRef4([]);
1840
+ const error = ref4(null);
1841
+ const isConnected = ref4(false);
1842
+ const isRecording = ref4(false);
1843
+ const partial = ref4("");
1844
+ const recordingError = ref4(null);
1845
+ const sessionId = ref4(controller.sessionId);
1846
+ const status = ref4(controller.status);
1847
+ const turns = shallowRef4([]);
1585
1848
  const sync = () => {
1586
1849
  assistantAudio.value = [...controller.assistantAudio];
1587
1850
  assistantTexts.value = [...controller.assistantTexts];
@@ -1600,7 +1863,7 @@ var useVoiceController = (path, options = {}) => {
1600
1863
  unsubscribe();
1601
1864
  controller.close();
1602
1865
  };
1603
- onUnmounted3(destroy);
1866
+ onUnmounted4(destroy);
1604
1867
  return {
1605
1868
  assistantAudio,
1606
1869
  assistantTexts,
@@ -1622,7 +1885,7 @@ var useVoiceController = (path, options = {}) => {
1622
1885
  };
1623
1886
  };
1624
1887
  // src/vue/useVoiceProviderStatus.ts
1625
- import { onUnmounted as onUnmounted4, ref as ref4, shallowRef as shallowRef4 } from "vue";
1888
+ import { onUnmounted as onUnmounted5, ref as ref5, shallowRef as shallowRef5 } from "vue";
1626
1889
 
1627
1890
  // src/client/providerStatus.ts
1628
1891
  var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
@@ -1707,10 +1970,10 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
1707
1970
  // src/vue/useVoiceProviderStatus.ts
1708
1971
  var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
1709
1972
  const store = createVoiceProviderStatusStore(path, options);
1710
- const error = ref4(null);
1711
- const isLoading = ref4(false);
1712
- const providers = shallowRef4([]);
1713
- const updatedAt = ref4(undefined);
1973
+ const error = ref5(null);
1974
+ const isLoading = ref5(false);
1975
+ const providers = shallowRef5([]);
1976
+ const updatedAt = ref5(undefined);
1714
1977
  const sync = () => {
1715
1978
  const snapshot = store.getSnapshot();
1716
1979
  error.value = snapshot.error;
@@ -1721,7 +1984,7 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
1721
1984
  const unsubscribe = store.subscribe(sync);
1722
1985
  sync();
1723
1986
  store.refresh().catch(() => {});
1724
- onUnmounted4(() => {
1987
+ onUnmounted5(() => {
1725
1988
  unsubscribe();
1726
1989
  store.close();
1727
1990
  });
@@ -1734,7 +1997,7 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
1734
1997
  };
1735
1998
  };
1736
1999
  // src/vue/useVoiceWorkflowStatus.ts
1737
- import { onUnmounted as onUnmounted5, ref as ref5, shallowRef as shallowRef5 } from "vue";
2000
+ import { onUnmounted as onUnmounted6, ref as ref6, shallowRef as shallowRef6 } from "vue";
1738
2001
 
1739
2002
  // src/client/workflowStatus.ts
1740
2003
  var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
@@ -1818,10 +2081,10 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
1818
2081
  // src/vue/useVoiceWorkflowStatus.ts
1819
2082
  var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
1820
2083
  const store = createVoiceWorkflowStatusStore(path, options);
1821
- const error = ref5(null);
1822
- const isLoading = ref5(false);
1823
- const report = shallowRef5(undefined);
1824
- const updatedAt = ref5(undefined);
2084
+ const error = ref6(null);
2085
+ const isLoading = ref6(false);
2086
+ const report = shallowRef6(undefined);
2087
+ const updatedAt = ref6(undefined);
1825
2088
  const sync = () => {
1826
2089
  const snapshot = store.getSnapshot();
1827
2090
  error.value = snapshot.error;
@@ -1834,7 +2097,7 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
1834
2097
  if (typeof window !== "undefined") {
1835
2098
  store.refresh().catch(() => {});
1836
2099
  }
1837
- onUnmounted5(() => {
2100
+ onUnmounted6(() => {
1838
2101
  unsubscribe();
1839
2102
  store.close();
1840
2103
  });
@@ -1849,8 +2112,10 @@ var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
1849
2112
  export {
1850
2113
  useVoiceWorkflowStatus,
1851
2114
  useVoiceStream,
2115
+ useVoiceRoutingStatus,
1852
2116
  useVoiceProviderStatus,
1853
2117
  useVoiceController,
1854
2118
  useVoiceAppKitStatus,
2119
+ VoiceRoutingStatus,
1855
2120
  VoiceOpsStatus
1856
2121
  };
@@ -0,0 +1,8 @@
1
+ import { type VoiceRoutingStatusClientOptions } from '../client/routingStatus';
2
+ export declare const useVoiceRoutingStatus: (path?: string, options?: VoiceRoutingStatusClientOptions) => {
3
+ decision: import("vue").ShallowRef<import("..").VoiceRoutingEvent | null, import("..").VoiceRoutingEvent | null>;
4
+ error: import("vue").Ref<string | null, string | null>;
5
+ isLoading: import("vue").Ref<boolean, boolean>;
6
+ refresh: () => Promise<import("..").VoiceRoutingEvent | null>;
7
+ updatedAt: import("vue").Ref<number | undefined, number | undefined>;
8
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.58",
3
+ "version": "0.0.22-beta.59",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",