@absolutejs/voice 0.0.22-beta.249 → 0.0.22-beta.250
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +95 -6
- package/dist/providerSlo.d.ts +28 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3938,6 +3938,25 @@ app
|
|
|
3938
3938
|
|
|
3939
3939
|
The provider SLO routes expose JSON at `/api/voice/provider-slos`, HTML at `/voice/provider-slos`, and Markdown at `/voice/provider-slos.md`. Readiness adds a `Provider SLO gates` check when `providerSlo` is configured; failing latency, timeout, fallback, or unresolved-error budgets close the deploy gate.
|
|
3940
3940
|
|
|
3941
|
+
Use `evaluateVoiceProviderSloEvidence(...)` or `assertVoiceProviderSloEvidence(...)` when a proof pack needs to verify the JSON directly instead of scraping rendered HTML. The assertion can require LLM/STT/TTS evidence, latency samples, fallback events, named providers, and per-kind latency ceilings:
|
|
3942
|
+
|
|
3943
|
+
```ts
|
|
3944
|
+
const providerReport = await buildVoiceProviderSloReport({
|
|
3945
|
+
requiredKinds: ['llm', 'stt', 'tts'],
|
|
3946
|
+
store: runtime.traces
|
|
3947
|
+
});
|
|
3948
|
+
|
|
3949
|
+
assertVoiceProviderSloEvidence(providerReport, {
|
|
3950
|
+
fallbackKinds: ['llm'],
|
|
3951
|
+
maxP95ElapsedMs: { llm: 4500, stt: 1500, tts: 2200 },
|
|
3952
|
+
maxStatus: 'pass',
|
|
3953
|
+
minFallbacks: 1,
|
|
3954
|
+
minLatencySamples: 3,
|
|
3955
|
+
requiredKinds: ['llm', 'stt', 'tts'],
|
|
3956
|
+
requiredProviders: ['openai', 'anthropic', 'deepgram']
|
|
3957
|
+
});
|
|
3958
|
+
```
|
|
3959
|
+
|
|
3941
3960
|
Use `createVoiceProviderContractMatrixPreset(...)` when you want readiness proof for the whole provider stack without hand-writing every LLM, STT, and TTS contract row. The preset stays primitive: you still own provider lists, selected providers, latency budgets, env, capabilities, and route mounting.
|
|
3942
3961
|
|
|
3943
3962
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export { createOpenAIVoiceTTS } from './openaiTTS';
|
|
|
50
50
|
export { createVoiceProviderHealthHTMLHandler, createVoiceProviderHealthJSONHandler, createVoiceProviderHealthRoutes, renderVoiceProviderHealthHTML, summarizeVoiceProviderHealth } from './providerHealth';
|
|
51
51
|
export { createVoiceProviderCapabilityHTMLHandler, createVoiceProviderCapabilityJSONHandler, createVoiceProviderCapabilityRoutes, renderVoiceProviderCapabilityHTML, summarizeVoiceProviderCapabilities } from './providerCapabilities';
|
|
52
52
|
export { assertVoiceProviderRoutingContract, runVoiceProviderRoutingContract } from './providerRoutingContract';
|
|
53
|
-
export { buildVoiceProviderSloReport, createVoiceProviderSloRoutes, renderVoiceProviderSloHTML, renderVoiceProviderSloMarkdown } from './providerSlo';
|
|
53
|
+
export { assertVoiceProviderSloEvidence, buildVoiceProviderSloReport, createVoiceProviderSloRoutes, evaluateVoiceProviderSloEvidence, renderVoiceProviderSloHTML, renderVoiceProviderSloMarkdown } from './providerSlo';
|
|
54
54
|
export { createVoicePhoneAgentProductionSmokeHTMLHandler, createVoicePhoneAgentProductionSmokeJSONHandler, createVoicePhoneAgentProductionSmokeRoutes, renderVoicePhoneAgentProductionSmokeHTML, runVoicePhoneAgentProductionSmokeContract } from './phoneAgentProductionSmoke';
|
|
55
55
|
export { buildVoiceProductionReadinessGate, buildVoiceProductionReadinessReport, createVoiceProductionReadinessRoutes, renderVoiceProductionReadinessHTML, summarizeVoiceProductionReadinessGate } from './productionReadiness';
|
|
56
56
|
export { createVoiceReadinessProfile, recommendVoiceReadinessProfile } from './readinessProfiles';
|
|
@@ -109,7 +109,7 @@ export type { OpenAIRealtimeAdapterOptions, OpenAIRealtimeModel, OpenAIRealtimeN
|
|
|
109
109
|
export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
|
|
110
110
|
export type { VoiceProviderCapabilityDefinition, VoiceProviderCapabilityHandlerOptions, VoiceProviderCapabilityHTMLHandlerOptions, VoiceProviderCapabilityKind, VoiceProviderCapabilityOptions, VoiceProviderCapabilityReport, VoiceProviderCapabilityRoutesOptions, VoiceProviderCapabilitySummary } from './providerCapabilities';
|
|
111
111
|
export type { VoiceProviderRoutingContractDefinition, VoiceProviderRoutingContractIssue, VoiceProviderRoutingContractReport, VoiceProviderRoutingContractRunOptions, VoiceProviderRoutingExpectation, VoiceProviderRoutingStatus } from './providerRoutingContract';
|
|
112
|
-
export type { VoiceProviderSloIssue, VoiceProviderSloKindReport, VoiceProviderSloMetric, VoiceProviderSloReport, VoiceProviderSloReportOptions, VoiceProviderSloRoutesOptions, VoiceProviderSloSessionReport, VoiceProviderSloStatus, VoiceProviderSloThresholdConfig, VoiceProviderSloThresholds } from './providerSlo';
|
|
112
|
+
export type { VoiceProviderSloAssertionInput, VoiceProviderSloAssertionReport, VoiceProviderSloIssue, VoiceProviderSloKindReport, VoiceProviderSloMetric, VoiceProviderSloReport, VoiceProviderSloReportOptions, VoiceProviderSloRoutesOptions, VoiceProviderSloSessionReport, VoiceProviderSloStatus, VoiceProviderSloThresholdConfig, VoiceProviderSloThresholds } from './providerSlo';
|
|
113
113
|
export type { VoiceTurnLatencyHTMLHandlerOptions, VoiceTurnLatencyItem, VoiceTurnLatencyOptions, VoiceTurnLatencyReport, VoiceTurnLatencyRoutesOptions, VoiceTurnLatencyStage, VoiceTurnLatencyStatus } from './turnLatency';
|
|
114
114
|
export type { VoiceLiveLatencyOptions, VoiceLiveLatencyReport, VoiceLiveLatencyRoutesOptions, VoiceLiveLatencySample, VoiceLiveLatencyStatus } from './liveLatency';
|
|
115
115
|
export type { VoiceLatencySLOBudget, VoiceLatencySLOGateError, VoiceLatencySLOGateOptions, VoiceLatencySLOGateReport, VoiceLatencySLOMeasurement, VoiceLatencySLOStage, VoiceLatencySLOStageSummary, VoiceLatencySLOStatus } from './latencySlo';
|
package/dist/index.js
CHANGED
|
@@ -22055,9 +22055,17 @@ var defaultThresholds = {
|
|
|
22055
22055
|
}
|
|
22056
22056
|
};
|
|
22057
22057
|
var providerKinds = ["llm", "stt", "tts"];
|
|
22058
|
+
var statusRank = {
|
|
22059
|
+
pass: 0,
|
|
22060
|
+
warn: 1,
|
|
22061
|
+
fail: 2
|
|
22062
|
+
};
|
|
22058
22063
|
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
22059
22064
|
var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
|
|
22060
22065
|
var rate3 = (count, total) => count / Math.max(1, total);
|
|
22066
|
+
var uniqueSorted = (values) => [
|
|
22067
|
+
...new Set(values.filter((value) => typeof value === "string"))
|
|
22068
|
+
].sort();
|
|
22061
22069
|
var percentile3 = (values, rank) => {
|
|
22062
22070
|
if (values.length === 0) {
|
|
22063
22071
|
return 0;
|
|
@@ -22235,6 +22243,85 @@ var buildVoiceProviderSloReport = async (options = {}) => {
|
|
|
22235
22243
|
thresholds
|
|
22236
22244
|
};
|
|
22237
22245
|
};
|
|
22246
|
+
var evaluateVoiceProviderSloEvidence = (report, input = {}) => {
|
|
22247
|
+
const issues = [];
|
|
22248
|
+
const kindReports = Object.values(report.kinds);
|
|
22249
|
+
const providers = uniqueSorted(kindReports.flatMap((kind) => kind.providers));
|
|
22250
|
+
const kinds = providerKinds.filter((kind) => report.kinds[kind].events > 0);
|
|
22251
|
+
const fallbacks = kindReports.reduce((total, kind) => total + kind.fallbacks, 0);
|
|
22252
|
+
const timeouts = kindReports.reduce((total, kind) => total + kind.timeouts, 0);
|
|
22253
|
+
const unresolvedErrors = kindReports.reduce((total, kind) => total + kind.unresolvedErrors, 0);
|
|
22254
|
+
const maxStatus = input.maxStatus ?? "pass";
|
|
22255
|
+
if (statusRank[report.status] > statusRank[maxStatus]) {
|
|
22256
|
+
issues.push(`Expected provider SLO status at most ${maxStatus}, found ${report.status}.`);
|
|
22257
|
+
}
|
|
22258
|
+
if (input.minEvents !== undefined && report.events < input.minEvents) {
|
|
22259
|
+
issues.push(`Expected at least ${String(input.minEvents)} provider routing events, found ${String(report.events)}.`);
|
|
22260
|
+
}
|
|
22261
|
+
if (input.minLatencySamples !== undefined && report.eventsWithLatency < input.minLatencySamples) {
|
|
22262
|
+
issues.push(`Expected at least ${String(input.minLatencySamples)} provider latency samples, found ${String(report.eventsWithLatency)}.`);
|
|
22263
|
+
}
|
|
22264
|
+
if (input.minFallbacks !== undefined && fallbacks < input.minFallbacks) {
|
|
22265
|
+
issues.push(`Expected at least ${String(input.minFallbacks)} provider fallback events, found ${String(fallbacks)}.`);
|
|
22266
|
+
}
|
|
22267
|
+
if (input.maxUnresolvedErrors !== undefined && unresolvedErrors > input.maxUnresolvedErrors) {
|
|
22268
|
+
issues.push(`Expected at most ${String(input.maxUnresolvedErrors)} unresolved provider errors, found ${String(unresolvedErrors)}.`);
|
|
22269
|
+
}
|
|
22270
|
+
if (input.maxTimeouts !== undefined && timeouts > input.maxTimeouts) {
|
|
22271
|
+
issues.push(`Expected at most ${String(input.maxTimeouts)} provider timeouts, found ${String(timeouts)}.`);
|
|
22272
|
+
}
|
|
22273
|
+
if (input.maxIssues !== undefined && report.issues.length > input.maxIssues) {
|
|
22274
|
+
issues.push(`Expected at most ${String(input.maxIssues)} provider SLO issues, found ${String(report.issues.length)}.`);
|
|
22275
|
+
}
|
|
22276
|
+
for (const kind of input.requiredKinds ?? []) {
|
|
22277
|
+
if (report.kinds[kind].events === 0) {
|
|
22278
|
+
issues.push(`Missing provider SLO kind evidence: ${kind}.`);
|
|
22279
|
+
}
|
|
22280
|
+
}
|
|
22281
|
+
for (const kind of input.fallbackKinds ?? []) {
|
|
22282
|
+
if (report.kinds[kind].fallbacks === 0) {
|
|
22283
|
+
issues.push(`Missing provider fallback evidence for kind: ${kind}.`);
|
|
22284
|
+
}
|
|
22285
|
+
}
|
|
22286
|
+
for (const provider of input.requiredProviders ?? []) {
|
|
22287
|
+
if (!providers.includes(provider)) {
|
|
22288
|
+
issues.push(`Missing provider SLO provider evidence: ${provider}.`);
|
|
22289
|
+
}
|
|
22290
|
+
}
|
|
22291
|
+
for (const [kind, maxAverageElapsedMs] of Object.entries(input.maxAverageElapsedMs ?? {})) {
|
|
22292
|
+
const metric = report.kinds[kind].metrics.averageElapsedMs;
|
|
22293
|
+
const actual = metric?.actual ?? 0;
|
|
22294
|
+
if (actual > maxAverageElapsedMs) {
|
|
22295
|
+
issues.push(`Expected ${kind} average provider latency <= ${String(maxAverageElapsedMs)}ms, found ${String(actual)}ms.`);
|
|
22296
|
+
}
|
|
22297
|
+
}
|
|
22298
|
+
for (const [kind, maxP95ElapsedMs] of Object.entries(input.maxP95ElapsedMs ?? {})) {
|
|
22299
|
+
const metric = report.kinds[kind].metrics.p95ElapsedMs;
|
|
22300
|
+
const actual = metric?.actual ?? 0;
|
|
22301
|
+
if (actual > maxP95ElapsedMs) {
|
|
22302
|
+
issues.push(`Expected ${kind} p95 provider latency <= ${String(maxP95ElapsedMs)}ms, found ${String(actual)}ms.`);
|
|
22303
|
+
}
|
|
22304
|
+
}
|
|
22305
|
+
return {
|
|
22306
|
+
events: report.events,
|
|
22307
|
+
eventsWithLatency: report.eventsWithLatency,
|
|
22308
|
+
fallbacks,
|
|
22309
|
+
issues,
|
|
22310
|
+
kinds,
|
|
22311
|
+
ok: issues.length === 0,
|
|
22312
|
+
providers,
|
|
22313
|
+
status: report.status,
|
|
22314
|
+
timeouts,
|
|
22315
|
+
unresolvedErrors
|
|
22316
|
+
};
|
|
22317
|
+
};
|
|
22318
|
+
var assertVoiceProviderSloEvidence = (report, input = {}) => {
|
|
22319
|
+
const assertion = evaluateVoiceProviderSloEvidence(report, input);
|
|
22320
|
+
if (!assertion.ok) {
|
|
22321
|
+
throw new Error(`Voice provider SLO assertion failed: ${assertion.issues.join(" ")}`);
|
|
22322
|
+
}
|
|
22323
|
+
return assertion;
|
|
22324
|
+
};
|
|
22238
22325
|
var formatMetricValue2 = (metric) => metric.unit === "rate" ? `${(metric.actual * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.actual)}ms` : String(metric.actual);
|
|
22239
22326
|
var formatMetricThreshold = (metric) => metric.unit === "rate" ? `${(metric.threshold * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.threshold)}ms` : String(metric.threshold);
|
|
22240
22327
|
var getMetric = (report, key) => report.metrics[key];
|
|
@@ -22880,7 +22967,7 @@ var hasPayloadValue = (payload, key, values) => {
|
|
|
22880
22967
|
return typeof value === "string" && values.has(value);
|
|
22881
22968
|
};
|
|
22882
22969
|
var countIntegrationDeliveryStatus = (events, status) => events.filter((event) => event.deliveryStatus === status).length;
|
|
22883
|
-
var
|
|
22970
|
+
var uniqueSorted2 = (values) => [
|
|
22884
22971
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
22885
22972
|
].sort();
|
|
22886
22973
|
var pushMissingValuesIssue = (input) => {
|
|
@@ -23059,11 +23146,11 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
23059
23146
|
var evaluateVoiceOperationsRecordGuardrails = (record, input = {}) => {
|
|
23060
23147
|
const issues = [];
|
|
23061
23148
|
const decisions = record.guardrails.decisions;
|
|
23062
|
-
const proofs =
|
|
23063
|
-
const ruleIds =
|
|
23064
|
-
const stages =
|
|
23065
|
-
const statuses =
|
|
23066
|
-
const toolNames =
|
|
23149
|
+
const proofs = uniqueSorted2(decisions.map((decision) => decision.proof));
|
|
23150
|
+
const ruleIds = uniqueSorted2(decisions.flatMap((decision) => decision.findings.map((finding) => finding.ruleId)));
|
|
23151
|
+
const stages = uniqueSorted2(decisions.map((decision) => decision.stage));
|
|
23152
|
+
const statuses = uniqueSorted2(decisions.map((decision) => decision.status));
|
|
23153
|
+
const toolNames = uniqueSorted2(decisions.map((decision) => decision.toolName));
|
|
23067
23154
|
const minDecisions = input.minDecisions ?? 1;
|
|
23068
23155
|
if (record.guardrails.total < minDecisions) {
|
|
23069
23156
|
issues.push(`Expected at least ${String(minDecisions)} guardrail decisions, found ${String(record.guardrails.total)}.`);
|
|
@@ -29986,6 +30073,7 @@ export {
|
|
|
29986
30073
|
evaluateVoiceTelephonyContract,
|
|
29987
30074
|
evaluateVoiceQuality,
|
|
29988
30075
|
evaluateVoiceProviderStackGaps,
|
|
30076
|
+
evaluateVoiceProviderSloEvidence,
|
|
29989
30077
|
evaluateVoiceOperationsRecordGuardrails,
|
|
29990
30078
|
evaluateVoiceGuardrailPolicy,
|
|
29991
30079
|
encodeTwilioMulawBase64,
|
|
@@ -30299,6 +30387,7 @@ export {
|
|
|
30299
30387
|
buildVoiceAuditDeliveryReport,
|
|
30300
30388
|
buildEmptyVoiceProofTrendReport,
|
|
30301
30389
|
assignVoiceOpsTask,
|
|
30390
|
+
assertVoiceProviderSloEvidence,
|
|
30302
30391
|
assertVoiceProviderRoutingContract,
|
|
30303
30392
|
assertVoiceOperationsRecordGuardrails,
|
|
30304
30393
|
assertVoiceObservabilityExportSchema,
|
package/dist/providerSlo.d.ts
CHANGED
|
@@ -79,7 +79,35 @@ export type VoiceProviderSloRoutesOptions = VoiceProviderSloReportOptions & {
|
|
|
79
79
|
render?: (report: VoiceProviderSloReport) => string | Promise<string>;
|
|
80
80
|
title?: string;
|
|
81
81
|
};
|
|
82
|
+
export type VoiceProviderSloAssertionInput = {
|
|
83
|
+
fallbackKinds?: VoiceRoutingEventKind[];
|
|
84
|
+
maxAverageElapsedMs?: Partial<Record<VoiceRoutingEventKind, number>>;
|
|
85
|
+
maxIssues?: number;
|
|
86
|
+
maxP95ElapsedMs?: Partial<Record<VoiceRoutingEventKind, number>>;
|
|
87
|
+
maxStatus?: VoiceProviderSloStatus;
|
|
88
|
+
maxTimeouts?: number;
|
|
89
|
+
maxUnresolvedErrors?: number;
|
|
90
|
+
minEvents?: number;
|
|
91
|
+
minFallbacks?: number;
|
|
92
|
+
minLatencySamples?: number;
|
|
93
|
+
requiredKinds?: VoiceRoutingEventKind[];
|
|
94
|
+
requiredProviders?: string[];
|
|
95
|
+
};
|
|
96
|
+
export type VoiceProviderSloAssertionReport = {
|
|
97
|
+
events: number;
|
|
98
|
+
eventsWithLatency: number;
|
|
99
|
+
fallbacks: number;
|
|
100
|
+
issues: string[];
|
|
101
|
+
kinds: VoiceRoutingEventKind[];
|
|
102
|
+
ok: boolean;
|
|
103
|
+
providers: string[];
|
|
104
|
+
status: VoiceProviderSloStatus;
|
|
105
|
+
timeouts: number;
|
|
106
|
+
unresolvedErrors: number;
|
|
107
|
+
};
|
|
82
108
|
export declare const buildVoiceProviderSloReport: (options?: VoiceProviderSloReportOptions) => Promise<VoiceProviderSloReport>;
|
|
109
|
+
export declare const evaluateVoiceProviderSloEvidence: (report: VoiceProviderSloReport, input?: VoiceProviderSloAssertionInput) => VoiceProviderSloAssertionReport;
|
|
110
|
+
export declare const assertVoiceProviderSloEvidence: (report: VoiceProviderSloReport, input?: VoiceProviderSloAssertionInput) => VoiceProviderSloAssertionReport;
|
|
83
111
|
export declare const renderVoiceProviderSloMarkdown: (report: VoiceProviderSloReport) => string;
|
|
84
112
|
export declare const renderVoiceProviderSloHTML: (report: VoiceProviderSloReport, options?: {
|
|
85
113
|
title?: string;
|