@absolutejs/voice 0.0.22-beta.45 → 0.0.22-beta.47
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 +123 -0
- package/dist/angular/voice-workflow-status.service.d.ts +12 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +80 -0
- package/dist/client/workflowStatus.d.ts +19 -0
- package/dist/evalRoutes.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +311 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +99 -0
- package/dist/react/useVoiceWorkflowStatus.d.ts +8 -0
- package/dist/svelte/createVoiceWorkflowStatus.d.ts +8 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +82 -0
- package/dist/trace.d.ts +1 -1
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +112 -0
- package/dist/vue/useVoiceWorkflowStatus.d.ts +9 -0
- package/dist/workflowContract.d.ts +91 -0
- package/package.json +1 -1
package/dist/angular/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { VoiceStreamService } from './voice-stream.service';
|
|
2
2
|
export { VoiceControllerService } from './voice-controller.service';
|
|
3
3
|
export { VoiceProviderStatusService } from './voice-provider-status.service';
|
|
4
|
+
export { VoiceWorkflowStatusService } from './voice-workflow-status.service';
|
package/dist/angular/index.js
CHANGED
|
@@ -1457,7 +1457,130 @@ VoiceProviderStatusService = __decorateElement(_init, 0, "VoiceProviderStatusSer
|
|
|
1457
1457
|
__runInitializers(_init, 1, VoiceProviderStatusService);
|
|
1458
1458
|
__decoratorMetadata(_init, VoiceProviderStatusService);
|
|
1459
1459
|
let _VoiceProviderStatusService = VoiceProviderStatusService;
|
|
1460
|
+
// src/angular/voice-workflow-status.service.ts
|
|
1461
|
+
import { computed as computed4, Injectable as Injectable4, signal as signal4 } from "@angular/core";
|
|
1462
|
+
|
|
1463
|
+
// src/client/workflowStatus.ts
|
|
1464
|
+
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
1465
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1466
|
+
const response = await fetchImpl(path);
|
|
1467
|
+
if (!response.ok) {
|
|
1468
|
+
throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
|
|
1469
|
+
}
|
|
1470
|
+
return await response.json();
|
|
1471
|
+
};
|
|
1472
|
+
var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
|
|
1473
|
+
const listeners = new Set;
|
|
1474
|
+
let closed = false;
|
|
1475
|
+
let timer;
|
|
1476
|
+
let snapshot = {
|
|
1477
|
+
error: null,
|
|
1478
|
+
isLoading: false
|
|
1479
|
+
};
|
|
1480
|
+
const emit = () => {
|
|
1481
|
+
for (const listener of listeners) {
|
|
1482
|
+
listener();
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
const refresh = async () => {
|
|
1486
|
+
if (closed) {
|
|
1487
|
+
return snapshot.report;
|
|
1488
|
+
}
|
|
1489
|
+
snapshot = {
|
|
1490
|
+
...snapshot,
|
|
1491
|
+
error: null,
|
|
1492
|
+
isLoading: true
|
|
1493
|
+
};
|
|
1494
|
+
emit();
|
|
1495
|
+
try {
|
|
1496
|
+
const report = await fetchVoiceWorkflowStatus(path, options);
|
|
1497
|
+
snapshot = {
|
|
1498
|
+
error: null,
|
|
1499
|
+
isLoading: false,
|
|
1500
|
+
report,
|
|
1501
|
+
updatedAt: Date.now()
|
|
1502
|
+
};
|
|
1503
|
+
emit();
|
|
1504
|
+
return report;
|
|
1505
|
+
} catch (error) {
|
|
1506
|
+
snapshot = {
|
|
1507
|
+
...snapshot,
|
|
1508
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1509
|
+
isLoading: false
|
|
1510
|
+
};
|
|
1511
|
+
emit();
|
|
1512
|
+
throw error;
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
const close = () => {
|
|
1516
|
+
closed = true;
|
|
1517
|
+
if (timer) {
|
|
1518
|
+
clearInterval(timer);
|
|
1519
|
+
timer = undefined;
|
|
1520
|
+
}
|
|
1521
|
+
listeners.clear();
|
|
1522
|
+
};
|
|
1523
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1524
|
+
timer = setInterval(() => {
|
|
1525
|
+
refresh().catch(() => {});
|
|
1526
|
+
}, options.intervalMs);
|
|
1527
|
+
}
|
|
1528
|
+
return {
|
|
1529
|
+
close,
|
|
1530
|
+
getServerSnapshot: () => snapshot,
|
|
1531
|
+
getSnapshot: () => snapshot,
|
|
1532
|
+
refresh,
|
|
1533
|
+
subscribe: (listener) => {
|
|
1534
|
+
listeners.add(listener);
|
|
1535
|
+
return () => {
|
|
1536
|
+
listeners.delete(listener);
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
};
|
|
1540
|
+
};
|
|
1541
|
+
|
|
1542
|
+
// src/angular/voice-workflow-status.service.ts
|
|
1543
|
+
var _dec = [
|
|
1544
|
+
Injectable4({ providedIn: "root" })
|
|
1545
|
+
];
|
|
1546
|
+
var _init = __decoratorStart(undefined);
|
|
1547
|
+
|
|
1548
|
+
class VoiceWorkflowStatusService {
|
|
1549
|
+
connect(path = "/evals/scenarios/json", options = {}) {
|
|
1550
|
+
const store = createVoiceWorkflowStatusStore(path, options);
|
|
1551
|
+
const errorSignal = signal4(null);
|
|
1552
|
+
const isLoadingSignal = signal4(false);
|
|
1553
|
+
const reportSignal = signal4(undefined);
|
|
1554
|
+
const updatedAtSignal = signal4(undefined);
|
|
1555
|
+
const sync = () => {
|
|
1556
|
+
const snapshot = store.getSnapshot();
|
|
1557
|
+
errorSignal.set(snapshot.error);
|
|
1558
|
+
isLoadingSignal.set(snapshot.isLoading);
|
|
1559
|
+
reportSignal.set(snapshot.report);
|
|
1560
|
+
updatedAtSignal.set(snapshot.updatedAt);
|
|
1561
|
+
};
|
|
1562
|
+
const unsubscribe = store.subscribe(sync);
|
|
1563
|
+
sync();
|
|
1564
|
+
store.refresh().catch(() => {});
|
|
1565
|
+
return {
|
|
1566
|
+
close: () => {
|
|
1567
|
+
unsubscribe();
|
|
1568
|
+
store.close();
|
|
1569
|
+
},
|
|
1570
|
+
error: computed4(() => errorSignal()),
|
|
1571
|
+
isLoading: computed4(() => isLoadingSignal()),
|
|
1572
|
+
refresh: store.refresh,
|
|
1573
|
+
report: computed4(() => reportSignal()),
|
|
1574
|
+
updatedAt: computed4(() => updatedAtSignal())
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
VoiceWorkflowStatusService = __decorateElement(_init, 0, "VoiceWorkflowStatusService", _dec, VoiceWorkflowStatusService);
|
|
1579
|
+
__runInitializers(_init, 1, VoiceWorkflowStatusService);
|
|
1580
|
+
__decoratorMetadata(_init, VoiceWorkflowStatusService);
|
|
1581
|
+
let _VoiceWorkflowStatusService = VoiceWorkflowStatusService;
|
|
1460
1582
|
export {
|
|
1583
|
+
VoiceWorkflowStatusService,
|
|
1461
1584
|
VoiceStreamService,
|
|
1462
1585
|
VoiceProviderStatusService,
|
|
1463
1586
|
VoiceControllerService
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type VoiceWorkflowStatusClientOptions } from '../client/workflowStatus';
|
|
2
|
+
import type { VoiceScenarioEvalReport } from '../evalRoutes';
|
|
3
|
+
export declare class VoiceWorkflowStatusService {
|
|
4
|
+
connect(path?: string, options?: VoiceWorkflowStatusClientOptions): {
|
|
5
|
+
close: () => void;
|
|
6
|
+
error: import("@angular/core").Signal<string | null>;
|
|
7
|
+
isLoading: import("@angular/core").Signal<boolean>;
|
|
8
|
+
refresh: () => Promise<VoiceScenarioEvalReport | undefined>;
|
|
9
|
+
report: import("@angular/core").Signal<VoiceScenarioEvalReport | undefined>;
|
|
10
|
+
updatedAt: import("@angular/core").Signal<number | undefined>;
|
|
11
|
+
};
|
|
12
|
+
}
|
package/dist/client/index.d.ts
CHANGED
|
@@ -6,4 +6,6 @@ export { bindVoiceBargeIn, createVoiceDuplexController } from './duplex';
|
|
|
6
6
|
export { bindVoiceHTMX } from './htmx';
|
|
7
7
|
export { createMicrophoneCapture } from './microphone';
|
|
8
8
|
export { createVoiceProviderStatusStore, fetchVoiceProviderStatus } from './providerStatus';
|
|
9
|
+
export { createVoiceWorkflowStatusStore, fetchVoiceWorkflowStatus } from './workflowStatus';
|
|
9
10
|
export type { VoiceProviderStatusClientOptions, VoiceProviderStatusSnapshot } from './providerStatus';
|
|
11
|
+
export type { VoiceWorkflowStatusClientOptions, VoiceWorkflowStatusSnapshot } from './workflowStatus';
|
package/dist/client/index.js
CHANGED
|
@@ -1702,9 +1702,89 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
|
|
|
1702
1702
|
}
|
|
1703
1703
|
};
|
|
1704
1704
|
};
|
|
1705
|
+
// src/client/workflowStatus.ts
|
|
1706
|
+
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
1707
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1708
|
+
const response = await fetchImpl(path);
|
|
1709
|
+
if (!response.ok) {
|
|
1710
|
+
throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
|
|
1711
|
+
}
|
|
1712
|
+
return await response.json();
|
|
1713
|
+
};
|
|
1714
|
+
var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
|
|
1715
|
+
const listeners = new Set;
|
|
1716
|
+
let closed = false;
|
|
1717
|
+
let timer;
|
|
1718
|
+
let snapshot = {
|
|
1719
|
+
error: null,
|
|
1720
|
+
isLoading: false
|
|
1721
|
+
};
|
|
1722
|
+
const emit = () => {
|
|
1723
|
+
for (const listener of listeners) {
|
|
1724
|
+
listener();
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
const refresh = async () => {
|
|
1728
|
+
if (closed) {
|
|
1729
|
+
return snapshot.report;
|
|
1730
|
+
}
|
|
1731
|
+
snapshot = {
|
|
1732
|
+
...snapshot,
|
|
1733
|
+
error: null,
|
|
1734
|
+
isLoading: true
|
|
1735
|
+
};
|
|
1736
|
+
emit();
|
|
1737
|
+
try {
|
|
1738
|
+
const report = await fetchVoiceWorkflowStatus(path, options);
|
|
1739
|
+
snapshot = {
|
|
1740
|
+
error: null,
|
|
1741
|
+
isLoading: false,
|
|
1742
|
+
report,
|
|
1743
|
+
updatedAt: Date.now()
|
|
1744
|
+
};
|
|
1745
|
+
emit();
|
|
1746
|
+
return report;
|
|
1747
|
+
} catch (error) {
|
|
1748
|
+
snapshot = {
|
|
1749
|
+
...snapshot,
|
|
1750
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1751
|
+
isLoading: false
|
|
1752
|
+
};
|
|
1753
|
+
emit();
|
|
1754
|
+
throw error;
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
const close = () => {
|
|
1758
|
+
closed = true;
|
|
1759
|
+
if (timer) {
|
|
1760
|
+
clearInterval(timer);
|
|
1761
|
+
timer = undefined;
|
|
1762
|
+
}
|
|
1763
|
+
listeners.clear();
|
|
1764
|
+
};
|
|
1765
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1766
|
+
timer = setInterval(() => {
|
|
1767
|
+
refresh().catch(() => {});
|
|
1768
|
+
}, options.intervalMs);
|
|
1769
|
+
}
|
|
1770
|
+
return {
|
|
1771
|
+
close,
|
|
1772
|
+
getServerSnapshot: () => snapshot,
|
|
1773
|
+
getSnapshot: () => snapshot,
|
|
1774
|
+
refresh,
|
|
1775
|
+
subscribe: (listener) => {
|
|
1776
|
+
listeners.add(listener);
|
|
1777
|
+
return () => {
|
|
1778
|
+
listeners.delete(listener);
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
};
|
|
1705
1783
|
export {
|
|
1784
|
+
fetchVoiceWorkflowStatus,
|
|
1706
1785
|
fetchVoiceProviderStatus,
|
|
1707
1786
|
decodeVoiceAudioChunk,
|
|
1787
|
+
createVoiceWorkflowStatusStore,
|
|
1708
1788
|
createVoiceStream,
|
|
1709
1789
|
createVoiceProviderStatusStore,
|
|
1710
1790
|
createVoiceDuplexController,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { VoiceScenarioEvalReport } from '../evalRoutes';
|
|
2
|
+
export type VoiceWorkflowStatusClientOptions = {
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
intervalMs?: number;
|
|
5
|
+
};
|
|
6
|
+
export type VoiceWorkflowStatusSnapshot = {
|
|
7
|
+
error: string | null;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
report?: VoiceScenarioEvalReport;
|
|
10
|
+
updatedAt?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const fetchVoiceWorkflowStatus: (path?: string, options?: Pick<VoiceWorkflowStatusClientOptions, "fetch">) => Promise<VoiceScenarioEvalReport>;
|
|
13
|
+
export declare const createVoiceWorkflowStatusStore: (path?: string, options?: VoiceWorkflowStatusClientOptions) => {
|
|
14
|
+
close: () => void;
|
|
15
|
+
getServerSnapshot: () => VoiceWorkflowStatusSnapshot;
|
|
16
|
+
getSnapshot: () => VoiceWorkflowStatusSnapshot;
|
|
17
|
+
refresh: () => Promise<VoiceScenarioEvalReport | undefined>;
|
|
18
|
+
subscribe: (listener: () => void) => () => void;
|
|
19
|
+
};
|
package/dist/evalRoutes.d.ts
CHANGED
|
@@ -75,6 +75,7 @@ export type VoiceScenarioEvalDefinition = {
|
|
|
75
75
|
requiredLifecycleTypes?: string[];
|
|
76
76
|
requiredPayloadPaths?: string[];
|
|
77
77
|
requiredTranscriptIncludes?: string[];
|
|
78
|
+
requiredWorkflowContracts?: string[];
|
|
78
79
|
scenarioId?: string;
|
|
79
80
|
};
|
|
80
81
|
export type VoiceScenarioEvalSessionResult = {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { createVoiceAssistant, createVoiceExperiment, summarizeVoiceAssistantRun
|
|
|
3
3
|
export { createVoiceAssistantHealthHTMLHandler, createVoiceAssistantHealthJSONHandler, createVoiceAssistantHealthRoutes, renderVoiceAssistantHealthHTML, summarizeVoiceAssistantHealth } from './assistantHealth';
|
|
4
4
|
export { buildVoiceDiagnosticsMarkdown, createVoiceDiagnosticsRoutes, resolveVoiceDiagnosticsTraceFilter } from './diagnosticsRoutes';
|
|
5
5
|
export { compareVoiceEvalBaseline, createVoiceFileEvalBaselineStore, createVoiceFileScenarioFixtureStore, createVoiceEvalRoutes, renderVoiceEvalBaselineHTML, renderVoiceEvalHTML, renderVoiceScenarioEvalHTML, renderVoiceScenarioFixtureEvalHTML, runVoiceScenarioEvals, runVoiceScenarioFixtureEvals, runVoiceSessionEvals } from './evalRoutes';
|
|
6
|
+
export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowContractPreset, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult } from './workflowContract';
|
|
6
7
|
export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceSessions, summarizeVoiceSessionReplay } from './sessionReplay';
|
|
7
8
|
export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
|
|
8
9
|
export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
|
|
@@ -41,6 +42,7 @@ export type { VoiceAssistantHealthFailure, VoiceAssistantHealthHTMLHandlerOption
|
|
|
41
42
|
export type { VoiceAssistantMemoryBinding, VoiceAssistantMemoryHandle, VoiceAssistantMemoryOptions, VoiceAssistantMemoryRecord, VoiceAssistantMemoryStore } from './assistantMemory';
|
|
42
43
|
export type { VoiceDiagnosticsRoutesOptions } from './diagnosticsRoutes';
|
|
43
44
|
export type { VoiceEvalBaselineComparison, VoiceEvalBaselineComparisonOptions, VoiceEvalBaselineStore, VoiceEvalBaselineSummary, VoiceEvalLink, VoiceEvalReport, VoiceEvalRoutesOptions, VoiceEvalSessionReport, VoiceEvalStatus, VoiceEvalTrendBucket, VoiceScenarioEvalDefinition, VoiceScenarioEvalReport, VoiceScenarioEvalResult, VoiceScenarioEvalSessionResult, VoiceScenarioFixture, VoiceScenarioFixtureEvalReport, VoiceScenarioFixtureEvalResult, VoiceScenarioFixtureStore } from './evalRoutes';
|
|
45
|
+
export type { VoiceWorkflowContract, VoiceWorkflowContractDefinition, VoiceWorkflowContractField, VoiceWorkflowContractFieldMatch, VoiceWorkflowContractPresetName, VoiceWorkflowContractPresetOptions, VoiceWorkflowContractTracePayload, VoiceWorkflowContractValidation, VoiceWorkflowContractValidationIssue, VoiceWorkflowOutcome } from './workflowContract';
|
|
44
46
|
export type { VoiceSessionListHTMLHandlerOptions, VoiceSessionListItem, VoiceSessionListOptions, VoiceSessionListRoutesOptions, VoiceSessionListStatus, VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn } from './sessionReplay';
|
|
45
47
|
export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterProviderHealth, VoiceProviderRouterProviderProfile, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
|
|
46
48
|
export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
|
package/dist/index.js
CHANGED
|
@@ -8078,6 +8078,7 @@ var evaluateScenarioSession = (scenario, sessionId, events) => {
|
|
|
8078
8078
|
const turnCount = events.filter((event) => event.type === "turn.committed").length;
|
|
8079
8079
|
const sessionErrorCount = events.filter((event) => event.type === "session.error").length;
|
|
8080
8080
|
const providerErrorCount = countProviderErrors(events);
|
|
8081
|
+
const workflowContractEvents = events.filter((event) => event.type === "workflow.contract");
|
|
8081
8082
|
for (const missing of includesAll(committedText, scenario.requiredTranscriptIncludes ?? [])) {
|
|
8082
8083
|
issues.push(`Missing transcript text: ${missing}`);
|
|
8083
8084
|
}
|
|
@@ -8121,6 +8122,16 @@ var evaluateScenarioSession = (scenario, sessionId, events) => {
|
|
|
8121
8122
|
issues.push(`Missing payload path: ${path}`);
|
|
8122
8123
|
}
|
|
8123
8124
|
}
|
|
8125
|
+
for (const contractId of scenario.requiredWorkflowContracts ?? []) {
|
|
8126
|
+
const matching = workflowContractEvents.filter((event) => getString6(event.payload.contractId) === contractId);
|
|
8127
|
+
if (matching.length === 0) {
|
|
8128
|
+
issues.push(`Missing workflow contract: ${contractId}`);
|
|
8129
|
+
continue;
|
|
8130
|
+
}
|
|
8131
|
+
if (matching.some((event) => getString6(event.payload.status) !== "pass")) {
|
|
8132
|
+
issues.push(`Workflow contract failed: ${contractId}`);
|
|
8133
|
+
}
|
|
8134
|
+
}
|
|
8124
8135
|
return {
|
|
8125
8136
|
eventCount: events.length,
|
|
8126
8137
|
issues,
|
|
@@ -8443,6 +8454,300 @@ var createVoiceEvalRoutes = (options) => {
|
|
|
8443
8454
|
});
|
|
8444
8455
|
return routes;
|
|
8445
8456
|
};
|
|
8457
|
+
// src/workflowContract.ts
|
|
8458
|
+
var getObject2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
8459
|
+
var getPathValue2 = (value, path) => {
|
|
8460
|
+
let current = value;
|
|
8461
|
+
for (const part of path.split(".").filter(Boolean)) {
|
|
8462
|
+
const record = getObject2(current);
|
|
8463
|
+
if (!record || !(part in record)) {
|
|
8464
|
+
return;
|
|
8465
|
+
}
|
|
8466
|
+
current = record[part];
|
|
8467
|
+
}
|
|
8468
|
+
return current;
|
|
8469
|
+
};
|
|
8470
|
+
var hasValue = (value, match) => {
|
|
8471
|
+
switch (match) {
|
|
8472
|
+
case "boolean":
|
|
8473
|
+
return typeof value === "boolean";
|
|
8474
|
+
case "number":
|
|
8475
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
8476
|
+
case "string":
|
|
8477
|
+
return typeof value === "string";
|
|
8478
|
+
case "truthy":
|
|
8479
|
+
return Boolean(value);
|
|
8480
|
+
case "non-empty":
|
|
8481
|
+
default:
|
|
8482
|
+
return Array.isArray(value) ? value.length > 0 : typeof value === "string" ? value.trim().length > 0 : value !== undefined && value !== null;
|
|
8483
|
+
}
|
|
8484
|
+
};
|
|
8485
|
+
var resolveOutcome2 = (routeResult) => {
|
|
8486
|
+
if (routeResult.complete)
|
|
8487
|
+
return "complete";
|
|
8488
|
+
if (routeResult.transfer)
|
|
8489
|
+
return "transfer";
|
|
8490
|
+
if (routeResult.escalate)
|
|
8491
|
+
return "escalate";
|
|
8492
|
+
if (routeResult.voicemail)
|
|
8493
|
+
return "voicemail";
|
|
8494
|
+
if (routeResult.noAnswer)
|
|
8495
|
+
return "no-answer";
|
|
8496
|
+
return;
|
|
8497
|
+
};
|
|
8498
|
+
var validateVoiceWorkflowRouteResult = (definition, routeResult) => {
|
|
8499
|
+
const issues = [];
|
|
8500
|
+
const requiredFields = (definition.fields ?? []).filter((field) => field.required !== false).map((field) => field.path);
|
|
8501
|
+
const missingFields = [];
|
|
8502
|
+
const outcome = resolveOutcome2(routeResult);
|
|
8503
|
+
if (definition.outcome && outcome !== definition.outcome) {
|
|
8504
|
+
issues.push({
|
|
8505
|
+
code: "workflow.outcome_mismatch",
|
|
8506
|
+
message: `Expected workflow outcome ${definition.outcome}, saw ${outcome ?? "none"}.`
|
|
8507
|
+
});
|
|
8508
|
+
}
|
|
8509
|
+
for (const field of definition.fields ?? []) {
|
|
8510
|
+
if (field.required === false)
|
|
8511
|
+
continue;
|
|
8512
|
+
const paths = [field.path, ...field.aliases ?? []];
|
|
8513
|
+
const present = paths.some((path) => hasValue(getPathValue2(routeResult.result, path), field.match ?? "non-empty"));
|
|
8514
|
+
if (!present) {
|
|
8515
|
+
missingFields.push(field.path);
|
|
8516
|
+
issues.push({
|
|
8517
|
+
code: "workflow.missing_field",
|
|
8518
|
+
field: field.path,
|
|
8519
|
+
message: `Missing required workflow field: ${field.label ?? field.path}.`
|
|
8520
|
+
});
|
|
8521
|
+
}
|
|
8522
|
+
}
|
|
8523
|
+
issues.push(...definition.validate?.({
|
|
8524
|
+
result: routeResult.result,
|
|
8525
|
+
routeResult
|
|
8526
|
+
}) ?? []);
|
|
8527
|
+
return {
|
|
8528
|
+
contractId: definition.id,
|
|
8529
|
+
issues,
|
|
8530
|
+
missingFields,
|
|
8531
|
+
outcome,
|
|
8532
|
+
pass: issues.length === 0,
|
|
8533
|
+
requiredFields
|
|
8534
|
+
};
|
|
8535
|
+
};
|
|
8536
|
+
var createVoiceWorkflowScenario = (definition, overrides = {}) => ({
|
|
8537
|
+
description: definition.description,
|
|
8538
|
+
forbiddenHandoffActions: definition.forbiddenHandoffActions,
|
|
8539
|
+
id: definition.id,
|
|
8540
|
+
label: definition.label,
|
|
8541
|
+
maxProviderErrors: definition.maxProviderErrors,
|
|
8542
|
+
maxSessionErrors: definition.maxSessionErrors,
|
|
8543
|
+
minSessions: definition.minSessions,
|
|
8544
|
+
minTurns: definition.minTurns,
|
|
8545
|
+
requiredAssistantIncludes: definition.requiredAssistantIncludes,
|
|
8546
|
+
requiredDisposition: definition.requiredDisposition,
|
|
8547
|
+
requiredHandoffActions: definition.requiredHandoffActions,
|
|
8548
|
+
requiredLifecycleTypes: definition.requiredLifecycleTypes,
|
|
8549
|
+
requiredTranscriptIncludes: definition.requiredTranscriptIncludes,
|
|
8550
|
+
requiredWorkflowContracts: [definition.id],
|
|
8551
|
+
scenarioId: definition.scenarioId,
|
|
8552
|
+
...overrides
|
|
8553
|
+
});
|
|
8554
|
+
var createVoiceWorkflowContract = (definition) => ({
|
|
8555
|
+
assertRouteResult: (routeResult) => {
|
|
8556
|
+
const validation = validateVoiceWorkflowRouteResult(definition, routeResult);
|
|
8557
|
+
if (!validation.pass) {
|
|
8558
|
+
throw new Error(`Voice workflow contract ${definition.id} failed: ${validation.issues.map((issue) => issue.message).join(" ")}`);
|
|
8559
|
+
}
|
|
8560
|
+
},
|
|
8561
|
+
definition,
|
|
8562
|
+
toScenarioEval: (overrides) => createVoiceWorkflowScenario(definition, overrides),
|
|
8563
|
+
validateRouteResult: (routeResult) => validateVoiceWorkflowRouteResult(definition, routeResult)
|
|
8564
|
+
});
|
|
8565
|
+
var presetDefinitions = {
|
|
8566
|
+
"appointment-booking": {
|
|
8567
|
+
description: "Appointment booking should complete with enough identity, appointment, and follow-up details to act on.",
|
|
8568
|
+
fields: [
|
|
8569
|
+
{ aliases: ["name", "customer.name"], label: "Caller name", path: "caller.name" },
|
|
8570
|
+
{
|
|
8571
|
+
aliases: ["phone", "customer.phone"],
|
|
8572
|
+
label: "Caller phone",
|
|
8573
|
+
path: "caller.phone"
|
|
8574
|
+
},
|
|
8575
|
+
{
|
|
8576
|
+
aliases: ["appointment.start", "appointment.time", "scheduledAt"],
|
|
8577
|
+
label: "Appointment time",
|
|
8578
|
+
path: "appointment.startsAt"
|
|
8579
|
+
},
|
|
8580
|
+
{
|
|
8581
|
+
aliases: ["summary", "assistantSummary"],
|
|
8582
|
+
label: "Summary",
|
|
8583
|
+
path: "appointment.summary"
|
|
8584
|
+
}
|
|
8585
|
+
],
|
|
8586
|
+
id: "appointment-booking",
|
|
8587
|
+
label: "Appointment booking",
|
|
8588
|
+
outcome: "complete",
|
|
8589
|
+
requiredDisposition: "completed"
|
|
8590
|
+
},
|
|
8591
|
+
"lead-qualification": {
|
|
8592
|
+
description: "Lead qualification should complete with contact, need, qualification, and next-step fields.",
|
|
8593
|
+
fields: [
|
|
8594
|
+
{ aliases: ["name", "lead.name"], label: "Lead name", path: "contact.name" },
|
|
8595
|
+
{
|
|
8596
|
+
aliases: ["email", "lead.email"],
|
|
8597
|
+
label: "Lead email",
|
|
8598
|
+
path: "contact.email"
|
|
8599
|
+
},
|
|
8600
|
+
{
|
|
8601
|
+
aliases: ["need", "pain", "summary"],
|
|
8602
|
+
label: "Need",
|
|
8603
|
+
path: "qualification.need"
|
|
8604
|
+
},
|
|
8605
|
+
{
|
|
8606
|
+
aliases: ["qualified", "qualification.qualified"],
|
|
8607
|
+
label: "Qualified",
|
|
8608
|
+
match: "boolean",
|
|
8609
|
+
path: "qualification.isQualified"
|
|
8610
|
+
},
|
|
8611
|
+
{
|
|
8612
|
+
aliases: ["nextStep", "followUp"],
|
|
8613
|
+
label: "Next step",
|
|
8614
|
+
path: "qualification.nextStep"
|
|
8615
|
+
}
|
|
8616
|
+
],
|
|
8617
|
+
id: "lead-qualification",
|
|
8618
|
+
label: "Lead qualification",
|
|
8619
|
+
outcome: "complete",
|
|
8620
|
+
requiredDisposition: "completed"
|
|
8621
|
+
},
|
|
8622
|
+
"support-triage": {
|
|
8623
|
+
description: "Support triage should capture identity, issue summary, severity, and the operational follow-up.",
|
|
8624
|
+
fields: [
|
|
8625
|
+
{
|
|
8626
|
+
aliases: ["name", "customer.name"],
|
|
8627
|
+
label: "Customer name",
|
|
8628
|
+
path: "customer.name"
|
|
8629
|
+
},
|
|
8630
|
+
{
|
|
8631
|
+
aliases: ["issue", "summary", "assistantSummary"],
|
|
8632
|
+
label: "Issue summary",
|
|
8633
|
+
path: "issue.summary"
|
|
8634
|
+
},
|
|
8635
|
+
{
|
|
8636
|
+
aliases: ["priority", "severity"],
|
|
8637
|
+
label: "Severity",
|
|
8638
|
+
path: "issue.severity"
|
|
8639
|
+
},
|
|
8640
|
+
{
|
|
8641
|
+
aliases: ["nextStep", "task.title"],
|
|
8642
|
+
label: "Next step",
|
|
8643
|
+
path: "resolution.nextStep"
|
|
8644
|
+
}
|
|
8645
|
+
],
|
|
8646
|
+
id: "support-triage",
|
|
8647
|
+
label: "Support triage",
|
|
8648
|
+
outcome: "complete",
|
|
8649
|
+
requiredDisposition: "completed"
|
|
8650
|
+
},
|
|
8651
|
+
"transfer-handoff": {
|
|
8652
|
+
description: "Transfer handoff should produce a routed transfer plus handoff evidence.",
|
|
8653
|
+
fields: [
|
|
8654
|
+
{
|
|
8655
|
+
aliases: ["target", "callTarget"],
|
|
8656
|
+
label: "Transfer target",
|
|
8657
|
+
path: "transfer.target"
|
|
8658
|
+
},
|
|
8659
|
+
{
|
|
8660
|
+
aliases: ["reason", "callReason"],
|
|
8661
|
+
label: "Transfer reason",
|
|
8662
|
+
path: "transfer.reason"
|
|
8663
|
+
},
|
|
8664
|
+
{
|
|
8665
|
+
aliases: ["summary", "assistantSummary"],
|
|
8666
|
+
label: "Transfer summary",
|
|
8667
|
+
path: "transfer.summary"
|
|
8668
|
+
}
|
|
8669
|
+
],
|
|
8670
|
+
id: "transfer-handoff",
|
|
8671
|
+
label: "Transfer handoff",
|
|
8672
|
+
outcome: "transfer",
|
|
8673
|
+
requiredDisposition: "transferred",
|
|
8674
|
+
requiredHandoffActions: ["transfer"]
|
|
8675
|
+
},
|
|
8676
|
+
"voicemail-callback": {
|
|
8677
|
+
description: "Voicemail callback should preserve enough caller and callback context for follow-up.",
|
|
8678
|
+
fields: [
|
|
8679
|
+
{
|
|
8680
|
+
aliases: ["name", "caller.name"],
|
|
8681
|
+
label: "Caller name",
|
|
8682
|
+
path: "voicemail.callerName"
|
|
8683
|
+
},
|
|
8684
|
+
{
|
|
8685
|
+
aliases: ["phone", "caller.phone"],
|
|
8686
|
+
label: "Callback phone",
|
|
8687
|
+
path: "voicemail.callbackPhone"
|
|
8688
|
+
},
|
|
8689
|
+
{
|
|
8690
|
+
aliases: ["message", "summary", "assistantSummary"],
|
|
8691
|
+
label: "Voicemail summary",
|
|
8692
|
+
path: "voicemail.summary"
|
|
8693
|
+
}
|
|
8694
|
+
],
|
|
8695
|
+
id: "voicemail-callback",
|
|
8696
|
+
label: "Voicemail callback",
|
|
8697
|
+
outcome: "voicemail",
|
|
8698
|
+
requiredDisposition: "voicemail",
|
|
8699
|
+
requiredHandoffActions: ["voicemail"]
|
|
8700
|
+
}
|
|
8701
|
+
};
|
|
8702
|
+
var createVoiceWorkflowContractPreset = (name, options = {}) => {
|
|
8703
|
+
const preset = presetDefinitions[name];
|
|
8704
|
+
return createVoiceWorkflowContract({
|
|
8705
|
+
...preset,
|
|
8706
|
+
...options,
|
|
8707
|
+
fields: options.fields ?? preset.fields,
|
|
8708
|
+
id: options.id ?? preset.id
|
|
8709
|
+
});
|
|
8710
|
+
};
|
|
8711
|
+
var recordVoiceWorkflowContractTrace = async (input) => input.store.append({
|
|
8712
|
+
at: input.at ?? Date.now(),
|
|
8713
|
+
payload: {
|
|
8714
|
+
contractId: input.contractId ?? input.validation.contractId,
|
|
8715
|
+
issues: input.validation.issues,
|
|
8716
|
+
missingFields: input.validation.missingFields,
|
|
8717
|
+
outcome: input.validation.outcome,
|
|
8718
|
+
requiredFields: input.validation.requiredFields,
|
|
8719
|
+
status: input.validation.pass ? "pass" : "fail"
|
|
8720
|
+
},
|
|
8721
|
+
scenarioId: input.scenarioId,
|
|
8722
|
+
sessionId: input.sessionId,
|
|
8723
|
+
traceId: input.traceId,
|
|
8724
|
+
turnId: input.turnId,
|
|
8725
|
+
type: "workflow.contract"
|
|
8726
|
+
});
|
|
8727
|
+
var createVoiceWorkflowContractHandler = (input) => {
|
|
8728
|
+
return async (session, turn, api, context) => {
|
|
8729
|
+
const legacyHandler = input.handler;
|
|
8730
|
+
const objectHandler = input.handler;
|
|
8731
|
+
const result = input.handler.length >= 4 ? await legacyHandler(session, turn, api, context) : await objectHandler({ api, context, session, turn });
|
|
8732
|
+
if (!result)
|
|
8733
|
+
return result;
|
|
8734
|
+
const resolved = input.resolveContract?.({ context, result, session, turn }) ?? input.contract;
|
|
8735
|
+
if (!resolved)
|
|
8736
|
+
return result;
|
|
8737
|
+
const contract = "validateRouteResult" in resolved ? resolved : createVoiceWorkflowContract(resolved);
|
|
8738
|
+
const validation = contract.validateRouteResult(result);
|
|
8739
|
+
if (input.store) {
|
|
8740
|
+
await recordVoiceWorkflowContractTrace({
|
|
8741
|
+
scenarioId: session.scenarioId,
|
|
8742
|
+
sessionId: session.id,
|
|
8743
|
+
store: input.store,
|
|
8744
|
+
turnId: turn.id,
|
|
8745
|
+
validation
|
|
8746
|
+
});
|
|
8747
|
+
}
|
|
8748
|
+
return result;
|
|
8749
|
+
};
|
|
8750
|
+
};
|
|
8446
8751
|
// src/sessionReplay.ts
|
|
8447
8752
|
import { Elysia as Elysia8 } from "elysia";
|
|
8448
8753
|
var getString7 = (value) => typeof value === "string" ? value : undefined;
|
|
@@ -13368,6 +13673,7 @@ export {
|
|
|
13368
13673
|
withVoiceIntegrationEventId,
|
|
13369
13674
|
voice,
|
|
13370
13675
|
verifyVoiceOpsWebhookSignature,
|
|
13676
|
+
validateVoiceWorkflowRouteResult,
|
|
13371
13677
|
transcodeTwilioInboundPayloadToPCM16,
|
|
13372
13678
|
transcodePCMToTwilioOutboundPayload,
|
|
13373
13679
|
summarizeVoiceTraceSinkDeliveries,
|
|
@@ -13421,6 +13727,7 @@ export {
|
|
|
13421
13727
|
redactVoiceTraceText,
|
|
13422
13728
|
redactVoiceTraceEvents,
|
|
13423
13729
|
redactVoiceTraceEvent,
|
|
13730
|
+
recordVoiceWorkflowContractTrace,
|
|
13424
13731
|
recordVoiceRuntimeOps,
|
|
13425
13732
|
pruneVoiceTraceEvents,
|
|
13426
13733
|
matchesVoiceOpsTaskAssignmentRule,
|
|
@@ -13446,6 +13753,10 @@ export {
|
|
|
13446
13753
|
createVoiceZendeskTicketUpdateSink,
|
|
13447
13754
|
createVoiceZendeskTicketSyncSinks,
|
|
13448
13755
|
createVoiceZendeskTicketSink,
|
|
13756
|
+
createVoiceWorkflowScenario,
|
|
13757
|
+
createVoiceWorkflowContractPreset,
|
|
13758
|
+
createVoiceWorkflowContractHandler,
|
|
13759
|
+
createVoiceWorkflowContract,
|
|
13449
13760
|
createVoiceWebhookHandoffAdapter,
|
|
13450
13761
|
createVoiceWebhookDeliveryWorkerLoop,
|
|
13451
13762
|
createVoiceWebhookDeliveryWorker,
|
package/dist/react/index.d.ts
CHANGED
package/dist/react/index.js
CHANGED
|
@@ -1379,7 +1379,106 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
|
1379
1379
|
refresh: store.refresh
|
|
1380
1380
|
};
|
|
1381
1381
|
};
|
|
1382
|
+
// src/react/useVoiceWorkflowStatus.tsx
|
|
1383
|
+
import { useEffect as useEffect4, useRef as useRef4, useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
1384
|
+
|
|
1385
|
+
// src/client/workflowStatus.ts
|
|
1386
|
+
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
1387
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1388
|
+
const response = await fetchImpl(path);
|
|
1389
|
+
if (!response.ok) {
|
|
1390
|
+
throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
|
|
1391
|
+
}
|
|
1392
|
+
return await response.json();
|
|
1393
|
+
};
|
|
1394
|
+
var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
|
|
1395
|
+
const listeners = new Set;
|
|
1396
|
+
let closed = false;
|
|
1397
|
+
let timer;
|
|
1398
|
+
let snapshot = {
|
|
1399
|
+
error: null,
|
|
1400
|
+
isLoading: false
|
|
1401
|
+
};
|
|
1402
|
+
const emit = () => {
|
|
1403
|
+
for (const listener of listeners) {
|
|
1404
|
+
listener();
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
const refresh = async () => {
|
|
1408
|
+
if (closed) {
|
|
1409
|
+
return snapshot.report;
|
|
1410
|
+
}
|
|
1411
|
+
snapshot = {
|
|
1412
|
+
...snapshot,
|
|
1413
|
+
error: null,
|
|
1414
|
+
isLoading: true
|
|
1415
|
+
};
|
|
1416
|
+
emit();
|
|
1417
|
+
try {
|
|
1418
|
+
const report = await fetchVoiceWorkflowStatus(path, options);
|
|
1419
|
+
snapshot = {
|
|
1420
|
+
error: null,
|
|
1421
|
+
isLoading: false,
|
|
1422
|
+
report,
|
|
1423
|
+
updatedAt: Date.now()
|
|
1424
|
+
};
|
|
1425
|
+
emit();
|
|
1426
|
+
return report;
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
snapshot = {
|
|
1429
|
+
...snapshot,
|
|
1430
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1431
|
+
isLoading: false
|
|
1432
|
+
};
|
|
1433
|
+
emit();
|
|
1434
|
+
throw error;
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
const close = () => {
|
|
1438
|
+
closed = true;
|
|
1439
|
+
if (timer) {
|
|
1440
|
+
clearInterval(timer);
|
|
1441
|
+
timer = undefined;
|
|
1442
|
+
}
|
|
1443
|
+
listeners.clear();
|
|
1444
|
+
};
|
|
1445
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1446
|
+
timer = setInterval(() => {
|
|
1447
|
+
refresh().catch(() => {});
|
|
1448
|
+
}, options.intervalMs);
|
|
1449
|
+
}
|
|
1450
|
+
return {
|
|
1451
|
+
close,
|
|
1452
|
+
getServerSnapshot: () => snapshot,
|
|
1453
|
+
getSnapshot: () => snapshot,
|
|
1454
|
+
refresh,
|
|
1455
|
+
subscribe: (listener) => {
|
|
1456
|
+
listeners.add(listener);
|
|
1457
|
+
return () => {
|
|
1458
|
+
listeners.delete(listener);
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
};
|
|
1463
|
+
|
|
1464
|
+
// src/react/useVoiceWorkflowStatus.tsx
|
|
1465
|
+
var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
|
|
1466
|
+
const storeRef = useRef4(null);
|
|
1467
|
+
if (!storeRef.current) {
|
|
1468
|
+
storeRef.current = createVoiceWorkflowStatusStore(path, options);
|
|
1469
|
+
}
|
|
1470
|
+
const store = storeRef.current;
|
|
1471
|
+
useEffect4(() => {
|
|
1472
|
+
store.refresh().catch(() => {});
|
|
1473
|
+
return () => store.close();
|
|
1474
|
+
}, [store]);
|
|
1475
|
+
return {
|
|
1476
|
+
...useSyncExternalStore4(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
1477
|
+
refresh: store.refresh
|
|
1478
|
+
};
|
|
1479
|
+
};
|
|
1382
1480
|
export {
|
|
1481
|
+
useVoiceWorkflowStatus,
|
|
1383
1482
|
useVoiceStream,
|
|
1384
1483
|
useVoiceProviderStatus,
|
|
1385
1484
|
useVoiceController
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type VoiceWorkflowStatusClientOptions } from '../client/workflowStatus';
|
|
2
|
+
export declare const useVoiceWorkflowStatus: (path?: string, options?: VoiceWorkflowStatusClientOptions) => {
|
|
3
|
+
refresh: () => Promise<import("..").VoiceScenarioEvalReport | undefined>;
|
|
4
|
+
error: string | null;
|
|
5
|
+
isLoading: boolean;
|
|
6
|
+
report?: import("..").VoiceScenarioEvalReport;
|
|
7
|
+
updatedAt?: number;
|
|
8
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { VoiceWorkflowStatusClientOptions } from '../client/workflowStatus';
|
|
2
|
+
export declare const createVoiceWorkflowStatus: (path?: string, options?: VoiceWorkflowStatusClientOptions) => {
|
|
3
|
+
close: () => void;
|
|
4
|
+
getServerSnapshot: () => import("../client").VoiceWorkflowStatusSnapshot;
|
|
5
|
+
getSnapshot: () => import("../client").VoiceWorkflowStatusSnapshot;
|
|
6
|
+
refresh: () => Promise<import("..").VoiceScenarioEvalReport | undefined>;
|
|
7
|
+
subscribe: (listener: () => void) => () => void;
|
|
8
|
+
};
|
package/dist/svelte/index.d.ts
CHANGED
package/dist/svelte/index.js
CHANGED
|
@@ -666,6 +666,87 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
|
|
|
666
666
|
|
|
667
667
|
// src/svelte/createVoiceProviderStatus.ts
|
|
668
668
|
var createVoiceProviderStatus = (path = "/api/provider-status", options = {}) => createVoiceProviderStatusStore(path, options);
|
|
669
|
+
// src/client/workflowStatus.ts
|
|
670
|
+
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
671
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
672
|
+
const response = await fetchImpl(path);
|
|
673
|
+
if (!response.ok) {
|
|
674
|
+
throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
|
|
675
|
+
}
|
|
676
|
+
return await response.json();
|
|
677
|
+
};
|
|
678
|
+
var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
|
|
679
|
+
const listeners = new Set;
|
|
680
|
+
let closed = false;
|
|
681
|
+
let timer;
|
|
682
|
+
let snapshot = {
|
|
683
|
+
error: null,
|
|
684
|
+
isLoading: false
|
|
685
|
+
};
|
|
686
|
+
const emit = () => {
|
|
687
|
+
for (const listener of listeners) {
|
|
688
|
+
listener();
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
const refresh = async () => {
|
|
692
|
+
if (closed) {
|
|
693
|
+
return snapshot.report;
|
|
694
|
+
}
|
|
695
|
+
snapshot = {
|
|
696
|
+
...snapshot,
|
|
697
|
+
error: null,
|
|
698
|
+
isLoading: true
|
|
699
|
+
};
|
|
700
|
+
emit();
|
|
701
|
+
try {
|
|
702
|
+
const report = await fetchVoiceWorkflowStatus(path, options);
|
|
703
|
+
snapshot = {
|
|
704
|
+
error: null,
|
|
705
|
+
isLoading: false,
|
|
706
|
+
report,
|
|
707
|
+
updatedAt: Date.now()
|
|
708
|
+
};
|
|
709
|
+
emit();
|
|
710
|
+
return report;
|
|
711
|
+
} catch (error) {
|
|
712
|
+
snapshot = {
|
|
713
|
+
...snapshot,
|
|
714
|
+
error: error instanceof Error ? error.message : String(error),
|
|
715
|
+
isLoading: false
|
|
716
|
+
};
|
|
717
|
+
emit();
|
|
718
|
+
throw error;
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
const close = () => {
|
|
722
|
+
closed = true;
|
|
723
|
+
if (timer) {
|
|
724
|
+
clearInterval(timer);
|
|
725
|
+
timer = undefined;
|
|
726
|
+
}
|
|
727
|
+
listeners.clear();
|
|
728
|
+
};
|
|
729
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
730
|
+
timer = setInterval(() => {
|
|
731
|
+
refresh().catch(() => {});
|
|
732
|
+
}, options.intervalMs);
|
|
733
|
+
}
|
|
734
|
+
return {
|
|
735
|
+
close,
|
|
736
|
+
getServerSnapshot: () => snapshot,
|
|
737
|
+
getSnapshot: () => snapshot,
|
|
738
|
+
refresh,
|
|
739
|
+
subscribe: (listener) => {
|
|
740
|
+
listeners.add(listener);
|
|
741
|
+
return () => {
|
|
742
|
+
listeners.delete(listener);
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
// src/svelte/createVoiceWorkflowStatus.ts
|
|
749
|
+
var createVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => createVoiceWorkflowStatusStore(path, options);
|
|
669
750
|
// src/client/htmx.ts
|
|
670
751
|
var DEFAULT_EVENT_NAME = "voice-refresh";
|
|
671
752
|
var DEFAULT_QUERY_PARAM = "sessionId";
|
|
@@ -1296,6 +1377,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1296
1377
|
};
|
|
1297
1378
|
};
|
|
1298
1379
|
export {
|
|
1380
|
+
createVoiceWorkflowStatus,
|
|
1299
1381
|
createVoiceStream2 as createVoiceStream,
|
|
1300
1382
|
createVoiceProviderStatus,
|
|
1301
1383
|
createVoiceController
|
package/dist/trace.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn.transcript';
|
|
1
|
+
export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn.transcript' | 'workflow.contract';
|
|
2
2
|
export type VoiceTraceEvent<TPayload extends Record<string, unknown> = Record<string, unknown>> = {
|
|
3
3
|
at: number;
|
|
4
4
|
id?: string;
|
package/dist/vue/index.d.ts
CHANGED
package/dist/vue/index.js
CHANGED
|
@@ -1428,7 +1428,119 @@ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
|
1428
1428
|
updatedAt
|
|
1429
1429
|
};
|
|
1430
1430
|
};
|
|
1431
|
+
// src/vue/useVoiceWorkflowStatus.ts
|
|
1432
|
+
import { onUnmounted as onUnmounted4, ref as ref4, shallowRef as shallowRef4 } from "vue";
|
|
1433
|
+
|
|
1434
|
+
// src/client/workflowStatus.ts
|
|
1435
|
+
var fetchVoiceWorkflowStatus = async (path = "/evals/scenarios/json", options = {}) => {
|
|
1436
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1437
|
+
const response = await fetchImpl(path);
|
|
1438
|
+
if (!response.ok) {
|
|
1439
|
+
throw new Error(`Voice workflow status failed: HTTP ${response.status}`);
|
|
1440
|
+
}
|
|
1441
|
+
return await response.json();
|
|
1442
|
+
};
|
|
1443
|
+
var createVoiceWorkflowStatusStore = (path = "/evals/scenarios/json", options = {}) => {
|
|
1444
|
+
const listeners = new Set;
|
|
1445
|
+
let closed = false;
|
|
1446
|
+
let timer;
|
|
1447
|
+
let snapshot = {
|
|
1448
|
+
error: null,
|
|
1449
|
+
isLoading: false
|
|
1450
|
+
};
|
|
1451
|
+
const emit = () => {
|
|
1452
|
+
for (const listener of listeners) {
|
|
1453
|
+
listener();
|
|
1454
|
+
}
|
|
1455
|
+
};
|
|
1456
|
+
const refresh = async () => {
|
|
1457
|
+
if (closed) {
|
|
1458
|
+
return snapshot.report;
|
|
1459
|
+
}
|
|
1460
|
+
snapshot = {
|
|
1461
|
+
...snapshot,
|
|
1462
|
+
error: null,
|
|
1463
|
+
isLoading: true
|
|
1464
|
+
};
|
|
1465
|
+
emit();
|
|
1466
|
+
try {
|
|
1467
|
+
const report = await fetchVoiceWorkflowStatus(path, options);
|
|
1468
|
+
snapshot = {
|
|
1469
|
+
error: null,
|
|
1470
|
+
isLoading: false,
|
|
1471
|
+
report,
|
|
1472
|
+
updatedAt: Date.now()
|
|
1473
|
+
};
|
|
1474
|
+
emit();
|
|
1475
|
+
return report;
|
|
1476
|
+
} catch (error) {
|
|
1477
|
+
snapshot = {
|
|
1478
|
+
...snapshot,
|
|
1479
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1480
|
+
isLoading: false
|
|
1481
|
+
};
|
|
1482
|
+
emit();
|
|
1483
|
+
throw error;
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1486
|
+
const close = () => {
|
|
1487
|
+
closed = true;
|
|
1488
|
+
if (timer) {
|
|
1489
|
+
clearInterval(timer);
|
|
1490
|
+
timer = undefined;
|
|
1491
|
+
}
|
|
1492
|
+
listeners.clear();
|
|
1493
|
+
};
|
|
1494
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
1495
|
+
timer = setInterval(() => {
|
|
1496
|
+
refresh().catch(() => {});
|
|
1497
|
+
}, options.intervalMs);
|
|
1498
|
+
}
|
|
1499
|
+
return {
|
|
1500
|
+
close,
|
|
1501
|
+
getServerSnapshot: () => snapshot,
|
|
1502
|
+
getSnapshot: () => snapshot,
|
|
1503
|
+
refresh,
|
|
1504
|
+
subscribe: (listener) => {
|
|
1505
|
+
listeners.add(listener);
|
|
1506
|
+
return () => {
|
|
1507
|
+
listeners.delete(listener);
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
};
|
|
1512
|
+
|
|
1513
|
+
// src/vue/useVoiceWorkflowStatus.ts
|
|
1514
|
+
var useVoiceWorkflowStatus = (path = "/evals/scenarios/json", options = {}) => {
|
|
1515
|
+
const store = createVoiceWorkflowStatusStore(path, options);
|
|
1516
|
+
const error = ref4(null);
|
|
1517
|
+
const isLoading = ref4(false);
|
|
1518
|
+
const report = shallowRef4(undefined);
|
|
1519
|
+
const updatedAt = ref4(undefined);
|
|
1520
|
+
const sync = () => {
|
|
1521
|
+
const snapshot = store.getSnapshot();
|
|
1522
|
+
error.value = snapshot.error;
|
|
1523
|
+
isLoading.value = snapshot.isLoading;
|
|
1524
|
+
report.value = snapshot.report;
|
|
1525
|
+
updatedAt.value = snapshot.updatedAt;
|
|
1526
|
+
};
|
|
1527
|
+
const unsubscribe = store.subscribe(sync);
|
|
1528
|
+
sync();
|
|
1529
|
+
store.refresh().catch(() => {});
|
|
1530
|
+
onUnmounted4(() => {
|
|
1531
|
+
unsubscribe();
|
|
1532
|
+
store.close();
|
|
1533
|
+
});
|
|
1534
|
+
return {
|
|
1535
|
+
error,
|
|
1536
|
+
isLoading,
|
|
1537
|
+
refresh: store.refresh,
|
|
1538
|
+
report,
|
|
1539
|
+
updatedAt
|
|
1540
|
+
};
|
|
1541
|
+
};
|
|
1431
1542
|
export {
|
|
1543
|
+
useVoiceWorkflowStatus,
|
|
1432
1544
|
useVoiceStream,
|
|
1433
1545
|
useVoiceProviderStatus,
|
|
1434
1546
|
useVoiceController
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type VoiceWorkflowStatusClientOptions } from '../client/workflowStatus';
|
|
2
|
+
import type { VoiceScenarioEvalReport } from '../evalRoutes';
|
|
3
|
+
export declare const useVoiceWorkflowStatus: (path?: string, options?: VoiceWorkflowStatusClientOptions) => {
|
|
4
|
+
error: import("vue").Ref<string | null, string | null>;
|
|
5
|
+
isLoading: import("vue").Ref<boolean, boolean>;
|
|
6
|
+
refresh: () => Promise<VoiceScenarioEvalReport | undefined>;
|
|
7
|
+
report: import("vue").ShallowRef<VoiceScenarioEvalReport | undefined, VoiceScenarioEvalReport | undefined>;
|
|
8
|
+
updatedAt: import("vue").Ref<number | undefined, number | undefined>;
|
|
9
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { VoiceScenarioEvalDefinition } from './evalRoutes';
|
|
2
|
+
import type { StoredVoiceTraceEvent, VoiceTraceEventStore } from './trace';
|
|
3
|
+
import type { VoiceOnTurnHandler, VoiceRouteResult, VoiceSessionRecord, VoiceTurnRecord } from './types';
|
|
4
|
+
export type VoiceWorkflowOutcome = 'complete' | 'transfer' | 'escalate' | 'voicemail' | 'no-answer';
|
|
5
|
+
export type VoiceWorkflowContractFieldMatch = 'boolean' | 'non-empty' | 'number' | 'string' | 'truthy';
|
|
6
|
+
export type VoiceWorkflowContractField = {
|
|
7
|
+
aliases?: string[];
|
|
8
|
+
label?: string;
|
|
9
|
+
match?: VoiceWorkflowContractFieldMatch;
|
|
10
|
+
path: string;
|
|
11
|
+
required?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export type VoiceWorkflowContractDefinition<TResult = unknown> = {
|
|
14
|
+
description?: string;
|
|
15
|
+
fields?: VoiceWorkflowContractField[];
|
|
16
|
+
forbiddenHandoffActions?: string[];
|
|
17
|
+
id: string;
|
|
18
|
+
label?: string;
|
|
19
|
+
maxProviderErrors?: number;
|
|
20
|
+
maxSessionErrors?: number;
|
|
21
|
+
minSessions?: number;
|
|
22
|
+
minTurns?: number;
|
|
23
|
+
outcome?: VoiceWorkflowOutcome;
|
|
24
|
+
requiredAssistantIncludes?: string[];
|
|
25
|
+
requiredDisposition?: string;
|
|
26
|
+
requiredHandoffActions?: string[];
|
|
27
|
+
requiredLifecycleTypes?: string[];
|
|
28
|
+
requiredTranscriptIncludes?: string[];
|
|
29
|
+
scenarioId?: string;
|
|
30
|
+
validate?: (input: {
|
|
31
|
+
result: TResult | undefined;
|
|
32
|
+
routeResult: VoiceRouteResult<TResult>;
|
|
33
|
+
}) => VoiceWorkflowContractValidationIssue[];
|
|
34
|
+
};
|
|
35
|
+
export type VoiceWorkflowContractPresetName = 'appointment-booking' | 'lead-qualification' | 'support-triage' | 'transfer-handoff' | 'voicemail-callback';
|
|
36
|
+
export type VoiceWorkflowContractPresetOptions<TResult = unknown> = Partial<Omit<VoiceWorkflowContractDefinition<TResult>, 'fields' | 'id'>> & {
|
|
37
|
+
fields?: VoiceWorkflowContractField[];
|
|
38
|
+
id?: string;
|
|
39
|
+
};
|
|
40
|
+
export type VoiceWorkflowContractValidationIssue = {
|
|
41
|
+
code: string;
|
|
42
|
+
field?: string;
|
|
43
|
+
message: string;
|
|
44
|
+
};
|
|
45
|
+
export type VoiceWorkflowContractValidation = {
|
|
46
|
+
contractId: string;
|
|
47
|
+
issues: VoiceWorkflowContractValidationIssue[];
|
|
48
|
+
missingFields: string[];
|
|
49
|
+
outcome?: VoiceWorkflowOutcome;
|
|
50
|
+
pass: boolean;
|
|
51
|
+
requiredFields: string[];
|
|
52
|
+
};
|
|
53
|
+
export type VoiceWorkflowContract<TResult = unknown> = {
|
|
54
|
+
assertRouteResult: (routeResult: VoiceRouteResult<TResult>) => void;
|
|
55
|
+
definition: VoiceWorkflowContractDefinition<TResult>;
|
|
56
|
+
toScenarioEval: (overrides?: Partial<VoiceScenarioEvalDefinition>) => VoiceScenarioEvalDefinition;
|
|
57
|
+
validateRouteResult: (routeResult: VoiceRouteResult<TResult>) => VoiceWorkflowContractValidation;
|
|
58
|
+
};
|
|
59
|
+
export type VoiceWorkflowContractTracePayload = {
|
|
60
|
+
contractId: string;
|
|
61
|
+
issues: VoiceWorkflowContractValidationIssue[];
|
|
62
|
+
missingFields: string[];
|
|
63
|
+
outcome?: VoiceWorkflowOutcome;
|
|
64
|
+
requiredFields: string[];
|
|
65
|
+
status: 'pass' | 'fail';
|
|
66
|
+
};
|
|
67
|
+
export declare const validateVoiceWorkflowRouteResult: <TResult = unknown>(definition: VoiceWorkflowContractDefinition<TResult>, routeResult: VoiceRouteResult<TResult>) => VoiceWorkflowContractValidation;
|
|
68
|
+
export declare const createVoiceWorkflowScenario: <TResult = unknown>(definition: VoiceWorkflowContractDefinition<TResult>, overrides?: Partial<VoiceScenarioEvalDefinition>) => VoiceScenarioEvalDefinition;
|
|
69
|
+
export declare const createVoiceWorkflowContract: <TResult = unknown>(definition: VoiceWorkflowContractDefinition<TResult>) => VoiceWorkflowContract<TResult>;
|
|
70
|
+
export declare const createVoiceWorkflowContractPreset: <TResult = unknown>(name: VoiceWorkflowContractPresetName, options?: VoiceWorkflowContractPresetOptions<TResult>) => VoiceWorkflowContract<TResult>;
|
|
71
|
+
export declare const recordVoiceWorkflowContractTrace: (input: {
|
|
72
|
+
at?: number;
|
|
73
|
+
contractId?: string;
|
|
74
|
+
scenarioId?: string;
|
|
75
|
+
sessionId: string;
|
|
76
|
+
store: VoiceTraceEventStore;
|
|
77
|
+
traceId?: string;
|
|
78
|
+
turnId?: string;
|
|
79
|
+
validation: VoiceWorkflowContractValidation;
|
|
80
|
+
}) => Promise<StoredVoiceTraceEvent<VoiceWorkflowContractTracePayload>>;
|
|
81
|
+
export declare const createVoiceWorkflowContractHandler: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(input: {
|
|
82
|
+
contract?: VoiceWorkflowContract<TResult> | VoiceWorkflowContractDefinition<TResult>;
|
|
83
|
+
handler: VoiceOnTurnHandler<TContext, TSession, TResult>;
|
|
84
|
+
resolveContract?: (args: {
|
|
85
|
+
context: TContext;
|
|
86
|
+
result: VoiceRouteResult<TResult>;
|
|
87
|
+
session: TSession;
|
|
88
|
+
turn: VoiceTurnRecord;
|
|
89
|
+
}) => VoiceWorkflowContract<TResult> | VoiceWorkflowContractDefinition<TResult> | undefined;
|
|
90
|
+
store?: VoiceTraceEventStore;
|
|
91
|
+
}) => VoiceOnTurnHandler<TContext, TSession, TResult>;
|