@absolutejs/voice 0.0.22-beta.201 → 0.0.22-beta.202
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/evalRoutes.d.ts +6 -0
- package/dist/index.js +29 -8
- package/package.json +1 -1
package/dist/evalRoutes.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type VoiceEvalStatus = 'pass' | 'fail';
|
|
|
5
5
|
export type VoiceEvalSessionReport = {
|
|
6
6
|
endedAt?: number;
|
|
7
7
|
eventCount: number;
|
|
8
|
+
operationsRecordHref?: string;
|
|
8
9
|
quality: VoiceQualityReport;
|
|
9
10
|
scenarioId?: string;
|
|
10
11
|
sessionId: string;
|
|
@@ -81,6 +82,7 @@ export type VoiceScenarioEvalDefinition = {
|
|
|
81
82
|
export type VoiceScenarioEvalSessionResult = {
|
|
82
83
|
eventCount: number;
|
|
83
84
|
issues: string[];
|
|
85
|
+
operationsRecordHref?: string;
|
|
84
86
|
sessionId: string;
|
|
85
87
|
status: VoiceEvalStatus;
|
|
86
88
|
};
|
|
@@ -142,6 +144,7 @@ export type VoiceEvalRoutesOptions = {
|
|
|
142
144
|
links?: VoiceEvalLink[];
|
|
143
145
|
limit?: number;
|
|
144
146
|
name?: string;
|
|
147
|
+
operationsRecordHref?: false | string | ((sessionId: string) => string);
|
|
145
148
|
path?: string;
|
|
146
149
|
scenarios?: VoiceScenarioEvalDefinition[];
|
|
147
150
|
store?: VoiceTraceEventStore;
|
|
@@ -151,17 +154,20 @@ export type VoiceEvalRoutesOptions = {
|
|
|
151
154
|
export declare const runVoiceSessionEvals: (options?: {
|
|
152
155
|
events?: StoredVoiceTraceEvent[];
|
|
153
156
|
limit?: number;
|
|
157
|
+
operationsRecordHref?: false | string | ((sessionId: string) => string);
|
|
154
158
|
store?: VoiceTraceEventStore;
|
|
155
159
|
thresholds?: VoiceQualityThresholds;
|
|
156
160
|
}) => Promise<VoiceEvalReport>;
|
|
157
161
|
export declare const runVoiceScenarioEvals: (options?: {
|
|
158
162
|
events?: StoredVoiceTraceEvent[];
|
|
163
|
+
operationsRecordHref?: false | string | ((sessionId: string) => string);
|
|
159
164
|
scenarios?: VoiceScenarioEvalDefinition[];
|
|
160
165
|
store?: VoiceTraceEventStore;
|
|
161
166
|
}) => Promise<VoiceScenarioEvalReport>;
|
|
162
167
|
export declare const runVoiceScenarioFixtureEvals: (options?: {
|
|
163
168
|
fixtures?: VoiceScenarioFixture[];
|
|
164
169
|
fixtureStore?: VoiceScenarioFixtureStore;
|
|
170
|
+
operationsRecordHref?: false | string | ((sessionId: string) => string);
|
|
165
171
|
scenarios?: VoiceScenarioEvalDefinition[];
|
|
166
172
|
}) => Promise<VoiceScenarioFixtureEvalReport>;
|
|
167
173
|
export declare const compareVoiceEvalBaseline: (currentReport: VoiceEvalReport, baselineReport: VoiceEvalReport, options?: VoiceEvalBaselineComparisonOptions) => VoiceEvalBaselineComparison;
|
package/dist/index.js
CHANGED
|
@@ -13573,6 +13573,17 @@ var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "&l
|
|
|
13573
13573
|
var rate2 = (count, total) => count / Math.max(1, total);
|
|
13574
13574
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
13575
13575
|
var getString9 = (value) => typeof value === "string" ? value : undefined;
|
|
13576
|
+
var resolveSessionHref = (value, sessionId) => {
|
|
13577
|
+
if (value === false) {
|
|
13578
|
+
return;
|
|
13579
|
+
}
|
|
13580
|
+
const href = value ?? "/voice-operations/:sessionId";
|
|
13581
|
+
if (typeof href === "function") {
|
|
13582
|
+
return href(sessionId);
|
|
13583
|
+
}
|
|
13584
|
+
const encoded = encodeURIComponent(sessionId);
|
|
13585
|
+
return href.includes(":sessionId") ? href.replace(":sessionId", encoded) : `${href.replace(/\/$/, "")}/${encoded}`;
|
|
13586
|
+
};
|
|
13576
13587
|
var getObject = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
13577
13588
|
var getPathValue = (value, path) => {
|
|
13578
13589
|
let current = value;
|
|
@@ -13638,6 +13649,7 @@ var runVoiceSessionEvals = async (options = {}) => {
|
|
|
13638
13649
|
return {
|
|
13639
13650
|
endedAt,
|
|
13640
13651
|
eventCount: sorted.length,
|
|
13652
|
+
operationsRecordHref: resolveSessionHref(options.operationsRecordHref, sessionId),
|
|
13641
13653
|
quality,
|
|
13642
13654
|
scenarioId,
|
|
13643
13655
|
sessionId,
|
|
@@ -13662,7 +13674,7 @@ var runVoiceSessionEvals = async (options = {}) => {
|
|
|
13662
13674
|
var getSessionText = (events, type) => events.filter((event) => event.type === type).map((event) => getString9(event.payload.text)).filter((text) => Boolean(text?.trim())).join(`
|
|
13663
13675
|
`);
|
|
13664
13676
|
var countProviderErrors = (events) => events.filter((event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.provider === "string")).length;
|
|
13665
|
-
var evaluateScenarioSession = (scenario, sessionId, events) => {
|
|
13677
|
+
var evaluateScenarioSession = (scenario, sessionId, events, operationsRecordHref) => {
|
|
13666
13678
|
const issues = [];
|
|
13667
13679
|
const committedText = getSessionText(events, "turn.committed");
|
|
13668
13680
|
const assistantText = getSessionText(events, "turn.assistant");
|
|
@@ -13729,6 +13741,7 @@ var evaluateScenarioSession = (scenario, sessionId, events) => {
|
|
|
13729
13741
|
return {
|
|
13730
13742
|
eventCount: events.length,
|
|
13731
13743
|
issues,
|
|
13744
|
+
operationsRecordHref,
|
|
13732
13745
|
sessionId,
|
|
13733
13746
|
status: issues.length > 0 ? "fail" : "pass"
|
|
13734
13747
|
};
|
|
@@ -13741,7 +13754,7 @@ var runVoiceScenarioEvals = async (options = {}) => {
|
|
|
13741
13754
|
grouped.set(event.sessionId, [...grouped.get(event.sessionId) ?? [], event]);
|
|
13742
13755
|
}
|
|
13743
13756
|
const results = scenarios.map((scenario) => {
|
|
13744
|
-
const sessions = [...grouped.entries()].filter(([, sessionEvents]) => scenario.scenarioId ? sessionEvents.some((event) => event.scenarioId === scenario.scenarioId) : true).map(([sessionId, sessionEvents]) => evaluateScenarioSession(scenario, sessionId, filterVoiceTraceEvents(sessionEvents))).sort((left, right) => left.sessionId.localeCompare(right.sessionId));
|
|
13757
|
+
const sessions = [...grouped.entries()].filter(([, sessionEvents]) => scenario.scenarioId ? sessionEvents.some((event) => event.scenarioId === scenario.scenarioId) : true).map(([sessionId, sessionEvents]) => evaluateScenarioSession(scenario, sessionId, filterVoiceTraceEvents(sessionEvents), resolveSessionHref(options.operationsRecordHref, sessionId))).sort((left, right) => left.sessionId.localeCompare(right.sessionId));
|
|
13745
13758
|
const issues = [];
|
|
13746
13759
|
const minSessions = scenario.minSessions ?? 1;
|
|
13747
13760
|
if (sessions.length < minSessions) {
|
|
@@ -13778,6 +13791,7 @@ var runVoiceScenarioFixtureEvals = async (options = {}) => {
|
|
|
13778
13791
|
const results = await Promise.all(fixtures.map(async (fixture) => {
|
|
13779
13792
|
const report = await runVoiceScenarioEvals({
|
|
13780
13793
|
events: fixture.events,
|
|
13794
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
13781
13795
|
scenarios: options.scenarios
|
|
13782
13796
|
});
|
|
13783
13797
|
return {
|
|
@@ -13902,7 +13916,8 @@ var renderVoiceEvalHTML = (report, options = {}) => {
|
|
|
13902
13916
|
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${escapeHtml20(bucket.key)}</td><td>${bucket.total}</td><td>${bucket.passed}</td><td>${bucket.failed}</td></tr>`).join("") : '<tr><td colspan="4">No eval buckets yet.</td></tr>';
|
|
13903
13917
|
const sessions = report.sessions.length ? report.sessions.map((session) => {
|
|
13904
13918
|
const failedMetrics = Object.entries(session.quality.metrics).filter(([, metric]) => !metric.pass).map(([, metric]) => metric.label).join(", ");
|
|
13905
|
-
|
|
13919
|
+
const sessionLabel = session.operationsRecordHref ? `<a href="${escapeHtml20(session.operationsRecordHref)}">${escapeHtml20(session.sessionId)}</a>` : escapeHtml20(session.sessionId);
|
|
13920
|
+
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${escapeHtml20(session.status)}</td><td>${session.eventCount}</td><td>${session.summary.turnCount}</td><td>${session.summary.errorCount}</td><td>${escapeHtml20(formatTime(session.endedAt))}</td><td>${escapeHtml20(failedMetrics || "none")}</td></tr>`;
|
|
13906
13921
|
}).join("") : '<tr><td colspan="7">No sessions found.</td></tr>';
|
|
13907
13922
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{color:#166534}.fail{color:#991b1b}.status.pass{background:#dcfce7}.status.fail{background:#fee2e2}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,.primitive{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3;margin:1rem 0}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.card strong{display:block;font-size:2rem}table{border-collapse:collapse;background:white;width:100%;margin:1rem 0 2rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml20(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${renderVoiceEvalPrimitiveCopy()}<h2>Trend</h2><table><thead><tr><th>Day</th><th>Total</th><th>Passed</th><th>Failed</th></tr></thead><tbody>${trend}</tbody></table><h2>Session Eval Results</h2><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Last event</th><th>Failed metrics</th></tr></thead><tbody>${sessions}</tbody></table></main></body></html>`;
|
|
13908
13923
|
};
|
|
@@ -13919,7 +13934,10 @@ var renderVoiceScenarioEvalHTML = (report, options = {}) => {
|
|
|
13919
13934
|
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml20(link.href)}">${escapeHtml20(link.label)}</a>`).join("")}</nav>` : "";
|
|
13920
13935
|
const scenarios = report.scenarios.length ? report.scenarios.map((scenario) => {
|
|
13921
13936
|
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${escapeHtml20(issue)}</li>`).join("")}</ul>` : "";
|
|
13922
|
-
const sessions = scenario.sessions.length ? scenario.sessions.map((session) =>
|
|
13937
|
+
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => {
|
|
13938
|
+
const sessionLabel = session.operationsRecordHref ? `<a href="${escapeHtml20(session.operationsRecordHref)}">${escapeHtml20(session.sessionId)}</a>` : escapeHtml20(session.sessionId);
|
|
13939
|
+
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${escapeHtml20(session.status)}</td><td>${session.eventCount}</td><td>${escapeHtml20(session.issues.join(", ") || "none")}</td></tr>`;
|
|
13940
|
+
}).join("") : '<tr><td colspan="4">No matching sessions.</td></tr>';
|
|
13923
13941
|
return `<section class="scenario ${scenario.status}"><h2>${escapeHtml20(scenario.label)}</h2>${scenario.description ? `<p>${escapeHtml20(scenario.description)}</p>` : ""}<p class="status ${scenario.status}">${scenario.status}</p><p>${scenario.passed} passed, ${scenario.failed} failed, ${scenario.matchedSessions} matched.</p>${scenarioIssues}<table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Issues</th></tr></thead><tbody>${sessions}</tbody></table></section>`;
|
|
13924
13942
|
}).join("") : "<section><p>No scenarios configured.</p></section>";
|
|
13925
13943
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml20(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${renderVoiceEvalPrimitiveCopy()}${scenarios}</main></body></html>`;
|
|
@@ -13941,6 +13959,7 @@ var createVoiceEvalRoutes = (options) => {
|
|
|
13941
13959
|
const getReport = () => runVoiceSessionEvals({
|
|
13942
13960
|
events: options.events,
|
|
13943
13961
|
limit: options.limit,
|
|
13962
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
13944
13963
|
store: options.store,
|
|
13945
13964
|
thresholds: options.thresholds
|
|
13946
13965
|
});
|
|
@@ -13951,12 +13970,14 @@ var createVoiceEvalRoutes = (options) => {
|
|
|
13951
13970
|
};
|
|
13952
13971
|
const getScenarioReport = () => runVoiceScenarioEvals({
|
|
13953
13972
|
events: options.events,
|
|
13973
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
13954
13974
|
scenarios: options.scenarios,
|
|
13955
13975
|
store: options.store
|
|
13956
13976
|
});
|
|
13957
13977
|
const getFixtureReport = () => runVoiceScenarioFixtureEvals({
|
|
13958
13978
|
fixtures: options.fixtures,
|
|
13959
13979
|
fixtureStore: options.fixtureStore,
|
|
13980
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
13960
13981
|
scenarios: options.scenarios
|
|
13961
13982
|
});
|
|
13962
13983
|
routes.get(path, async () => {
|
|
@@ -15170,7 +15191,7 @@ var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "&l
|
|
|
15170
15191
|
var increment4 = (record, key) => {
|
|
15171
15192
|
record[key] = (record[key] ?? 0) + 1;
|
|
15172
15193
|
};
|
|
15173
|
-
var
|
|
15194
|
+
var resolveSessionHref2 = (value, session) => {
|
|
15174
15195
|
if (value === false) {
|
|
15175
15196
|
return;
|
|
15176
15197
|
}
|
|
@@ -15334,7 +15355,7 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
15334
15355
|
const replayHref = options.replayHref === false ? "" : typeof options.replayHref === "function" ? options.replayHref(item) : `${options.replayHref ?? "/api/voice-sessions"}/${encodeURIComponent(sessionId)}/replay/htmx`;
|
|
15335
15356
|
return {
|
|
15336
15357
|
...item,
|
|
15337
|
-
operationsRecordHref:
|
|
15358
|
+
operationsRecordHref: resolveSessionHref2(options.operationsRecordHref, item),
|
|
15338
15359
|
replayHref
|
|
15339
15360
|
};
|
|
15340
15361
|
});
|
|
@@ -23959,7 +23980,7 @@ var eventStatus = (event) => firstString3(event.payload, [
|
|
|
23959
23980
|
"reason"
|
|
23960
23981
|
]);
|
|
23961
23982
|
var eventElapsedMs2 = (event) => firstNumber3(event.payload, ["elapsedMs", "latencyMs", "durationMs"]);
|
|
23962
|
-
var
|
|
23983
|
+
var resolveSessionHref3 = (value, sessionId) => {
|
|
23963
23984
|
if (value === false) {
|
|
23964
23985
|
return;
|
|
23965
23986
|
}
|
|
@@ -24089,7 +24110,7 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
24089
24110
|
type: event.type
|
|
24090
24111
|
})),
|
|
24091
24112
|
lastEventAt: sorted.at(-1)?.at,
|
|
24092
|
-
operationsRecordHref:
|
|
24113
|
+
operationsRecordHref: resolveSessionHref3(options.operationsRecordHref, sessionId),
|
|
24093
24114
|
providers: summarizeProviders(sorted),
|
|
24094
24115
|
sessionId,
|
|
24095
24116
|
startedAt: summary.startedAt,
|