@absolutejs/voice 0.0.22-beta.367 → 0.0.22-beta.369
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 +101 -0
- package/dist/client/index.js +197 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +210 -10
- package/dist/proofTrends.d.ts +15 -0
- package/dist/react/index.js +157 -0
- package/dist/svelte/index.js +40 -0
- package/dist/testing/index.js +40 -0
- package/dist/trace.d.ts +12 -0
- package/dist/vue/index.js +157 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1401,6 +1401,107 @@ assertVoiceProductionReadinessEvidence(readiness, {
|
|
|
1401
1401
|
});
|
|
1402
1402
|
```
|
|
1403
1403
|
|
|
1404
|
+
Use `createVoiceProductionReadinessProofRuntime(...)` when the app needs a fresh, isolated proof window instead of letting stale local traces certify a deploy. The runtime is intentionally small: it owns a bounded in-memory trace store, route-cache defaults, a reusable TTL cache, a proof-freshness check, and optional synthetic provider/live-latency seed events. Your app still mounts routes, writes artifacts, and decides which proof sources matter.
|
|
1405
|
+
|
|
1406
|
+
```ts
|
|
1407
|
+
import {
|
|
1408
|
+
createVoiceProductionReadinessProofRuntime,
|
|
1409
|
+
createVoiceProductionReadinessRoutes,
|
|
1410
|
+
createVoiceReadinessProfile
|
|
1411
|
+
} from '@absolutejs/voice';
|
|
1412
|
+
|
|
1413
|
+
const readinessProof = createVoiceProductionReadinessProofRuntime({
|
|
1414
|
+
cacheMs: 10_000,
|
|
1415
|
+
traceMaxAgeMs: 30 * 60_000
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
const refreshReadinessProof = () =>
|
|
1419
|
+
readinessProof.refresh(async (metadata) => {
|
|
1420
|
+
await readinessProof.seedTraceProof({
|
|
1421
|
+
llmProvider: 'openai',
|
|
1422
|
+
scenarioId: 'provider-slo-proof',
|
|
1423
|
+
sttProvider: 'deepgram',
|
|
1424
|
+
ttsProvider: 'openai'
|
|
1425
|
+
});
|
|
1426
|
+
|
|
1427
|
+
await writeProofPack({
|
|
1428
|
+
generatedAt: metadata.generatedAt,
|
|
1429
|
+
runId: metadata.runId
|
|
1430
|
+
});
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
app.use(
|
|
1434
|
+
createVoiceProductionReadinessRoutes({
|
|
1435
|
+
...createVoiceReadinessProfile('phone-agent', {
|
|
1436
|
+
explain: true
|
|
1437
|
+
}),
|
|
1438
|
+
additionalChecks: async () => [
|
|
1439
|
+
await readinessProof.buildFreshnessCheck()
|
|
1440
|
+
],
|
|
1441
|
+
cacheMs: readinessProof.options.cacheMs,
|
|
1442
|
+
providerSlo: async () => {
|
|
1443
|
+
await refreshReadinessProof();
|
|
1444
|
+
return {
|
|
1445
|
+
events: await readinessProof.store.list(),
|
|
1446
|
+
requiredKinds: ['llm', 'stt', 'tts']
|
|
1447
|
+
};
|
|
1448
|
+
},
|
|
1449
|
+
resolveOptions: async () => {
|
|
1450
|
+
await refreshReadinessProof();
|
|
1451
|
+
return {};
|
|
1452
|
+
},
|
|
1453
|
+
store: readinessProof.store,
|
|
1454
|
+
traceMaxAgeMs: readinessProof.options.traceMaxAgeMs
|
|
1455
|
+
})
|
|
1456
|
+
);
|
|
1457
|
+
```
|
|
1458
|
+
|
|
1459
|
+
This primitive does not start workers, create persistent storage, mount a dashboard, or prescribe a deploy workflow. It only gives self-hosted apps one clean readiness-proof runtime so JSON, HTML, gate checks, proof packs, and trend artifacts agree on the same fresh evidence window.
|
|
1460
|
+
|
|
1461
|
+
Use `buildVoiceRealCallProfileEvidenceFromTraceEvents(...)` or `loadVoiceRealCallProfileEvidenceFromTraceStore(...)` when repeated real browser/phone sessions should drive profile defaults and provider/runtime recommendations. These helpers read ordinary trace events such as `session.error`, `provider.decision`, `client.live_latency`, `client.browser_media`, `client.telephony_media`, `client.barge_in`, and `turn_latency.stage`, then emit `VoiceProofTrendRealCallProfileEvidence[]` for `buildVoiceRealCallProfileHistoryReport(...)`.
|
|
1462
|
+
|
|
1463
|
+
```ts
|
|
1464
|
+
import {
|
|
1465
|
+
buildVoiceRealCallProfileHistoryReport,
|
|
1466
|
+
createVoiceRealCallProfileHistoryRoutes,
|
|
1467
|
+
loadVoiceRealCallProfileEvidenceFromTraceStore
|
|
1468
|
+
} from '@absolutejs/voice';
|
|
1469
|
+
|
|
1470
|
+
const buildRealCallHistory = async () =>
|
|
1471
|
+
buildVoiceRealCallProfileHistoryReport({
|
|
1472
|
+
evidence: await loadVoiceRealCallProfileEvidenceFromTraceStore({
|
|
1473
|
+
defaultProfileId: 'meeting-recorder',
|
|
1474
|
+
defaultProfileLabel: 'Meeting recorder',
|
|
1475
|
+
store: runtime.traces
|
|
1476
|
+
}),
|
|
1477
|
+
source: 'runtime.traces'
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
app.use(
|
|
1481
|
+
createVoiceRealCallProfileHistoryRoutes({
|
|
1482
|
+
source: buildRealCallHistory
|
|
1483
|
+
})
|
|
1484
|
+
);
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
The point is not to benchmark a fake demo once. The point is to let every real call add profile evidence so `/api/voice/real-call-profile-history`, provider recommendations, profile-switch readiness, and operations records can explain which provider/runtime path is winning for each call shape.
|
|
1488
|
+
|
|
1489
|
+
Use `createVoiceProfileTraceTagger(...)` when the app already has a trace store and needs every appended trace to carry a benchmark profile label. It wraps any `VoiceTraceEventStore`, preserves the underlying store behavior, and adds `profileId`/`benchmarkProfileId` metadata and payload fields that real-call profile history can ingest later.
|
|
1490
|
+
|
|
1491
|
+
```ts
|
|
1492
|
+
import { createVoiceProfileTraceTagger } from '@absolutejs/voice';
|
|
1493
|
+
|
|
1494
|
+
const trace = createVoiceProfileTraceTagger({
|
|
1495
|
+
defaultProfile: {
|
|
1496
|
+
id: 'meeting-recorder',
|
|
1497
|
+
label: 'Meeting recorder'
|
|
1498
|
+
},
|
|
1499
|
+
resolveProfile: (event) =>
|
|
1500
|
+
event.sessionId.startsWith('support-') ? 'support-agent' : undefined,
|
|
1501
|
+
store: runtime.traces
|
|
1502
|
+
});
|
|
1503
|
+
```
|
|
1504
|
+
|
|
1404
1505
|
Built-in profiles:
|
|
1405
1506
|
|
|
1406
1507
|
- `meeting-recorder`: live latency, session health, provider fallback, routing contracts, reconnect proof, and barge-in interruption proof.
|
package/dist/client/index.js
CHANGED
|
@@ -4016,6 +4016,20 @@ var maxNumber = (values) => {
|
|
|
4016
4016
|
const finite = values.filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
4017
4017
|
return finite.length > 0 ? Math.max(...finite) : undefined;
|
|
4018
4018
|
};
|
|
4019
|
+
var percentile = (values, rank) => {
|
|
4020
|
+
const finite = values.filter((value) => Number.isFinite(value)).sort((left, right) => left - right);
|
|
4021
|
+
if (finite.length === 0) {
|
|
4022
|
+
return;
|
|
4023
|
+
}
|
|
4024
|
+
const index = Math.min(finite.length - 1, Math.max(0, Math.ceil(rank / 100 * finite.length) - 1));
|
|
4025
|
+
return finite[index];
|
|
4026
|
+
};
|
|
4027
|
+
var averageNumber = (values) => {
|
|
4028
|
+
const finite = values.filter((value) => Number.isFinite(value));
|
|
4029
|
+
return finite.length === 0 ? undefined : Math.round(finite.reduce((total, value) => total + value, 0) / finite.length);
|
|
4030
|
+
};
|
|
4031
|
+
var readString = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
4032
|
+
var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
4019
4033
|
var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
|
|
4020
4034
|
var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
|
|
4021
4035
|
var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
|
|
@@ -4079,6 +4093,149 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
|
|
|
4079
4093
|
status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
|
|
4080
4094
|
};
|
|
4081
4095
|
};
|
|
4096
|
+
var readTraceRecord = (event) => event.payload;
|
|
4097
|
+
var readTraceProfileId = (events, options) => {
|
|
4098
|
+
for (const event of events) {
|
|
4099
|
+
const payload = readTraceRecord(event);
|
|
4100
|
+
const profileId = readString(payload.profileId) ?? readString(event.metadata?.profileId) ?? readString(payload.benchmarkProfileId) ?? readString(event.metadata?.benchmarkProfileId);
|
|
4101
|
+
if (profileId) {
|
|
4102
|
+
return profileId;
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
return options.defaultProfileId;
|
|
4106
|
+
};
|
|
4107
|
+
var readProviderTraceRole = (payload) => readString(payload.kind) ?? readString(payload.role) ?? readString(payload.surface) ?? "provider";
|
|
4108
|
+
var readProviderTraceLatency = (payload) => readNumber2(payload.elapsedMs) ?? readNumber2(payload.latencyMs) ?? readNumber2(payload.durationMs);
|
|
4109
|
+
var readProviderTraceId = (payload) => readString(payload.selectedProvider) ?? readString(payload.provider) ?? readString(payload.model) ?? readString(payload.adapter);
|
|
4110
|
+
var readTraceStatus = (payload) => readString(payload.providerStatus) ?? readString(payload.status);
|
|
4111
|
+
var isFailingTraceStatus = (status) => status === "error" || status === "fail" || status === "failed" || status === "timeout";
|
|
4112
|
+
var summarizeProviderTraceEvidence = (events, maxProviderP95Ms) => {
|
|
4113
|
+
const providerLatencies = new Map;
|
|
4114
|
+
const providerMeta = new Map;
|
|
4115
|
+
for (const event of events) {
|
|
4116
|
+
if (event.type !== "session.error" && event.type !== "provider.decision") {
|
|
4117
|
+
continue;
|
|
4118
|
+
}
|
|
4119
|
+
const payload = readTraceRecord(event);
|
|
4120
|
+
const provider = readProviderTraceId(payload);
|
|
4121
|
+
if (!provider) {
|
|
4122
|
+
continue;
|
|
4123
|
+
}
|
|
4124
|
+
const role = readProviderTraceRole(payload);
|
|
4125
|
+
const id = `${role}:${provider}`;
|
|
4126
|
+
const latency = readProviderTraceLatency(payload);
|
|
4127
|
+
if (latency !== undefined) {
|
|
4128
|
+
providerLatencies.set(id, [...providerLatencies.get(id) ?? [], latency]);
|
|
4129
|
+
}
|
|
4130
|
+
const existing = providerMeta.get(id);
|
|
4131
|
+
providerMeta.set(id, {
|
|
4132
|
+
failed: existing?.failed === true || isFailingTraceStatus(readTraceStatus(payload)),
|
|
4133
|
+
label: existing?.label ?? `${role.toUpperCase()} ${provider}`,
|
|
4134
|
+
role: existing?.role ?? role
|
|
4135
|
+
});
|
|
4136
|
+
}
|
|
4137
|
+
return [...providerMeta.entries()].map(([id, meta]) => {
|
|
4138
|
+
const latencies = providerLatencies.get(id) ?? [];
|
|
4139
|
+
const p95Ms = percentile(latencies, 95);
|
|
4140
|
+
return {
|
|
4141
|
+
averageMs: averageNumber(latencies),
|
|
4142
|
+
id,
|
|
4143
|
+
label: meta.label,
|
|
4144
|
+
p50Ms: percentile(latencies, 50),
|
|
4145
|
+
p95Ms,
|
|
4146
|
+
role: meta.role,
|
|
4147
|
+
samples: latencies.length,
|
|
4148
|
+
status: meta.failed || (p95Ms ?? 0) > (maxProviderP95Ms ?? Number.POSITIVE_INFINITY) ? "fail" : latencies.length > 0 ? "pass" : "warn"
|
|
4149
|
+
};
|
|
4150
|
+
});
|
|
4151
|
+
};
|
|
4152
|
+
var summarizeTurnTraceP95 = (events) => {
|
|
4153
|
+
const explicit = events.filter((event) => event.type === "turn_latency.stage").map((event) => {
|
|
4154
|
+
const payload = readTraceRecord(event);
|
|
4155
|
+
return readNumber2(payload.totalMs) ?? readNumber2(payload.elapsedMs) ?? readNumber2(payload.latencyMs);
|
|
4156
|
+
}).filter((value) => value !== undefined);
|
|
4157
|
+
if (explicit.length > 0) {
|
|
4158
|
+
return percentile(explicit, 95);
|
|
4159
|
+
}
|
|
4160
|
+
const turnStages = new Map;
|
|
4161
|
+
for (const event of events) {
|
|
4162
|
+
if (event.type !== "turn_latency.stage" || !event.turnId) {
|
|
4163
|
+
continue;
|
|
4164
|
+
}
|
|
4165
|
+
const key = `${event.sessionId}:${event.turnId}`;
|
|
4166
|
+
turnStages.set(key, [...turnStages.get(key) ?? [], event.at]);
|
|
4167
|
+
}
|
|
4168
|
+
const totals = [...turnStages.values()].map((stages) => stages.length < 2 ? undefined : Math.max(...stages) - Math.min(...stages)).filter((value) => value !== undefined);
|
|
4169
|
+
return percentile(totals, 95);
|
|
4170
|
+
};
|
|
4171
|
+
var summarizeRuntimeChannelTraceEvidence = (events) => {
|
|
4172
|
+
const runtimeEvents = events.filter((event) => event.type === "client.browser_media" || event.type === "client.telephony_media" || event.type === "client.barge_in");
|
|
4173
|
+
if (runtimeEvents.length === 0) {
|
|
4174
|
+
return;
|
|
4175
|
+
}
|
|
4176
|
+
const firstAudio = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).firstAudioLatencyMs)).filter((value) => value !== undefined);
|
|
4177
|
+
const jitter = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).jitterMs)).filter((value) => value !== undefined);
|
|
4178
|
+
const timestampDrift = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).timestampDriftMs)).filter((value) => value !== undefined);
|
|
4179
|
+
const backpressure = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).backpressureEvents)).filter((value) => value !== undefined);
|
|
4180
|
+
const interruptions = runtimeEvents.map((event) => {
|
|
4181
|
+
const payload = readTraceRecord(event);
|
|
4182
|
+
return readNumber2(payload.interruptionLatencyMs) ?? readNumber2(payload.interruptionMs) ?? readNumber2(payload.elapsedMs);
|
|
4183
|
+
}).filter((value) => value !== undefined);
|
|
4184
|
+
return {
|
|
4185
|
+
maxBackpressureEvents: maxNumber(backpressure),
|
|
4186
|
+
maxFirstAudioLatencyMs: maxNumber(firstAudio),
|
|
4187
|
+
maxInterruptionP95Ms: percentile(interruptions, 95),
|
|
4188
|
+
maxJitterMs: maxNumber(jitter),
|
|
4189
|
+
maxTimestampDriftMs: maxNumber(timestampDrift),
|
|
4190
|
+
samples: runtimeEvents.length,
|
|
4191
|
+
status: runtimeEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
|
|
4192
|
+
};
|
|
4193
|
+
};
|
|
4194
|
+
var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) => {
|
|
4195
|
+
const sessionFilter = new Set(options.sessionIds ?? []);
|
|
4196
|
+
const eventsBySession = new Map;
|
|
4197
|
+
for (const event of events) {
|
|
4198
|
+
if (sessionFilter.size > 0 && !sessionFilter.has(event.sessionId)) {
|
|
4199
|
+
continue;
|
|
4200
|
+
}
|
|
4201
|
+
eventsBySession.set(event.sessionId, [
|
|
4202
|
+
...eventsBySession.get(event.sessionId) ?? [],
|
|
4203
|
+
event
|
|
4204
|
+
]);
|
|
4205
|
+
}
|
|
4206
|
+
return [...eventsBySession.entries()].map(([
|
|
4207
|
+
sessionId,
|
|
4208
|
+
sessionEvents
|
|
4209
|
+
]) => {
|
|
4210
|
+
const profileId = readTraceProfileId(sessionEvents, options);
|
|
4211
|
+
if (!profileId) {
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4214
|
+
const providers = summarizeProviderTraceEvidence(sessionEvents, options.maxProviderP95Ms);
|
|
4215
|
+
const liveLatencies = sessionEvents.filter((event) => event.type === "client.live_latency").map((event) => {
|
|
4216
|
+
const payload = readTraceRecord(event);
|
|
4217
|
+
return readNumber2(payload.latencyMs) ?? readNumber2(payload.elapsedMs);
|
|
4218
|
+
}).filter((value) => value !== undefined);
|
|
4219
|
+
const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
|
|
4220
|
+
const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
|
|
4221
|
+
const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
|
|
4222
|
+
return {
|
|
4223
|
+
generatedAt: new Date(Math.max(...sessionEvents.map((event) => event.at))).toISOString(),
|
|
4224
|
+
liveP95Ms: percentile(liveLatencies, 95),
|
|
4225
|
+
ok: providers.every((provider) => provider.status !== "fail") && (runtimeChannel?.status ?? "pass") !== "fail",
|
|
4226
|
+
operationsRecordHref: `/voice-operations/${sessionId}`,
|
|
4227
|
+
profileDescription: options.profileDescriptions?.[profileId],
|
|
4228
|
+
profileId,
|
|
4229
|
+
profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
|
|
4230
|
+
providerP95Ms,
|
|
4231
|
+
providers,
|
|
4232
|
+
runtimeChannel,
|
|
4233
|
+
sessionId,
|
|
4234
|
+
turnP95Ms
|
|
4235
|
+
};
|
|
4236
|
+
}).filter((evidence) => evidence !== undefined);
|
|
4237
|
+
};
|
|
4238
|
+
var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
|
|
4082
4239
|
var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.flatMap((report) => report.summary.providers && report.summary.providers.length > 0 ? report.summary.providers : report.cycles.flatMap((cycle) => cycle.providers ?? [])));
|
|
4083
4240
|
var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
|
|
4084
4241
|
var readProofTrendProfileStatus = (profile, budgets) => {
|
|
@@ -5924,6 +6081,46 @@ var createVoiceTraceSinkStore = (options) => {
|
|
|
5924
6081
|
remove: (id) => options.store.remove(id)
|
|
5925
6082
|
};
|
|
5926
6083
|
};
|
|
6084
|
+
var normalizeVoiceProfileTraceTaggerProfile = (profile) => typeof profile === "string" ? { id: profile } : profile?.id ? profile : undefined;
|
|
6085
|
+
var createVoiceProfileTraceTagger = (options) => {
|
|
6086
|
+
const profiles = new Map((options.profiles ?? []).map((profile) => [profile.id, profile]));
|
|
6087
|
+
const defaultProfile = normalizeVoiceProfileTraceTaggerProfile(options.defaultProfile);
|
|
6088
|
+
const resolveProfile = async (event) => {
|
|
6089
|
+
const resolved = normalizeVoiceProfileTraceTaggerProfile(await options.resolveProfile?.(event));
|
|
6090
|
+
const profile = resolved ?? defaultProfile;
|
|
6091
|
+
return profile ? profiles.get(profile.id) ?? profile : undefined;
|
|
6092
|
+
};
|
|
6093
|
+
return {
|
|
6094
|
+
append: async (event) => {
|
|
6095
|
+
const profile = await resolveProfile(event);
|
|
6096
|
+
if (!profile) {
|
|
6097
|
+
return options.store.append(event);
|
|
6098
|
+
}
|
|
6099
|
+
const metadata = {
|
|
6100
|
+
...event.metadata ?? {},
|
|
6101
|
+
benchmarkProfileId: profile.id,
|
|
6102
|
+
profileDescription: event.metadata?.profileDescription ?? profile.description,
|
|
6103
|
+
profileId: profile.id,
|
|
6104
|
+
profileLabel: event.metadata?.profileLabel ?? profile.label
|
|
6105
|
+
};
|
|
6106
|
+
const payload = event.payload && typeof event.payload === "object" ? {
|
|
6107
|
+
...event.payload,
|
|
6108
|
+
benchmarkProfileId: event.payload.benchmarkProfileId ?? profile.id,
|
|
6109
|
+
profileDescription: event.payload.profileDescription ?? profile.description,
|
|
6110
|
+
profileId: event.payload.profileId ?? profile.id,
|
|
6111
|
+
profileLabel: event.payload.profileLabel ?? profile.label
|
|
6112
|
+
} : event.payload;
|
|
6113
|
+
return options.store.append({
|
|
6114
|
+
...event,
|
|
6115
|
+
metadata,
|
|
6116
|
+
payload
|
|
6117
|
+
});
|
|
6118
|
+
},
|
|
6119
|
+
get: (id) => options.store.get(id),
|
|
6120
|
+
list: (filter) => options.store.list(filter),
|
|
6121
|
+
remove: (id) => options.store.remove(id)
|
|
6122
|
+
};
|
|
6123
|
+
};
|
|
5927
6124
|
var createVoiceMemoryTraceSinkDeliveryStore = () => {
|
|
5928
6125
|
const deliveries = new Map;
|
|
5929
6126
|
return {
|
package/dist/index.d.ts
CHANGED
|
@@ -30,12 +30,12 @@ export { assertVoicePlatformCoverage, buildVoicePlatformCoverageSummary, createV
|
|
|
30
30
|
export { assertVoiceCompetitiveCoverage, buildVoiceCompetitiveCoverageReport, createVoiceCompetitiveCoverageRoutes, evaluateVoiceCompetitiveCoverage, renderVoiceCompetitiveCoverageHTML, renderVoiceCompetitiveCoverageMarkdown } from './competitiveCoverage';
|
|
31
31
|
export type { VoiceCompetitiveCoverageAssertionInput, VoiceCompetitiveCoverageAssertionReport, VoiceCompetitiveCoverageIssue, VoiceCompetitiveCoverageLevel, VoiceCompetitiveCoverageReport, VoiceCompetitiveCoverageReportInput, VoiceCompetitiveCoverageRoutesOptions, VoiceCompetitiveCoverageStatus, VoiceCompetitiveCoverageSummary, VoiceCompetitiveDepthLevel, VoiceCompetitiveEvidence, VoiceCompetitiveSurface } from './competitiveCoverage';
|
|
32
32
|
export type { VoicePlatformCoverageAssertionInput, VoicePlatformCoverageAssertionReport, VoicePlatformCoverageEvidence, VoicePlatformCoverageRoutesOptions, VoicePlatformCoverageStatus, VoicePlatformCoverageSummary, VoicePlatformCoverageSummaryInput, VoicePlatformCoverageSurface } from './platformCoverage';
|
|
33
|
-
export { assertVoiceProofTrendEvidence, buildEmptyVoiceProofTrendReport, buildVoiceProofTrendProfileSummaries, buildVoiceProofTrendRecommendationReport, buildVoiceProofTrendReportFromRealCallProfiles, buildVoiceProofTrendReport, buildVoiceRealCallProfileDefaults, buildVoiceRealCallProfileHistoryReport, createVoiceProofTrendRecommendationRoutes, createVoiceProofTrendRoutes, createVoiceRealCallProfileHistoryRoutes, DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS, DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS, evaluateVoiceProofTrendEvidence, formatVoiceProofTrendAge, normalizeVoiceProofTrendReport, readVoiceProofTrendReportFile, renderVoiceProofTrendRecommendationHTML, renderVoiceProofTrendRecommendationMarkdown, renderVoiceRealCallProfileHistoryHTML, renderVoiceRealCallProfileHistoryMarkdown, resolveVoiceRealCallProfileProviderRoute } from './proofTrends';
|
|
33
|
+
export { assertVoiceProofTrendEvidence, buildEmptyVoiceProofTrendReport, buildVoiceProofTrendProfileSummaries, buildVoiceProofTrendRecommendationReport, buildVoiceProofTrendReportFromRealCallProfiles, buildVoiceProofTrendReport, buildVoiceRealCallProfileEvidenceFromTraceEvents, buildVoiceRealCallProfileDefaults, buildVoiceRealCallProfileHistoryReport, createVoiceProofTrendRecommendationRoutes, createVoiceProofTrendRoutes, createVoiceRealCallProfileHistoryRoutes, DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS, DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS, evaluateVoiceProofTrendEvidence, formatVoiceProofTrendAge, loadVoiceRealCallProfileEvidenceFromTraceStore, normalizeVoiceProofTrendReport, readVoiceProofTrendReportFile, renderVoiceProofTrendRecommendationHTML, renderVoiceProofTrendRecommendationMarkdown, renderVoiceRealCallProfileHistoryHTML, renderVoiceRealCallProfileHistoryMarkdown, resolveVoiceRealCallProfileProviderRoute } from './proofTrends';
|
|
34
34
|
export { applyVoiceProfileSwitchGuard, buildVoiceProfileSwitchReadinessReport, buildVoiceProfileSwitchLiveDecisionReport, createVoiceProfileSwitchLiveDecisionRoutes, createVoiceProfileSwitchPolicyProofRoutes, createVoiceProfileSwitchReadinessRoutes, recommendVoiceProfileSwitch, renderVoiceProfileSwitchLiveDecisionHTML, renderVoiceProfileSwitchPolicyProofHTML, renderVoiceProfileSwitchReadinessHTML, runVoiceProfileSwitchPolicyProof } from './profileSwitchRecommendation';
|
|
35
35
|
export type { VoiceProfileSwitchGuardAction, VoiceProfileSwitchGuardDecision, VoiceProfileSwitchGuardMode, VoiceProfileSwitchGuardOptions, VoiceProfileSwitchObservedSignals, VoiceProfileSwitchLiveDecisionEvidence, VoiceProfileSwitchLiveDecisionReport, VoiceProfileSwitchLiveDecisionReportOptions, VoiceProfileSwitchLiveDecisionRoutesOptions, VoiceProfileSwitchLiveDecisionSession, VoiceProfileSwitchPolicyProofCase, VoiceProfileSwitchPolicyProofCaseResult, VoiceProfileSwitchPolicyProofOptions, VoiceProfileSwitchPolicyProofReport, VoiceProfileSwitchPolicyProofRoutesOptions, VoiceProfileSwitchReadinessIssue, VoiceProfileSwitchReadinessOptions, VoiceProfileSwitchReadinessReport, VoiceProfileSwitchReadinessRoutesOptions, VoiceProfileSwitchReadinessStatus, VoiceProfileSwitchRecommendation, VoiceProfileSwitchRecommendationOptions } from './profileSwitchRecommendation';
|
|
36
36
|
export { buildVoiceProviderDecisionTraceReport, createVoiceProviderDecisionTraceEvent, createVoiceProviderDecisionTraceRoutes, listVoiceProviderDecisionTraces, renderVoiceProviderDecisionTraceHTML, renderVoiceProviderDecisionTraceMarkdown } from './providerDecisionTraces';
|
|
37
37
|
export type { VoiceProviderDecisionStatus, VoiceProviderDecisionSurfaceReport, VoiceProviderDecisionTrace, VoiceProviderDecisionTraceInput, VoiceProviderDecisionTraceIssue, VoiceProviderDecisionTraceReport, VoiceProviderDecisionTraceReportOptions, VoiceProviderDecisionTraceRoutesOptions } from './providerDecisionTraces';
|
|
38
|
-
export type { VoiceProofTrendAssertionInput, VoiceProofTrendAssertionReport, VoiceProofTrendCycle, VoiceProofTrendProfileDefinition, VoiceProofTrendProfileRecommendation, VoiceProofTrendProfileSummaryOptions, VoiceProofTrendProfileSummary, VoiceProofTrendProviderRecommendation, VoiceProofTrendProviderSummary, VoiceProofTrendRecommendation, VoiceProofTrendRecommendationOptions, VoiceProofTrendRecommendationReport, VoiceProofTrendRecommendationRoutesOptions, VoiceProofTrendRecommendationStatus, VoiceProofTrendRecommendationSurface, VoiceProofTrendRealCallProfileEvidence, VoiceProofTrendRealCallProfileReportOptions, VoiceProofTrendReport, VoiceProofTrendReportInput, VoiceProofTrendRoutesOptions, VoiceProofTrendRuntimeChannelSummary, VoiceProofTrendStatus, VoiceProofTrendSummary, VoiceRealCallProfileDefault, VoiceRealCallProfileDefaultsOptions, VoiceRealCallProfileDefaultsReport, VoiceRealCallProfileHistoryOptions, VoiceRealCallProfileHistoryReport, VoiceRealCallProfileHistoryRoutesOptions, VoiceRealCallProfileProviderRouteOptions } from './proofTrends';
|
|
38
|
+
export type { VoiceProofTrendAssertionInput, VoiceProofTrendAssertionReport, VoiceProofTrendCycle, VoiceProofTrendProfileDefinition, VoiceProofTrendProfileRecommendation, VoiceProofTrendProfileSummaryOptions, VoiceProofTrendProfileSummary, VoiceProofTrendProviderRecommendation, VoiceProofTrendProviderSummary, VoiceProofTrendRecommendation, VoiceProofTrendRecommendationOptions, VoiceProofTrendRecommendationReport, VoiceProofTrendRecommendationRoutesOptions, VoiceProofTrendRecommendationStatus, VoiceProofTrendRecommendationSurface, VoiceProofTrendRealCallProfileEvidence, VoiceProofTrendRealCallProfileReportOptions, VoiceProofTrendReport, VoiceProofTrendReportInput, VoiceProofTrendRoutesOptions, VoiceProofTrendRuntimeChannelSummary, VoiceProofTrendStatus, VoiceProofTrendSummary, VoiceRealCallProfileDefault, VoiceRealCallProfileDefaultsOptions, VoiceRealCallProfileDefaultsReport, VoiceRealCallProfileHistoryOptions, VoiceRealCallProfileHistoryReport, VoiceRealCallProfileHistoryRoutesOptions, VoiceRealCallProfileProviderRouteOptions, VoiceRealCallProfileTraceEvidenceOptions, VoiceRealCallProfileTraceStoreEvidenceOptions } from './proofTrends';
|
|
39
39
|
export { assertVoiceSloCalibration, buildVoiceSloCalibrationReport, buildVoiceSloReadinessThresholdReport, createVoiceSloReadinessThresholdOptions, createVoiceSloReadinessThresholdRoutes, createVoiceSloThresholdProfile, createVoiceSloCalibrationRoutes, renderVoiceSloCalibrationMarkdown, renderVoiceSloReadinessThresholdHTML, renderVoiceSloReadinessThresholdMarkdown } from './sloCalibration';
|
|
40
40
|
export type { VoiceSloCalibrationMetricKey, VoiceSloCalibrationOptions, VoiceSloCalibrationReport, VoiceSloCalibrationRoutesOptions, VoiceSloCalibrationSample, VoiceSloCalibrationStatus, VoiceSloCalibrationThreshold, VoiceSloCalibrationThresholds, VoiceSloReadinessThresholdReport, VoiceSloReadinessThresholdReportOptions, VoiceSloReadinessThresholdOptions, VoiceSloReadinessThresholdRoutesOptions, VoiceSloThresholdProfile } from './sloCalibration';
|
|
41
41
|
export { assertVoiceLiveOpsControlEvidence, assertVoiceLiveOpsEvidence, buildVoiceLiveOpsControlState, createVoiceLiveOpsController, createVoiceLiveOpsRoutes, createVoiceMemoryLiveOpsControlStore, evaluateVoiceLiveOpsControlEvidence, evaluateVoiceLiveOpsEvidence, getVoiceLiveOpsControlStatus, VOICE_LIVE_OPS_ACTIONS } from './liveOps';
|
|
@@ -86,7 +86,7 @@ export { createVoiceOpsStatusRoutes, renderVoiceOpsStatusHTML } from './opsStatu
|
|
|
86
86
|
export { createVoiceQualityRoutes, evaluateVoiceQuality, renderVoiceQualityHTML } from './qualityRoutes';
|
|
87
87
|
export { createVoiceResilienceRoutes, createVoiceRoutingDecisionSummary, listVoiceRoutingEvents, renderVoiceResilienceHTML, summarizeVoiceRoutingDecision, summarizeVoiceRoutingSessions } from './resilienceRoutes';
|
|
88
88
|
export { createVoiceSTTProviderRouter, createVoiceTTSProviderRouter } from './providerAdapters';
|
|
89
|
-
export { buildVoiceTraceReplay, createVoiceMemoryTraceSinkDeliveryStore, createVoiceTraceHTTPSink, createVoiceTraceS3Sink, createVoiceMemoryTraceEventStore, createVoiceTraceSinkDeliveryId, createVoiceTraceSinkDeliveryRecord, createVoiceTraceSinkStore, createVoiceTraceEvent, createVoiceTraceEventId, deliverVoiceTraceEventsToSinks, evaluateVoiceTrace, exportVoiceTrace, filterVoiceTraceEvents, pruneVoiceTraceEvents, redactVoiceTraceEvent, redactVoiceTraceEvents, redactVoiceTraceText, renderVoiceTraceHTML, renderVoiceTraceMarkdown, resolveVoiceTraceRedactionOptions, selectVoiceTraceEventsForPrune, summarizeVoiceTrace } from './trace';
|
|
89
|
+
export { buildVoiceTraceReplay, createVoiceMemoryTraceSinkDeliveryStore, createVoiceProfileTraceTagger, createVoiceTraceHTTPSink, createVoiceTraceS3Sink, createVoiceMemoryTraceEventStore, createVoiceTraceSinkDeliveryId, createVoiceTraceSinkDeliveryRecord, createVoiceTraceSinkStore, createVoiceTraceEvent, createVoiceTraceEventId, deliverVoiceTraceEventsToSinks, evaluateVoiceTrace, exportVoiceTrace, filterVoiceTraceEvents, pruneVoiceTraceEvents, redactVoiceTraceEvent, redactVoiceTraceEvents, redactVoiceTraceText, renderVoiceTraceHTML, renderVoiceTraceMarkdown, resolveVoiceTraceRedactionOptions, selectVoiceTraceEventsForPrune, summarizeVoiceTrace } from './trace';
|
|
90
90
|
export { buildVoiceTraceDeliveryReport, createVoiceTraceDeliveryHTMLHandler, createVoiceTraceDeliveryJSONHandler, createVoiceTraceDeliveryRoutes, renderVoiceTraceDeliveryHTML, resolveVoiceTraceDeliveryFilter } from './traceDeliveryRoutes';
|
|
91
91
|
export { createVoiceTraceTimelineRoutes, renderVoiceTraceTimelineHTML, renderVoiceTraceTimelineSessionHTML, summarizeVoiceTraceTimeline } from './traceTimeline';
|
|
92
92
|
export { createVoiceSQLiteAuditEventStore, createVoiceSQLiteAuditSinkDeliveryStore, createVoiceSQLiteCampaignStore, createVoiceSQLiteExternalObjectMapStore, createVoiceSQLiteIntegrationEventStore, createVoiceSQLiteReviewStore, createVoiceSQLiteRuntimeStorage, createVoiceSQLiteSessionStore, createVoiceSQLiteTaskStore, createVoiceSQLiteTelephonyWebhookIdempotencyStore, createVoiceSQLiteTraceSinkDeliveryStore, createVoiceSQLiteTraceEventStore } from './sqliteStore';
|
|
@@ -174,7 +174,7 @@ export type { VoiceAuditExport } from './auditExport';
|
|
|
174
174
|
export type { VoiceAuditHTTPSinkOptions, VoiceAuditS3SinkOptions, VoiceS3AuditSinkClient, VoiceS3AuditSinkFile, VoiceAuditSink, VoiceAuditSinkDeliveryQueueStatus, VoiceAuditSinkDeliveryQueueSummary, VoiceAuditSinkDeliveryRecord, VoiceAuditSinkDeliveryResult, VoiceAuditSinkDeliveryStatus, VoiceAuditSinkDeliveryStore, VoiceAuditSinkDeliveryWorkerLoop, VoiceAuditSinkDeliveryWorkerLoopOptions, VoiceAuditSinkDeliveryWorkerOptions, VoiceAuditSinkDeliveryWorkerResult, VoiceAuditSinkFanoutResult, VoiceAuditSinkStoreOptions } from './auditSinks';
|
|
175
175
|
export type { VoiceAuditDeliveryDrainReport, VoiceAuditDeliveryDrainWorker, VoiceAuditDeliveryFilter, VoiceAuditDeliveryReport, VoiceAuditDeliveryRoutesOptions } from './auditDeliveryRoutes';
|
|
176
176
|
export type { VoiceFileRuntimeStorage, VoiceFileStoreOptions } from './fileStore';
|
|
177
|
-
export type { StoredVoiceTraceEvent, VoiceTraceEvaluation, VoiceTraceEvaluationOptions, VoiceTraceEvent, VoiceTraceEventFilter, VoiceTraceEventStore, VoiceTraceEventType, VoiceTraceIssue, VoiceTraceIssueSeverity, VoiceTraceHTTPSinkOptions, VoiceTraceS3SinkOptions, VoiceTracePruneFilter, VoiceTracePruneOptions, VoiceTracePruneResult, VoiceTraceRedactionConfig, VoiceTraceRedactionOptions, VoiceTraceRedactionReplacement, VoiceResolvedTraceRedactionOptions, VoiceTraceSink, VoiceTraceSinkDeliveryQueueStatus, VoiceTraceSinkDeliveryRecord, VoiceTraceSinkDeliveryResult, VoiceTraceSinkDeliveryStatus, VoiceTraceSinkDeliveryStore, VoiceTraceSinkFanoutResult, VoiceTraceSinkStoreOptions, VoiceTraceSummary, VoiceS3TraceSinkClient, VoiceS3TraceSinkFile } from './trace';
|
|
177
|
+
export type { VoiceProfileTraceTaggerOptions, VoiceProfileTraceTaggerProfile, StoredVoiceTraceEvent, VoiceTraceEvaluation, VoiceTraceEvaluationOptions, VoiceTraceEvent, VoiceTraceEventFilter, VoiceTraceEventStore, VoiceTraceEventType, VoiceTraceIssue, VoiceTraceIssueSeverity, VoiceTraceHTTPSinkOptions, VoiceTraceS3SinkOptions, VoiceTracePruneFilter, VoiceTracePruneOptions, VoiceTracePruneResult, VoiceTraceRedactionConfig, VoiceTraceRedactionOptions, VoiceTraceRedactionReplacement, VoiceResolvedTraceRedactionOptions, VoiceTraceSink, VoiceTraceSinkDeliveryQueueStatus, VoiceTraceSinkDeliveryRecord, VoiceTraceSinkDeliveryResult, VoiceTraceSinkDeliveryStatus, VoiceTraceSinkDeliveryStore, VoiceTraceSinkFanoutResult, VoiceTraceSinkStoreOptions, VoiceTraceSummary, VoiceS3TraceSinkClient, VoiceS3TraceSinkFile } from './trace';
|
|
178
178
|
export type { VoiceTraceDeliveryDrainReport, VoiceTraceDeliveryDrainWorker, VoiceTraceDeliveryFilter, VoiceTraceDeliveryReport, VoiceTraceDeliveryRoutesOptions } from './traceDeliveryRoutes';
|
|
179
179
|
export type { VoiceTraceTimelineEvent, VoiceTraceTimelineProviderSummary, VoiceTraceTimelineReport, VoiceTraceTimelineRoutesOptions, VoiceTraceTimelineSession } from './traceTimeline';
|
|
180
180
|
export type { VoicePostgresClient, VoicePostgresRuntimeStorage, VoicePostgresStoreOptions } from './postgresStore';
|
package/dist/index.js
CHANGED
|
@@ -10096,6 +10096,46 @@ var createVoiceTraceSinkStore = (options) => {
|
|
|
10096
10096
|
remove: (id) => options.store.remove(id)
|
|
10097
10097
|
};
|
|
10098
10098
|
};
|
|
10099
|
+
var normalizeVoiceProfileTraceTaggerProfile = (profile) => typeof profile === "string" ? { id: profile } : profile?.id ? profile : undefined;
|
|
10100
|
+
var createVoiceProfileTraceTagger = (options) => {
|
|
10101
|
+
const profiles = new Map((options.profiles ?? []).map((profile) => [profile.id, profile]));
|
|
10102
|
+
const defaultProfile = normalizeVoiceProfileTraceTaggerProfile(options.defaultProfile);
|
|
10103
|
+
const resolveProfile = async (event) => {
|
|
10104
|
+
const resolved = normalizeVoiceProfileTraceTaggerProfile(await options.resolveProfile?.(event));
|
|
10105
|
+
const profile = resolved ?? defaultProfile;
|
|
10106
|
+
return profile ? profiles.get(profile.id) ?? profile : undefined;
|
|
10107
|
+
};
|
|
10108
|
+
return {
|
|
10109
|
+
append: async (event) => {
|
|
10110
|
+
const profile = await resolveProfile(event);
|
|
10111
|
+
if (!profile) {
|
|
10112
|
+
return options.store.append(event);
|
|
10113
|
+
}
|
|
10114
|
+
const metadata = {
|
|
10115
|
+
...event.metadata ?? {},
|
|
10116
|
+
benchmarkProfileId: profile.id,
|
|
10117
|
+
profileDescription: event.metadata?.profileDescription ?? profile.description,
|
|
10118
|
+
profileId: profile.id,
|
|
10119
|
+
profileLabel: event.metadata?.profileLabel ?? profile.label
|
|
10120
|
+
};
|
|
10121
|
+
const payload = event.payload && typeof event.payload === "object" ? {
|
|
10122
|
+
...event.payload,
|
|
10123
|
+
benchmarkProfileId: event.payload.benchmarkProfileId ?? profile.id,
|
|
10124
|
+
profileDescription: event.payload.profileDescription ?? profile.description,
|
|
10125
|
+
profileId: event.payload.profileId ?? profile.id,
|
|
10126
|
+
profileLabel: event.payload.profileLabel ?? profile.label
|
|
10127
|
+
} : event.payload;
|
|
10128
|
+
return options.store.append({
|
|
10129
|
+
...event,
|
|
10130
|
+
metadata,
|
|
10131
|
+
payload
|
|
10132
|
+
});
|
|
10133
|
+
},
|
|
10134
|
+
get: (id) => options.store.get(id),
|
|
10135
|
+
list: (filter) => options.store.list(filter),
|
|
10136
|
+
remove: (id) => options.store.remove(id)
|
|
10137
|
+
};
|
|
10138
|
+
};
|
|
10099
10139
|
var createVoiceMemoryTraceSinkDeliveryStore = () => {
|
|
10100
10140
|
const deliveries = new Map;
|
|
10101
10141
|
return {
|
|
@@ -15445,6 +15485,20 @@ var maxNumber = (values) => {
|
|
|
15445
15485
|
const finite = values.filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
15446
15486
|
return finite.length > 0 ? Math.max(...finite) : undefined;
|
|
15447
15487
|
};
|
|
15488
|
+
var percentile2 = (values, rank) => {
|
|
15489
|
+
const finite = values.filter((value) => Number.isFinite(value)).sort((left, right) => left - right);
|
|
15490
|
+
if (finite.length === 0) {
|
|
15491
|
+
return;
|
|
15492
|
+
}
|
|
15493
|
+
const index = Math.min(finite.length - 1, Math.max(0, Math.ceil(rank / 100 * finite.length) - 1));
|
|
15494
|
+
return finite[index];
|
|
15495
|
+
};
|
|
15496
|
+
var averageNumber = (values) => {
|
|
15497
|
+
const finite = values.filter((value) => Number.isFinite(value));
|
|
15498
|
+
return finite.length === 0 ? undefined : Math.round(finite.reduce((total, value) => total + value, 0) / finite.length);
|
|
15499
|
+
};
|
|
15500
|
+
var readString2 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
15501
|
+
var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
15448
15502
|
var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
|
|
15449
15503
|
var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
|
|
15450
15504
|
var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
|
|
@@ -15508,6 +15562,149 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
|
|
|
15508
15562
|
status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
|
|
15509
15563
|
};
|
|
15510
15564
|
};
|
|
15565
|
+
var readTraceRecord = (event) => event.payload;
|
|
15566
|
+
var readTraceProfileId = (events, options) => {
|
|
15567
|
+
for (const event of events) {
|
|
15568
|
+
const payload = readTraceRecord(event);
|
|
15569
|
+
const profileId = readString2(payload.profileId) ?? readString2(event.metadata?.profileId) ?? readString2(payload.benchmarkProfileId) ?? readString2(event.metadata?.benchmarkProfileId);
|
|
15570
|
+
if (profileId) {
|
|
15571
|
+
return profileId;
|
|
15572
|
+
}
|
|
15573
|
+
}
|
|
15574
|
+
return options.defaultProfileId;
|
|
15575
|
+
};
|
|
15576
|
+
var readProviderTraceRole = (payload) => readString2(payload.kind) ?? readString2(payload.role) ?? readString2(payload.surface) ?? "provider";
|
|
15577
|
+
var readProviderTraceLatency = (payload) => readNumber2(payload.elapsedMs) ?? readNumber2(payload.latencyMs) ?? readNumber2(payload.durationMs);
|
|
15578
|
+
var readProviderTraceId = (payload) => readString2(payload.selectedProvider) ?? readString2(payload.provider) ?? readString2(payload.model) ?? readString2(payload.adapter);
|
|
15579
|
+
var readTraceStatus = (payload) => readString2(payload.providerStatus) ?? readString2(payload.status);
|
|
15580
|
+
var isFailingTraceStatus = (status) => status === "error" || status === "fail" || status === "failed" || status === "timeout";
|
|
15581
|
+
var summarizeProviderTraceEvidence = (events, maxProviderP95Ms) => {
|
|
15582
|
+
const providerLatencies = new Map;
|
|
15583
|
+
const providerMeta = new Map;
|
|
15584
|
+
for (const event of events) {
|
|
15585
|
+
if (event.type !== "session.error" && event.type !== "provider.decision") {
|
|
15586
|
+
continue;
|
|
15587
|
+
}
|
|
15588
|
+
const payload = readTraceRecord(event);
|
|
15589
|
+
const provider = readProviderTraceId(payload);
|
|
15590
|
+
if (!provider) {
|
|
15591
|
+
continue;
|
|
15592
|
+
}
|
|
15593
|
+
const role = readProviderTraceRole(payload);
|
|
15594
|
+
const id = `${role}:${provider}`;
|
|
15595
|
+
const latency = readProviderTraceLatency(payload);
|
|
15596
|
+
if (latency !== undefined) {
|
|
15597
|
+
providerLatencies.set(id, [...providerLatencies.get(id) ?? [], latency]);
|
|
15598
|
+
}
|
|
15599
|
+
const existing = providerMeta.get(id);
|
|
15600
|
+
providerMeta.set(id, {
|
|
15601
|
+
failed: existing?.failed === true || isFailingTraceStatus(readTraceStatus(payload)),
|
|
15602
|
+
label: existing?.label ?? `${role.toUpperCase()} ${provider}`,
|
|
15603
|
+
role: existing?.role ?? role
|
|
15604
|
+
});
|
|
15605
|
+
}
|
|
15606
|
+
return [...providerMeta.entries()].map(([id, meta]) => {
|
|
15607
|
+
const latencies = providerLatencies.get(id) ?? [];
|
|
15608
|
+
const p95Ms = percentile2(latencies, 95);
|
|
15609
|
+
return {
|
|
15610
|
+
averageMs: averageNumber(latencies),
|
|
15611
|
+
id,
|
|
15612
|
+
label: meta.label,
|
|
15613
|
+
p50Ms: percentile2(latencies, 50),
|
|
15614
|
+
p95Ms,
|
|
15615
|
+
role: meta.role,
|
|
15616
|
+
samples: latencies.length,
|
|
15617
|
+
status: meta.failed || (p95Ms ?? 0) > (maxProviderP95Ms ?? Number.POSITIVE_INFINITY) ? "fail" : latencies.length > 0 ? "pass" : "warn"
|
|
15618
|
+
};
|
|
15619
|
+
});
|
|
15620
|
+
};
|
|
15621
|
+
var summarizeTurnTraceP95 = (events) => {
|
|
15622
|
+
const explicit = events.filter((event) => event.type === "turn_latency.stage").map((event) => {
|
|
15623
|
+
const payload = readTraceRecord(event);
|
|
15624
|
+
return readNumber2(payload.totalMs) ?? readNumber2(payload.elapsedMs) ?? readNumber2(payload.latencyMs);
|
|
15625
|
+
}).filter((value) => value !== undefined);
|
|
15626
|
+
if (explicit.length > 0) {
|
|
15627
|
+
return percentile2(explicit, 95);
|
|
15628
|
+
}
|
|
15629
|
+
const turnStages = new Map;
|
|
15630
|
+
for (const event of events) {
|
|
15631
|
+
if (event.type !== "turn_latency.stage" || !event.turnId) {
|
|
15632
|
+
continue;
|
|
15633
|
+
}
|
|
15634
|
+
const key = `${event.sessionId}:${event.turnId}`;
|
|
15635
|
+
turnStages.set(key, [...turnStages.get(key) ?? [], event.at]);
|
|
15636
|
+
}
|
|
15637
|
+
const totals = [...turnStages.values()].map((stages) => stages.length < 2 ? undefined : Math.max(...stages) - Math.min(...stages)).filter((value) => value !== undefined);
|
|
15638
|
+
return percentile2(totals, 95);
|
|
15639
|
+
};
|
|
15640
|
+
var summarizeRuntimeChannelTraceEvidence = (events) => {
|
|
15641
|
+
const runtimeEvents = events.filter((event) => event.type === "client.browser_media" || event.type === "client.telephony_media" || event.type === "client.barge_in");
|
|
15642
|
+
if (runtimeEvents.length === 0) {
|
|
15643
|
+
return;
|
|
15644
|
+
}
|
|
15645
|
+
const firstAudio = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).firstAudioLatencyMs)).filter((value) => value !== undefined);
|
|
15646
|
+
const jitter = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).jitterMs)).filter((value) => value !== undefined);
|
|
15647
|
+
const timestampDrift = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).timestampDriftMs)).filter((value) => value !== undefined);
|
|
15648
|
+
const backpressure = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).backpressureEvents)).filter((value) => value !== undefined);
|
|
15649
|
+
const interruptions = runtimeEvents.map((event) => {
|
|
15650
|
+
const payload = readTraceRecord(event);
|
|
15651
|
+
return readNumber2(payload.interruptionLatencyMs) ?? readNumber2(payload.interruptionMs) ?? readNumber2(payload.elapsedMs);
|
|
15652
|
+
}).filter((value) => value !== undefined);
|
|
15653
|
+
return {
|
|
15654
|
+
maxBackpressureEvents: maxNumber(backpressure),
|
|
15655
|
+
maxFirstAudioLatencyMs: maxNumber(firstAudio),
|
|
15656
|
+
maxInterruptionP95Ms: percentile2(interruptions, 95),
|
|
15657
|
+
maxJitterMs: maxNumber(jitter),
|
|
15658
|
+
maxTimestampDriftMs: maxNumber(timestampDrift),
|
|
15659
|
+
samples: runtimeEvents.length,
|
|
15660
|
+
status: runtimeEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
|
|
15661
|
+
};
|
|
15662
|
+
};
|
|
15663
|
+
var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) => {
|
|
15664
|
+
const sessionFilter = new Set(options.sessionIds ?? []);
|
|
15665
|
+
const eventsBySession = new Map;
|
|
15666
|
+
for (const event of events) {
|
|
15667
|
+
if (sessionFilter.size > 0 && !sessionFilter.has(event.sessionId)) {
|
|
15668
|
+
continue;
|
|
15669
|
+
}
|
|
15670
|
+
eventsBySession.set(event.sessionId, [
|
|
15671
|
+
...eventsBySession.get(event.sessionId) ?? [],
|
|
15672
|
+
event
|
|
15673
|
+
]);
|
|
15674
|
+
}
|
|
15675
|
+
return [...eventsBySession.entries()].map(([
|
|
15676
|
+
sessionId,
|
|
15677
|
+
sessionEvents
|
|
15678
|
+
]) => {
|
|
15679
|
+
const profileId = readTraceProfileId(sessionEvents, options);
|
|
15680
|
+
if (!profileId) {
|
|
15681
|
+
return;
|
|
15682
|
+
}
|
|
15683
|
+
const providers = summarizeProviderTraceEvidence(sessionEvents, options.maxProviderP95Ms);
|
|
15684
|
+
const liveLatencies = sessionEvents.filter((event) => event.type === "client.live_latency").map((event) => {
|
|
15685
|
+
const payload = readTraceRecord(event);
|
|
15686
|
+
return readNumber2(payload.latencyMs) ?? readNumber2(payload.elapsedMs);
|
|
15687
|
+
}).filter((value) => value !== undefined);
|
|
15688
|
+
const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
|
|
15689
|
+
const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
|
|
15690
|
+
const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
|
|
15691
|
+
return {
|
|
15692
|
+
generatedAt: new Date(Math.max(...sessionEvents.map((event) => event.at))).toISOString(),
|
|
15693
|
+
liveP95Ms: percentile2(liveLatencies, 95),
|
|
15694
|
+
ok: providers.every((provider) => provider.status !== "fail") && (runtimeChannel?.status ?? "pass") !== "fail",
|
|
15695
|
+
operationsRecordHref: `/voice-operations/${sessionId}`,
|
|
15696
|
+
profileDescription: options.profileDescriptions?.[profileId],
|
|
15697
|
+
profileId,
|
|
15698
|
+
profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
|
|
15699
|
+
providerP95Ms,
|
|
15700
|
+
providers,
|
|
15701
|
+
runtimeChannel,
|
|
15702
|
+
sessionId,
|
|
15703
|
+
turnP95Ms
|
|
15704
|
+
};
|
|
15705
|
+
}).filter((evidence) => evidence !== undefined);
|
|
15706
|
+
};
|
|
15707
|
+
var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
|
|
15511
15708
|
var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.flatMap((report) => report.summary.providers && report.summary.providers.length > 0 ? report.summary.providers : report.cycles.flatMap((cycle) => cycle.providers ?? [])));
|
|
15512
15709
|
var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
|
|
15513
15710
|
var readProofTrendProfileStatus = (profile, budgets) => {
|
|
@@ -17189,7 +17386,7 @@ var DEFAULT_WARN_RATIO = 0.8;
|
|
|
17189
17386
|
var DEFAULT_MIN_PASSING_RUNS = 3;
|
|
17190
17387
|
var roundMs = (value) => Math.max(1, Math.ceil(value));
|
|
17191
17388
|
var finiteNumber = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
17192
|
-
var
|
|
17389
|
+
var percentile3 = (values, rank) => {
|
|
17193
17390
|
if (values.length === 0) {
|
|
17194
17391
|
return;
|
|
17195
17392
|
}
|
|
@@ -17213,7 +17410,7 @@ var normalizeSample = (input) => {
|
|
|
17213
17410
|
return input;
|
|
17214
17411
|
};
|
|
17215
17412
|
var createThreshold = (metric, values, options) => {
|
|
17216
|
-
const baselineP95Ms =
|
|
17413
|
+
const baselineP95Ms = percentile3(values, 95);
|
|
17217
17414
|
const maxObservedMs = values.length > 0 ? Math.max(...values) : undefined;
|
|
17218
17415
|
const recommendedMs = baselineP95Ms === undefined ? undefined : roundMs(Math.max(options.minimumMs, baselineP95Ms * options.headroomMultiplier));
|
|
17219
17416
|
const warnAfterMs = recommendedMs === undefined ? undefined : roundMs(Math.max(options.minimumMs, recommendedMs * options.warnRatio));
|
|
@@ -21729,7 +21926,7 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
21729
21926
|
// src/liveLatency.ts
|
|
21730
21927
|
import { Elysia as Elysia37 } from "elysia";
|
|
21731
21928
|
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21732
|
-
var
|
|
21929
|
+
var percentile4 = (values, percentileValue) => {
|
|
21733
21930
|
if (values.length === 0) {
|
|
21734
21931
|
return;
|
|
21735
21932
|
}
|
|
@@ -21765,8 +21962,8 @@ var summarizeVoiceLiveLatency = async (options) => {
|
|
|
21765
21962
|
averageLatencyMs: latencies.length > 0 ? Math.round(latencies.reduce((total, value) => total + value, 0) / latencies.length) : undefined,
|
|
21766
21963
|
checkedAt: Date.now(),
|
|
21767
21964
|
failed,
|
|
21768
|
-
p50LatencyMs:
|
|
21769
|
-
p95LatencyMs:
|
|
21965
|
+
p50LatencyMs: percentile4(latencies, 50),
|
|
21966
|
+
p95LatencyMs: percentile4(latencies, 95),
|
|
21770
21967
|
recent,
|
|
21771
21968
|
status: latencies.length === 0 ? "empty" : failed > 0 ? "fail" : warnings > 0 ? "warn" : "pass",
|
|
21772
21969
|
total: latencies.length,
|
|
@@ -21842,7 +22039,7 @@ var TRACE_TYPES = [
|
|
|
21842
22039
|
];
|
|
21843
22040
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
21844
22041
|
var getString14 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
21845
|
-
var
|
|
22042
|
+
var percentile5 = (values, percentileValue) => {
|
|
21846
22043
|
if (values.length === 0) {
|
|
21847
22044
|
return;
|
|
21848
22045
|
}
|
|
@@ -22050,8 +22247,8 @@ var summarizeStage = (stage, measurements, options) => {
|
|
|
22050
22247
|
label: STAGE_LABELS[stage],
|
|
22051
22248
|
maxMs: latencies.length > 0 ? Math.max(...latencies) : undefined,
|
|
22052
22249
|
measurements: stageMeasurements,
|
|
22053
|
-
p50Ms:
|
|
22054
|
-
p95Ms:
|
|
22250
|
+
p50Ms: percentile5(latencies, 50),
|
|
22251
|
+
p95Ms: percentile5(latencies, 95),
|
|
22055
22252
|
stage,
|
|
22056
22253
|
status: stageMeasurements.length === 0 ? "empty" : failed > 0 ? "fail" : warnings > 0 ? "warn" : "pass",
|
|
22057
22254
|
total: stageMeasurements.length,
|
|
@@ -27869,7 +28066,7 @@ var rate3 = (count, total) => count / Math.max(1, total);
|
|
|
27869
28066
|
var uniqueSorted7 = (values) => [
|
|
27870
28067
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
27871
28068
|
].sort();
|
|
27872
|
-
var
|
|
28069
|
+
var percentile6 = (values, rank) => {
|
|
27873
28070
|
if (values.length === 0) {
|
|
27874
28071
|
return 0;
|
|
27875
28072
|
}
|
|
@@ -27927,7 +28124,7 @@ var summarizeKind = (kind, events, thresholds, required) => {
|
|
|
27927
28124
|
unit: "rate"
|
|
27928
28125
|
}),
|
|
27929
28126
|
p95ElapsedMs: createMetric2({
|
|
27930
|
-
actual:
|
|
28127
|
+
actual: percentile6(latencies, 95),
|
|
27931
28128
|
label: "P95 latency",
|
|
27932
28129
|
threshold: thresholds.maxP95ElapsedMs,
|
|
27933
28130
|
unit: "ms"
|
|
@@ -38256,6 +38453,7 @@ export {
|
|
|
38256
38453
|
muteVoiceMonitorIssue,
|
|
38257
38454
|
matchesVoiceOpsTaskAssignmentRule,
|
|
38258
38455
|
markVoiceOpsTaskSLABreached,
|
|
38456
|
+
loadVoiceRealCallProfileEvidenceFromTraceStore,
|
|
38259
38457
|
loadVoiceObservabilityExportReplaySource,
|
|
38260
38458
|
listVoiceRoutingEvents,
|
|
38261
38459
|
listVoiceProviderDecisionTraces,
|
|
@@ -38439,6 +38637,7 @@ export {
|
|
|
38439
38637
|
createVoiceProviderCapabilityHTMLHandler,
|
|
38440
38638
|
createVoiceProofTrendRoutes,
|
|
38441
38639
|
createVoiceProofTrendRecommendationRoutes,
|
|
38640
|
+
createVoiceProfileTraceTagger,
|
|
38442
38641
|
createVoiceProfileSwitchReadinessRoutes,
|
|
38443
38642
|
createVoiceProfileSwitchPolicyProofRoutes,
|
|
38444
38643
|
createVoiceProfileSwitchLiveDecisionRoutes,
|
|
@@ -38634,6 +38833,7 @@ export {
|
|
|
38634
38833
|
buildVoiceRealtimeChannelRuntimeSamplesFromTrace,
|
|
38635
38834
|
buildVoiceRealtimeChannelReport,
|
|
38636
38835
|
buildVoiceRealCallProfileHistoryReport,
|
|
38836
|
+
buildVoiceRealCallProfileEvidenceFromTraceEvents,
|
|
38637
38837
|
buildVoiceRealCallProfileDefaults,
|
|
38638
38838
|
buildVoiceProviderSloReport,
|
|
38639
38839
|
buildVoiceProviderOrchestrationReport,
|
package/dist/proofTrends.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
|
+
import type { StoredVoiceTraceEvent, VoiceTraceEventStore } from './trace';
|
|
2
3
|
export type VoiceProofTrendStatus = 'empty' | 'fail' | 'pass' | 'stale';
|
|
3
4
|
export type VoiceProofTrendSummary = {
|
|
4
5
|
cycles?: number;
|
|
@@ -134,6 +135,18 @@ export type VoiceProofTrendRealCallProfileEvidence = {
|
|
|
134
135
|
sessionId: string;
|
|
135
136
|
turnP95Ms?: number;
|
|
136
137
|
};
|
|
138
|
+
export type VoiceRealCallProfileTraceEvidenceOptions = {
|
|
139
|
+
defaultProfileId?: string;
|
|
140
|
+
defaultProfileLabel?: string;
|
|
141
|
+
maxProviderP95Ms?: number;
|
|
142
|
+
profileDescriptions?: Record<string, string>;
|
|
143
|
+
profileLabels?: Record<string, string>;
|
|
144
|
+
sessionIds?: readonly string[];
|
|
145
|
+
};
|
|
146
|
+
export type VoiceRealCallProfileTraceStoreEvidenceOptions = VoiceRealCallProfileTraceEvidenceOptions & {
|
|
147
|
+
limit?: number;
|
|
148
|
+
store: VoiceTraceEventStore;
|
|
149
|
+
};
|
|
137
150
|
export type VoiceProofTrendRealCallProfileReportOptions = VoiceProofTrendProfileSummaryOptions & {
|
|
138
151
|
baseUrl?: string;
|
|
139
152
|
evidence: readonly VoiceProofTrendRealCallProfileEvidence[];
|
|
@@ -370,6 +383,8 @@ export declare const normalizeVoiceProofTrendReport: (value: VoiceProofTrendRepo
|
|
|
370
383
|
export declare const readVoiceProofTrendReportFile: (path: string, options?: {
|
|
371
384
|
maxAgeMs?: number;
|
|
372
385
|
}) => Promise<VoiceProofTrendReport>;
|
|
386
|
+
export declare const buildVoiceRealCallProfileEvidenceFromTraceEvents: (events: readonly StoredVoiceTraceEvent[], options?: VoiceRealCallProfileTraceEvidenceOptions) => VoiceProofTrendRealCallProfileEvidence[];
|
|
387
|
+
export declare const loadVoiceRealCallProfileEvidenceFromTraceStore: (options: VoiceRealCallProfileTraceStoreEvidenceOptions) => Promise<VoiceProofTrendRealCallProfileEvidence[]>;
|
|
373
388
|
export declare const buildVoiceProofTrendProfileSummaries: (input: VoiceProofTrendReport | readonly VoiceProofTrendReport[], options?: VoiceProofTrendProfileSummaryOptions) => VoiceProofTrendProfileSummary[];
|
|
374
389
|
export declare const buildVoiceProofTrendReportFromRealCallProfiles: (options: VoiceProofTrendRealCallProfileReportOptions) => VoiceProofTrendReport;
|
|
375
390
|
export declare const buildVoiceRealCallProfileDefaults: (input: VoiceRealCallProfileHistoryReport | VoiceProofTrendReport, options?: VoiceRealCallProfileDefaultsOptions) => VoiceRealCallProfileDefaultsReport;
|
package/dist/react/index.js
CHANGED
|
@@ -1603,6 +1603,20 @@ var maxNumber = (values) => {
|
|
|
1603
1603
|
const finite = values.filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
1604
1604
|
return finite.length > 0 ? Math.max(...finite) : undefined;
|
|
1605
1605
|
};
|
|
1606
|
+
var percentile = (values, rank) => {
|
|
1607
|
+
const finite = values.filter((value) => Number.isFinite(value)).sort((left, right) => left - right);
|
|
1608
|
+
if (finite.length === 0) {
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
const index = Math.min(finite.length - 1, Math.max(0, Math.ceil(rank / 100 * finite.length) - 1));
|
|
1612
|
+
return finite[index];
|
|
1613
|
+
};
|
|
1614
|
+
var averageNumber = (values) => {
|
|
1615
|
+
const finite = values.filter((value) => Number.isFinite(value));
|
|
1616
|
+
return finite.length === 0 ? undefined : Math.round(finite.reduce((total, value) => total + value, 0) / finite.length);
|
|
1617
|
+
};
|
|
1618
|
+
var readString = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
1619
|
+
var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
1606
1620
|
var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
|
|
1607
1621
|
var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
|
|
1608
1622
|
var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
|
|
@@ -1666,6 +1680,149 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
|
|
|
1666
1680
|
status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
|
|
1667
1681
|
};
|
|
1668
1682
|
};
|
|
1683
|
+
var readTraceRecord = (event) => event.payload;
|
|
1684
|
+
var readTraceProfileId = (events, options) => {
|
|
1685
|
+
for (const event of events) {
|
|
1686
|
+
const payload = readTraceRecord(event);
|
|
1687
|
+
const profileId = readString(payload.profileId) ?? readString(event.metadata?.profileId) ?? readString(payload.benchmarkProfileId) ?? readString(event.metadata?.benchmarkProfileId);
|
|
1688
|
+
if (profileId) {
|
|
1689
|
+
return profileId;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return options.defaultProfileId;
|
|
1693
|
+
};
|
|
1694
|
+
var readProviderTraceRole = (payload) => readString(payload.kind) ?? readString(payload.role) ?? readString(payload.surface) ?? "provider";
|
|
1695
|
+
var readProviderTraceLatency = (payload) => readNumber2(payload.elapsedMs) ?? readNumber2(payload.latencyMs) ?? readNumber2(payload.durationMs);
|
|
1696
|
+
var readProviderTraceId = (payload) => readString(payload.selectedProvider) ?? readString(payload.provider) ?? readString(payload.model) ?? readString(payload.adapter);
|
|
1697
|
+
var readTraceStatus = (payload) => readString(payload.providerStatus) ?? readString(payload.status);
|
|
1698
|
+
var isFailingTraceStatus = (status) => status === "error" || status === "fail" || status === "failed" || status === "timeout";
|
|
1699
|
+
var summarizeProviderTraceEvidence = (events, maxProviderP95Ms) => {
|
|
1700
|
+
const providerLatencies = new Map;
|
|
1701
|
+
const providerMeta = new Map;
|
|
1702
|
+
for (const event of events) {
|
|
1703
|
+
if (event.type !== "session.error" && event.type !== "provider.decision") {
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
const payload = readTraceRecord(event);
|
|
1707
|
+
const provider = readProviderTraceId(payload);
|
|
1708
|
+
if (!provider) {
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
const role = readProviderTraceRole(payload);
|
|
1712
|
+
const id = `${role}:${provider}`;
|
|
1713
|
+
const latency = readProviderTraceLatency(payload);
|
|
1714
|
+
if (latency !== undefined) {
|
|
1715
|
+
providerLatencies.set(id, [...providerLatencies.get(id) ?? [], latency]);
|
|
1716
|
+
}
|
|
1717
|
+
const existing = providerMeta.get(id);
|
|
1718
|
+
providerMeta.set(id, {
|
|
1719
|
+
failed: existing?.failed === true || isFailingTraceStatus(readTraceStatus(payload)),
|
|
1720
|
+
label: existing?.label ?? `${role.toUpperCase()} ${provider}`,
|
|
1721
|
+
role: existing?.role ?? role
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
return [...providerMeta.entries()].map(([id, meta]) => {
|
|
1725
|
+
const latencies = providerLatencies.get(id) ?? [];
|
|
1726
|
+
const p95Ms = percentile(latencies, 95);
|
|
1727
|
+
return {
|
|
1728
|
+
averageMs: averageNumber(latencies),
|
|
1729
|
+
id,
|
|
1730
|
+
label: meta.label,
|
|
1731
|
+
p50Ms: percentile(latencies, 50),
|
|
1732
|
+
p95Ms,
|
|
1733
|
+
role: meta.role,
|
|
1734
|
+
samples: latencies.length,
|
|
1735
|
+
status: meta.failed || (p95Ms ?? 0) > (maxProviderP95Ms ?? Number.POSITIVE_INFINITY) ? "fail" : latencies.length > 0 ? "pass" : "warn"
|
|
1736
|
+
};
|
|
1737
|
+
});
|
|
1738
|
+
};
|
|
1739
|
+
var summarizeTurnTraceP95 = (events) => {
|
|
1740
|
+
const explicit = events.filter((event) => event.type === "turn_latency.stage").map((event) => {
|
|
1741
|
+
const payload = readTraceRecord(event);
|
|
1742
|
+
return readNumber2(payload.totalMs) ?? readNumber2(payload.elapsedMs) ?? readNumber2(payload.latencyMs);
|
|
1743
|
+
}).filter((value) => value !== undefined);
|
|
1744
|
+
if (explicit.length > 0) {
|
|
1745
|
+
return percentile(explicit, 95);
|
|
1746
|
+
}
|
|
1747
|
+
const turnStages = new Map;
|
|
1748
|
+
for (const event of events) {
|
|
1749
|
+
if (event.type !== "turn_latency.stage" || !event.turnId) {
|
|
1750
|
+
continue;
|
|
1751
|
+
}
|
|
1752
|
+
const key = `${event.sessionId}:${event.turnId}`;
|
|
1753
|
+
turnStages.set(key, [...turnStages.get(key) ?? [], event.at]);
|
|
1754
|
+
}
|
|
1755
|
+
const totals = [...turnStages.values()].map((stages) => stages.length < 2 ? undefined : Math.max(...stages) - Math.min(...stages)).filter((value) => value !== undefined);
|
|
1756
|
+
return percentile(totals, 95);
|
|
1757
|
+
};
|
|
1758
|
+
var summarizeRuntimeChannelTraceEvidence = (events) => {
|
|
1759
|
+
const runtimeEvents = events.filter((event) => event.type === "client.browser_media" || event.type === "client.telephony_media" || event.type === "client.barge_in");
|
|
1760
|
+
if (runtimeEvents.length === 0) {
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
const firstAudio = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).firstAudioLatencyMs)).filter((value) => value !== undefined);
|
|
1764
|
+
const jitter = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).jitterMs)).filter((value) => value !== undefined);
|
|
1765
|
+
const timestampDrift = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).timestampDriftMs)).filter((value) => value !== undefined);
|
|
1766
|
+
const backpressure = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).backpressureEvents)).filter((value) => value !== undefined);
|
|
1767
|
+
const interruptions = runtimeEvents.map((event) => {
|
|
1768
|
+
const payload = readTraceRecord(event);
|
|
1769
|
+
return readNumber2(payload.interruptionLatencyMs) ?? readNumber2(payload.interruptionMs) ?? readNumber2(payload.elapsedMs);
|
|
1770
|
+
}).filter((value) => value !== undefined);
|
|
1771
|
+
return {
|
|
1772
|
+
maxBackpressureEvents: maxNumber(backpressure),
|
|
1773
|
+
maxFirstAudioLatencyMs: maxNumber(firstAudio),
|
|
1774
|
+
maxInterruptionP95Ms: percentile(interruptions, 95),
|
|
1775
|
+
maxJitterMs: maxNumber(jitter),
|
|
1776
|
+
maxTimestampDriftMs: maxNumber(timestampDrift),
|
|
1777
|
+
samples: runtimeEvents.length,
|
|
1778
|
+
status: runtimeEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
|
|
1779
|
+
};
|
|
1780
|
+
};
|
|
1781
|
+
var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) => {
|
|
1782
|
+
const sessionFilter = new Set(options.sessionIds ?? []);
|
|
1783
|
+
const eventsBySession = new Map;
|
|
1784
|
+
for (const event of events) {
|
|
1785
|
+
if (sessionFilter.size > 0 && !sessionFilter.has(event.sessionId)) {
|
|
1786
|
+
continue;
|
|
1787
|
+
}
|
|
1788
|
+
eventsBySession.set(event.sessionId, [
|
|
1789
|
+
...eventsBySession.get(event.sessionId) ?? [],
|
|
1790
|
+
event
|
|
1791
|
+
]);
|
|
1792
|
+
}
|
|
1793
|
+
return [...eventsBySession.entries()].map(([
|
|
1794
|
+
sessionId,
|
|
1795
|
+
sessionEvents
|
|
1796
|
+
]) => {
|
|
1797
|
+
const profileId = readTraceProfileId(sessionEvents, options);
|
|
1798
|
+
if (!profileId) {
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
const providers = summarizeProviderTraceEvidence(sessionEvents, options.maxProviderP95Ms);
|
|
1802
|
+
const liveLatencies = sessionEvents.filter((event) => event.type === "client.live_latency").map((event) => {
|
|
1803
|
+
const payload = readTraceRecord(event);
|
|
1804
|
+
return readNumber2(payload.latencyMs) ?? readNumber2(payload.elapsedMs);
|
|
1805
|
+
}).filter((value) => value !== undefined);
|
|
1806
|
+
const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
|
|
1807
|
+
const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
|
|
1808
|
+
const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
|
|
1809
|
+
return {
|
|
1810
|
+
generatedAt: new Date(Math.max(...sessionEvents.map((event) => event.at))).toISOString(),
|
|
1811
|
+
liveP95Ms: percentile(liveLatencies, 95),
|
|
1812
|
+
ok: providers.every((provider) => provider.status !== "fail") && (runtimeChannel?.status ?? "pass") !== "fail",
|
|
1813
|
+
operationsRecordHref: `/voice-operations/${sessionId}`,
|
|
1814
|
+
profileDescription: options.profileDescriptions?.[profileId],
|
|
1815
|
+
profileId,
|
|
1816
|
+
profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
|
|
1817
|
+
providerP95Ms,
|
|
1818
|
+
providers,
|
|
1819
|
+
runtimeChannel,
|
|
1820
|
+
sessionId,
|
|
1821
|
+
turnP95Ms
|
|
1822
|
+
};
|
|
1823
|
+
}).filter((evidence) => evidence !== undefined);
|
|
1824
|
+
};
|
|
1825
|
+
var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
|
|
1669
1826
|
var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.flatMap((report) => report.summary.providers && report.summary.providers.length > 0 ? report.summary.providers : report.cycles.flatMap((cycle) => cycle.providers ?? [])));
|
|
1670
1827
|
var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
|
|
1671
1828
|
var readProofTrendProfileStatus = (profile, budgets) => {
|
package/dist/svelte/index.js
CHANGED
|
@@ -1359,6 +1359,46 @@ var createVoiceTraceSinkStore = (options) => {
|
|
|
1359
1359
|
remove: (id) => options.store.remove(id)
|
|
1360
1360
|
};
|
|
1361
1361
|
};
|
|
1362
|
+
var normalizeVoiceProfileTraceTaggerProfile = (profile) => typeof profile === "string" ? { id: profile } : profile?.id ? profile : undefined;
|
|
1363
|
+
var createVoiceProfileTraceTagger = (options) => {
|
|
1364
|
+
const profiles = new Map((options.profiles ?? []).map((profile) => [profile.id, profile]));
|
|
1365
|
+
const defaultProfile = normalizeVoiceProfileTraceTaggerProfile(options.defaultProfile);
|
|
1366
|
+
const resolveProfile = async (event) => {
|
|
1367
|
+
const resolved = normalizeVoiceProfileTraceTaggerProfile(await options.resolveProfile?.(event));
|
|
1368
|
+
const profile = resolved ?? defaultProfile;
|
|
1369
|
+
return profile ? profiles.get(profile.id) ?? profile : undefined;
|
|
1370
|
+
};
|
|
1371
|
+
return {
|
|
1372
|
+
append: async (event) => {
|
|
1373
|
+
const profile = await resolveProfile(event);
|
|
1374
|
+
if (!profile) {
|
|
1375
|
+
return options.store.append(event);
|
|
1376
|
+
}
|
|
1377
|
+
const metadata = {
|
|
1378
|
+
...event.metadata ?? {},
|
|
1379
|
+
benchmarkProfileId: profile.id,
|
|
1380
|
+
profileDescription: event.metadata?.profileDescription ?? profile.description,
|
|
1381
|
+
profileId: profile.id,
|
|
1382
|
+
profileLabel: event.metadata?.profileLabel ?? profile.label
|
|
1383
|
+
};
|
|
1384
|
+
const payload = event.payload && typeof event.payload === "object" ? {
|
|
1385
|
+
...event.payload,
|
|
1386
|
+
benchmarkProfileId: event.payload.benchmarkProfileId ?? profile.id,
|
|
1387
|
+
profileDescription: event.payload.profileDescription ?? profile.description,
|
|
1388
|
+
profileId: event.payload.profileId ?? profile.id,
|
|
1389
|
+
profileLabel: event.payload.profileLabel ?? profile.label
|
|
1390
|
+
} : event.payload;
|
|
1391
|
+
return options.store.append({
|
|
1392
|
+
...event,
|
|
1393
|
+
metadata,
|
|
1394
|
+
payload
|
|
1395
|
+
});
|
|
1396
|
+
},
|
|
1397
|
+
get: (id) => options.store.get(id),
|
|
1398
|
+
list: (filter) => options.store.list(filter),
|
|
1399
|
+
remove: (id) => options.store.remove(id)
|
|
1400
|
+
};
|
|
1401
|
+
};
|
|
1362
1402
|
var createVoiceMemoryTraceSinkDeliveryStore = () => {
|
|
1363
1403
|
const deliveries = new Map;
|
|
1364
1404
|
return {
|
package/dist/testing/index.js
CHANGED
|
@@ -9524,6 +9524,46 @@ var createVoiceTraceSinkStore = (options) => {
|
|
|
9524
9524
|
remove: (id) => options.store.remove(id)
|
|
9525
9525
|
};
|
|
9526
9526
|
};
|
|
9527
|
+
var normalizeVoiceProfileTraceTaggerProfile = (profile) => typeof profile === "string" ? { id: profile } : profile?.id ? profile : undefined;
|
|
9528
|
+
var createVoiceProfileTraceTagger = (options) => {
|
|
9529
|
+
const profiles = new Map((options.profiles ?? []).map((profile) => [profile.id, profile]));
|
|
9530
|
+
const defaultProfile = normalizeVoiceProfileTraceTaggerProfile(options.defaultProfile);
|
|
9531
|
+
const resolveProfile = async (event) => {
|
|
9532
|
+
const resolved = normalizeVoiceProfileTraceTaggerProfile(await options.resolveProfile?.(event));
|
|
9533
|
+
const profile = resolved ?? defaultProfile;
|
|
9534
|
+
return profile ? profiles.get(profile.id) ?? profile : undefined;
|
|
9535
|
+
};
|
|
9536
|
+
return {
|
|
9537
|
+
append: async (event) => {
|
|
9538
|
+
const profile = await resolveProfile(event);
|
|
9539
|
+
if (!profile) {
|
|
9540
|
+
return options.store.append(event);
|
|
9541
|
+
}
|
|
9542
|
+
const metadata = {
|
|
9543
|
+
...event.metadata ?? {},
|
|
9544
|
+
benchmarkProfileId: profile.id,
|
|
9545
|
+
profileDescription: event.metadata?.profileDescription ?? profile.description,
|
|
9546
|
+
profileId: profile.id,
|
|
9547
|
+
profileLabel: event.metadata?.profileLabel ?? profile.label
|
|
9548
|
+
};
|
|
9549
|
+
const payload = event.payload && typeof event.payload === "object" ? {
|
|
9550
|
+
...event.payload,
|
|
9551
|
+
benchmarkProfileId: event.payload.benchmarkProfileId ?? profile.id,
|
|
9552
|
+
profileDescription: event.payload.profileDescription ?? profile.description,
|
|
9553
|
+
profileId: event.payload.profileId ?? profile.id,
|
|
9554
|
+
profileLabel: event.payload.profileLabel ?? profile.label
|
|
9555
|
+
} : event.payload;
|
|
9556
|
+
return options.store.append({
|
|
9557
|
+
...event,
|
|
9558
|
+
metadata,
|
|
9559
|
+
payload
|
|
9560
|
+
});
|
|
9561
|
+
},
|
|
9562
|
+
get: (id) => options.store.get(id),
|
|
9563
|
+
list: (filter) => options.store.list(filter),
|
|
9564
|
+
remove: (id) => options.store.remove(id)
|
|
9565
|
+
};
|
|
9566
|
+
};
|
|
9527
9567
|
var createVoiceMemoryTraceSinkDeliveryStore = () => {
|
|
9528
9568
|
const deliveries = new Map;
|
|
9529
9569
|
return {
|
package/dist/trace.d.ts
CHANGED
|
@@ -127,6 +127,17 @@ export type VoiceTraceSinkStoreOptions<TEvent extends StoredVoiceTraceEvent = St
|
|
|
127
127
|
sinks: VoiceTraceSink[];
|
|
128
128
|
store: VoiceTraceEventStore<TEvent>;
|
|
129
129
|
};
|
|
130
|
+
export type VoiceProfileTraceTaggerProfile = {
|
|
131
|
+
description?: string;
|
|
132
|
+
id: string;
|
|
133
|
+
label?: string;
|
|
134
|
+
};
|
|
135
|
+
export type VoiceProfileTraceTaggerOptions<TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent> = {
|
|
136
|
+
defaultProfile?: VoiceProfileTraceTaggerProfile | string;
|
|
137
|
+
profiles?: readonly VoiceProfileTraceTaggerProfile[];
|
|
138
|
+
resolveProfile?: (event: VoiceTraceEvent | TEvent) => Promise<VoiceProfileTraceTaggerProfile | string | undefined> | VoiceProfileTraceTaggerProfile | string | undefined;
|
|
139
|
+
store: VoiceTraceEventStore<TEvent>;
|
|
140
|
+
};
|
|
130
141
|
export type VoiceTraceSummary = {
|
|
131
142
|
assistantReplyCount: number;
|
|
132
143
|
callDurationMs?: number;
|
|
@@ -211,6 +222,7 @@ export declare const deliverVoiceTraceEventsToSinks: (input: {
|
|
|
211
222
|
sinks: VoiceTraceSink[];
|
|
212
223
|
}) => Promise<VoiceTraceSinkFanoutResult>;
|
|
213
224
|
export declare const createVoiceTraceSinkStore: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>(options: VoiceTraceSinkStoreOptions<TEvent>) => VoiceTraceEventStore<TEvent>;
|
|
225
|
+
export declare const createVoiceProfileTraceTagger: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>(options: VoiceProfileTraceTaggerOptions<TEvent>) => VoiceTraceEventStore<TEvent>;
|
|
214
226
|
export declare const createVoiceMemoryTraceSinkDeliveryStore: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>() => VoiceTraceSinkDeliveryStore<TDelivery>;
|
|
215
227
|
export declare const createVoiceMemoryTraceEventStore: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>() => VoiceTraceEventStore<TEvent>;
|
|
216
228
|
export declare const exportVoiceTrace: (input: {
|
package/dist/vue/index.js
CHANGED
|
@@ -1524,6 +1524,20 @@ var maxNumber = (values) => {
|
|
|
1524
1524
|
const finite = values.filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
1525
1525
|
return finite.length > 0 ? Math.max(...finite) : undefined;
|
|
1526
1526
|
};
|
|
1527
|
+
var percentile = (values, rank) => {
|
|
1528
|
+
const finite = values.filter((value) => Number.isFinite(value)).sort((left, right) => left - right);
|
|
1529
|
+
if (finite.length === 0) {
|
|
1530
|
+
return;
|
|
1531
|
+
}
|
|
1532
|
+
const index = Math.min(finite.length - 1, Math.max(0, Math.ceil(rank / 100 * finite.length) - 1));
|
|
1533
|
+
return finite[index];
|
|
1534
|
+
};
|
|
1535
|
+
var averageNumber = (values) => {
|
|
1536
|
+
const finite = values.filter((value) => Number.isFinite(value));
|
|
1537
|
+
return finite.length === 0 ? undefined : Math.round(finite.reduce((total, value) => total + value, 0) / finite.length);
|
|
1538
|
+
};
|
|
1539
|
+
var readString = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
1540
|
+
var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
1527
1541
|
var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
|
|
1528
1542
|
var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
|
|
1529
1543
|
var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
|
|
@@ -1587,6 +1601,149 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
|
|
|
1587
1601
|
status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
|
|
1588
1602
|
};
|
|
1589
1603
|
};
|
|
1604
|
+
var readTraceRecord = (event) => event.payload;
|
|
1605
|
+
var readTraceProfileId = (events, options) => {
|
|
1606
|
+
for (const event of events) {
|
|
1607
|
+
const payload = readTraceRecord(event);
|
|
1608
|
+
const profileId = readString(payload.profileId) ?? readString(event.metadata?.profileId) ?? readString(payload.benchmarkProfileId) ?? readString(event.metadata?.benchmarkProfileId);
|
|
1609
|
+
if (profileId) {
|
|
1610
|
+
return profileId;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
return options.defaultProfileId;
|
|
1614
|
+
};
|
|
1615
|
+
var readProviderTraceRole = (payload) => readString(payload.kind) ?? readString(payload.role) ?? readString(payload.surface) ?? "provider";
|
|
1616
|
+
var readProviderTraceLatency = (payload) => readNumber2(payload.elapsedMs) ?? readNumber2(payload.latencyMs) ?? readNumber2(payload.durationMs);
|
|
1617
|
+
var readProviderTraceId = (payload) => readString(payload.selectedProvider) ?? readString(payload.provider) ?? readString(payload.model) ?? readString(payload.adapter);
|
|
1618
|
+
var readTraceStatus = (payload) => readString(payload.providerStatus) ?? readString(payload.status);
|
|
1619
|
+
var isFailingTraceStatus = (status) => status === "error" || status === "fail" || status === "failed" || status === "timeout";
|
|
1620
|
+
var summarizeProviderTraceEvidence = (events, maxProviderP95Ms) => {
|
|
1621
|
+
const providerLatencies = new Map;
|
|
1622
|
+
const providerMeta = new Map;
|
|
1623
|
+
for (const event of events) {
|
|
1624
|
+
if (event.type !== "session.error" && event.type !== "provider.decision") {
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
const payload = readTraceRecord(event);
|
|
1628
|
+
const provider = readProviderTraceId(payload);
|
|
1629
|
+
if (!provider) {
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
const role = readProviderTraceRole(payload);
|
|
1633
|
+
const id = `${role}:${provider}`;
|
|
1634
|
+
const latency = readProviderTraceLatency(payload);
|
|
1635
|
+
if (latency !== undefined) {
|
|
1636
|
+
providerLatencies.set(id, [...providerLatencies.get(id) ?? [], latency]);
|
|
1637
|
+
}
|
|
1638
|
+
const existing = providerMeta.get(id);
|
|
1639
|
+
providerMeta.set(id, {
|
|
1640
|
+
failed: existing?.failed === true || isFailingTraceStatus(readTraceStatus(payload)),
|
|
1641
|
+
label: existing?.label ?? `${role.toUpperCase()} ${provider}`,
|
|
1642
|
+
role: existing?.role ?? role
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
return [...providerMeta.entries()].map(([id, meta]) => {
|
|
1646
|
+
const latencies = providerLatencies.get(id) ?? [];
|
|
1647
|
+
const p95Ms = percentile(latencies, 95);
|
|
1648
|
+
return {
|
|
1649
|
+
averageMs: averageNumber(latencies),
|
|
1650
|
+
id,
|
|
1651
|
+
label: meta.label,
|
|
1652
|
+
p50Ms: percentile(latencies, 50),
|
|
1653
|
+
p95Ms,
|
|
1654
|
+
role: meta.role,
|
|
1655
|
+
samples: latencies.length,
|
|
1656
|
+
status: meta.failed || (p95Ms ?? 0) > (maxProviderP95Ms ?? Number.POSITIVE_INFINITY) ? "fail" : latencies.length > 0 ? "pass" : "warn"
|
|
1657
|
+
};
|
|
1658
|
+
});
|
|
1659
|
+
};
|
|
1660
|
+
var summarizeTurnTraceP95 = (events) => {
|
|
1661
|
+
const explicit = events.filter((event) => event.type === "turn_latency.stage").map((event) => {
|
|
1662
|
+
const payload = readTraceRecord(event);
|
|
1663
|
+
return readNumber2(payload.totalMs) ?? readNumber2(payload.elapsedMs) ?? readNumber2(payload.latencyMs);
|
|
1664
|
+
}).filter((value) => value !== undefined);
|
|
1665
|
+
if (explicit.length > 0) {
|
|
1666
|
+
return percentile(explicit, 95);
|
|
1667
|
+
}
|
|
1668
|
+
const turnStages = new Map;
|
|
1669
|
+
for (const event of events) {
|
|
1670
|
+
if (event.type !== "turn_latency.stage" || !event.turnId) {
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1673
|
+
const key = `${event.sessionId}:${event.turnId}`;
|
|
1674
|
+
turnStages.set(key, [...turnStages.get(key) ?? [], event.at]);
|
|
1675
|
+
}
|
|
1676
|
+
const totals = [...turnStages.values()].map((stages) => stages.length < 2 ? undefined : Math.max(...stages) - Math.min(...stages)).filter((value) => value !== undefined);
|
|
1677
|
+
return percentile(totals, 95);
|
|
1678
|
+
};
|
|
1679
|
+
var summarizeRuntimeChannelTraceEvidence = (events) => {
|
|
1680
|
+
const runtimeEvents = events.filter((event) => event.type === "client.browser_media" || event.type === "client.telephony_media" || event.type === "client.barge_in");
|
|
1681
|
+
if (runtimeEvents.length === 0) {
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
const firstAudio = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).firstAudioLatencyMs)).filter((value) => value !== undefined);
|
|
1685
|
+
const jitter = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).jitterMs)).filter((value) => value !== undefined);
|
|
1686
|
+
const timestampDrift = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).timestampDriftMs)).filter((value) => value !== undefined);
|
|
1687
|
+
const backpressure = runtimeEvents.map((event) => readNumber2(readTraceRecord(event).backpressureEvents)).filter((value) => value !== undefined);
|
|
1688
|
+
const interruptions = runtimeEvents.map((event) => {
|
|
1689
|
+
const payload = readTraceRecord(event);
|
|
1690
|
+
return readNumber2(payload.interruptionLatencyMs) ?? readNumber2(payload.interruptionMs) ?? readNumber2(payload.elapsedMs);
|
|
1691
|
+
}).filter((value) => value !== undefined);
|
|
1692
|
+
return {
|
|
1693
|
+
maxBackpressureEvents: maxNumber(backpressure),
|
|
1694
|
+
maxFirstAudioLatencyMs: maxNumber(firstAudio),
|
|
1695
|
+
maxInterruptionP95Ms: percentile(interruptions, 95),
|
|
1696
|
+
maxJitterMs: maxNumber(jitter),
|
|
1697
|
+
maxTimestampDriftMs: maxNumber(timestampDrift),
|
|
1698
|
+
samples: runtimeEvents.length,
|
|
1699
|
+
status: runtimeEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
|
|
1700
|
+
};
|
|
1701
|
+
};
|
|
1702
|
+
var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) => {
|
|
1703
|
+
const sessionFilter = new Set(options.sessionIds ?? []);
|
|
1704
|
+
const eventsBySession = new Map;
|
|
1705
|
+
for (const event of events) {
|
|
1706
|
+
if (sessionFilter.size > 0 && !sessionFilter.has(event.sessionId)) {
|
|
1707
|
+
continue;
|
|
1708
|
+
}
|
|
1709
|
+
eventsBySession.set(event.sessionId, [
|
|
1710
|
+
...eventsBySession.get(event.sessionId) ?? [],
|
|
1711
|
+
event
|
|
1712
|
+
]);
|
|
1713
|
+
}
|
|
1714
|
+
return [...eventsBySession.entries()].map(([
|
|
1715
|
+
sessionId,
|
|
1716
|
+
sessionEvents
|
|
1717
|
+
]) => {
|
|
1718
|
+
const profileId = readTraceProfileId(sessionEvents, options);
|
|
1719
|
+
if (!profileId) {
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
const providers = summarizeProviderTraceEvidence(sessionEvents, options.maxProviderP95Ms);
|
|
1723
|
+
const liveLatencies = sessionEvents.filter((event) => event.type === "client.live_latency").map((event) => {
|
|
1724
|
+
const payload = readTraceRecord(event);
|
|
1725
|
+
return readNumber2(payload.latencyMs) ?? readNumber2(payload.elapsedMs);
|
|
1726
|
+
}).filter((value) => value !== undefined);
|
|
1727
|
+
const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
|
|
1728
|
+
const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
|
|
1729
|
+
const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
|
|
1730
|
+
return {
|
|
1731
|
+
generatedAt: new Date(Math.max(...sessionEvents.map((event) => event.at))).toISOString(),
|
|
1732
|
+
liveP95Ms: percentile(liveLatencies, 95),
|
|
1733
|
+
ok: providers.every((provider) => provider.status !== "fail") && (runtimeChannel?.status ?? "pass") !== "fail",
|
|
1734
|
+
operationsRecordHref: `/voice-operations/${sessionId}`,
|
|
1735
|
+
profileDescription: options.profileDescriptions?.[profileId],
|
|
1736
|
+
profileId,
|
|
1737
|
+
profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
|
|
1738
|
+
providerP95Ms,
|
|
1739
|
+
providers,
|
|
1740
|
+
runtimeChannel,
|
|
1741
|
+
sessionId,
|
|
1742
|
+
turnP95Ms
|
|
1743
|
+
};
|
|
1744
|
+
}).filter((evidence) => evidence !== undefined);
|
|
1745
|
+
};
|
|
1746
|
+
var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
|
|
1590
1747
|
var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.flatMap((report) => report.summary.providers && report.summary.providers.length > 0 ? report.summary.providers : report.cycles.flatMap((cycle) => cycle.providers ?? [])));
|
|
1591
1748
|
var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
|
|
1592
1749
|
var readProofTrendProfileStatus = (profile, budgets) => {
|