@absolutejs/voice 0.0.22-beta.201 → 0.0.22-beta.203
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 +37 -11
- package/dist/simulationSuite.d.ts +1 -0
- 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 () => {
|
|
@@ -14684,7 +14705,7 @@ var collectSimulationActions = (input) => {
|
|
|
14684
14705
|
const firstFailed = input.sessions.sessions.find((session) => session.status === "fail");
|
|
14685
14706
|
actions.push({
|
|
14686
14707
|
description: firstFailed ? `Inspect session ${firstFailed.sessionId}; at least one quality metric is outside threshold.` : "Inspect failing session quality reports.",
|
|
14687
|
-
href: input.links?.sessions,
|
|
14708
|
+
href: firstFailed?.operationsRecordHref ?? input.links?.sessions,
|
|
14688
14709
|
label: "Review failing session quality",
|
|
14689
14710
|
section: "sessions",
|
|
14690
14711
|
severity: "error"
|
|
@@ -14695,9 +14716,10 @@ var collectSimulationActions = (input) => {
|
|
|
14695
14716
|
continue;
|
|
14696
14717
|
}
|
|
14697
14718
|
const issue = scenario.issues[0] ?? scenario.sessions.find((session) => session.issues.length > 0)?.issues[0] ?? "Scenario did not meet its expected trace conditions.";
|
|
14719
|
+
const failedSession = scenario.sessions.find((session) => session.status === "fail");
|
|
14698
14720
|
actions.push({
|
|
14699
14721
|
description: `${scenario.label}: ${issue}`,
|
|
14700
|
-
href: input.links?.scenarios,
|
|
14722
|
+
href: failedSession?.operationsRecordHref ?? input.links?.scenarios,
|
|
14701
14723
|
label: `Fix scenario ${scenario.label}`,
|
|
14702
14724
|
section: "scenarios",
|
|
14703
14725
|
severity: "error"
|
|
@@ -14708,9 +14730,10 @@ var collectSimulationActions = (input) => {
|
|
|
14708
14730
|
continue;
|
|
14709
14731
|
}
|
|
14710
14732
|
const failedScenario = fixture.report.scenarios.find((scenario) => scenario.status === "fail");
|
|
14733
|
+
const failedSession = failedScenario?.sessions.find((session) => session.status === "fail");
|
|
14711
14734
|
actions.push({
|
|
14712
14735
|
description: failedScenario ? `${fixture.label}: ${failedScenario.label} failed.` : `${fixture.label}: fixture simulation failed.`,
|
|
14713
|
-
href: input.links?.fixtures,
|
|
14736
|
+
href: failedSession?.operationsRecordHref ?? input.links?.fixtures,
|
|
14714
14737
|
label: `Update fixture ${fixture.label}`,
|
|
14715
14738
|
section: "fixtures",
|
|
14716
14739
|
severity: "error"
|
|
@@ -14753,16 +14776,19 @@ var runVoiceSimulationSuite = async (options) => {
|
|
|
14753
14776
|
const [sessions, scenarios, fixtures, tools, outcomes] = await Promise.all([
|
|
14754
14777
|
shouldRunSessions ? runVoiceSessionEvals({
|
|
14755
14778
|
limit: options.limit,
|
|
14779
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
14756
14780
|
store: options.store,
|
|
14757
14781
|
thresholds: options.thresholds
|
|
14758
14782
|
}) : undefined,
|
|
14759
14783
|
shouldRunScenarios ? runVoiceScenarioEvals({
|
|
14784
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
14760
14785
|
scenarios: options.scenarios,
|
|
14761
14786
|
store: options.store
|
|
14762
14787
|
}) : undefined,
|
|
14763
14788
|
shouldRunFixtures ? runVoiceScenarioFixtureEvals({
|
|
14764
14789
|
fixtures: options.fixtures,
|
|
14765
14790
|
fixtureStore: options.fixtureStore,
|
|
14791
|
+
operationsRecordHref: options.operationsRecordHref,
|
|
14766
14792
|
scenarios: options.scenarios
|
|
14767
14793
|
}) : undefined,
|
|
14768
14794
|
shouldRunTools ? runVoiceToolContractSuite({
|
|
@@ -15170,7 +15196,7 @@ var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "&l
|
|
|
15170
15196
|
var increment4 = (record, key) => {
|
|
15171
15197
|
record[key] = (record[key] ?? 0) + 1;
|
|
15172
15198
|
};
|
|
15173
|
-
var
|
|
15199
|
+
var resolveSessionHref2 = (value, session) => {
|
|
15174
15200
|
if (value === false) {
|
|
15175
15201
|
return;
|
|
15176
15202
|
}
|
|
@@ -15334,7 +15360,7 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
15334
15360
|
const replayHref = options.replayHref === false ? "" : typeof options.replayHref === "function" ? options.replayHref(item) : `${options.replayHref ?? "/api/voice-sessions"}/${encodeURIComponent(sessionId)}/replay/htmx`;
|
|
15335
15361
|
return {
|
|
15336
15362
|
...item,
|
|
15337
|
-
operationsRecordHref:
|
|
15363
|
+
operationsRecordHref: resolveSessionHref2(options.operationsRecordHref, item),
|
|
15338
15364
|
replayHref
|
|
15339
15365
|
};
|
|
15340
15366
|
});
|
|
@@ -23959,7 +23985,7 @@ var eventStatus = (event) => firstString3(event.payload, [
|
|
|
23959
23985
|
"reason"
|
|
23960
23986
|
]);
|
|
23961
23987
|
var eventElapsedMs2 = (event) => firstNumber3(event.payload, ["elapsedMs", "latencyMs", "durationMs"]);
|
|
23962
|
-
var
|
|
23988
|
+
var resolveSessionHref3 = (value, sessionId) => {
|
|
23963
23989
|
if (value === false) {
|
|
23964
23990
|
return;
|
|
23965
23991
|
}
|
|
@@ -24089,7 +24115,7 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
24089
24115
|
type: event.type
|
|
24090
24116
|
})),
|
|
24091
24117
|
lastEventAt: sorted.at(-1)?.at,
|
|
24092
|
-
operationsRecordHref:
|
|
24118
|
+
operationsRecordHref: resolveSessionHref3(options.operationsRecordHref, sessionId),
|
|
24093
24119
|
providers: summarizeProviders(sorted),
|
|
24094
24120
|
sessionId,
|
|
24095
24121
|
startedAt: summary.startedAt,
|
|
@@ -57,6 +57,7 @@ export type VoiceSimulationSuiteOptions<TSession extends VoiceSessionRecord = Vo
|
|
|
57
57
|
tools?: boolean;
|
|
58
58
|
};
|
|
59
59
|
limit?: number;
|
|
60
|
+
operationsRecordHref?: false | string | ((sessionId: string) => string);
|
|
60
61
|
outcomes?: Omit<VoiceOutcomeContractOptions<TSession>, 'contracts'> & {
|
|
61
62
|
contracts: VoiceOutcomeContractDefinition[];
|
|
62
63
|
};
|