@absolutejs/voice 0.0.22-beta.6 → 0.0.22-beta.60
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/README.md +205 -0
- package/dist/angular/index.d.ts +4 -0
- package/dist/angular/index.js +587 -43
- package/dist/angular/voice-app-kit-status.service.d.ts +12 -0
- package/dist/angular/voice-ops-status.component.d.ts +15 -0
- package/dist/angular/voice-provider-status.service.d.ts +12 -0
- package/dist/angular/voice-routing-status.service.d.ts +11 -0
- package/dist/angular/voice-stream.service.d.ts +2 -0
- package/dist/angular/voice-workflow-status.service.d.ts +12 -0
- package/dist/appKit.d.ts +92 -0
- package/dist/assistantHealth.d.ts +81 -0
- package/dist/client/actions.d.ts +22 -0
- package/dist/client/appKitStatus.d.ts +19 -0
- package/dist/client/connection.d.ts +3 -0
- package/dist/client/htmxBootstrap.js +44 -2
- package/dist/client/index.d.ts +14 -0
- package/dist/client/index.js +713 -2
- package/dist/client/opsStatusWidget.d.ts +40 -0
- package/dist/client/providerStatus.d.ts +19 -0
- package/dist/client/providerStatusWidget.d.ts +32 -0
- package/dist/client/routingStatus.d.ts +19 -0
- package/dist/client/routingStatusWidget.d.ts +28 -0
- package/dist/client/workflowStatus.d.ts +19 -0
- package/dist/diagnosticsRoutes.d.ts +44 -0
- package/dist/evalRoutes.d.ts +213 -0
- package/dist/handoff.d.ts +54 -0
- package/dist/handoffHealth.d.ts +94 -0
- package/dist/index.d.ts +32 -4
- package/dist/index.js +4222 -133
- package/dist/modelAdapters.d.ts +75 -0
- package/dist/opsConsoleRoutes.d.ts +77 -0
- package/dist/opsWebhook.d.ts +126 -0
- package/dist/providerAdapters.d.ts +48 -0
- package/dist/providerHealth.d.ts +79 -0
- package/dist/qualityRoutes.d.ts +76 -0
- package/dist/queue.d.ts +52 -0
- package/dist/react/VoiceOpsStatus.d.ts +6 -0
- package/dist/react/VoiceProviderStatus.d.ts +6 -0
- package/dist/react/VoiceRoutingStatus.d.ts +6 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +1024 -11
- package/dist/react/useVoiceAppKitStatus.d.ts +8 -0
- package/dist/react/useVoiceController.d.ts +2 -0
- package/dist/react/useVoiceProviderStatus.d.ts +8 -0
- package/dist/react/useVoiceRoutingStatus.d.ts +8 -0
- package/dist/react/useVoiceStream.d.ts +2 -0
- package/dist/react/useVoiceWorkflowStatus.d.ts +8 -0
- package/dist/resilienceRoutes.d.ts +117 -0
- package/dist/sessionReplay.d.ts +175 -0
- package/dist/svelte/createVoiceAppKitStatus.d.ts +8 -0
- package/dist/svelte/createVoiceOpsStatus.d.ts +9 -0
- package/dist/svelte/createVoiceProviderStatus.d.ts +10 -0
- package/dist/svelte/createVoiceRoutingStatus.d.ts +10 -0
- package/dist/svelte/createVoiceWorkflowStatus.d.ts +8 -0
- package/dist/svelte/index.d.ts +5 -0
- package/dist/svelte/index.js +736 -3
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1537 -7
- package/dist/testing/ioProviderSimulator.d.ts +41 -0
- package/dist/testing/providerSimulator.d.ts +44 -0
- package/dist/trace.d.ts +1 -1
- package/dist/types.d.ts +84 -2
- package/dist/vue/VoiceOpsStatus.d.ts +30 -0
- package/dist/vue/VoiceProviderStatus.d.ts +51 -0
- package/dist/vue/VoiceRoutingStatus.d.ts +51 -0
- package/dist/vue/index.d.ts +7 -0
- package/dist/vue/index.js +1062 -25
- package/dist/vue/useVoiceAppKitStatus.d.ts +9 -0
- package/dist/vue/useVoiceProviderStatus.d.ts +9 -0
- package/dist/vue/useVoiceRoutingStatus.d.ts +8 -0
- package/dist/vue/useVoiceStream.d.ts +2 -0
- package/dist/vue/useVoiceWorkflowStatus.d.ts +9 -0
- package/dist/workflowContract.d.ts +91 -0
- package/package.json +1 -1
package/dist/svelte/index.js
CHANGED
|
@@ -69,6 +69,224 @@ var __decorateElement = (array, flags, name, decorators, target, extra) => {
|
|
|
69
69
|
return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
// src/client/appKitStatus.ts
|
|
73
|
+
var fetchVoiceAppKitStatus = async (path = "/app-kit/status", options = {}) => {
|
|
74
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
75
|
+
const response = await fetchImpl(path);
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
throw new Error(`Voice app kit status failed: HTTP ${response.status}`);
|
|
78
|
+
}
|
|
79
|
+
return await response.json();
|
|
80
|
+
};
|
|
81
|
+
var createVoiceAppKitStatusStore = (path = "/app-kit/status", options = {}) => {
|
|
82
|
+
const listeners = new Set;
|
|
83
|
+
let closed = false;
|
|
84
|
+
let timer;
|
|
85
|
+
let snapshot = {
|
|
86
|
+
error: null,
|
|
87
|
+
isLoading: false
|
|
88
|
+
};
|
|
89
|
+
const emit = () => {
|
|
90
|
+
for (const listener of listeners) {
|
|
91
|
+
listener();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
const refresh = async () => {
|
|
95
|
+
if (closed) {
|
|
96
|
+
return snapshot.report;
|
|
97
|
+
}
|
|
98
|
+
snapshot = {
|
|
99
|
+
...snapshot,
|
|
100
|
+
error: null,
|
|
101
|
+
isLoading: true
|
|
102
|
+
};
|
|
103
|
+
emit();
|
|
104
|
+
try {
|
|
105
|
+
const report = await fetchVoiceAppKitStatus(path, options);
|
|
106
|
+
snapshot = {
|
|
107
|
+
error: null,
|
|
108
|
+
isLoading: false,
|
|
109
|
+
report,
|
|
110
|
+
updatedAt: Date.now()
|
|
111
|
+
};
|
|
112
|
+
emit();
|
|
113
|
+
return report;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
snapshot = {
|
|
116
|
+
...snapshot,
|
|
117
|
+
error: error instanceof Error ? error.message : String(error),
|
|
118
|
+
isLoading: false
|
|
119
|
+
};
|
|
120
|
+
emit();
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const close = () => {
|
|
125
|
+
closed = true;
|
|
126
|
+
if (timer) {
|
|
127
|
+
clearInterval(timer);
|
|
128
|
+
timer = undefined;
|
|
129
|
+
}
|
|
130
|
+
listeners.clear();
|
|
131
|
+
};
|
|
132
|
+
if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
|
|
133
|
+
timer = setInterval(() => {
|
|
134
|
+
refresh().catch(() => {});
|
|
135
|
+
}, options.intervalMs);
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
close,
|
|
139
|
+
getServerSnapshot: () => snapshot,
|
|
140
|
+
getSnapshot: () => snapshot,
|
|
141
|
+
refresh,
|
|
142
|
+
subscribe: (listener) => {
|
|
143
|
+
listeners.add(listener);
|
|
144
|
+
return () => {
|
|
145
|
+
listeners.delete(listener);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/svelte/createVoiceAppKitStatus.ts
|
|
152
|
+
var createVoiceAppKitStatus = (path = "/app-kit/status", options = {}) => createVoiceAppKitStatusStore(path, options);
|
|
153
|
+
// src/client/opsStatusWidget.ts
|
|
154
|
+
var DEFAULT_TITLE = "Voice Ops Status";
|
|
155
|
+
var DEFAULT_DESCRIPTION = "Certified workflow, provider, and handoff readiness from the AbsoluteJS voice app kit.";
|
|
156
|
+
var SURFACE_LABELS = {
|
|
157
|
+
handoffs: "Handoffs",
|
|
158
|
+
providers: "Providers",
|
|
159
|
+
quality: "Quality",
|
|
160
|
+
sessions: "Sessions",
|
|
161
|
+
workflows: "Workflows"
|
|
162
|
+
};
|
|
163
|
+
var escapeHtml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
164
|
+
var readNumber = (value, key) => value && typeof value === "object" && (key in value) ? Number(value[key] ?? 0) : 0;
|
|
165
|
+
var surfaceDetail = (surface) => {
|
|
166
|
+
const total = readNumber(surface, "total");
|
|
167
|
+
const failed = readNumber(surface, "failed");
|
|
168
|
+
const degraded = readNumber(surface, "degraded");
|
|
169
|
+
const source = surface && typeof surface === "object" && "source" in surface && typeof surface.source === "string" ? ` from ${surface.source}` : "";
|
|
170
|
+
if (degraded > 0) {
|
|
171
|
+
return `${degraded} degraded of ${total}`;
|
|
172
|
+
}
|
|
173
|
+
if (failed > 0) {
|
|
174
|
+
return `${failed} failing of ${total}${source}`;
|
|
175
|
+
}
|
|
176
|
+
return total > 0 ? `${total} passing${source}` : `No failures${source}`;
|
|
177
|
+
};
|
|
178
|
+
var getVoiceOpsStatusLabel = (report, error) => {
|
|
179
|
+
if (error) {
|
|
180
|
+
return "Unavailable";
|
|
181
|
+
}
|
|
182
|
+
if (!report) {
|
|
183
|
+
return "Checking";
|
|
184
|
+
}
|
|
185
|
+
return report.status === "pass" ? "Passing" : "Needs attention";
|
|
186
|
+
};
|
|
187
|
+
var createVoiceOpsStatusViewModel = (snapshot, options = {}) => {
|
|
188
|
+
const report = snapshot.report;
|
|
189
|
+
const surfaces = Object.entries(report?.surfaces ?? {}).map(([id, surface]) => {
|
|
190
|
+
const failed = readNumber(surface, "failed") || readNumber(surface, "degraded");
|
|
191
|
+
const total = readNumber(surface, "total");
|
|
192
|
+
const status = surface && typeof surface === "object" && "status" in surface ? surface.status ?? "pass" : "pass";
|
|
193
|
+
return {
|
|
194
|
+
detail: surfaceDetail(surface),
|
|
195
|
+
failed,
|
|
196
|
+
id,
|
|
197
|
+
label: SURFACE_LABELS[id] ?? id,
|
|
198
|
+
status,
|
|
199
|
+
total
|
|
200
|
+
};
|
|
201
|
+
});
|
|
202
|
+
return {
|
|
203
|
+
description: options.description ?? DEFAULT_DESCRIPTION,
|
|
204
|
+
error: snapshot.error,
|
|
205
|
+
isLoading: snapshot.isLoading,
|
|
206
|
+
label: getVoiceOpsStatusLabel(report, snapshot.error),
|
|
207
|
+
links: options.includeLinks === false ? [] : report?.links ?? [],
|
|
208
|
+
passed: report?.passed ?? 0,
|
|
209
|
+
status: snapshot.error ? "error" : report ? report.status : snapshot.isLoading ? "loading" : "loading",
|
|
210
|
+
surfaces,
|
|
211
|
+
title: options.title ?? DEFAULT_TITLE,
|
|
212
|
+
total: report?.total ?? 0,
|
|
213
|
+
updatedAt: snapshot.updatedAt
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
var renderVoiceOpsStatusHTML = (snapshot, options = {}) => {
|
|
217
|
+
const model = createVoiceOpsStatusViewModel(snapshot, options);
|
|
218
|
+
const surfaces = model.surfaces.length ? model.surfaces.map((surface) => `<li class="absolute-voice-ops-status__surface absolute-voice-ops-status__surface--${escapeHtml(surface.status)}">
|
|
219
|
+
<span>${escapeHtml(surface.label)}</span>
|
|
220
|
+
<strong>${escapeHtml(surface.detail)}</strong>
|
|
221
|
+
</li>`).join("") : '<li class="absolute-voice-ops-status__surface"><span>Status</span><strong>Waiting for first check</strong></li>';
|
|
222
|
+
const links = model.links.length ? `<nav class="absolute-voice-ops-status__links">${model.links.slice(0, 4).map((link) => `<a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join("")}</nav>` : "";
|
|
223
|
+
return `<section class="absolute-voice-ops-status absolute-voice-ops-status--${escapeHtml(model.status)}">
|
|
224
|
+
<header class="absolute-voice-ops-status__header">
|
|
225
|
+
<span class="absolute-voice-ops-status__eyebrow">${escapeHtml(model.title)}</span>
|
|
226
|
+
<strong class="absolute-voice-ops-status__label">${escapeHtml(model.label)}</strong>
|
|
227
|
+
</header>
|
|
228
|
+
<p class="absolute-voice-ops-status__description">${escapeHtml(model.description)}</p>
|
|
229
|
+
<div class="absolute-voice-ops-status__summary">
|
|
230
|
+
<span>${model.passed} passing</span>
|
|
231
|
+
<span>${Math.max(model.total - model.passed, 0)} failing</span>
|
|
232
|
+
<span>${model.total} checks</span>
|
|
233
|
+
</div>
|
|
234
|
+
<ul class="absolute-voice-ops-status__surfaces">${surfaces}</ul>
|
|
235
|
+
${model.error ? `<p class="absolute-voice-ops-status__error">${escapeHtml(model.error)}</p>` : ""}
|
|
236
|
+
${links}
|
|
237
|
+
</section>`;
|
|
238
|
+
};
|
|
239
|
+
var getVoiceOpsStatusCSS = () => `.absolute-voice-ops-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-ops-status--fail,.absolute-voice-ops-status--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-ops-status__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-ops-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-ops-status__label{font-size:28px;line-height:1}.absolute-voice-ops-status__description{color:#514733;margin:12px 0 0}.absolute-voice-ops-status__summary,.absolute-voice-ops-status__links{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}.absolute-voice-ops-status__summary span,.absolute-voice-ops-status__links a{border:1px solid #e6ddca;border-radius:999px;color:inherit;padding:6px 10px;text-decoration:none}.absolute-voice-ops-status__surfaces{display:grid;gap:8px;list-style:none;margin:16px 0 0;padding:0}.absolute-voice-ops-status__surface{align-items:center;background:#fff;border:1px solid #eee4d2;border-radius:14px;display:flex;gap:12px;justify-content:space-between;padding:10px 12px}.absolute-voice-ops-status__surface--fail{border-color:#f2a7a7}.absolute-voice-ops-status__surface span{color:#655944}.absolute-voice-ops-status__error{color:#9f1239;font-weight:700}`;
|
|
240
|
+
var mountVoiceOpsStatus = (element, path = "/app-kit/status", options = {}) => {
|
|
241
|
+
const store = createVoiceAppKitStatusStore(path, options);
|
|
242
|
+
const render = () => {
|
|
243
|
+
element.innerHTML = renderVoiceOpsStatusHTML(store.getSnapshot(), options);
|
|
244
|
+
};
|
|
245
|
+
const unsubscribe = store.subscribe(render);
|
|
246
|
+
render();
|
|
247
|
+
store.refresh().catch(() => {});
|
|
248
|
+
return {
|
|
249
|
+
close: () => {
|
|
250
|
+
unsubscribe();
|
|
251
|
+
store.close();
|
|
252
|
+
},
|
|
253
|
+
refresh: store.refresh
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
var defineVoiceOpsStatusElement = (tagName = "absolute-voice-ops-status") => {
|
|
257
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
customElements.define(tagName, class AbsoluteVoiceOpsStatusElement extends HTMLElement {
|
|
261
|
+
mounted;
|
|
262
|
+
connectedCallback() {
|
|
263
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
264
|
+
this.mounted = mountVoiceOpsStatus(this, this.getAttribute("path") ?? "/app-kit/status", {
|
|
265
|
+
description: this.getAttribute("description") ?? undefined,
|
|
266
|
+
includeLinks: this.getAttribute("include-links") !== "false",
|
|
267
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
268
|
+
title: this.getAttribute("title") ?? undefined
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
disconnectedCallback() {
|
|
272
|
+
this.mounted?.close();
|
|
273
|
+
this.mounted = undefined;
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// src/svelte/createVoiceOpsStatus.ts
|
|
279
|
+
var createVoiceOpsStatus = (path = "/app-kit/status", options = {}) => {
|
|
280
|
+
const store = createVoiceAppKitStatusStore(path, options);
|
|
281
|
+
return {
|
|
282
|
+
close: store.close,
|
|
283
|
+
getHTML: () => renderVoiceOpsStatusHTML(store.getSnapshot(), options),
|
|
284
|
+
getSnapshot: store.getSnapshot,
|
|
285
|
+
getViewModel: () => createVoiceOpsStatusViewModel(store.getSnapshot(), options),
|
|
286
|
+
refresh: store.refresh,
|
|
287
|
+
subscribe: store.subscribe
|
|
288
|
+
};
|
|
289
|
+
};
|
|
72
290
|
// src/client/actions.ts
|
|
73
291
|
var normalizeErrorMessage = (value) => {
|
|
74
292
|
if (typeof value === "string" && value.trim()) {
|
|
@@ -117,6 +335,12 @@ var serverMessageToAction = (message) => {
|
|
|
117
335
|
sessionId: message.sessionId,
|
|
118
336
|
type: "complete"
|
|
119
337
|
};
|
|
338
|
+
case "call_lifecycle":
|
|
339
|
+
return {
|
|
340
|
+
event: message.event,
|
|
341
|
+
sessionId: message.sessionId,
|
|
342
|
+
type: "call_lifecycle"
|
|
343
|
+
};
|
|
120
344
|
case "error":
|
|
121
345
|
return {
|
|
122
346
|
message: normalizeErrorMessage(message.message),
|
|
@@ -160,7 +384,7 @@ var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
|
|
|
160
384
|
var noop = () => {};
|
|
161
385
|
var noopUnsubscribe = () => noop;
|
|
162
386
|
var NOOP_CONNECTION = {
|
|
163
|
-
|
|
387
|
+
callControl: noop,
|
|
164
388
|
close: noop,
|
|
165
389
|
endTurn: noop,
|
|
166
390
|
getReadyState: () => WS_CLOSED,
|
|
@@ -168,6 +392,7 @@ var NOOP_CONNECTION = {
|
|
|
168
392
|
getSessionId: () => "",
|
|
169
393
|
send: noop,
|
|
170
394
|
sendAudio: noop,
|
|
395
|
+
start: () => {},
|
|
171
396
|
subscribe: noopUnsubscribe
|
|
172
397
|
};
|
|
173
398
|
var createSessionId = () => crypto.randomUUID();
|
|
@@ -189,6 +414,7 @@ var isVoiceServerMessage = (value) => {
|
|
|
189
414
|
switch (value.type) {
|
|
190
415
|
case "audio":
|
|
191
416
|
case "assistant":
|
|
417
|
+
case "call_lifecycle":
|
|
192
418
|
case "complete":
|
|
193
419
|
case "error":
|
|
194
420
|
case "final":
|
|
@@ -329,6 +555,12 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
329
555
|
const endTurn = () => {
|
|
330
556
|
send({ type: "end_turn" });
|
|
331
557
|
};
|
|
558
|
+
const callControl = (message) => {
|
|
559
|
+
send({
|
|
560
|
+
...message,
|
|
561
|
+
type: "call_control"
|
|
562
|
+
});
|
|
563
|
+
};
|
|
332
564
|
const close = () => {
|
|
333
565
|
clearTimers();
|
|
334
566
|
if (state.ws) {
|
|
@@ -346,7 +578,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
346
578
|
};
|
|
347
579
|
connect();
|
|
348
580
|
return {
|
|
349
|
-
|
|
581
|
+
callControl,
|
|
350
582
|
close,
|
|
351
583
|
endTurn,
|
|
352
584
|
getReadyState: () => state.ws?.readyState ?? WS_CLOSED,
|
|
@@ -354,6 +586,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
354
586
|
getSessionId: () => state.sessionId,
|
|
355
587
|
send,
|
|
356
588
|
sendAudio,
|
|
589
|
+
start,
|
|
357
590
|
subscribe
|
|
358
591
|
};
|
|
359
592
|
};
|
|
@@ -362,6 +595,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
362
595
|
var createInitialState = () => ({
|
|
363
596
|
assistantAudio: [],
|
|
364
597
|
assistantTexts: [],
|
|
598
|
+
call: null,
|
|
365
599
|
error: null,
|
|
366
600
|
isConnected: false,
|
|
367
601
|
scenarioId: null,
|
|
@@ -405,6 +639,20 @@ var createVoiceStreamStore = () => {
|
|
|
405
639
|
status: "completed"
|
|
406
640
|
};
|
|
407
641
|
break;
|
|
642
|
+
case "call_lifecycle":
|
|
643
|
+
state = {
|
|
644
|
+
...state,
|
|
645
|
+
call: {
|
|
646
|
+
...state.call,
|
|
647
|
+
disposition: action.event.type === "end" ? action.event.disposition : state.call?.disposition,
|
|
648
|
+
endedAt: action.event.type === "end" ? action.event.at : state.call?.endedAt,
|
|
649
|
+
events: [...state.call?.events ?? [], action.event],
|
|
650
|
+
lastEventAt: action.event.at,
|
|
651
|
+
startedAt: state.call?.startedAt ?? action.event.at
|
|
652
|
+
},
|
|
653
|
+
sessionId: action.sessionId
|
|
654
|
+
};
|
|
655
|
+
break;
|
|
408
656
|
case "connected":
|
|
409
657
|
state = {
|
|
410
658
|
...state,
|
|
@@ -491,6 +739,9 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
491
739
|
}
|
|
492
740
|
});
|
|
493
741
|
return {
|
|
742
|
+
callControl(message) {
|
|
743
|
+
connection.callControl(message);
|
|
744
|
+
},
|
|
494
745
|
close() {
|
|
495
746
|
unsubscribeConnection();
|
|
496
747
|
connection.close();
|
|
@@ -534,6 +785,9 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
534
785
|
get assistantAudio() {
|
|
535
786
|
return store.getSnapshot().assistantAudio;
|
|
536
787
|
},
|
|
788
|
+
get call() {
|
|
789
|
+
return store.getSnapshot().call;
|
|
790
|
+
},
|
|
537
791
|
sendAudio(audio) {
|
|
538
792
|
connection.sendAudio(audio);
|
|
539
793
|
},
|
|
@@ -548,6 +802,474 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
548
802
|
|
|
549
803
|
// src/svelte/createVoiceStream.ts
|
|
550
804
|
var createVoiceStream2 = (path, options = {}) => createVoiceStream(path, options);
|
|
805
|
+
// src/client/providerStatus.ts
|
|
806
|
+
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
807
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
808
|
+
const response = await fetchImpl(path);
|
|
809
|
+
if (!response.ok) {
|
|
810
|
+
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
811
|
+
}
|
|
812
|
+
return await response.json();
|
|
813
|
+
};
|
|
814
|
+
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
815
|
+
const listeners = new Set;
|
|
816
|
+
let closed = false;
|
|
817
|
+
let timer;
|
|
818
|
+
let snapshot = {
|
|
819
|
+
error: null,
|
|
820
|
+
isLoading: false,
|
|
821
|
+
providers: []
|
|
822
|
+
};
|
|
823
|
+
const emit = () => {
|
|
824
|
+
for (const listener of listeners) {
|
|
825
|
+
listener();
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
const refresh = async () => {
|
|
829
|
+
if (closed) {
|
|
830
|
+
return snapshot.providers;
|
|
831
|
+
}
|
|
832
|
+
snapshot = {
|
|
833
|
+
...snapshot,
|
|
834
|
+
error: null,
|
|
835
|
+
isLoading: true
|
|
836
|
+
};
|
|
837
|
+
emit();
|
|
838
|
+
try {
|
|
839
|
+
const providers = await fetchVoiceProviderStatus(path, options);
|
|
840
|
+
snapshot = {
|
|
841
|
+
error: null,
|
|
842
|
+
isLoading: false,
|
|
843
|
+
providers,
|
|
844
|
+
updatedAt: Date.now()
|
|
845
|
+
};
|
|
846
|
+
emit();
|
|
847
|
+
return providers;
|
|
848
|
+
} catch (error) {
|
|
849
|
+
snapshot = {
|
|
850
|
+
...snapshot,
|
|
851
|
+
error: error instanceof Error ? error.message : String(error),
|
|
852
|
+
isLoading: false
|
|
853
|
+
};
|
|
854
|
+
emit();
|
|
855
|
+
throw error;
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
const close = () => {
|
|
859
|
+
closed = true;
|
|
860
|
+
if (timer) {
|
|
861
|
+
clearInterval(timer);
|
|
862
|
+
timer = undefined;
|
|
863
|
+
}
|
|
864
|
+
listeners.clear();
|
|
865
|
+
};
|
|
866
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
867
|
+
timer = setInterval(() => {
|
|
868
|
+
refresh().catch(() => {});
|
|
869
|
+
}, options.intervalMs);
|
|
870
|
+
}
|
|
871
|
+
return {
|
|
872
|
+
close,
|
|
873
|
+
getServerSnapshot: () => snapshot,
|
|
874
|
+
getSnapshot: () => snapshot,
|
|
875
|
+
refresh,
|
|
876
|
+
subscribe: (listener) => {
|
|
877
|
+
listeners.add(listener);
|
|
878
|
+
return () => {
|
|
879
|
+
listeners.delete(listener);
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
// src/client/providerStatusWidget.ts
|
|
886
|
+
var DEFAULT_TITLE2 = "Voice Providers";
|
|
887
|
+
var DEFAULT_DESCRIPTION2 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
|
|
888
|
+
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
889
|
+
var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
890
|
+
var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
891
|
+
var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
|
|
892
|
+
var formatSuppression = (value) => typeof value === "number" ? `${Math.ceil(value / 1000)}s` : "None";
|
|
893
|
+
var getProviderDetail = (provider) => {
|
|
894
|
+
if (provider.status === "suppressed") {
|
|
895
|
+
return provider.lastError ? `Suppressed for ${formatSuppression(provider.suppressionRemainingMs)} after ${provider.lastError}.` : `Suppressed for ${formatSuppression(provider.suppressionRemainingMs)}.`;
|
|
896
|
+
}
|
|
897
|
+
if (provider.status === "recoverable") {
|
|
898
|
+
return "Cooldown expired; ready for recovery traffic.";
|
|
899
|
+
}
|
|
900
|
+
if (provider.status === "rate-limited") {
|
|
901
|
+
return "Rate limit detected; router should avoid this provider.";
|
|
902
|
+
}
|
|
903
|
+
if (provider.status === "degraded") {
|
|
904
|
+
return provider.lastError ?? "Recent provider errors detected.";
|
|
905
|
+
}
|
|
906
|
+
if (provider.status === "healthy") {
|
|
907
|
+
return provider.recommended ? "Healthy and currently recommended." : "Healthy and available for routing.";
|
|
908
|
+
}
|
|
909
|
+
return "No provider traffic observed yet.";
|
|
910
|
+
};
|
|
911
|
+
var isWarningStatus = (status) => status === "degraded" || status === "rate-limited" || status === "recoverable" || status === "suppressed";
|
|
912
|
+
var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
|
|
913
|
+
const providers = snapshot.providers.map((provider) => ({
|
|
914
|
+
...provider,
|
|
915
|
+
detail: getProviderDetail(provider),
|
|
916
|
+
label: `${formatProvider(provider.provider)}${provider.recommended ? " recommended" : ""}`,
|
|
917
|
+
rows: [
|
|
918
|
+
{ label: "Runs", value: String(provider.runCount) },
|
|
919
|
+
{ label: "Avg latency", value: formatLatency(provider.averageElapsedMs) },
|
|
920
|
+
{ label: "Errors", value: String(provider.errorCount) },
|
|
921
|
+
{ label: "Timeouts", value: String(provider.timeoutCount) },
|
|
922
|
+
{ label: "Fallbacks", value: String(provider.fallbackCount) },
|
|
923
|
+
{
|
|
924
|
+
label: "Suppression",
|
|
925
|
+
value: formatSuppression(provider.suppressionRemainingMs)
|
|
926
|
+
}
|
|
927
|
+
]
|
|
928
|
+
}));
|
|
929
|
+
const warningCount = providers.filter((provider) => isWarningStatus(provider.status)).length;
|
|
930
|
+
const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
|
|
931
|
+
return {
|
|
932
|
+
description: options.description ?? DEFAULT_DESCRIPTION2,
|
|
933
|
+
error: snapshot.error,
|
|
934
|
+
isLoading: snapshot.isLoading,
|
|
935
|
+
label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
|
|
936
|
+
providers,
|
|
937
|
+
status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
938
|
+
title: options.title ?? DEFAULT_TITLE2,
|
|
939
|
+
updatedAt: snapshot.updatedAt
|
|
940
|
+
};
|
|
941
|
+
};
|
|
942
|
+
var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
|
|
943
|
+
const model = createVoiceProviderStatusViewModel(snapshot, options);
|
|
944
|
+
const providers = model.providers.length ? `<div class="absolute-voice-provider-status__providers">${model.providers.map((provider) => `<article class="absolute-voice-provider-status__provider absolute-voice-provider-status__provider--${escapeHtml2(provider.status)}">
|
|
945
|
+
<header>
|
|
946
|
+
<strong>${escapeHtml2(provider.label)}</strong>
|
|
947
|
+
<span>${escapeHtml2(formatStatus(provider.status))}</span>
|
|
948
|
+
</header>
|
|
949
|
+
<p>${escapeHtml2(provider.detail)}</p>
|
|
950
|
+
<dl>${provider.rows.map((row) => `<div>
|
|
951
|
+
<dt>${escapeHtml2(row.label)}</dt>
|
|
952
|
+
<dd>${escapeHtml2(row.value)}</dd>
|
|
953
|
+
</div>`).join("")}</dl>
|
|
954
|
+
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
|
|
955
|
+
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml2(model.status)}">
|
|
956
|
+
<header class="absolute-voice-provider-status__header">
|
|
957
|
+
<span class="absolute-voice-provider-status__eyebrow">${escapeHtml2(model.title)}</span>
|
|
958
|
+
<strong class="absolute-voice-provider-status__label">${escapeHtml2(model.label)}</strong>
|
|
959
|
+
</header>
|
|
960
|
+
<p class="absolute-voice-provider-status__description">${escapeHtml2(model.description)}</p>
|
|
961
|
+
${providers}
|
|
962
|
+
${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml2(model.error)}</p>` : ""}
|
|
963
|
+
</section>`;
|
|
964
|
+
};
|
|
965
|
+
var getVoiceProviderStatusCSS = () => `.absolute-voice-provider-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-provider-status--error,.absolute-voice-provider-status--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-provider-status__header,.absolute-voice-provider-status__provider header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-status__label{font-size:24px;line-height:1}.absolute-voice-provider-status__description,.absolute-voice-provider-status__provider p,.absolute-voice-provider-status__provider dt,.absolute-voice-provider-status__empty{color:#514733}.absolute-voice-provider-status__providers{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-status__provider{background:#fff;border:1px solid #eee4d2;border-radius:16px;padding:14px}.absolute-voice-provider-status__provider--degraded,.absolute-voice-provider-status__provider--rate-limited,.absolute-voice-provider-status__provider--suppressed{border-color:#f2a7a7}.absolute-voice-provider-status__provider--recoverable{border-color:#fbbf24}.absolute-voice-provider-status__provider p{margin:10px 0}.absolute-voice-provider-status__provider dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-status__provider div{background:#fffaf0;border:1px solid #eee4d2;border-radius:12px;padding:8px}.absolute-voice-provider-status__provider dt{font-size:12px}.absolute-voice-provider-status__provider dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-status__empty{margin:14px 0 0}.absolute-voice-provider-status__error{color:#9f1239;font-weight:700}`;
|
|
966
|
+
var mountVoiceProviderStatus = (element, path = "/api/provider-status", options = {}) => {
|
|
967
|
+
const store = createVoiceProviderStatusStore(path, options);
|
|
968
|
+
const render = () => {
|
|
969
|
+
element.innerHTML = renderVoiceProviderStatusHTML(store.getSnapshot(), options);
|
|
970
|
+
};
|
|
971
|
+
const unsubscribe = store.subscribe(render);
|
|
972
|
+
render();
|
|
973
|
+
store.refresh().catch(() => {});
|
|
974
|
+
return {
|
|
975
|
+
close: () => {
|
|
976
|
+
unsubscribe();
|
|
977
|
+
store.close();
|
|
978
|
+
},
|
|
979
|
+
refresh: store.refresh
|
|
980
|
+
};
|
|
981
|
+
};
|
|
982
|
+
var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-status") => {
|
|
983
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
customElements.define(tagName, class AbsoluteVoiceProviderStatusElement extends HTMLElement {
|
|
987
|
+
mounted;
|
|
988
|
+
connectedCallback() {
|
|
989
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
990
|
+
this.mounted = mountVoiceProviderStatus(this, this.getAttribute("path") ?? "/api/provider-status", {
|
|
991
|
+
description: this.getAttribute("description") ?? undefined,
|
|
992
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
993
|
+
title: this.getAttribute("title") ?? undefined
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
disconnectedCallback() {
|
|
997
|
+
this.mounted?.close();
|
|
998
|
+
this.mounted = undefined;
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
};
|
|
1002
|
+
|
|
1003
|
+
// src/svelte/createVoiceProviderStatus.ts
|
|
1004
|
+
var createVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
1005
|
+
const store = createVoiceProviderStatusStore(path, options);
|
|
1006
|
+
return {
|
|
1007
|
+
...store,
|
|
1008
|
+
getHTML: () => renderVoiceProviderStatusHTML(store.getSnapshot(), options),
|
|
1009
|
+
getViewModel: () => createVoiceProviderStatusViewModel(store.getSnapshot(), options)
|
|
1010
|
+
};
|
|
1011
|
+
};
|
|
1012
|
+
// src/client/routingStatus.ts
|
|
1013
|
+
var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
|
|
1014
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1015
|
+
const response = await fetchImpl(path);
|
|
1016
|
+
if (!response.ok) {
|
|
1017
|
+
throw new Error(`Voice routing status failed: HTTP ${response.status}`);
|
|
1018
|
+
}
|
|
1019
|
+
return await response.json();
|
|
1020
|
+
};
|
|
1021
|
+
var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {}) => {
|
|
1022
|
+
const listeners = new Set;
|
|
1023
|
+
let closed = false;
|
|
1024
|
+
let timer;
|
|
1025
|
+
let snapshot = {
|
|
1026
|
+
decision: null,
|
|
1027
|
+
error: null,
|
|
1028
|
+
isLoading: false
|
|
1029
|
+
};
|
|
1030
|
+
const emit = () => {
|
|
1031
|
+
for (const listener of listeners) {
|
|
1032
|
+
listener();
|
|
1033
|
+
}
|
|
1034
|
+
};
|
|
1035
|
+
const refresh = async () => {
|
|
1036
|
+
if (closed) {
|
|
1037
|
+
return snapshot.decision;
|
|
1038
|
+
}
|
|
1039
|
+
snapshot = {
|
|
1040
|
+
...snapshot,
|
|
1041
|
+
error: null,
|
|
1042
|
+
isLoading: true
|
|
1043
|
+
};
|
|
1044
|
+
emit();
|
|
1045
|
+
try {
|
|
1046
|
+
const decision = await fetchVoiceRoutingStatus(path, options);
|
|
1047
|
+
snapshot = {
|
|
1048
|
+
decision,
|
|
1049
|
+
error: null,
|
|
1050
|
+
isLoading: false,
|
|
1051
|
+
updatedAt: Date.now()
|
|
1052
|
+
};
|
|
1053
|
+
emit();
|
|
1054
|
+
return decision;
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
snapshot = {
|
|
1057
|
+
...snapshot,
|
|
1058
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1059
|
+
isLoading: false
|
|
1060
|
+
};
|
|
1061
|
+
emit();
|
|
1062
|
+
throw error;
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
const close = () => {
|
|
1066
|
+
closed = true;
|
|
1067
|
+
if (timer) {
|
|
1068
|
+
clearInterval(timer);
|
|
1069
|
+
timer = undefined;
|
|
1070
|
+
}
|
|
1071
|
+
listeners.clear();
|
|
1072
|
+
};
|
|
1073
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1074
|
+
timer = setInterval(() => {
|
|
1075
|
+
refresh().catch(() => {});
|
|
1076
|
+
}, options.intervalMs);
|
|
1077
|
+
}
|
|
1078
|
+
return {
|
|
1079
|
+
close,
|
|
1080
|
+
getServerSnapshot: () => snapshot,
|
|
1081
|
+
getSnapshot: () => snapshot,
|
|
1082
|
+
refresh,
|
|
1083
|
+
subscribe: (listener) => {
|
|
1084
|
+
listeners.add(listener);
|
|
1085
|
+
return () => {
|
|
1086
|
+
listeners.delete(listener);
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
};
|
|
1091
|
+
|
|
1092
|
+
// src/client/routingStatusWidget.ts
|
|
1093
|
+
var DEFAULT_TITLE3 = "Voice Routing";
|
|
1094
|
+
var DEFAULT_DESCRIPTION3 = "Latest provider routing decision from the self-hosted trace store.";
|
|
1095
|
+
var escapeHtml3 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1096
|
+
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
1097
|
+
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
1098
|
+
const decision = snapshot.decision;
|
|
1099
|
+
const rows = decision ? [
|
|
1100
|
+
{ label: "Kind", value: decision.kind.toUpperCase() },
|
|
1101
|
+
{ label: "Policy", value: formatValue(decision.routing, "Unknown") },
|
|
1102
|
+
{ label: "Provider", value: formatValue(decision.provider, "Unknown") },
|
|
1103
|
+
{
|
|
1104
|
+
label: "Selected",
|
|
1105
|
+
value: formatValue(decision.selectedProvider, "Unknown")
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
label: "Fallback",
|
|
1109
|
+
value: formatValue(decision.fallbackProvider)
|
|
1110
|
+
},
|
|
1111
|
+
{ label: "Status", value: formatValue(decision.status, "unknown") },
|
|
1112
|
+
{
|
|
1113
|
+
label: "Latency budget",
|
|
1114
|
+
value: typeof decision.latencyBudgetMs === "number" ? `${decision.latencyBudgetMs}ms` : "None"
|
|
1115
|
+
}
|
|
1116
|
+
] : [];
|
|
1117
|
+
return {
|
|
1118
|
+
decision,
|
|
1119
|
+
description: options.description ?? DEFAULT_DESCRIPTION3,
|
|
1120
|
+
error: snapshot.error,
|
|
1121
|
+
isLoading: snapshot.isLoading,
|
|
1122
|
+
label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
|
|
1123
|
+
rows,
|
|
1124
|
+
status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1125
|
+
title: options.title ?? DEFAULT_TITLE3,
|
|
1126
|
+
updatedAt: snapshot.updatedAt
|
|
1127
|
+
};
|
|
1128
|
+
};
|
|
1129
|
+
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
1130
|
+
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
1131
|
+
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
1132
|
+
<span>${escapeHtml3(row.label)}</span>
|
|
1133
|
+
<strong>${escapeHtml3(row.value)}</strong>
|
|
1134
|
+
</div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
|
|
1135
|
+
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml3(model.status)}">
|
|
1136
|
+
<header class="absolute-voice-routing-status__header">
|
|
1137
|
+
<span class="absolute-voice-routing-status__eyebrow">${escapeHtml3(model.title)}</span>
|
|
1138
|
+
<strong class="absolute-voice-routing-status__label">${escapeHtml3(model.label)}</strong>
|
|
1139
|
+
</header>
|
|
1140
|
+
<p class="absolute-voice-routing-status__description">${escapeHtml3(model.description)}</p>
|
|
1141
|
+
${rows}
|
|
1142
|
+
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml3(model.error)}</p>` : ""}
|
|
1143
|
+
</section>`;
|
|
1144
|
+
};
|
|
1145
|
+
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}`;
|
|
1146
|
+
var mountVoiceRoutingStatus = (element, path = "/api/routing/latest", options = {}) => {
|
|
1147
|
+
const store = createVoiceRoutingStatusStore(path, options);
|
|
1148
|
+
const render = () => {
|
|
1149
|
+
element.innerHTML = renderVoiceRoutingStatusHTML(store.getSnapshot(), options);
|
|
1150
|
+
};
|
|
1151
|
+
const unsubscribe = store.subscribe(render);
|
|
1152
|
+
render();
|
|
1153
|
+
store.refresh().catch(() => {});
|
|
1154
|
+
return {
|
|
1155
|
+
close: () => {
|
|
1156
|
+
unsubscribe();
|
|
1157
|
+
store.close();
|
|
1158
|
+
},
|
|
1159
|
+
refresh: store.refresh
|
|
1160
|
+
};
|
|
1161
|
+
};
|
|
1162
|
+
var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status") => {
|
|
1163
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
customElements.define(tagName, class AbsoluteVoiceRoutingStatusElement extends HTMLElement {
|
|
1167
|
+
mounted;
|
|
1168
|
+
connectedCallback() {
|
|
1169
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
1170
|
+
this.mounted = mountVoiceRoutingStatus(this, this.getAttribute("path") ?? "/api/routing/latest", {
|
|
1171
|
+
description: this.getAttribute("description") ?? undefined,
|
|
1172
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
1173
|
+
title: this.getAttribute("title") ?? undefined
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
disconnectedCallback() {
|
|
1177
|
+
this.mounted?.close();
|
|
1178
|
+
this.mounted = undefined;
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
// src/svelte/createVoiceRoutingStatus.ts
|
|
1184
|
+
var createVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
|
|
1185
|
+
const store = createVoiceRoutingStatusStore(path, options);
|
|
1186
|
+
return {
|
|
1187
|
+
...store,
|
|
1188
|
+
getHTML: () => renderVoiceRoutingStatusHTML(store.getSnapshot(), options),
|
|
1189
|
+
getViewModel: () => createVoiceRoutingStatusViewModel(store.getSnapshot(), options)
|
|
1190
|
+
};
|
|
1191
|
+
};
|
|
1192
|
+
// src/client/workflowStatus.ts
|
|
1193
|
+
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
1194
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1195
|
+
const response = await fetchImpl(path);
|
|
1196
|
+
if (!response.ok) {
|
|
1197
|
+
throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
|
|
1198
|
+
}
|
|
1199
|
+
return await response.json();
|
|
1200
|
+
};
|
|
1201
|
+
var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
|
|
1202
|
+
const listeners = new Set;
|
|
1203
|
+
let closed = false;
|
|
1204
|
+
let timer;
|
|
1205
|
+
let snapshot = {
|
|
1206
|
+
error: null,
|
|
1207
|
+
isLoading: false
|
|
1208
|
+
};
|
|
1209
|
+
const emit = () => {
|
|
1210
|
+
for (const listener of listeners) {
|
|
1211
|
+
listener();
|
|
1212
|
+
}
|
|
1213
|
+
};
|
|
1214
|
+
const refresh = async () => {
|
|
1215
|
+
if (closed) {
|
|
1216
|
+
return snapshot.report;
|
|
1217
|
+
}
|
|
1218
|
+
snapshot = {
|
|
1219
|
+
...snapshot,
|
|
1220
|
+
error: null,
|
|
1221
|
+
isLoading: true
|
|
1222
|
+
};
|
|
1223
|
+
emit();
|
|
1224
|
+
try {
|
|
1225
|
+
const report = await fetchVoiceWorkflowStatus(path, options);
|
|
1226
|
+
snapshot = {
|
|
1227
|
+
error: null,
|
|
1228
|
+
isLoading: false,
|
|
1229
|
+
report,
|
|
1230
|
+
updatedAt: Date.now()
|
|
1231
|
+
};
|
|
1232
|
+
emit();
|
|
1233
|
+
return report;
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
snapshot = {
|
|
1236
|
+
...snapshot,
|
|
1237
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1238
|
+
isLoading: false
|
|
1239
|
+
};
|
|
1240
|
+
emit();
|
|
1241
|
+
throw error;
|
|
1242
|
+
}
|
|
1243
|
+
};
|
|
1244
|
+
const close = () => {
|
|
1245
|
+
closed = true;
|
|
1246
|
+
if (timer) {
|
|
1247
|
+
clearInterval(timer);
|
|
1248
|
+
timer = undefined;
|
|
1249
|
+
}
|
|
1250
|
+
listeners.clear();
|
|
1251
|
+
};
|
|
1252
|
+
if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
|
|
1253
|
+
timer = setInterval(() => {
|
|
1254
|
+
refresh().catch(() => {});
|
|
1255
|
+
}, options.intervalMs);
|
|
1256
|
+
}
|
|
1257
|
+
return {
|
|
1258
|
+
close,
|
|
1259
|
+
getServerSnapshot: () => snapshot,
|
|
1260
|
+
getSnapshot: () => snapshot,
|
|
1261
|
+
refresh,
|
|
1262
|
+
subscribe: (listener) => {
|
|
1263
|
+
listeners.add(listener);
|
|
1264
|
+
return () => {
|
|
1265
|
+
listeners.delete(listener);
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
};
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
// src/svelte/createVoiceWorkflowStatus.ts
|
|
1272
|
+
var createVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => createVoiceWorkflowStatusStore(path, options);
|
|
551
1273
|
// src/client/htmx.ts
|
|
552
1274
|
var DEFAULT_EVENT_NAME = "voice-refresh";
|
|
553
1275
|
var DEFAULT_QUERY_PARAM = "sessionId";
|
|
@@ -1010,6 +1732,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
|
|
|
1010
1732
|
var createInitialState2 = (stream) => ({
|
|
1011
1733
|
assistantAudio: [...stream.assistantAudio],
|
|
1012
1734
|
assistantTexts: [...stream.assistantTexts],
|
|
1735
|
+
call: stream.call,
|
|
1013
1736
|
error: stream.error,
|
|
1014
1737
|
isConnected: stream.isConnected,
|
|
1015
1738
|
isRecording: false,
|
|
@@ -1039,6 +1762,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1039
1762
|
...state,
|
|
1040
1763
|
assistantAudio: [...stream.assistantAudio],
|
|
1041
1764
|
assistantTexts: [...stream.assistantTexts],
|
|
1765
|
+
call: stream.call,
|
|
1042
1766
|
error: stream.error,
|
|
1043
1767
|
isConnected: stream.isConnected,
|
|
1044
1768
|
partial: stream.partial,
|
|
@@ -1116,6 +1840,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1116
1840
|
bindHTMX(bindingOptions) {
|
|
1117
1841
|
return bindVoiceHTMX(stream, bindingOptions);
|
|
1118
1842
|
},
|
|
1843
|
+
callControl: (message) => stream.callControl(message),
|
|
1119
1844
|
close,
|
|
1120
1845
|
endTurn: () => stream.endTurn(),
|
|
1121
1846
|
get error() {
|
|
@@ -1168,10 +1893,18 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1168
1893
|
},
|
|
1169
1894
|
get assistantAudio() {
|
|
1170
1895
|
return state.assistantAudio;
|
|
1896
|
+
},
|
|
1897
|
+
get call() {
|
|
1898
|
+
return state.call;
|
|
1171
1899
|
}
|
|
1172
1900
|
};
|
|
1173
1901
|
};
|
|
1174
1902
|
export {
|
|
1903
|
+
createVoiceWorkflowStatus,
|
|
1175
1904
|
createVoiceStream2 as createVoiceStream,
|
|
1176
|
-
|
|
1905
|
+
createVoiceRoutingStatus,
|
|
1906
|
+
createVoiceProviderStatus,
|
|
1907
|
+
createVoiceOpsStatus,
|
|
1908
|
+
createVoiceController,
|
|
1909
|
+
createVoiceAppKitStatus
|
|
1177
1910
|
};
|