@absolutejs/voice 0.0.22-beta.351 → 0.0.22-beta.353
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 +313 -192
- package/dist/angular/voice-profile-comparison.service.d.ts +12 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +429 -211
- package/dist/client/profileComparison.d.ts +19 -0
- package/dist/client/profileComparisonWidget.d.ts +41 -0
- package/dist/client/routingStatusWidget.d.ts +4 -0
- package/dist/react/VoiceProfileComparison.d.ts +6 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +715 -396
- package/dist/react/useVoiceProfileComparison.d.ts +8 -0
- package/dist/svelte/createVoiceProfileComparison.d.ts +7 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +128 -1
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +203 -52
- package/dist/vue/useVoiceProfileComparison.d.ts +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type VoiceProfileComparisonClientOptions } from '../client/profileComparison';
|
|
2
|
+
export declare const useVoiceProfileComparison: (path?: string, options?: VoiceProfileComparisonClientOptions) => {
|
|
3
|
+
refresh: () => Promise<import("..").VoiceRealCallProfileHistoryReport | undefined>;
|
|
4
|
+
error: string | null;
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
report?: import("..").VoiceRealCallProfileHistoryReport;
|
|
7
|
+
updatedAt?: number;
|
|
8
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { VoiceProfileComparisonClientOptions } from '../client/profileComparison';
|
|
2
|
+
export declare const createVoiceProfileComparison: (path?: string, options?: VoiceProfileComparisonClientOptions) => {
|
|
3
|
+
close: () => void;
|
|
4
|
+
getSnapshot: () => import("../client").VoiceProfileComparisonSnapshot;
|
|
5
|
+
refresh: () => Promise<import("..").VoiceRealCallProfileHistoryReport | undefined>;
|
|
6
|
+
subscribe: (listener: () => void) => () => void;
|
|
7
|
+
};
|
package/dist/svelte/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { createVoiceLiveOps } from './createVoiceLiveOps';
|
|
|
5
5
|
export { createVoiceOpsStatus } from './createVoiceOpsStatus';
|
|
6
6
|
export { createVoicePlatformCoverage } from './createVoicePlatformCoverage';
|
|
7
7
|
export { createVoiceProofTrends } from './createVoiceProofTrends';
|
|
8
|
+
export { createVoiceProfileComparison } from './createVoiceProfileComparison';
|
|
8
9
|
export { createVoiceReadinessFailures } from './createVoiceReadinessFailures';
|
|
9
10
|
export { createVoiceProviderSimulationControls } from './createVoiceProviderSimulationControls';
|
|
10
11
|
export { createVoiceProviderCapabilities } from './createVoiceProviderCapabilities';
|
package/dist/svelte/index.js
CHANGED
|
@@ -2588,6 +2588,91 @@ var createVoiceProofTrends = (path = "/api/voice/proof-trends", options = {}) =>
|
|
|
2588
2588
|
subscribe: store.subscribe
|
|
2589
2589
|
};
|
|
2590
2590
|
};
|
|
2591
|
+
// src/client/profileComparison.ts
|
|
2592
|
+
var fetchVoiceProfileComparison = async (path = "/api/voice/real-call-profile-history", options = {}) => {
|
|
2593
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
2594
|
+
const response = await fetchImpl(path);
|
|
2595
|
+
if (!response.ok) {
|
|
2596
|
+
throw new Error(`Voice profile comparison failed: HTTP ${response.status}`);
|
|
2597
|
+
}
|
|
2598
|
+
return await response.json();
|
|
2599
|
+
};
|
|
2600
|
+
var createVoiceProfileComparisonStore = (path = "/api/voice/real-call-profile-history", options = {}) => {
|
|
2601
|
+
const listeners = new Set;
|
|
2602
|
+
let closed = false;
|
|
2603
|
+
let timer;
|
|
2604
|
+
let snapshot = {
|
|
2605
|
+
error: null,
|
|
2606
|
+
isLoading: false
|
|
2607
|
+
};
|
|
2608
|
+
const emit = () => {
|
|
2609
|
+
for (const listener of listeners) {
|
|
2610
|
+
listener();
|
|
2611
|
+
}
|
|
2612
|
+
};
|
|
2613
|
+
const refresh = async () => {
|
|
2614
|
+
if (closed) {
|
|
2615
|
+
return snapshot.report;
|
|
2616
|
+
}
|
|
2617
|
+
snapshot = { ...snapshot, error: null, isLoading: true };
|
|
2618
|
+
emit();
|
|
2619
|
+
try {
|
|
2620
|
+
const report = await fetchVoiceProfileComparison(path, options);
|
|
2621
|
+
snapshot = {
|
|
2622
|
+
error: null,
|
|
2623
|
+
isLoading: false,
|
|
2624
|
+
report,
|
|
2625
|
+
updatedAt: Date.now()
|
|
2626
|
+
};
|
|
2627
|
+
emit();
|
|
2628
|
+
return report;
|
|
2629
|
+
} catch (error) {
|
|
2630
|
+
snapshot = {
|
|
2631
|
+
...snapshot,
|
|
2632
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2633
|
+
isLoading: false
|
|
2634
|
+
};
|
|
2635
|
+
emit();
|
|
2636
|
+
throw error;
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
const close = () => {
|
|
2640
|
+
closed = true;
|
|
2641
|
+
if (timer) {
|
|
2642
|
+
clearInterval(timer);
|
|
2643
|
+
timer = undefined;
|
|
2644
|
+
}
|
|
2645
|
+
listeners.clear();
|
|
2646
|
+
};
|
|
2647
|
+
if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
|
|
2648
|
+
timer = setInterval(() => {
|
|
2649
|
+
refresh().catch(() => {});
|
|
2650
|
+
}, options.intervalMs);
|
|
2651
|
+
}
|
|
2652
|
+
return {
|
|
2653
|
+
close,
|
|
2654
|
+
getServerSnapshot: () => snapshot,
|
|
2655
|
+
getSnapshot: () => snapshot,
|
|
2656
|
+
refresh,
|
|
2657
|
+
subscribe: (listener) => {
|
|
2658
|
+
listeners.add(listener);
|
|
2659
|
+
return () => {
|
|
2660
|
+
listeners.delete(listener);
|
|
2661
|
+
};
|
|
2662
|
+
}
|
|
2663
|
+
};
|
|
2664
|
+
};
|
|
2665
|
+
|
|
2666
|
+
// src/svelte/createVoiceProfileComparison.ts
|
|
2667
|
+
var createVoiceProfileComparison = (path = "/api/voice/real-call-profile-history", options = {}) => {
|
|
2668
|
+
const store = createVoiceProfileComparisonStore(path, options);
|
|
2669
|
+
return {
|
|
2670
|
+
close: store.close,
|
|
2671
|
+
getSnapshot: store.getSnapshot,
|
|
2672
|
+
refresh: store.refresh,
|
|
2673
|
+
subscribe: store.subscribe
|
|
2674
|
+
};
|
|
2675
|
+
};
|
|
2591
2676
|
// src/client/readinessFailures.ts
|
|
2592
2677
|
var fetchVoiceReadinessFailures = async (path = "/api/production-readiness", options = {}) => {
|
|
2593
2678
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -4932,8 +5017,43 @@ var DEFAULT_DESCRIPTION7 = "Latest provider routing decision from the self-hoste
|
|
|
4932
5017
|
var escapeHtml10 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
4933
5018
|
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
4934
5019
|
var formatProviderRoutes = (routes) => routes && typeof routes === "object" ? Object.entries(routes).map(([role, provider]) => `${role}: ${formatValue(provider)}`).join(", ") || "None" : "None";
|
|
5020
|
+
var getProviderRoute = (routes, role) => routes && typeof routes === "object" ? formatValue(routes[role], "Not configured") : "Not configured";
|
|
5021
|
+
var formatFallbackPath = (decision) => {
|
|
5022
|
+
const provider = formatValue(decision.provider, "Unknown");
|
|
5023
|
+
const selectedProvider = formatValue(decision.selectedProvider, provider);
|
|
5024
|
+
const fallbackProvider = formatValue(decision.fallbackProvider, "");
|
|
5025
|
+
if (fallbackProvider !== "None" && fallbackProvider.trim()) {
|
|
5026
|
+
return `${provider} -> ${fallbackProvider}`;
|
|
5027
|
+
}
|
|
5028
|
+
if (selectedProvider !== provider) {
|
|
5029
|
+
return `${provider} -> ${selectedProvider}`;
|
|
5030
|
+
}
|
|
5031
|
+
return `${provider} primary`;
|
|
5032
|
+
};
|
|
4935
5033
|
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
4936
5034
|
const decision = snapshot.decision;
|
|
5035
|
+
const activeStack = decision ? [
|
|
5036
|
+
{
|
|
5037
|
+
label: "Profile",
|
|
5038
|
+
value: formatValue(decision.profileLabel ?? decision.profileId)
|
|
5039
|
+
},
|
|
5040
|
+
{
|
|
5041
|
+
label: "LLM",
|
|
5042
|
+
value: getProviderRoute(decision.providerRoutes, "llm")
|
|
5043
|
+
},
|
|
5044
|
+
{
|
|
5045
|
+
label: "STT",
|
|
5046
|
+
value: getProviderRoute(decision.providerRoutes, "stt")
|
|
5047
|
+
},
|
|
5048
|
+
{
|
|
5049
|
+
label: "TTS",
|
|
5050
|
+
value: getProviderRoute(decision.providerRoutes, "tts")
|
|
5051
|
+
},
|
|
5052
|
+
{
|
|
5053
|
+
label: "Fallback path",
|
|
5054
|
+
value: formatFallbackPath(decision)
|
|
5055
|
+
}
|
|
5056
|
+
] : [];
|
|
4937
5057
|
const rows = decision ? [
|
|
4938
5058
|
{ label: "Kind", value: decision.kind.toUpperCase() },
|
|
4939
5059
|
{ label: "Policy", value: formatValue(decision.routing, "Unknown") },
|
|
@@ -4961,6 +5081,7 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
4961
5081
|
}
|
|
4962
5082
|
] : [];
|
|
4963
5083
|
return {
|
|
5084
|
+
activeStack,
|
|
4964
5085
|
decision,
|
|
4965
5086
|
description: options.description ?? DEFAULT_DESCRIPTION7,
|
|
4966
5087
|
error: snapshot.error,
|
|
@@ -4974,6 +5095,10 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
4974
5095
|
};
|
|
4975
5096
|
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
4976
5097
|
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
5098
|
+
const activeStack = model.activeStack.length ? `<div class="absolute-voice-routing-status__stack" aria-label="Active voice stack">${model.activeStack.map((item) => `<div>
|
|
5099
|
+
<span>${escapeHtml10(item.label)}</span>
|
|
5100
|
+
<strong>${escapeHtml10(item.value)}</strong>
|
|
5101
|
+
</div>`).join("")}</div>` : "";
|
|
4977
5102
|
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
4978
5103
|
<span>${escapeHtml10(row.label)}</span>
|
|
4979
5104
|
<strong>${escapeHtml10(row.value)}</strong>
|
|
@@ -4984,11 +5109,12 @@ var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
|
4984
5109
|
<strong class="absolute-voice-routing-status__label">${escapeHtml10(model.label)}</strong>
|
|
4985
5110
|
</header>
|
|
4986
5111
|
<p class="absolute-voice-routing-status__description">${escapeHtml10(model.description)}</p>
|
|
5112
|
+
${activeStack}
|
|
4987
5113
|
${rows}
|
|
4988
5114
|
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml10(model.error)}</p>` : ""}
|
|
4989
5115
|
</section>`;
|
|
4990
5116
|
};
|
|
4991
|
-
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}`;
|
|
5117
|
+
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__stack{background:linear-gradient(135deg,#16130d,#49391f);border-radius:18px;color:#fff;display:grid;gap:8px;grid-template-columns:repeat(5,minmax(0,1fr));margin-top:14px;padding:12px}.absolute-voice-routing-status__stack div{border-left:1px solid rgba(255,255,255,.18);padding-left:10px}.absolute-voice-routing-status__stack div:first-child{border-left:0;padding-left:0}.absolute-voice-routing-status__stack span{color:#e9d9b8;display:block;font-size:11px;font-weight:800;letter-spacing:.08em;margin-bottom:5px;text-transform:uppercase}.absolute-voice-routing-status__stack strong{display:block;font-size:13px;line-height:1.25;overflow-wrap:anywhere}.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}@media (max-width:760px){.absolute-voice-routing-status__stack{grid-template-columns:repeat(2,minmax(0,1fr))}.absolute-voice-routing-status__stack div{border-left:0;border-top:1px solid rgba(255,255,255,.18);padding-left:0;padding-top:8px}.absolute-voice-routing-status__stack div:first-child{border-top:0;padding-top:0}}`;
|
|
4992
5118
|
var mountVoiceRoutingStatus = (element, path = "/api/routing/latest", options = {}) => {
|
|
4993
5119
|
const store = createVoiceRoutingStatusStore(path, options);
|
|
4994
5120
|
const render = () => {
|
|
@@ -6546,6 +6672,7 @@ export {
|
|
|
6546
6672
|
createVoiceProviderContracts,
|
|
6547
6673
|
createVoiceProviderCapabilities,
|
|
6548
6674
|
createVoiceProofTrends,
|
|
6675
|
+
createVoiceProfileComparison,
|
|
6549
6676
|
createVoicePlatformCoverage,
|
|
6550
6677
|
createVoiceOpsStatus,
|
|
6551
6678
|
createVoiceOpsActionCenter,
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { VoiceTurnQuality } from './VoiceTurnQuality';
|
|
|
15
15
|
export { useVoiceOpsStatus } from './useVoiceOpsStatus';
|
|
16
16
|
export { useVoicePlatformCoverage } from './useVoicePlatformCoverage';
|
|
17
17
|
export { useVoiceProofTrends } from './useVoiceProofTrends';
|
|
18
|
+
export { useVoiceProfileComparison } from './useVoiceProfileComparison';
|
|
18
19
|
export { useVoiceReadinessFailures } from './useVoiceReadinessFailures';
|
|
19
20
|
export { useVoiceOpsActionCenter } from './useVoiceOpsActionCenter';
|
|
20
21
|
export { useVoiceLiveOps } from './useVoiceLiveOps';
|
package/dist/vue/index.js
CHANGED
|
@@ -4280,8 +4280,43 @@ var DEFAULT_DESCRIPTION10 = "Latest provider routing decision from the self-host
|
|
|
4280
4280
|
var escapeHtml12 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
4281
4281
|
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
4282
4282
|
var formatProviderRoutes = (routes) => routes && typeof routes === "object" ? Object.entries(routes).map(([role, provider]) => `${role}: ${formatValue(provider)}`).join(", ") || "None" : "None";
|
|
4283
|
+
var getProviderRoute = (routes, role) => routes && typeof routes === "object" ? formatValue(routes[role], "Not configured") : "Not configured";
|
|
4284
|
+
var formatFallbackPath = (decision) => {
|
|
4285
|
+
const provider = formatValue(decision.provider, "Unknown");
|
|
4286
|
+
const selectedProvider = formatValue(decision.selectedProvider, provider);
|
|
4287
|
+
const fallbackProvider = formatValue(decision.fallbackProvider, "");
|
|
4288
|
+
if (fallbackProvider !== "None" && fallbackProvider.trim()) {
|
|
4289
|
+
return `${provider} -> ${fallbackProvider}`;
|
|
4290
|
+
}
|
|
4291
|
+
if (selectedProvider !== provider) {
|
|
4292
|
+
return `${provider} -> ${selectedProvider}`;
|
|
4293
|
+
}
|
|
4294
|
+
return `${provider} primary`;
|
|
4295
|
+
};
|
|
4283
4296
|
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
4284
4297
|
const decision = snapshot.decision;
|
|
4298
|
+
const activeStack = decision ? [
|
|
4299
|
+
{
|
|
4300
|
+
label: "Profile",
|
|
4301
|
+
value: formatValue(decision.profileLabel ?? decision.profileId)
|
|
4302
|
+
},
|
|
4303
|
+
{
|
|
4304
|
+
label: "LLM",
|
|
4305
|
+
value: getProviderRoute(decision.providerRoutes, "llm")
|
|
4306
|
+
},
|
|
4307
|
+
{
|
|
4308
|
+
label: "STT",
|
|
4309
|
+
value: getProviderRoute(decision.providerRoutes, "stt")
|
|
4310
|
+
},
|
|
4311
|
+
{
|
|
4312
|
+
label: "TTS",
|
|
4313
|
+
value: getProviderRoute(decision.providerRoutes, "tts")
|
|
4314
|
+
},
|
|
4315
|
+
{
|
|
4316
|
+
label: "Fallback path",
|
|
4317
|
+
value: formatFallbackPath(decision)
|
|
4318
|
+
}
|
|
4319
|
+
] : [];
|
|
4285
4320
|
const rows = decision ? [
|
|
4286
4321
|
{ label: "Kind", value: decision.kind.toUpperCase() },
|
|
4287
4322
|
{ label: "Policy", value: formatValue(decision.routing, "Unknown") },
|
|
@@ -4309,6 +4344,7 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
4309
4344
|
}
|
|
4310
4345
|
] : [];
|
|
4311
4346
|
return {
|
|
4347
|
+
activeStack,
|
|
4312
4348
|
decision,
|
|
4313
4349
|
description: options.description ?? DEFAULT_DESCRIPTION10,
|
|
4314
4350
|
error: snapshot.error,
|
|
@@ -4322,6 +4358,10 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
4322
4358
|
};
|
|
4323
4359
|
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
4324
4360
|
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
4361
|
+
const activeStack = model.activeStack.length ? `<div class="absolute-voice-routing-status__stack" aria-label="Active voice stack">${model.activeStack.map((item) => `<div>
|
|
4362
|
+
<span>${escapeHtml12(item.label)}</span>
|
|
4363
|
+
<strong>${escapeHtml12(item.value)}</strong>
|
|
4364
|
+
</div>`).join("")}</div>` : "";
|
|
4325
4365
|
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
4326
4366
|
<span>${escapeHtml12(row.label)}</span>
|
|
4327
4367
|
<strong>${escapeHtml12(row.value)}</strong>
|
|
@@ -4332,11 +4372,12 @@ var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
|
4332
4372
|
<strong class="absolute-voice-routing-status__label">${escapeHtml12(model.label)}</strong>
|
|
4333
4373
|
</header>
|
|
4334
4374
|
<p class="absolute-voice-routing-status__description">${escapeHtml12(model.description)}</p>
|
|
4375
|
+
${activeStack}
|
|
4335
4376
|
${rows}
|
|
4336
4377
|
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml12(model.error)}</p>` : ""}
|
|
4337
4378
|
</section>`;
|
|
4338
4379
|
};
|
|
4339
|
-
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}`;
|
|
4380
|
+
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__stack{background:linear-gradient(135deg,#16130d,#49391f);border-radius:18px;color:#fff;display:grid;gap:8px;grid-template-columns:repeat(5,minmax(0,1fr));margin-top:14px;padding:12px}.absolute-voice-routing-status__stack div{border-left:1px solid rgba(255,255,255,.18);padding-left:10px}.absolute-voice-routing-status__stack div:first-child{border-left:0;padding-left:0}.absolute-voice-routing-status__stack span{color:#e9d9b8;display:block;font-size:11px;font-weight:800;letter-spacing:.08em;margin-bottom:5px;text-transform:uppercase}.absolute-voice-routing-status__stack strong{display:block;font-size:13px;line-height:1.25;overflow-wrap:anywhere}.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}@media (max-width:760px){.absolute-voice-routing-status__stack{grid-template-columns:repeat(2,minmax(0,1fr))}.absolute-voice-routing-status__stack div{border-left:0;border-top:1px solid rgba(255,255,255,.18);padding-left:0;padding-top:8px}.absolute-voice-routing-status__stack div:first-child{border-top:0;padding-top:0}}`;
|
|
4340
4381
|
var mountVoiceRoutingStatus = (element, path = "/api/routing/latest", options = {}) => {
|
|
4341
4382
|
const store = createVoiceRoutingStatusStore(path, options);
|
|
4342
4383
|
const render = () => {
|
|
@@ -5233,9 +5274,118 @@ var VoiceTurnQuality = defineComponent13({
|
|
|
5233
5274
|
]);
|
|
5234
5275
|
}
|
|
5235
5276
|
});
|
|
5236
|
-
// src/vue/
|
|
5277
|
+
// src/vue/useVoiceProfileComparison.ts
|
|
5237
5278
|
import { onUnmounted as onUnmounted14, ref as ref11, shallowRef as shallowRef13 } from "vue";
|
|
5238
5279
|
|
|
5280
|
+
// src/client/profileComparison.ts
|
|
5281
|
+
var fetchVoiceProfileComparison = async (path = "/api/voice/real-call-profile-history", options = {}) => {
|
|
5282
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
5283
|
+
const response = await fetchImpl(path);
|
|
5284
|
+
if (!response.ok) {
|
|
5285
|
+
throw new Error(`Voice profile comparison failed: HTTP ${response.status}`);
|
|
5286
|
+
}
|
|
5287
|
+
return await response.json();
|
|
5288
|
+
};
|
|
5289
|
+
var createVoiceProfileComparisonStore = (path = "/api/voice/real-call-profile-history", options = {}) => {
|
|
5290
|
+
const listeners = new Set;
|
|
5291
|
+
let closed = false;
|
|
5292
|
+
let timer;
|
|
5293
|
+
let snapshot = {
|
|
5294
|
+
error: null,
|
|
5295
|
+
isLoading: false
|
|
5296
|
+
};
|
|
5297
|
+
const emit = () => {
|
|
5298
|
+
for (const listener of listeners) {
|
|
5299
|
+
listener();
|
|
5300
|
+
}
|
|
5301
|
+
};
|
|
5302
|
+
const refresh = async () => {
|
|
5303
|
+
if (closed) {
|
|
5304
|
+
return snapshot.report;
|
|
5305
|
+
}
|
|
5306
|
+
snapshot = { ...snapshot, error: null, isLoading: true };
|
|
5307
|
+
emit();
|
|
5308
|
+
try {
|
|
5309
|
+
const report = await fetchVoiceProfileComparison(path, options);
|
|
5310
|
+
snapshot = {
|
|
5311
|
+
error: null,
|
|
5312
|
+
isLoading: false,
|
|
5313
|
+
report,
|
|
5314
|
+
updatedAt: Date.now()
|
|
5315
|
+
};
|
|
5316
|
+
emit();
|
|
5317
|
+
return report;
|
|
5318
|
+
} catch (error) {
|
|
5319
|
+
snapshot = {
|
|
5320
|
+
...snapshot,
|
|
5321
|
+
error: error instanceof Error ? error.message : String(error),
|
|
5322
|
+
isLoading: false
|
|
5323
|
+
};
|
|
5324
|
+
emit();
|
|
5325
|
+
throw error;
|
|
5326
|
+
}
|
|
5327
|
+
};
|
|
5328
|
+
const close = () => {
|
|
5329
|
+
closed = true;
|
|
5330
|
+
if (timer) {
|
|
5331
|
+
clearInterval(timer);
|
|
5332
|
+
timer = undefined;
|
|
5333
|
+
}
|
|
5334
|
+
listeners.clear();
|
|
5335
|
+
};
|
|
5336
|
+
if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
|
|
5337
|
+
timer = setInterval(() => {
|
|
5338
|
+
refresh().catch(() => {});
|
|
5339
|
+
}, options.intervalMs);
|
|
5340
|
+
}
|
|
5341
|
+
return {
|
|
5342
|
+
close,
|
|
5343
|
+
getServerSnapshot: () => snapshot,
|
|
5344
|
+
getSnapshot: () => snapshot,
|
|
5345
|
+
refresh,
|
|
5346
|
+
subscribe: (listener) => {
|
|
5347
|
+
listeners.add(listener);
|
|
5348
|
+
return () => {
|
|
5349
|
+
listeners.delete(listener);
|
|
5350
|
+
};
|
|
5351
|
+
}
|
|
5352
|
+
};
|
|
5353
|
+
};
|
|
5354
|
+
|
|
5355
|
+
// src/vue/useVoiceProfileComparison.ts
|
|
5356
|
+
function useVoiceProfileComparison(path = "/api/voice/real-call-profile-history", options = {}) {
|
|
5357
|
+
const store = createVoiceProfileComparisonStore(path, options);
|
|
5358
|
+
const error = ref11(null);
|
|
5359
|
+
const isLoading = ref11(false);
|
|
5360
|
+
const report = shallowRef13(undefined);
|
|
5361
|
+
const updatedAt = ref11(undefined);
|
|
5362
|
+
const sync = () => {
|
|
5363
|
+
const snapshot = store.getSnapshot();
|
|
5364
|
+
error.value = snapshot.error;
|
|
5365
|
+
isLoading.value = snapshot.isLoading;
|
|
5366
|
+
report.value = snapshot.report;
|
|
5367
|
+
updatedAt.value = snapshot.updatedAt;
|
|
5368
|
+
};
|
|
5369
|
+
const unsubscribe = store.subscribe(sync);
|
|
5370
|
+
sync();
|
|
5371
|
+
if (typeof window !== "undefined") {
|
|
5372
|
+
store.refresh().catch(() => {});
|
|
5373
|
+
}
|
|
5374
|
+
onUnmounted14(() => {
|
|
5375
|
+
unsubscribe();
|
|
5376
|
+
store.close();
|
|
5377
|
+
});
|
|
5378
|
+
return {
|
|
5379
|
+
error,
|
|
5380
|
+
isLoading,
|
|
5381
|
+
refresh: store.refresh,
|
|
5382
|
+
report,
|
|
5383
|
+
updatedAt
|
|
5384
|
+
};
|
|
5385
|
+
}
|
|
5386
|
+
// src/vue/useVoiceLiveOps.ts
|
|
5387
|
+
import { onUnmounted as onUnmounted15, ref as ref12, shallowRef as shallowRef14 } from "vue";
|
|
5388
|
+
|
|
5239
5389
|
// src/client/liveOps.ts
|
|
5240
5390
|
var postVoiceLiveOpsAction = async (input, options = {}) => {
|
|
5241
5391
|
if (!input.sessionId) {
|
|
@@ -5325,11 +5475,11 @@ var createVoiceLiveOpsStore = (options = {}) => {
|
|
|
5325
5475
|
// src/vue/useVoiceLiveOps.ts
|
|
5326
5476
|
function useVoiceLiveOps(options = {}) {
|
|
5327
5477
|
const store = createVoiceLiveOpsStore(options);
|
|
5328
|
-
const error =
|
|
5329
|
-
const isRunning =
|
|
5330
|
-
const lastResult =
|
|
5331
|
-
const runningAction =
|
|
5332
|
-
const updatedAt =
|
|
5478
|
+
const error = ref12(null);
|
|
5479
|
+
const isRunning = ref12(false);
|
|
5480
|
+
const lastResult = shallowRef14(undefined);
|
|
5481
|
+
const runningAction = ref12(undefined);
|
|
5482
|
+
const updatedAt = ref12(undefined);
|
|
5333
5483
|
const sync = () => {
|
|
5334
5484
|
const snapshot = store.getSnapshot();
|
|
5335
5485
|
error.value = snapshot.error;
|
|
@@ -5340,7 +5490,7 @@ function useVoiceLiveOps(options = {}) {
|
|
|
5340
5490
|
};
|
|
5341
5491
|
const unsubscribe = store.subscribe(sync);
|
|
5342
5492
|
sync();
|
|
5343
|
-
|
|
5493
|
+
onUnmounted15(() => {
|
|
5344
5494
|
unsubscribe();
|
|
5345
5495
|
store.close();
|
|
5346
5496
|
});
|
|
@@ -5354,7 +5504,7 @@ function useVoiceLiveOps(options = {}) {
|
|
|
5354
5504
|
};
|
|
5355
5505
|
}
|
|
5356
5506
|
// src/vue/useVoiceCampaignDialerProof.ts
|
|
5357
|
-
import { onUnmounted as
|
|
5507
|
+
import { onUnmounted as onUnmounted16, shallowRef as shallowRef15 } from "vue";
|
|
5358
5508
|
|
|
5359
5509
|
// src/client/campaignDialerProof.ts
|
|
5360
5510
|
var fetchVoiceCampaignDialerProofStatus = async (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
|
|
@@ -5477,11 +5627,11 @@ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-p
|
|
|
5477
5627
|
// src/vue/useVoiceCampaignDialerProof.ts
|
|
5478
5628
|
function useVoiceCampaignDialerProof(path = "/api/voice/campaigns/dialer-proof", options = {}) {
|
|
5479
5629
|
const store = createVoiceCampaignDialerProofStore(path, options);
|
|
5480
|
-
const error =
|
|
5481
|
-
const isLoading =
|
|
5482
|
-
const report =
|
|
5483
|
-
const status =
|
|
5484
|
-
const updatedAt =
|
|
5630
|
+
const error = shallowRef15(null);
|
|
5631
|
+
const isLoading = shallowRef15(false);
|
|
5632
|
+
const report = shallowRef15();
|
|
5633
|
+
const status = shallowRef15();
|
|
5634
|
+
const updatedAt = shallowRef15(undefined);
|
|
5485
5635
|
const sync = () => {
|
|
5486
5636
|
const snapshot = store.getSnapshot();
|
|
5487
5637
|
error.value = snapshot.error;
|
|
@@ -5495,7 +5645,7 @@ function useVoiceCampaignDialerProof(path = "/api/voice/campaigns/dialer-proof",
|
|
|
5495
5645
|
if (typeof window !== "undefined") {
|
|
5496
5646
|
store.refresh().catch(() => {});
|
|
5497
5647
|
}
|
|
5498
|
-
|
|
5648
|
+
onUnmounted16(() => {
|
|
5499
5649
|
unsubscribe();
|
|
5500
5650
|
store.close();
|
|
5501
5651
|
});
|
|
@@ -5510,7 +5660,7 @@ function useVoiceCampaignDialerProof(path = "/api/voice/campaigns/dialer-proof",
|
|
|
5510
5660
|
};
|
|
5511
5661
|
}
|
|
5512
5662
|
// src/vue/useVoiceStream.ts
|
|
5513
|
-
import { onUnmounted as
|
|
5663
|
+
import { onUnmounted as onUnmounted17, ref as ref13, shallowRef as shallowRef16 } from "vue";
|
|
5514
5664
|
|
|
5515
5665
|
// src/client/actions.ts
|
|
5516
5666
|
var normalizeErrorMessage = (value) => {
|
|
@@ -6897,16 +7047,16 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
6897
7047
|
// src/vue/useVoiceStream.ts
|
|
6898
7048
|
function useVoiceStream(path, options = {}) {
|
|
6899
7049
|
const stream = createVoiceStream(path, options);
|
|
6900
|
-
const assistantAudio =
|
|
6901
|
-
const assistantTexts =
|
|
6902
|
-
const call =
|
|
6903
|
-
const error =
|
|
6904
|
-
const isConnected =
|
|
6905
|
-
const partial =
|
|
6906
|
-
const reconnect =
|
|
6907
|
-
const sessionId =
|
|
6908
|
-
const status =
|
|
6909
|
-
const turns =
|
|
7050
|
+
const assistantAudio = shallowRef16([]);
|
|
7051
|
+
const assistantTexts = shallowRef16([]);
|
|
7052
|
+
const call = shallowRef16(null);
|
|
7053
|
+
const error = ref13(null);
|
|
7054
|
+
const isConnected = ref13(false);
|
|
7055
|
+
const partial = ref13("");
|
|
7056
|
+
const reconnect = shallowRef16(stream.reconnect);
|
|
7057
|
+
const sessionId = ref13(stream.sessionId);
|
|
7058
|
+
const status = ref13(stream.status);
|
|
7059
|
+
const turns = shallowRef16([]);
|
|
6910
7060
|
const sync = () => {
|
|
6911
7061
|
assistantAudio.value = [...stream.assistantAudio];
|
|
6912
7062
|
assistantTexts.value = [...stream.assistantTexts];
|
|
@@ -6925,7 +7075,7 @@ function useVoiceStream(path, options = {}) {
|
|
|
6925
7075
|
unsubscribe();
|
|
6926
7076
|
stream.close();
|
|
6927
7077
|
};
|
|
6928
|
-
|
|
7078
|
+
onUnmounted17(destroy);
|
|
6929
7079
|
return {
|
|
6930
7080
|
assistantAudio,
|
|
6931
7081
|
assistantTexts,
|
|
@@ -6944,7 +7094,7 @@ function useVoiceStream(path, options = {}) {
|
|
|
6944
7094
|
};
|
|
6945
7095
|
}
|
|
6946
7096
|
// src/vue/useVoiceController.ts
|
|
6947
|
-
import { onUnmounted as
|
|
7097
|
+
import { onUnmounted as onUnmounted18, ref as ref14, shallowRef as shallowRef17 } from "vue";
|
|
6948
7098
|
|
|
6949
7099
|
// src/client/htmx.ts
|
|
6950
7100
|
var DEFAULT_EVENT_NAME = "voice-refresh";
|
|
@@ -7590,17 +7740,17 @@ var createVoiceController = (path, options = {}) => {
|
|
|
7590
7740
|
// src/vue/useVoiceController.ts
|
|
7591
7741
|
function useVoiceController(path, options = {}) {
|
|
7592
7742
|
const controller = createVoiceController(path, options);
|
|
7593
|
-
const assistantAudio =
|
|
7594
|
-
const assistantTexts =
|
|
7595
|
-
const error =
|
|
7596
|
-
const isConnected =
|
|
7597
|
-
const isRecording =
|
|
7598
|
-
const partial =
|
|
7599
|
-
const reconnect =
|
|
7600
|
-
const recordingError =
|
|
7601
|
-
const sessionId =
|
|
7602
|
-
const status =
|
|
7603
|
-
const turns =
|
|
7743
|
+
const assistantAudio = shallowRef17([]);
|
|
7744
|
+
const assistantTexts = shallowRef17([]);
|
|
7745
|
+
const error = ref14(null);
|
|
7746
|
+
const isConnected = ref14(false);
|
|
7747
|
+
const isRecording = ref14(false);
|
|
7748
|
+
const partial = ref14("");
|
|
7749
|
+
const reconnect = shallowRef17(controller.reconnect);
|
|
7750
|
+
const recordingError = ref14(null);
|
|
7751
|
+
const sessionId = ref14(controller.sessionId);
|
|
7752
|
+
const status = ref14(controller.status);
|
|
7753
|
+
const turns = shallowRef17([]);
|
|
7604
7754
|
const sync = () => {
|
|
7605
7755
|
assistantAudio.value = [...controller.assistantAudio];
|
|
7606
7756
|
assistantTexts.value = [...controller.assistantTexts];
|
|
@@ -7620,7 +7770,7 @@ function useVoiceController(path, options = {}) {
|
|
|
7620
7770
|
unsubscribe();
|
|
7621
7771
|
controller.close();
|
|
7622
7772
|
};
|
|
7623
|
-
|
|
7773
|
+
onUnmounted18(destroy);
|
|
7624
7774
|
return {
|
|
7625
7775
|
assistantAudio,
|
|
7626
7776
|
assistantTexts,
|
|
@@ -7643,13 +7793,13 @@ function useVoiceController(path, options = {}) {
|
|
|
7643
7793
|
};
|
|
7644
7794
|
}
|
|
7645
7795
|
// src/vue/useVoiceTraceTimeline.ts
|
|
7646
|
-
import { onUnmounted as
|
|
7796
|
+
import { onUnmounted as onUnmounted19, ref as ref15, shallowRef as shallowRef18 } from "vue";
|
|
7647
7797
|
function useVoiceTraceTimeline(path = "/api/voice-traces", options = {}) {
|
|
7648
7798
|
const store = createVoiceTraceTimelineStore(path, options);
|
|
7649
|
-
const error =
|
|
7650
|
-
const isLoading =
|
|
7651
|
-
const report =
|
|
7652
|
-
const updatedAt =
|
|
7799
|
+
const error = ref15(null);
|
|
7800
|
+
const isLoading = ref15(false);
|
|
7801
|
+
const report = shallowRef18(null);
|
|
7802
|
+
const updatedAt = ref15(undefined);
|
|
7653
7803
|
const sync = () => {
|
|
7654
7804
|
const snapshot = store.getSnapshot();
|
|
7655
7805
|
error.value = snapshot.error;
|
|
@@ -7660,7 +7810,7 @@ function useVoiceTraceTimeline(path = "/api/voice-traces", options = {}) {
|
|
|
7660
7810
|
const unsubscribe = store.subscribe(sync);
|
|
7661
7811
|
sync();
|
|
7662
7812
|
store.refresh().catch(() => {});
|
|
7663
|
-
|
|
7813
|
+
onUnmounted19(() => {
|
|
7664
7814
|
unsubscribe();
|
|
7665
7815
|
store.close();
|
|
7666
7816
|
});
|
|
@@ -7673,7 +7823,7 @@ function useVoiceTraceTimeline(path = "/api/voice-traces", options = {}) {
|
|
|
7673
7823
|
};
|
|
7674
7824
|
}
|
|
7675
7825
|
// src/vue/useVoiceWorkflowStatus.ts
|
|
7676
|
-
import { onUnmounted as
|
|
7826
|
+
import { onUnmounted as onUnmounted20, ref as ref16, shallowRef as shallowRef19 } from "vue";
|
|
7677
7827
|
|
|
7678
7828
|
// src/client/workflowStatus.ts
|
|
7679
7829
|
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
@@ -7757,10 +7907,10 @@ var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options =
|
|
|
7757
7907
|
// src/vue/useVoiceWorkflowStatus.ts
|
|
7758
7908
|
function useVoiceWorkflowStatus(path = "/evals/scenarios/json", options = {}) {
|
|
7759
7909
|
const store = createVoiceWorkflowStatusStore(path, options);
|
|
7760
|
-
const error =
|
|
7761
|
-
const isLoading =
|
|
7762
|
-
const report =
|
|
7763
|
-
const updatedAt =
|
|
7910
|
+
const error = ref16(null);
|
|
7911
|
+
const isLoading = ref16(false);
|
|
7912
|
+
const report = shallowRef19(undefined);
|
|
7913
|
+
const updatedAt = ref16(undefined);
|
|
7764
7914
|
const sync = () => {
|
|
7765
7915
|
const snapshot = store.getSnapshot();
|
|
7766
7916
|
error.value = snapshot.error;
|
|
@@ -7773,7 +7923,7 @@ function useVoiceWorkflowStatus(path = "/evals/scenarios/json", options = {}) {
|
|
|
7773
7923
|
if (typeof window !== "undefined") {
|
|
7774
7924
|
store.refresh().catch(() => {});
|
|
7775
7925
|
}
|
|
7776
|
-
|
|
7926
|
+
onUnmounted20(() => {
|
|
7777
7927
|
unsubscribe();
|
|
7778
7928
|
store.close();
|
|
7779
7929
|
});
|
|
@@ -7798,6 +7948,7 @@ export {
|
|
|
7798
7948
|
useVoiceProviderContracts,
|
|
7799
7949
|
useVoiceProviderCapabilities,
|
|
7800
7950
|
useVoiceProofTrends,
|
|
7951
|
+
useVoiceProfileComparison,
|
|
7801
7952
|
useVoicePlatformCoverage,
|
|
7802
7953
|
useVoiceOpsStatus,
|
|
7803
7954
|
useVoiceOpsActionCenter,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type VoiceProfileComparisonClientOptions } from '../client/profileComparison';
|
|
2
|
+
import type { VoiceRealCallProfileHistoryReport } from '../proofTrends';
|
|
3
|
+
export declare function useVoiceProfileComparison(path?: string, options?: VoiceProfileComparisonClientOptions): {
|
|
4
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
5
|
+
isLoading: import("vue").Ref<boolean, boolean>;
|
|
6
|
+
refresh: () => Promise<VoiceRealCallProfileHistoryReport | undefined>;
|
|
7
|
+
report: import("vue").ShallowRef<VoiceRealCallProfileHistoryReport | undefined, VoiceRealCallProfileHistoryReport | undefined>;
|
|
8
|
+
updatedAt: import("vue").Ref<number | undefined, number | undefined>;
|
|
9
|
+
};
|