@absolutejs/voice 0.0.22-beta.13 → 0.0.22-beta.15
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 +124 -0
- package/dist/angular/voice-provider-status.service.d.ts +12 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +81 -0
- package/dist/client/providerStatus.d.ts +19 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +186 -3
- package/dist/modelAdapters.d.ts +3 -0
- package/dist/providerHealth.d.ts +25 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +100 -0
- package/dist/react/useVoiceProviderStatus.d.ts +8 -0
- package/dist/svelte/createVoiceProviderStatus.d.ts +8 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +83 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +113 -0
- package/dist/vue/useVoiceProviderStatus.d.ts +9 -0
- package/package.json +1 -1
package/dist/angular/index.d.ts
CHANGED
package/dist/angular/index.js
CHANGED
|
@@ -1288,7 +1288,131 @@ VoiceControllerService = __decorateElement(_init, 0, "VoiceControllerService", _
|
|
|
1288
1288
|
__runInitializers(_init, 1, VoiceControllerService);
|
|
1289
1289
|
__decoratorMetadata(_init, VoiceControllerService);
|
|
1290
1290
|
let _VoiceControllerService = VoiceControllerService;
|
|
1291
|
+
// src/angular/voice-provider-status.service.ts
|
|
1292
|
+
import { computed as computed3, Injectable as Injectable3, signal as signal3 } from "@angular/core";
|
|
1293
|
+
|
|
1294
|
+
// src/client/providerStatus.ts
|
|
1295
|
+
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
1296
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1297
|
+
const response = await fetchImpl(path);
|
|
1298
|
+
if (!response.ok) {
|
|
1299
|
+
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
1300
|
+
}
|
|
1301
|
+
return await response.json();
|
|
1302
|
+
};
|
|
1303
|
+
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
1304
|
+
const listeners = new Set;
|
|
1305
|
+
let closed = false;
|
|
1306
|
+
let timer;
|
|
1307
|
+
let snapshot = {
|
|
1308
|
+
error: null,
|
|
1309
|
+
isLoading: false,
|
|
1310
|
+
providers: []
|
|
1311
|
+
};
|
|
1312
|
+
const emit = () => {
|
|
1313
|
+
for (const listener of listeners) {
|
|
1314
|
+
listener();
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
const refresh = async () => {
|
|
1318
|
+
if (closed) {
|
|
1319
|
+
return snapshot.providers;
|
|
1320
|
+
}
|
|
1321
|
+
snapshot = {
|
|
1322
|
+
...snapshot,
|
|
1323
|
+
error: null,
|
|
1324
|
+
isLoading: true
|
|
1325
|
+
};
|
|
1326
|
+
emit();
|
|
1327
|
+
try {
|
|
1328
|
+
const providers = await fetchVoiceProviderStatus(path, options);
|
|
1329
|
+
snapshot = {
|
|
1330
|
+
error: null,
|
|
1331
|
+
isLoading: false,
|
|
1332
|
+
providers,
|
|
1333
|
+
updatedAt: Date.now()
|
|
1334
|
+
};
|
|
1335
|
+
emit();
|
|
1336
|
+
return providers;
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
snapshot = {
|
|
1339
|
+
...snapshot,
|
|
1340
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1341
|
+
isLoading: false
|
|
1342
|
+
};
|
|
1343
|
+
emit();
|
|
1344
|
+
throw error;
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
const close = () => {
|
|
1348
|
+
closed = true;
|
|
1349
|
+
if (timer) {
|
|
1350
|
+
clearInterval(timer);
|
|
1351
|
+
timer = undefined;
|
|
1352
|
+
}
|
|
1353
|
+
listeners.clear();
|
|
1354
|
+
};
|
|
1355
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1356
|
+
timer = setInterval(() => {
|
|
1357
|
+
refresh().catch(() => {});
|
|
1358
|
+
}, options.intervalMs);
|
|
1359
|
+
}
|
|
1360
|
+
return {
|
|
1361
|
+
close,
|
|
1362
|
+
getServerSnapshot: () => snapshot,
|
|
1363
|
+
getSnapshot: () => snapshot,
|
|
1364
|
+
refresh,
|
|
1365
|
+
subscribe: (listener) => {
|
|
1366
|
+
listeners.add(listener);
|
|
1367
|
+
return () => {
|
|
1368
|
+
listeners.delete(listener);
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
// src/angular/voice-provider-status.service.ts
|
|
1375
|
+
var _dec = [
|
|
1376
|
+
Injectable3({ providedIn: "root" })
|
|
1377
|
+
];
|
|
1378
|
+
var _init = __decoratorStart(undefined);
|
|
1379
|
+
|
|
1380
|
+
class VoiceProviderStatusService {
|
|
1381
|
+
connect(path = "/api/provider-status", options = {}) {
|
|
1382
|
+
const store = createVoiceProviderStatusStore(path, options);
|
|
1383
|
+
const errorSignal = signal3(null);
|
|
1384
|
+
const isLoadingSignal = signal3(false);
|
|
1385
|
+
const providersSignal = signal3([]);
|
|
1386
|
+
const updatedAtSignal = signal3(undefined);
|
|
1387
|
+
const sync = () => {
|
|
1388
|
+
const snapshot = store.getSnapshot();
|
|
1389
|
+
errorSignal.set(snapshot.error);
|
|
1390
|
+
isLoadingSignal.set(snapshot.isLoading);
|
|
1391
|
+
providersSignal.set([...snapshot.providers]);
|
|
1392
|
+
updatedAtSignal.set(snapshot.updatedAt);
|
|
1393
|
+
};
|
|
1394
|
+
const unsubscribe = store.subscribe(sync);
|
|
1395
|
+
sync();
|
|
1396
|
+
store.refresh().catch(() => {});
|
|
1397
|
+
return {
|
|
1398
|
+
close: () => {
|
|
1399
|
+
unsubscribe();
|
|
1400
|
+
store.close();
|
|
1401
|
+
},
|
|
1402
|
+
error: computed3(() => errorSignal()),
|
|
1403
|
+
isLoading: computed3(() => isLoadingSignal()),
|
|
1404
|
+
providers: computed3(() => providersSignal()),
|
|
1405
|
+
refresh: store.refresh,
|
|
1406
|
+
updatedAt: computed3(() => updatedAtSignal())
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
VoiceProviderStatusService = __decorateElement(_init, 0, "VoiceProviderStatusService", _dec, VoiceProviderStatusService);
|
|
1411
|
+
__runInitializers(_init, 1, VoiceProviderStatusService);
|
|
1412
|
+
__decoratorMetadata(_init, VoiceProviderStatusService);
|
|
1413
|
+
let _VoiceProviderStatusService = VoiceProviderStatusService;
|
|
1291
1414
|
export {
|
|
1292
1415
|
VoiceStreamService,
|
|
1416
|
+
VoiceProviderStatusService,
|
|
1293
1417
|
VoiceControllerService
|
|
1294
1418
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type VoiceProviderStatusClientOptions } from '../client/providerStatus';
|
|
2
|
+
import type { VoiceProviderHealthSummary } from '../providerHealth';
|
|
3
|
+
export declare class VoiceProviderStatusService {
|
|
4
|
+
connect<TProvider extends string = string>(path?: string, options?: VoiceProviderStatusClientOptions): {
|
|
5
|
+
close: () => void;
|
|
6
|
+
error: import("@angular/core").Signal<string | null>;
|
|
7
|
+
isLoading: import("@angular/core").Signal<boolean>;
|
|
8
|
+
providers: import("@angular/core").Signal<VoiceProviderHealthSummary<TProvider>[]>;
|
|
9
|
+
refresh: () => Promise<VoiceProviderHealthSummary<TProvider>[]>;
|
|
10
|
+
updatedAt: import("@angular/core").Signal<number | undefined>;
|
|
11
|
+
};
|
|
12
|
+
}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -5,3 +5,5 @@ export { createVoiceController } from './controller';
|
|
|
5
5
|
export { bindVoiceBargeIn, createVoiceDuplexController } from './duplex';
|
|
6
6
|
export { bindVoiceHTMX } from './htmx';
|
|
7
7
|
export { createMicrophoneCapture } from './microphone';
|
|
8
|
+
export { createVoiceProviderStatusStore, fetchVoiceProviderStatus } from './providerStatus';
|
|
9
|
+
export type { VoiceProviderStatusClientOptions, VoiceProviderStatusSnapshot } from './providerStatus';
|
package/dist/client/index.js
CHANGED
|
@@ -1581,9 +1581,90 @@ var createVoiceDuplexController = (path, options = {}) => {
|
|
|
1581
1581
|
}
|
|
1582
1582
|
};
|
|
1583
1583
|
};
|
|
1584
|
+
// src/client/providerStatus.ts
|
|
1585
|
+
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
1586
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1587
|
+
const response = await fetchImpl(path);
|
|
1588
|
+
if (!response.ok) {
|
|
1589
|
+
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
1590
|
+
}
|
|
1591
|
+
return await response.json();
|
|
1592
|
+
};
|
|
1593
|
+
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
1594
|
+
const listeners = new Set;
|
|
1595
|
+
let closed = false;
|
|
1596
|
+
let timer;
|
|
1597
|
+
let snapshot = {
|
|
1598
|
+
error: null,
|
|
1599
|
+
isLoading: false,
|
|
1600
|
+
providers: []
|
|
1601
|
+
};
|
|
1602
|
+
const emit = () => {
|
|
1603
|
+
for (const listener of listeners) {
|
|
1604
|
+
listener();
|
|
1605
|
+
}
|
|
1606
|
+
};
|
|
1607
|
+
const refresh = async () => {
|
|
1608
|
+
if (closed) {
|
|
1609
|
+
return snapshot.providers;
|
|
1610
|
+
}
|
|
1611
|
+
snapshot = {
|
|
1612
|
+
...snapshot,
|
|
1613
|
+
error: null,
|
|
1614
|
+
isLoading: true
|
|
1615
|
+
};
|
|
1616
|
+
emit();
|
|
1617
|
+
try {
|
|
1618
|
+
const providers = await fetchVoiceProviderStatus(path, options);
|
|
1619
|
+
snapshot = {
|
|
1620
|
+
error: null,
|
|
1621
|
+
isLoading: false,
|
|
1622
|
+
providers,
|
|
1623
|
+
updatedAt: Date.now()
|
|
1624
|
+
};
|
|
1625
|
+
emit();
|
|
1626
|
+
return providers;
|
|
1627
|
+
} catch (error) {
|
|
1628
|
+
snapshot = {
|
|
1629
|
+
...snapshot,
|
|
1630
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1631
|
+
isLoading: false
|
|
1632
|
+
};
|
|
1633
|
+
emit();
|
|
1634
|
+
throw error;
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
const close = () => {
|
|
1638
|
+
closed = true;
|
|
1639
|
+
if (timer) {
|
|
1640
|
+
clearInterval(timer);
|
|
1641
|
+
timer = undefined;
|
|
1642
|
+
}
|
|
1643
|
+
listeners.clear();
|
|
1644
|
+
};
|
|
1645
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1646
|
+
timer = setInterval(() => {
|
|
1647
|
+
refresh().catch(() => {});
|
|
1648
|
+
}, options.intervalMs);
|
|
1649
|
+
}
|
|
1650
|
+
return {
|
|
1651
|
+
close,
|
|
1652
|
+
getServerSnapshot: () => snapshot,
|
|
1653
|
+
getSnapshot: () => snapshot,
|
|
1654
|
+
refresh,
|
|
1655
|
+
subscribe: (listener) => {
|
|
1656
|
+
listeners.add(listener);
|
|
1657
|
+
return () => {
|
|
1658
|
+
listeners.delete(listener);
|
|
1659
|
+
};
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
};
|
|
1584
1663
|
export {
|
|
1664
|
+
fetchVoiceProviderStatus,
|
|
1585
1665
|
decodeVoiceAudioChunk,
|
|
1586
1666
|
createVoiceStream,
|
|
1667
|
+
createVoiceProviderStatusStore,
|
|
1587
1668
|
createVoiceDuplexController,
|
|
1588
1669
|
createVoiceController,
|
|
1589
1670
|
createVoiceConnection,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { VoiceProviderHealthSummary } from '../providerHealth';
|
|
2
|
+
export type VoiceProviderStatusClientOptions = {
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
intervalMs?: number;
|
|
5
|
+
};
|
|
6
|
+
export type VoiceProviderStatusSnapshot<TProvider extends string = string> = {
|
|
7
|
+
error: string | null;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
providers: VoiceProviderHealthSummary<TProvider>[];
|
|
10
|
+
updatedAt?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const fetchVoiceProviderStatus: <TProvider extends string = string>(path?: string, options?: Pick<VoiceProviderStatusClientOptions, "fetch">) => Promise<VoiceProviderHealthSummary<TProvider>[]>;
|
|
13
|
+
export declare const createVoiceProviderStatusStore: <TProvider extends string = string>(path?: string, options?: VoiceProviderStatusClientOptions) => {
|
|
14
|
+
close: () => void;
|
|
15
|
+
getServerSnapshot: () => VoiceProviderStatusSnapshot<TProvider>;
|
|
16
|
+
getSnapshot: () => VoiceProviderStatusSnapshot<TProvider>;
|
|
17
|
+
refresh: () => Promise<VoiceProviderHealthSummary<TProvider>[]>;
|
|
18
|
+
subscribe: (listener: () => void) => () => void;
|
|
19
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from '.
|
|
|
4
4
|
export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
|
|
5
5
|
export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
|
|
6
6
|
export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, createVoiceProviderRouter } from './modelAdapters';
|
|
7
|
+
export { renderVoiceProviderHealthHTML, summarizeVoiceProviderHealth } from './providerHealth';
|
|
7
8
|
export { buildVoiceTraceReplay, createVoiceMemoryTraceSinkDeliveryStore, createVoiceTraceHTTPSink, createVoiceMemoryTraceEventStore, createVoiceTraceSinkDeliveryId, createVoiceTraceSinkDeliveryRecord, createVoiceTraceSinkStore, createVoiceTraceEvent, createVoiceTraceEventId, deliverVoiceTraceEventsToSinks, evaluateVoiceTrace, exportVoiceTrace, filterVoiceTraceEvents, pruneVoiceTraceEvents, redactVoiceTraceEvent, redactVoiceTraceEvents, redactVoiceTraceText, renderVoiceTraceHTML, renderVoiceTraceMarkdown, resolveVoiceTraceRedactionOptions, selectVoiceTraceEventsForPrune, summarizeVoiceTrace } from './trace';
|
|
8
9
|
export { createVoiceSQLiteExternalObjectMapStore, createVoiceSQLiteIntegrationEventStore, createVoiceSQLiteReviewStore, createVoiceSQLiteRuntimeStorage, createVoiceSQLiteSessionStore, createVoiceSQLiteTaskStore, createVoiceSQLiteTraceSinkDeliveryStore, createVoiceSQLiteTraceEventStore } from './sqliteStore';
|
|
9
10
|
export { createVoicePostgresExternalObjectMapStore, createVoicePostgresIntegrationEventStore, createVoicePostgresReviewStore, createVoicePostgresRuntimeStorage, createVoicePostgresSessionStore, createVoicePostgresTaskStore, createVoicePostgresTraceSinkDeliveryStore, createVoicePostgresTraceEventStore } from './postgresStore';
|
|
@@ -27,6 +28,7 @@ export { createVoiceCallReviewFromLiveTelephonyReport, createVoiceCallReviewReco
|
|
|
27
28
|
export type { VoiceAssistant, VoiceAssistantArtifactPlan, VoiceAssistantExperiment, VoiceAssistantExperimentOptions, VoiceAssistantGuardrailInput, VoiceAssistantGuardrails, VoiceAssistantMemoryLifecycle, VoiceAssistantMemoryLifecycleInput, VoiceAssistantOptions, VoiceAssistantOutputGuardrailInput, VoiceAssistantPreset, VoiceAssistantRunsSummary, VoiceAssistantRunSummary, VoiceAssistantVariant } from './assistant';
|
|
28
29
|
export type { VoiceAssistantMemoryBinding, VoiceAssistantMemoryHandle, VoiceAssistantMemoryOptions, VoiceAssistantMemoryRecord, VoiceAssistantMemoryStore } from './assistantMemory';
|
|
29
30
|
export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterProviderHealth, VoiceProviderRouterProviderProfile, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
|
|
31
|
+
export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
|
|
30
32
|
export type { VoiceAgent, VoiceAgentMessage, VoiceAgentMessageRole, VoiceAgentModel, VoiceAgentModelInput, VoiceAgentModelOutput, VoiceAgentOptions, VoiceAgentRunResult, VoiceAgentSquadOptions, VoiceAgentTool, VoiceAgentToolCall, VoiceAgentToolResult } from './agent';
|
|
31
33
|
export type { VoiceOpsRuntime, VoiceOpsRuntimeConfig, VoiceOpsRuntimeSummary, VoiceOpsRuntimeSinkWorkerConfig, VoiceOpsRuntimeTaskWorkerConfig, VoiceOpsRuntimeTickResult, VoiceOpsRuntimeWebhookWorkerConfig } from './opsRuntime';
|
|
32
34
|
export type { VoiceOpsPresetName, VoiceOpsPresetOverrides, VoiceResolvedOpsPreset } from './opsPresets';
|
package/dist/index.js
CHANGED
|
@@ -7265,6 +7265,21 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7265
7265
|
healthState.set(provider, next);
|
|
7266
7266
|
return next;
|
|
7267
7267
|
};
|
|
7268
|
+
const cloneHealth = (provider) => {
|
|
7269
|
+
if (!healthOptions) {
|
|
7270
|
+
return;
|
|
7271
|
+
}
|
|
7272
|
+
return {
|
|
7273
|
+
...getHealth(provider)
|
|
7274
|
+
};
|
|
7275
|
+
};
|
|
7276
|
+
const getSuppressionRemainingMs = (provider) => {
|
|
7277
|
+
if (!healthOptions) {
|
|
7278
|
+
return;
|
|
7279
|
+
}
|
|
7280
|
+
const suppressedUntil = getHealth(provider).suppressedUntil;
|
|
7281
|
+
return typeof suppressedUntil === "number" ? Math.max(0, suppressedUntil - now()) : undefined;
|
|
7282
|
+
};
|
|
7268
7283
|
const isSuppressed = (provider) => {
|
|
7269
7284
|
if (!healthOptions) {
|
|
7270
7285
|
return false;
|
|
@@ -7280,10 +7295,11 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7280
7295
|
health.consecutiveFailures = 0;
|
|
7281
7296
|
health.status = "healthy";
|
|
7282
7297
|
health.suppressedUntil = undefined;
|
|
7298
|
+
return cloneHealth(provider);
|
|
7283
7299
|
};
|
|
7284
7300
|
const recordProviderError = (provider, isProviderError, rateLimited) => {
|
|
7285
7301
|
if (!healthOptions || !isProviderError) {
|
|
7286
|
-
return;
|
|
7302
|
+
return cloneHealth(provider);
|
|
7287
7303
|
}
|
|
7288
7304
|
const currentTime = now();
|
|
7289
7305
|
const health = getHealth(provider);
|
|
@@ -7296,6 +7312,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7296
7312
|
health.status = "suppressed";
|
|
7297
7313
|
health.suppressedUntil = currentTime + (rateLimited ? rateLimitCooldownMs : cooldownMs);
|
|
7298
7314
|
}
|
|
7315
|
+
return cloneHealth(provider);
|
|
7299
7316
|
};
|
|
7300
7317
|
const resolveAllowedProviders = async (input) => {
|
|
7301
7318
|
const allowProviders = policy?.allowProviders ?? options.allowProviders;
|
|
@@ -7361,12 +7378,13 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7361
7378
|
const startedAt = Date.now();
|
|
7362
7379
|
try {
|
|
7363
7380
|
const output = await model.generate(input);
|
|
7364
|
-
recordProviderSuccess(provider);
|
|
7381
|
+
const providerHealth = recordProviderSuccess(provider);
|
|
7365
7382
|
await emit({
|
|
7366
7383
|
at: Date.now(),
|
|
7367
7384
|
elapsedMs: Date.now() - startedAt,
|
|
7368
7385
|
fallbackProvider: provider === selectedProvider ? undefined : provider,
|
|
7369
7386
|
provider,
|
|
7387
|
+
providerHealth,
|
|
7370
7388
|
recovered: provider !== selectedProvider,
|
|
7371
7389
|
selectedProvider,
|
|
7372
7390
|
status: provider === selectedProvider ? "success" : "fallback"
|
|
@@ -7378,7 +7396,7 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7378
7396
|
const isProviderError = options.isProviderError?.(error, provider) ?? true;
|
|
7379
7397
|
const rateLimited = options.isRateLimitError?.(error, provider) ?? defaultIsRateLimitError(error);
|
|
7380
7398
|
const shouldFallback = fallbackMode === "provider-error" ? isProviderError : fallbackMode === "rate-limit" ? isProviderError && rateLimited : false;
|
|
7381
|
-
recordProviderError(provider, isProviderError, rateLimited);
|
|
7399
|
+
const providerHealth = recordProviderError(provider, isProviderError, rateLimited);
|
|
7382
7400
|
const nextProvider = hasNextProvider ? order[index + 1] : undefined;
|
|
7383
7401
|
await emit({
|
|
7384
7402
|
at: Date.now(),
|
|
@@ -7386,8 +7404,11 @@ var createVoiceProviderRouter = (options) => {
|
|
|
7386
7404
|
error: errorMessage(error),
|
|
7387
7405
|
fallbackProvider: shouldFallback ? nextProvider : undefined,
|
|
7388
7406
|
provider,
|
|
7407
|
+
providerHealth,
|
|
7389
7408
|
rateLimited,
|
|
7390
7409
|
selectedProvider,
|
|
7410
|
+
suppressionRemainingMs: getSuppressionRemainingMs(provider),
|
|
7411
|
+
suppressedUntil: providerHealth?.suppressedUntil,
|
|
7391
7412
|
status: "error"
|
|
7392
7413
|
}, input);
|
|
7393
7414
|
if (!hasNextProvider || !shouldFallback) {
|
|
@@ -7834,6 +7855,166 @@ var createGeminiVoiceAssistantModel = (options) => {
|
|
|
7834
7855
|
}
|
|
7835
7856
|
};
|
|
7836
7857
|
};
|
|
7858
|
+
// src/providerHealth.ts
|
|
7859
|
+
var getString = (value) => typeof value === "string" ? value : undefined;
|
|
7860
|
+
var getNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
7861
|
+
var isProviderStatus = (value) => value === "success" || value === "fallback" || value === "error";
|
|
7862
|
+
var summarizeVoiceProviderHealth = async (input) => {
|
|
7863
|
+
const options = Array.isArray(input) ? { events: input } : input;
|
|
7864
|
+
const events = options.events ?? await options.store?.list() ?? [];
|
|
7865
|
+
const providers = options.providers ?? [];
|
|
7866
|
+
const providerSet = new Set(providers);
|
|
7867
|
+
const now = options.now ?? Date.now();
|
|
7868
|
+
const entries = new Map;
|
|
7869
|
+
const isAllowedProvider = (value) => typeof value === "string" && (providerSet.size === 0 || providerSet.has(value));
|
|
7870
|
+
const getEntry = (provider) => {
|
|
7871
|
+
const existing = entries.get(provider);
|
|
7872
|
+
if (existing) {
|
|
7873
|
+
return existing;
|
|
7874
|
+
}
|
|
7875
|
+
const entry = {
|
|
7876
|
+
elapsedCount: 0,
|
|
7877
|
+
elapsedTotal: 0,
|
|
7878
|
+
errorCount: 0,
|
|
7879
|
+
fallbackCount: 0,
|
|
7880
|
+
provider,
|
|
7881
|
+
rateLimited: false,
|
|
7882
|
+
recommended: false,
|
|
7883
|
+
runCount: 0,
|
|
7884
|
+
status: "idle"
|
|
7885
|
+
};
|
|
7886
|
+
entries.set(provider, entry);
|
|
7887
|
+
return entry;
|
|
7888
|
+
};
|
|
7889
|
+
for (const provider of providers) {
|
|
7890
|
+
getEntry(provider);
|
|
7891
|
+
}
|
|
7892
|
+
const hasProviderRouterEvents = events.some((event) => event.type === "session.error" && isAllowedProvider(event.payload.provider) && isProviderStatus(event.payload.providerStatus));
|
|
7893
|
+
for (const event of events) {
|
|
7894
|
+
if (event.type === "assistant.run") {
|
|
7895
|
+
if (hasProviderRouterEvents) {
|
|
7896
|
+
continue;
|
|
7897
|
+
}
|
|
7898
|
+
const provider2 = event.payload.variantId;
|
|
7899
|
+
if (!isAllowedProvider(provider2)) {
|
|
7900
|
+
continue;
|
|
7901
|
+
}
|
|
7902
|
+
const entry2 = getEntry(provider2);
|
|
7903
|
+
entry2.runCount += 1;
|
|
7904
|
+
const elapsedMs = getNumber(event.payload.elapsedMs);
|
|
7905
|
+
if (elapsedMs !== undefined) {
|
|
7906
|
+
entry2.elapsedCount += 1;
|
|
7907
|
+
entry2.elapsedTotal += elapsedMs;
|
|
7908
|
+
}
|
|
7909
|
+
continue;
|
|
7910
|
+
}
|
|
7911
|
+
if (event.type !== "session.error") {
|
|
7912
|
+
continue;
|
|
7913
|
+
}
|
|
7914
|
+
const provider = event.payload.provider;
|
|
7915
|
+
if (!isAllowedProvider(provider)) {
|
|
7916
|
+
continue;
|
|
7917
|
+
}
|
|
7918
|
+
const providerStatus = isProviderStatus(event.payload.providerStatus) ? event.payload.providerStatus : undefined;
|
|
7919
|
+
const applyProviderHealth = () => {
|
|
7920
|
+
const entry2 = getEntry(provider);
|
|
7921
|
+
const providerHealth = event.payload.providerHealth;
|
|
7922
|
+
if (providerHealth && typeof providerHealth === "object") {
|
|
7923
|
+
const suppressedUntil2 = getNumber(providerHealth.suppressedUntil);
|
|
7924
|
+
if (suppressedUntil2 !== undefined) {
|
|
7925
|
+
entry2.suppressedUntil = suppressedUntil2;
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7928
|
+
const suppressedUntil = getNumber(event.payload.suppressedUntil);
|
|
7929
|
+
if (suppressedUntil !== undefined) {
|
|
7930
|
+
entry2.suppressedUntil = suppressedUntil;
|
|
7931
|
+
}
|
|
7932
|
+
const suppressionRemainingMs = getNumber(event.payload.suppressionRemainingMs);
|
|
7933
|
+
if (suppressionRemainingMs !== undefined) {
|
|
7934
|
+
entry2.suppressionRemainingMs = suppressionRemainingMs;
|
|
7935
|
+
}
|
|
7936
|
+
return entry2;
|
|
7937
|
+
};
|
|
7938
|
+
if (providerStatus === "success" || providerStatus === "fallback") {
|
|
7939
|
+
const entry2 = applyProviderHealth();
|
|
7940
|
+
entry2.runCount += 1;
|
|
7941
|
+
entry2.lastSuccessAt = event.at;
|
|
7942
|
+
if (providerStatus === "success") {
|
|
7943
|
+
entry2.lastError = undefined;
|
|
7944
|
+
entry2.rateLimited = false;
|
|
7945
|
+
entry2.suppressedUntil = undefined;
|
|
7946
|
+
entry2.suppressionRemainingMs = undefined;
|
|
7947
|
+
}
|
|
7948
|
+
const elapsedMs = getNumber(event.payload.elapsedMs);
|
|
7949
|
+
if (elapsedMs !== undefined) {
|
|
7950
|
+
entry2.elapsedCount += 1;
|
|
7951
|
+
entry2.elapsedTotal += elapsedMs;
|
|
7952
|
+
}
|
|
7953
|
+
const selectedProvider = event.payload.selectedProvider;
|
|
7954
|
+
if (providerStatus === "fallback" && isAllowedProvider(selectedProvider) && selectedProvider !== provider) {
|
|
7955
|
+
getEntry(selectedProvider).fallbackCount += 1;
|
|
7956
|
+
}
|
|
7957
|
+
continue;
|
|
7958
|
+
}
|
|
7959
|
+
const entry = applyProviderHealth();
|
|
7960
|
+
entry.errorCount += 1;
|
|
7961
|
+
entry.lastError = getString(event.payload.error);
|
|
7962
|
+
entry.lastErrorAt = event.at;
|
|
7963
|
+
entry.rateLimited ||= event.payload.rateLimited === true;
|
|
7964
|
+
}
|
|
7965
|
+
const summaries = [...entries.values()].map((entry) => {
|
|
7966
|
+
const hadSuppression = typeof entry.suppressedUntil === "number" || typeof entry.suppressionRemainingMs === "number";
|
|
7967
|
+
const suppressionRemainingMs = typeof entry.suppressedUntil === "number" ? Math.max(0, entry.suppressedUntil - now) : entry.suppressionRemainingMs;
|
|
7968
|
+
const activeSuppression = typeof suppressionRemainingMs === "number" && suppressionRemainingMs > 0;
|
|
7969
|
+
const recoverable = hadSuppression && !activeSuppression;
|
|
7970
|
+
const averageElapsedMs = entry.elapsedCount > 0 ? Math.round(entry.elapsedTotal / entry.elapsedCount) : undefined;
|
|
7971
|
+
const status = activeSuppression ? "suppressed" : recoverable ? "recoverable" : entry.rateLimited ? "rate-limited" : entry.errorCount > 0 && (!entry.lastSuccessAt || !entry.lastErrorAt || entry.lastErrorAt > entry.lastSuccessAt) ? "degraded" : entry.runCount > 0 ? "healthy" : "idle";
|
|
7972
|
+
return {
|
|
7973
|
+
averageElapsedMs,
|
|
7974
|
+
errorCount: entry.errorCount,
|
|
7975
|
+
fallbackCount: entry.fallbackCount,
|
|
7976
|
+
lastError: entry.lastError,
|
|
7977
|
+
lastErrorAt: entry.lastErrorAt,
|
|
7978
|
+
lastSuccessAt: entry.lastSuccessAt,
|
|
7979
|
+
provider: entry.provider,
|
|
7980
|
+
rateLimited: entry.rateLimited,
|
|
7981
|
+
recommended: false,
|
|
7982
|
+
runCount: entry.runCount,
|
|
7983
|
+
status,
|
|
7984
|
+
suppressionRemainingMs: activeSuppression ? suppressionRemainingMs : undefined,
|
|
7985
|
+
suppressedUntil: entry.suppressedUntil
|
|
7986
|
+
};
|
|
7987
|
+
});
|
|
7988
|
+
const recommended = summaries.filter((entry) => entry.status === "healthy").sort((left, right) => (left.averageElapsedMs ?? Number.MAX_SAFE_INTEGER) - (right.averageElapsedMs ?? Number.MAX_SAFE_INTEGER))[0];
|
|
7989
|
+
if (recommended) {
|
|
7990
|
+
recommended.recommended = true;
|
|
7991
|
+
}
|
|
7992
|
+
return summaries;
|
|
7993
|
+
};
|
|
7994
|
+
var escapeHtml4 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
7995
|
+
var renderVoiceProviderHealthHTML = (providers) => providers.length === 0 ? '<p class="voice-provider-empty">No provider status yet.</p>' : [
|
|
7996
|
+
'<div class="voice-provider-health">',
|
|
7997
|
+
...providers.map((provider) => {
|
|
7998
|
+
const suppressionSeconds = typeof provider.suppressionRemainingMs === "number" ? Math.ceil(provider.suppressionRemainingMs / 1000) : undefined;
|
|
7999
|
+
return [
|
|
8000
|
+
`<article class="voice-provider-card ${escapeHtml4(provider.status)}">`,
|
|
8001
|
+
'<div class="voice-provider-card-header">',
|
|
8002
|
+
`<strong>${escapeHtml4(provider.provider)}</strong>`,
|
|
8003
|
+
`<span>${escapeHtml4(provider.status)}${provider.recommended ? " \xB7 recommended" : ""}</span>`,
|
|
8004
|
+
"</div>",
|
|
8005
|
+
"<dl>",
|
|
8006
|
+
`<div><dt>Runs</dt><dd>${String(provider.runCount)}</dd></div>`,
|
|
8007
|
+
`<div><dt>Avg latency</dt><dd>${String(provider.averageElapsedMs ?? 0)}ms</dd></div>`,
|
|
8008
|
+
`<div><dt>Errors</dt><dd>${String(provider.errorCount)}</dd></div>`,
|
|
8009
|
+
`<div><dt>Fallbacks</dt><dd>${String(provider.fallbackCount)}</dd></div>`,
|
|
8010
|
+
"</dl>",
|
|
8011
|
+
suppressionSeconds ? `<p>Temporarily suppressed for ${String(suppressionSeconds)}s.</p>` : "",
|
|
8012
|
+
provider.lastError ? `<p>${escapeHtml4(provider.lastError)}</p>` : "",
|
|
8013
|
+
"</article>"
|
|
8014
|
+
].join("");
|
|
8015
|
+
}),
|
|
8016
|
+
"</div>"
|
|
8017
|
+
].join("");
|
|
7837
8018
|
// src/sqliteStore.ts
|
|
7838
8019
|
import { Database } from "bun:sqlite";
|
|
7839
8020
|
var normalizeTableNameSegment = (value) => value.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice";
|
|
@@ -10284,6 +10465,7 @@ export {
|
|
|
10284
10465
|
transcodePCMToTwilioOutboundPayload,
|
|
10285
10466
|
summarizeVoiceTraceSinkDeliveries,
|
|
10286
10467
|
summarizeVoiceTrace,
|
|
10468
|
+
summarizeVoiceProviderHealth,
|
|
10287
10469
|
summarizeVoiceOpsTasks,
|
|
10288
10470
|
summarizeVoiceOpsTaskQueue,
|
|
10289
10471
|
summarizeVoiceOpsTaskAnalytics,
|
|
@@ -10307,6 +10489,7 @@ export {
|
|
|
10307
10489
|
reopenVoiceOpsTask,
|
|
10308
10490
|
renderVoiceTraceMarkdown,
|
|
10309
10491
|
renderVoiceTraceHTML,
|
|
10492
|
+
renderVoiceProviderHealthHTML,
|
|
10310
10493
|
renderVoiceCallReviewMarkdown,
|
|
10311
10494
|
renderVoiceCallReviewHTML,
|
|
10312
10495
|
redactVoiceTraceText,
|
package/dist/modelAdapters.d.ts
CHANGED
|
@@ -40,9 +40,12 @@ export type VoiceProviderRouterEvent<TProvider extends string = string> = {
|
|
|
40
40
|
error?: string;
|
|
41
41
|
fallbackProvider?: TProvider;
|
|
42
42
|
provider: TProvider;
|
|
43
|
+
providerHealth?: VoiceProviderRouterProviderHealth<TProvider>;
|
|
43
44
|
rateLimited?: boolean;
|
|
44
45
|
recovered?: boolean;
|
|
45
46
|
selectedProvider: TProvider;
|
|
47
|
+
suppressionRemainingMs?: number;
|
|
48
|
+
suppressedUntil?: number;
|
|
46
49
|
status: 'error' | 'fallback' | 'success';
|
|
47
50
|
};
|
|
48
51
|
export type VoiceProviderRouterFallbackMode = 'never' | 'provider-error' | 'rate-limit';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { StoredVoiceTraceEvent, VoiceTraceEventStore } from './trace';
|
|
2
|
+
export type VoiceProviderHealthStatus = 'healthy' | 'idle' | 'rate-limited' | 'degraded' | 'recoverable' | 'suppressed';
|
|
3
|
+
export type VoiceProviderHealthSummary<TProvider extends string = string> = {
|
|
4
|
+
averageElapsedMs?: number;
|
|
5
|
+
errorCount: number;
|
|
6
|
+
fallbackCount: number;
|
|
7
|
+
lastError?: string;
|
|
8
|
+
lastErrorAt?: number;
|
|
9
|
+
lastSuccessAt?: number;
|
|
10
|
+
provider: TProvider;
|
|
11
|
+
rateLimited: boolean;
|
|
12
|
+
recommended: boolean;
|
|
13
|
+
runCount: number;
|
|
14
|
+
status: VoiceProviderHealthStatus;
|
|
15
|
+
suppressionRemainingMs?: number;
|
|
16
|
+
suppressedUntil?: number;
|
|
17
|
+
};
|
|
18
|
+
export type VoiceProviderHealthSummaryOptions<TProvider extends string = string> = {
|
|
19
|
+
events?: StoredVoiceTraceEvent[];
|
|
20
|
+
now?: number;
|
|
21
|
+
providers?: readonly TProvider[];
|
|
22
|
+
store?: VoiceTraceEventStore;
|
|
23
|
+
};
|
|
24
|
+
export declare const summarizeVoiceProviderHealth: <TProvider extends string = string>(input: StoredVoiceTraceEvent[] | VoiceProviderHealthSummaryOptions<TProvider>) => Promise<VoiceProviderHealthSummary<TProvider>[]>;
|
|
25
|
+
export declare const renderVoiceProviderHealthHTML: (providers: VoiceProviderHealthSummary[]) => string;
|
package/dist/react/index.d.ts
CHANGED
package/dist/react/index.js
CHANGED
|
@@ -1234,7 +1234,107 @@ var useVoiceController = (path, options = {}) => {
|
|
|
1234
1234
|
toggleRecording: () => controller.toggleRecording()
|
|
1235
1235
|
};
|
|
1236
1236
|
};
|
|
1237
|
+
// src/react/useVoiceProviderStatus.tsx
|
|
1238
|
+
import { useEffect as useEffect3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
1239
|
+
|
|
1240
|
+
// src/client/providerStatus.ts
|
|
1241
|
+
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
1242
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1243
|
+
const response = await fetchImpl(path);
|
|
1244
|
+
if (!response.ok) {
|
|
1245
|
+
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
1246
|
+
}
|
|
1247
|
+
return await response.json();
|
|
1248
|
+
};
|
|
1249
|
+
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
1250
|
+
const listeners = new Set;
|
|
1251
|
+
let closed = false;
|
|
1252
|
+
let timer;
|
|
1253
|
+
let snapshot = {
|
|
1254
|
+
error: null,
|
|
1255
|
+
isLoading: false,
|
|
1256
|
+
providers: []
|
|
1257
|
+
};
|
|
1258
|
+
const emit = () => {
|
|
1259
|
+
for (const listener of listeners) {
|
|
1260
|
+
listener();
|
|
1261
|
+
}
|
|
1262
|
+
};
|
|
1263
|
+
const refresh = async () => {
|
|
1264
|
+
if (closed) {
|
|
1265
|
+
return snapshot.providers;
|
|
1266
|
+
}
|
|
1267
|
+
snapshot = {
|
|
1268
|
+
...snapshot,
|
|
1269
|
+
error: null,
|
|
1270
|
+
isLoading: true
|
|
1271
|
+
};
|
|
1272
|
+
emit();
|
|
1273
|
+
try {
|
|
1274
|
+
const providers = await fetchVoiceProviderStatus(path, options);
|
|
1275
|
+
snapshot = {
|
|
1276
|
+
error: null,
|
|
1277
|
+
isLoading: false,
|
|
1278
|
+
providers,
|
|
1279
|
+
updatedAt: Date.now()
|
|
1280
|
+
};
|
|
1281
|
+
emit();
|
|
1282
|
+
return providers;
|
|
1283
|
+
} catch (error) {
|
|
1284
|
+
snapshot = {
|
|
1285
|
+
...snapshot,
|
|
1286
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1287
|
+
isLoading: false
|
|
1288
|
+
};
|
|
1289
|
+
emit();
|
|
1290
|
+
throw error;
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
const close = () => {
|
|
1294
|
+
closed = true;
|
|
1295
|
+
if (timer) {
|
|
1296
|
+
clearInterval(timer);
|
|
1297
|
+
timer = undefined;
|
|
1298
|
+
}
|
|
1299
|
+
listeners.clear();
|
|
1300
|
+
};
|
|
1301
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1302
|
+
timer = setInterval(() => {
|
|
1303
|
+
refresh().catch(() => {});
|
|
1304
|
+
}, options.intervalMs);
|
|
1305
|
+
}
|
|
1306
|
+
return {
|
|
1307
|
+
close,
|
|
1308
|
+
getServerSnapshot: () => snapshot,
|
|
1309
|
+
getSnapshot: () => snapshot,
|
|
1310
|
+
refresh,
|
|
1311
|
+
subscribe: (listener) => {
|
|
1312
|
+
listeners.add(listener);
|
|
1313
|
+
return () => {
|
|
1314
|
+
listeners.delete(listener);
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
// src/react/useVoiceProviderStatus.tsx
|
|
1321
|
+
var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
1322
|
+
const storeRef = useRef3(null);
|
|
1323
|
+
if (!storeRef.current) {
|
|
1324
|
+
storeRef.current = createVoiceProviderStatusStore(path, options);
|
|
1325
|
+
}
|
|
1326
|
+
const store = storeRef.current;
|
|
1327
|
+
useEffect3(() => {
|
|
1328
|
+
store.refresh().catch(() => {});
|
|
1329
|
+
return () => store.close();
|
|
1330
|
+
}, [store]);
|
|
1331
|
+
return {
|
|
1332
|
+
...useSyncExternalStore3(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
1333
|
+
refresh: store.refresh
|
|
1334
|
+
};
|
|
1335
|
+
};
|
|
1237
1336
|
export {
|
|
1238
1337
|
useVoiceStream,
|
|
1338
|
+
useVoiceProviderStatus,
|
|
1239
1339
|
useVoiceController
|
|
1240
1340
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type VoiceProviderStatusClientOptions } from '../client/providerStatus';
|
|
2
|
+
export declare const useVoiceProviderStatus: <TProvider extends string = string>(path?: string, options?: VoiceProviderStatusClientOptions) => {
|
|
3
|
+
refresh: () => Promise<import("..").VoiceProviderHealthSummary<TProvider>[]>;
|
|
4
|
+
error: string | null;
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
providers: import("..").VoiceProviderHealthSummary<TProvider>[];
|
|
7
|
+
updatedAt?: number;
|
|
8
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { VoiceProviderStatusClientOptions } from '../client/providerStatus';
|
|
2
|
+
export declare const createVoiceProviderStatus: <TProvider extends string = string>(path?: string, options?: VoiceProviderStatusClientOptions) => {
|
|
3
|
+
close: () => void;
|
|
4
|
+
getServerSnapshot: () => import("../client").VoiceProviderStatusSnapshot<TProvider>;
|
|
5
|
+
getSnapshot: () => import("../client").VoiceProviderStatusSnapshot<TProvider>;
|
|
6
|
+
refresh: () => Promise<import("..").VoiceProviderHealthSummary<TProvider>[]>;
|
|
7
|
+
subscribe: (listener: () => void) => () => void;
|
|
8
|
+
};
|
package/dist/svelte/index.d.ts
CHANGED
package/dist/svelte/index.js
CHANGED
|
@@ -548,6 +548,88 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
548
548
|
|
|
549
549
|
// src/svelte/createVoiceStream.ts
|
|
550
550
|
var createVoiceStream2 = (path, options = {}) => createVoiceStream(path, options);
|
|
551
|
+
// src/client/providerStatus.ts
|
|
552
|
+
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
553
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
554
|
+
const response = await fetchImpl(path);
|
|
555
|
+
if (!response.ok) {
|
|
556
|
+
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
557
|
+
}
|
|
558
|
+
return await response.json();
|
|
559
|
+
};
|
|
560
|
+
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
561
|
+
const listeners = new Set;
|
|
562
|
+
let closed = false;
|
|
563
|
+
let timer;
|
|
564
|
+
let snapshot = {
|
|
565
|
+
error: null,
|
|
566
|
+
isLoading: false,
|
|
567
|
+
providers: []
|
|
568
|
+
};
|
|
569
|
+
const emit = () => {
|
|
570
|
+
for (const listener of listeners) {
|
|
571
|
+
listener();
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
const refresh = async () => {
|
|
575
|
+
if (closed) {
|
|
576
|
+
return snapshot.providers;
|
|
577
|
+
}
|
|
578
|
+
snapshot = {
|
|
579
|
+
...snapshot,
|
|
580
|
+
error: null,
|
|
581
|
+
isLoading: true
|
|
582
|
+
};
|
|
583
|
+
emit();
|
|
584
|
+
try {
|
|
585
|
+
const providers = await fetchVoiceProviderStatus(path, options);
|
|
586
|
+
snapshot = {
|
|
587
|
+
error: null,
|
|
588
|
+
isLoading: false,
|
|
589
|
+
providers,
|
|
590
|
+
updatedAt: Date.now()
|
|
591
|
+
};
|
|
592
|
+
emit();
|
|
593
|
+
return providers;
|
|
594
|
+
} catch (error) {
|
|
595
|
+
snapshot = {
|
|
596
|
+
...snapshot,
|
|
597
|
+
error: error instanceof Error ? error.message : String(error),
|
|
598
|
+
isLoading: false
|
|
599
|
+
};
|
|
600
|
+
emit();
|
|
601
|
+
throw error;
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
const close = () => {
|
|
605
|
+
closed = true;
|
|
606
|
+
if (timer) {
|
|
607
|
+
clearInterval(timer);
|
|
608
|
+
timer = undefined;
|
|
609
|
+
}
|
|
610
|
+
listeners.clear();
|
|
611
|
+
};
|
|
612
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
613
|
+
timer = setInterval(() => {
|
|
614
|
+
refresh().catch(() => {});
|
|
615
|
+
}, options.intervalMs);
|
|
616
|
+
}
|
|
617
|
+
return {
|
|
618
|
+
close,
|
|
619
|
+
getServerSnapshot: () => snapshot,
|
|
620
|
+
getSnapshot: () => snapshot,
|
|
621
|
+
refresh,
|
|
622
|
+
subscribe: (listener) => {
|
|
623
|
+
listeners.add(listener);
|
|
624
|
+
return () => {
|
|
625
|
+
listeners.delete(listener);
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
// src/svelte/createVoiceProviderStatus.ts
|
|
632
|
+
var createVoiceProviderStatus = (path = "/api/provider-status", options = {}) => createVoiceProviderStatusStore(path, options);
|
|
551
633
|
// src/client/htmx.ts
|
|
552
634
|
var DEFAULT_EVENT_NAME = "voice-refresh";
|
|
553
635
|
var DEFAULT_QUERY_PARAM = "sessionId";
|
|
@@ -1173,5 +1255,6 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1173
1255
|
};
|
|
1174
1256
|
export {
|
|
1175
1257
|
createVoiceStream2 as createVoiceStream,
|
|
1258
|
+
createVoiceProviderStatus,
|
|
1176
1259
|
createVoiceController
|
|
1177
1260
|
};
|
package/dist/vue/index.d.ts
CHANGED
package/dist/vue/index.js
CHANGED
|
@@ -1270,7 +1270,120 @@ var useVoiceController = (path, options = {}) => {
|
|
|
1270
1270
|
turns
|
|
1271
1271
|
};
|
|
1272
1272
|
};
|
|
1273
|
+
// src/vue/useVoiceProviderStatus.ts
|
|
1274
|
+
import { onUnmounted as onUnmounted3, ref as ref3, shallowRef as shallowRef3 } from "vue";
|
|
1275
|
+
|
|
1276
|
+
// src/client/providerStatus.ts
|
|
1277
|
+
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
1278
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1279
|
+
const response = await fetchImpl(path);
|
|
1280
|
+
if (!response.ok) {
|
|
1281
|
+
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
1282
|
+
}
|
|
1283
|
+
return await response.json();
|
|
1284
|
+
};
|
|
1285
|
+
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
1286
|
+
const listeners = new Set;
|
|
1287
|
+
let closed = false;
|
|
1288
|
+
let timer;
|
|
1289
|
+
let snapshot = {
|
|
1290
|
+
error: null,
|
|
1291
|
+
isLoading: false,
|
|
1292
|
+
providers: []
|
|
1293
|
+
};
|
|
1294
|
+
const emit = () => {
|
|
1295
|
+
for (const listener of listeners) {
|
|
1296
|
+
listener();
|
|
1297
|
+
}
|
|
1298
|
+
};
|
|
1299
|
+
const refresh = async () => {
|
|
1300
|
+
if (closed) {
|
|
1301
|
+
return snapshot.providers;
|
|
1302
|
+
}
|
|
1303
|
+
snapshot = {
|
|
1304
|
+
...snapshot,
|
|
1305
|
+
error: null,
|
|
1306
|
+
isLoading: true
|
|
1307
|
+
};
|
|
1308
|
+
emit();
|
|
1309
|
+
try {
|
|
1310
|
+
const providers = await fetchVoiceProviderStatus(path, options);
|
|
1311
|
+
snapshot = {
|
|
1312
|
+
error: null,
|
|
1313
|
+
isLoading: false,
|
|
1314
|
+
providers,
|
|
1315
|
+
updatedAt: Date.now()
|
|
1316
|
+
};
|
|
1317
|
+
emit();
|
|
1318
|
+
return providers;
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
snapshot = {
|
|
1321
|
+
...snapshot,
|
|
1322
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1323
|
+
isLoading: false
|
|
1324
|
+
};
|
|
1325
|
+
emit();
|
|
1326
|
+
throw error;
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
const close = () => {
|
|
1330
|
+
closed = true;
|
|
1331
|
+
if (timer) {
|
|
1332
|
+
clearInterval(timer);
|
|
1333
|
+
timer = undefined;
|
|
1334
|
+
}
|
|
1335
|
+
listeners.clear();
|
|
1336
|
+
};
|
|
1337
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1338
|
+
timer = setInterval(() => {
|
|
1339
|
+
refresh().catch(() => {});
|
|
1340
|
+
}, options.intervalMs);
|
|
1341
|
+
}
|
|
1342
|
+
return {
|
|
1343
|
+
close,
|
|
1344
|
+
getServerSnapshot: () => snapshot,
|
|
1345
|
+
getSnapshot: () => snapshot,
|
|
1346
|
+
refresh,
|
|
1347
|
+
subscribe: (listener) => {
|
|
1348
|
+
listeners.add(listener);
|
|
1349
|
+
return () => {
|
|
1350
|
+
listeners.delete(listener);
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
};
|
|
1355
|
+
|
|
1356
|
+
// src/vue/useVoiceProviderStatus.ts
|
|
1357
|
+
var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
1358
|
+
const store = createVoiceProviderStatusStore(path, options);
|
|
1359
|
+
const error = ref3(null);
|
|
1360
|
+
const isLoading = ref3(false);
|
|
1361
|
+
const providers = shallowRef3([]);
|
|
1362
|
+
const updatedAt = ref3(undefined);
|
|
1363
|
+
const sync = () => {
|
|
1364
|
+
const snapshot = store.getSnapshot();
|
|
1365
|
+
error.value = snapshot.error;
|
|
1366
|
+
isLoading.value = snapshot.isLoading;
|
|
1367
|
+
providers.value = [...snapshot.providers];
|
|
1368
|
+
updatedAt.value = snapshot.updatedAt;
|
|
1369
|
+
};
|
|
1370
|
+
const unsubscribe = store.subscribe(sync);
|
|
1371
|
+
sync();
|
|
1372
|
+
store.refresh().catch(() => {});
|
|
1373
|
+
onUnmounted3(() => {
|
|
1374
|
+
unsubscribe();
|
|
1375
|
+
store.close();
|
|
1376
|
+
});
|
|
1377
|
+
return {
|
|
1378
|
+
error,
|
|
1379
|
+
isLoading,
|
|
1380
|
+
providers,
|
|
1381
|
+
refresh: store.refresh,
|
|
1382
|
+
updatedAt
|
|
1383
|
+
};
|
|
1384
|
+
};
|
|
1273
1385
|
export {
|
|
1274
1386
|
useVoiceStream,
|
|
1387
|
+
useVoiceProviderStatus,
|
|
1275
1388
|
useVoiceController
|
|
1276
1389
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type VoiceProviderStatusClientOptions } from '../client/providerStatus';
|
|
2
|
+
import type { VoiceProviderHealthSummary } from '../providerHealth';
|
|
3
|
+
export declare const useVoiceProviderStatus: <TProvider extends string = string>(path?: string, options?: VoiceProviderStatusClientOptions) => {
|
|
4
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
5
|
+
isLoading: import("vue").Ref<boolean, boolean>;
|
|
6
|
+
providers: import("vue").ShallowRef<VoiceProviderHealthSummary<TProvider>[], VoiceProviderHealthSummary<TProvider>[]>;
|
|
7
|
+
refresh: () => Promise<VoiceProviderHealthSummary<TProvider>[]>;
|
|
8
|
+
updatedAt: import("vue").Ref<number | undefined, number | undefined>;
|
|
9
|
+
};
|