@botpress/evals 1.19.0-beta.9 → 2.0.0-alpha.0
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/client.d.ts +21 -9
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +104 -75
- package/dist/client.js.map +4 -4
- package/dist/definition.d.ts +9 -2
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js.map +2 -2
- package/dist/graders/index.js +21 -74
- package/dist/graders/index.js.map +2 -2
- package/dist/graders/llm.d.ts +12 -12
- package/dist/graders/llm.d.ts.map +1 -1
- package/dist/graders/llm.js +30 -75
- package/dist/graders/llm.js.map +2 -2
- package/dist/graders/response.js +16 -65
- package/dist/graders/response.js.map +2 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +3 -3
- package/dist/loader.js +1 -39
- package/dist/loader.js.map +3 -3
- package/dist/manifest.d.ts +13 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/runner.d.ts +5 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +318 -227
- package/dist/runner.js.map +4 -4
- package/dist/spans/index.d.ts +5 -0
- package/dist/spans/index.d.ts.map +1 -0
- package/dist/spans/index.js +687 -0
- package/dist/spans/index.js.map +7 -0
- package/dist/spans/local-span-source.d.ts +19 -0
- package/dist/spans/local-span-source.d.ts.map +1 -0
- package/dist/spans/local-span-source.js +505 -0
- package/dist/spans/local-span-source.js.map +7 -0
- package/dist/spans/span-source.d.ts +40 -0
- package/dist/spans/span-source.d.ts.map +1 -0
- package/dist/spans/span-source.js +1 -0
- package/dist/{sse-collector.d.ts → spans/sse-collector.d.ts} +44 -5
- package/dist/spans/sse-collector.d.ts.map +1 -0
- package/dist/{sse-collector.js → spans/sse-collector.js} +98 -59
- package/dist/spans/sse-collector.js.map +7 -0
- package/dist/spans/trace.d.ts.map +1 -0
- package/dist/spans/trace.js.map +7 -0
- package/dist/spans/vortex-span-source.d.ts +42 -0
- package/dist/spans/vortex-span-source.d.ts.map +1 -0
- package/dist/spans/vortex-span-source.js +184 -0
- package/dist/spans/vortex-span-source.js.map +7 -0
- package/dist/stores/eval-definition-loader.d.ts +4 -0
- package/dist/stores/eval-definition-loader.d.ts.map +1 -0
- package/dist/stores/eval-definition-loader.js +104 -0
- package/dist/stores/eval-definition-loader.js.map +7 -0
- package/dist/stores/eval-store.d.ts +61 -0
- package/dist/stores/eval-store.d.ts.map +1 -0
- package/dist/stores/eval-store.js +1 -0
- package/dist/stores/eval-store.js.map +7 -0
- package/dist/stores/index.d.ts +8 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +1118 -0
- package/dist/stores/index.js.map +7 -0
- package/dist/stores/local-eval-store.d.ts +66 -0
- package/dist/stores/local-eval-store.d.ts.map +1 -0
- package/dist/stores/local-eval-store.js +557 -0
- package/dist/stores/local-eval-store.js.map +7 -0
- package/dist/stores/vortex-eval-store-entry.d.ts +6 -0
- package/dist/stores/vortex-eval-store-entry.d.ts.map +1 -0
- package/dist/stores/vortex-eval-store-entry.js +664 -0
- package/dist/stores/vortex-eval-store-entry.js.map +7 -0
- package/dist/stores/vortex-eval-store.d.ts +78 -0
- package/dist/stores/vortex-eval-store.d.ts.map +1 -0
- package/dist/stores/vortex-eval-store.js +562 -0
- package/dist/stores/vortex-eval-store.js.map +7 -0
- package/dist/transformer.d.ts +2 -2
- package/dist/transformer.d.ts.map +1 -1
- package/dist/transformer.js +1 -1
- package/dist/transformer.js.map +2 -2
- package/dist/types.d.ts +38 -33
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +2 -2
- package/package.json +66 -6
- package/dist/sse-collector.d.ts.map +0 -1
- package/dist/sse-collector.js.map +0 -7
- package/dist/trace.d.ts.map +0 -1
- /package/dist/{trace.js.map → spans/span-source.js.map} +0 -0
- /package/dist/{trace.d.ts → spans/trace.d.ts} +0 -0
- /package/dist/{trace.js → spans/trace.js} +0 -0
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
// src/stores/vortex-eval-store.ts
|
|
2
|
+
var OUTCOME_TURN_INDEX = -1;
|
|
3
|
+
function vortexEntryToEvalReport(entry) {
|
|
4
|
+
const outcomeAssertions = entry.results.filter((r) => r.turnIndex === OUTCOME_TURN_INDEX).map((r) => ({
|
|
5
|
+
assertion: r.graderName,
|
|
6
|
+
pass: r.passed,
|
|
7
|
+
expected: r.evidence?.expected ?? "",
|
|
8
|
+
actual: r.evidence?.actual ?? ""
|
|
9
|
+
}));
|
|
10
|
+
const resultsByTurn = /* @__PURE__ */ new Map();
|
|
11
|
+
for (const r of entry.results) {
|
|
12
|
+
if (r.turnIndex === OUTCOME_TURN_INDEX) continue;
|
|
13
|
+
const existing = resultsByTurn.get(r.turnIndex) ?? [];
|
|
14
|
+
existing.push(r);
|
|
15
|
+
resultsByTurn.set(r.turnIndex, existing);
|
|
16
|
+
}
|
|
17
|
+
const turns = [...resultsByTurn.entries()].sort(([a], [b]) => a - b).map(([turnIndex, results]) => {
|
|
18
|
+
const assertions = results.map((r) => ({
|
|
19
|
+
assertion: r.graderName,
|
|
20
|
+
pass: r.passed,
|
|
21
|
+
expected: r.evidence?.expected ?? "",
|
|
22
|
+
actual: r.evidence?.actual ?? ""
|
|
23
|
+
}));
|
|
24
|
+
const first = results[0];
|
|
25
|
+
return {
|
|
26
|
+
turnIndex,
|
|
27
|
+
userMessage: first.userMessage,
|
|
28
|
+
botResponse: first.botResponse,
|
|
29
|
+
assertions,
|
|
30
|
+
pass: assertions.every((a) => a.pass),
|
|
31
|
+
botDuration: first.botDurationMs ?? 0,
|
|
32
|
+
evalDuration: first.graderDurationMs ?? 0
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
name: entry.evalName,
|
|
37
|
+
description: entry.description || void 0,
|
|
38
|
+
type: entry.evalType,
|
|
39
|
+
tags: entry.tags,
|
|
40
|
+
turns,
|
|
41
|
+
outcomeAssertions,
|
|
42
|
+
pass: entry.passed ?? false,
|
|
43
|
+
duration: entry.durationMs ?? 0,
|
|
44
|
+
error: entry.error ?? void 0
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function mapWithConcurrency(items, concurrency, fn) {
|
|
48
|
+
const results = new Array(items.length);
|
|
49
|
+
let next = 0;
|
|
50
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
51
|
+
while (next < items.length) {
|
|
52
|
+
const i = next++;
|
|
53
|
+
results[i] = await fn(items[i]);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
await Promise.all(workers);
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
function turnToResultRows(turn) {
|
|
60
|
+
return graderResultsToRows(turn.assertions, {
|
|
61
|
+
turnIndex: turn.turnIndex,
|
|
62
|
+
userMessage: turn.userMessage,
|
|
63
|
+
botResponse: turn.botResponse,
|
|
64
|
+
botDurationMs: turn.botDuration,
|
|
65
|
+
graderDurationMs: turn.evalDuration
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function outcomeToResultRows(assertions) {
|
|
69
|
+
return graderResultsToRows(assertions, {
|
|
70
|
+
turnIndex: OUTCOME_TURN_INDEX,
|
|
71
|
+
userMessage: "",
|
|
72
|
+
botResponse: "",
|
|
73
|
+
botDurationMs: 0,
|
|
74
|
+
graderDurationMs: 0
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function graderResultsToRows(assertions, context) {
|
|
78
|
+
return assertions.map((assertion) => ({
|
|
79
|
+
...context,
|
|
80
|
+
graderName: assertion.assertion,
|
|
81
|
+
passed: assertion.pass,
|
|
82
|
+
evidence: { expected: assertion.expected, actual: assertion.actual }
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
function runMatchesWatchOptions(run, options) {
|
|
86
|
+
if (options.runId && run.id !== options.runId) return false;
|
|
87
|
+
if (options.workflowId && run.metadata?.workflowId !== options.workflowId) return false;
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
function vortexRunToReport(run) {
|
|
91
|
+
const evals = run.entries.map(vortexEntryToEvalReport);
|
|
92
|
+
return {
|
|
93
|
+
id: run.id,
|
|
94
|
+
timestamp: run.createdAt,
|
|
95
|
+
evals,
|
|
96
|
+
passed: evals.filter((e) => e.pass).length,
|
|
97
|
+
failed: evals.filter((e) => !e.pass).length,
|
|
98
|
+
total: evals.length,
|
|
99
|
+
duration: evals.reduce((s, e) => s + e.duration, 0)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function vortexRunToSummary(run) {
|
|
103
|
+
const startMs = run.startedAt ? new Date(run.startedAt).getTime() : 0;
|
|
104
|
+
const endMs = run.completedAt ? new Date(run.completedAt).getTime() : 0;
|
|
105
|
+
if (run.entries) {
|
|
106
|
+
const evals = run.entries.map(vortexEntryToEvalReport);
|
|
107
|
+
return {
|
|
108
|
+
id: run.id,
|
|
109
|
+
timestamp: run.createdAt,
|
|
110
|
+
passed: evals.filter((e) => e.pass).length,
|
|
111
|
+
failed: evals.filter((e) => !e.pass).length,
|
|
112
|
+
total: evals.length,
|
|
113
|
+
duration: evals.reduce((s, e) => s + e.duration, 0),
|
|
114
|
+
botDuration: evals.reduce((s, e) => e.turns.reduce((ts, t) => ts + t.botDuration, s), 0),
|
|
115
|
+
evalDuration: evals.reduce((s, e) => e.turns.reduce((ts, t) => ts + t.evalDuration, s), 0),
|
|
116
|
+
evalNames: evals.map((e) => e.name),
|
|
117
|
+
aborted: false
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
id: run.id,
|
|
122
|
+
timestamp: run.createdAt,
|
|
123
|
+
passed: 0,
|
|
124
|
+
failed: 0,
|
|
125
|
+
total: 0,
|
|
126
|
+
duration: startMs && endMs ? endMs - startMs : 0,
|
|
127
|
+
botDuration: 0,
|
|
128
|
+
evalDuration: 0,
|
|
129
|
+
evalNames: [],
|
|
130
|
+
aborted: false
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
var VortexEvalStore = class {
|
|
134
|
+
url;
|
|
135
|
+
botId;
|
|
136
|
+
workspaceId;
|
|
137
|
+
token;
|
|
138
|
+
_evalManifestId;
|
|
139
|
+
_loadEvalDefinitions;
|
|
140
|
+
constructor(config) {
|
|
141
|
+
this.url = config.url.replace(/\/$/, "");
|
|
142
|
+
this.botId = config.botId;
|
|
143
|
+
if (config.workspaceId) this.workspaceId = config.workspaceId;
|
|
144
|
+
if (config.token) this.token = config.token;
|
|
145
|
+
this._evalManifestId = config.evalManifestId;
|
|
146
|
+
this._loadEvalDefinitions = config.loadEvalDefinitions;
|
|
147
|
+
}
|
|
148
|
+
// ── Eval definitions (from Files API manifest) ───────────────────────
|
|
149
|
+
async listEvals(filter) {
|
|
150
|
+
const defs = await this.getEvalDefinitions();
|
|
151
|
+
const filtered = defs.filter((e) => {
|
|
152
|
+
if (filter?.names && !filter.names.includes(e.name)) return false;
|
|
153
|
+
if (filter?.type && e.type !== filter.type) return false;
|
|
154
|
+
if (filter?.tags && !filter.tags.every((tag) => e.tags?.includes(tag))) return false;
|
|
155
|
+
return true;
|
|
156
|
+
});
|
|
157
|
+
return filtered.map((e) => ({
|
|
158
|
+
name: e.name,
|
|
159
|
+
description: e.description,
|
|
160
|
+
tags: e.tags || [],
|
|
161
|
+
type: e.type || "capability",
|
|
162
|
+
turnCount: e.conversation.length,
|
|
163
|
+
hasOutcome: !!e.outcome
|
|
164
|
+
}));
|
|
165
|
+
}
|
|
166
|
+
async getEval(name) {
|
|
167
|
+
const defs = await this.getEvalDefinitions();
|
|
168
|
+
return defs.find((e) => e.name === name) ?? null;
|
|
169
|
+
}
|
|
170
|
+
// ── Run lifecycle — write ───────────────────────────────────────────
|
|
171
|
+
async createRun(runType = "scheduled", metadata) {
|
|
172
|
+
if (!this.workspaceId) {
|
|
173
|
+
throw new Error("workspaceId is required to create Vortex eval runs");
|
|
174
|
+
}
|
|
175
|
+
const triggerType = runType === "manual" ? "manual" : "scheduled";
|
|
176
|
+
const res = await this.fetch(`/v1/evals/bot/${this.botId}/runs`, {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: { "Content-Type": "application/json" },
|
|
179
|
+
body: JSON.stringify({
|
|
180
|
+
evalManifestId: (typeof this._evalManifestId === "function" ? this._evalManifestId() : this._evalManifestId) ?? "",
|
|
181
|
+
workspaceId: this.workspaceId,
|
|
182
|
+
triggerType,
|
|
183
|
+
...metadata ? { metadata } : {}
|
|
184
|
+
})
|
|
185
|
+
});
|
|
186
|
+
if (!res.ok) {
|
|
187
|
+
const text = await res.text().catch(() => "");
|
|
188
|
+
throw new Error(`Vortex create run failed: ${res.status} ${text}`);
|
|
189
|
+
}
|
|
190
|
+
const { id } = await res.json();
|
|
191
|
+
return id;
|
|
192
|
+
}
|
|
193
|
+
async addRunResults(runId, evalReport) {
|
|
194
|
+
const entry = {
|
|
195
|
+
evalName: evalReport.name,
|
|
196
|
+
evalType: evalReport.type ?? "capability",
|
|
197
|
+
description: evalReport.description ?? "",
|
|
198
|
+
tags: evalReport.tags ?? [],
|
|
199
|
+
passed: evalReport.pass,
|
|
200
|
+
durationMs: evalReport.duration,
|
|
201
|
+
error: evalReport.error,
|
|
202
|
+
results: [...evalReport.turns.flatMap(turnToResultRows), ...outcomeToResultRows(evalReport.outcomeAssertions)]
|
|
203
|
+
};
|
|
204
|
+
const res = await this.fetch(`/v1/evals/runs/${runId}/entries`, {
|
|
205
|
+
method: "POST",
|
|
206
|
+
headers: { "Content-Type": "application/json" },
|
|
207
|
+
body: JSON.stringify({ entries: [entry] })
|
|
208
|
+
});
|
|
209
|
+
if (!res.ok) {
|
|
210
|
+
const text = await res.text().catch(() => "");
|
|
211
|
+
throw new Error(`Vortex ingest entries failed: ${res.status} ${text}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async completeRun(runId, report) {
|
|
215
|
+
const hasRunError = report.aborted === true || report.evals.some((e) => e.error !== void 0);
|
|
216
|
+
const res = await this.fetch(`/v1/evals/runs/${runId}/entries`, {
|
|
217
|
+
method: "POST",
|
|
218
|
+
headers: { "Content-Type": "application/json" },
|
|
219
|
+
body: JSON.stringify({
|
|
220
|
+
entries: [],
|
|
221
|
+
completed: !hasRunError,
|
|
222
|
+
failed: hasRunError
|
|
223
|
+
})
|
|
224
|
+
});
|
|
225
|
+
if (!res.ok) {
|
|
226
|
+
const text = await res.text().catch(() => "");
|
|
227
|
+
throw new Error(`Vortex complete run failed: ${res.status} ${text}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// ── Per-turn incremental lifecycle (Option B) ──
|
|
231
|
+
//
|
|
232
|
+
// The eval-runner workflow builds each entry up as the eval runs (rather than
|
|
233
|
+
// writing it whole at the end), so the dev console can stream turn-by-turn
|
|
234
|
+
// progress. This is the prod ingestion path and requires Vortex to provide:
|
|
235
|
+
// startEntry POST /runs/{runId}/entries → { entries:[{ id }] } (passed:null, in-progress)
|
|
236
|
+
// appendTurnResults POST /runs/{runId}/entries/{entryId}/results (idempotent upsert by turnIndex+graderName)
|
|
237
|
+
// finalizeEntry PATCH /runs/{runId}/entries/{entryId} (sets the verdict)
|
|
238
|
+
// markRunComplete POST /runs/{runId}/complete (status → completed|failed)
|
|
239
|
+
/** Create an in-progress entry (metadata only, no results) and return its server id. */
|
|
240
|
+
async startEntry(runId, meta) {
|
|
241
|
+
const res = await this.fetch(`/v1/evals/runs/${runId}/entries`, {
|
|
242
|
+
method: "POST",
|
|
243
|
+
headers: { "Content-Type": "application/json" },
|
|
244
|
+
body: JSON.stringify({
|
|
245
|
+
entries: [
|
|
246
|
+
{
|
|
247
|
+
evalName: meta.evalName,
|
|
248
|
+
evalType: meta.evalType ?? "capability",
|
|
249
|
+
description: meta.description ?? "",
|
|
250
|
+
tags: meta.tags ?? []
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
})
|
|
254
|
+
});
|
|
255
|
+
if (!res.ok) {
|
|
256
|
+
const text = await res.text().catch(() => "");
|
|
257
|
+
throw new Error(`Vortex start entry failed: ${res.status} ${text}`);
|
|
258
|
+
}
|
|
259
|
+
const data = await res.json();
|
|
260
|
+
const entryId = data.entries?.[0]?.id ?? data.id;
|
|
261
|
+
if (!entryId) {
|
|
262
|
+
throw new Error("Vortex start entry returned no entry id");
|
|
263
|
+
}
|
|
264
|
+
return entryId;
|
|
265
|
+
}
|
|
266
|
+
/** Append one turn's grader results to an existing entry. */
|
|
267
|
+
async appendTurnResults(runId, entryId, turn) {
|
|
268
|
+
const res = await this.fetch(`/v1/evals/runs/${runId}/entries/${entryId}/results`, {
|
|
269
|
+
method: "POST",
|
|
270
|
+
headers: { "Content-Type": "application/json" },
|
|
271
|
+
body: JSON.stringify({ results: turnToResultRows(turn) })
|
|
272
|
+
});
|
|
273
|
+
if (!res.ok) {
|
|
274
|
+
const text = await res.text().catch(() => "");
|
|
275
|
+
throw new Error(`Vortex append results failed: ${res.status} ${text}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/** Append outcome-level grader results to an existing entry. */
|
|
279
|
+
async appendOutcomeResults(runId, entryId, assertions) {
|
|
280
|
+
if (assertions.length === 0) return;
|
|
281
|
+
const res = await this.fetch(`/v1/evals/runs/${runId}/entries/${entryId}/results`, {
|
|
282
|
+
method: "POST",
|
|
283
|
+
headers: { "Content-Type": "application/json" },
|
|
284
|
+
body: JSON.stringify({ results: outcomeToResultRows(assertions) })
|
|
285
|
+
});
|
|
286
|
+
if (!res.ok) {
|
|
287
|
+
const text = await res.text().catch(() => "");
|
|
288
|
+
throw new Error(`Vortex append results failed: ${res.status} ${text}`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/** Set the verdict on an entry once its eval finishes. */
|
|
292
|
+
async finalizeEntry(runId, entryId, verdict) {
|
|
293
|
+
const res = await this.fetch(`/v1/evals/runs/${runId}/entries/${entryId}`, {
|
|
294
|
+
method: "PATCH",
|
|
295
|
+
headers: { "Content-Type": "application/json" },
|
|
296
|
+
body: JSON.stringify({
|
|
297
|
+
passed: verdict.passed,
|
|
298
|
+
...verdict.durationMs !== void 0 ? { durationMs: verdict.durationMs } : {},
|
|
299
|
+
...verdict.error !== void 0 ? { error: verdict.error } : {}
|
|
300
|
+
})
|
|
301
|
+
});
|
|
302
|
+
if (!res.ok) {
|
|
303
|
+
const text = await res.text().catch(() => "");
|
|
304
|
+
throw new Error(`Vortex finalize entry failed: ${res.status} ${text}`);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/** Flip the run to its terminal status (replaces completeRun's empty-entries hack). */
|
|
308
|
+
async markRunComplete(runId, opts = {}) {
|
|
309
|
+
const res = await this.fetch(`/v1/evals/runs/${runId}/complete`, {
|
|
310
|
+
method: "POST",
|
|
311
|
+
headers: { "Content-Type": "application/json" },
|
|
312
|
+
body: JSON.stringify({ ...opts.failed !== void 0 ? { failed: opts.failed } : {} })
|
|
313
|
+
});
|
|
314
|
+
if (!res.ok) {
|
|
315
|
+
const text = await res.text().catch(() => "");
|
|
316
|
+
throw new Error(`Vortex complete run failed: ${res.status} ${text}`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
// ── Run results — read ─────────────────────────────────────────────
|
|
320
|
+
async loadRunResult(runId) {
|
|
321
|
+
const data = await this.fetchJson(`/v1/evals/runs/${runId}`);
|
|
322
|
+
return vortexRunToReport(data);
|
|
323
|
+
}
|
|
324
|
+
async getLatestRun() {
|
|
325
|
+
const data = await this.fetchJson(`/v1/evals/bot/${this.botId}/runs`, { limit: "1" });
|
|
326
|
+
if (!data.runs.length) return null;
|
|
327
|
+
const latest = data.runs[0];
|
|
328
|
+
return this.loadRunResult(latest.id);
|
|
329
|
+
}
|
|
330
|
+
async listRunSummaries(opts) {
|
|
331
|
+
const runs = await this.fetchRunsWithEntries(opts?.limit ?? 50, opts?.since);
|
|
332
|
+
return runs.map(vortexRunToSummary);
|
|
333
|
+
}
|
|
334
|
+
async listEvalReportsByName(evalName, opts) {
|
|
335
|
+
const limit = opts?.limit ?? 20;
|
|
336
|
+
const runs = await this.fetchRunsWithEntries(50, opts?.since);
|
|
337
|
+
const entries = [];
|
|
338
|
+
for (const run of runs) {
|
|
339
|
+
if (entries.length >= limit) break;
|
|
340
|
+
const entry = run.entries.find((e) => e.evalName === evalName);
|
|
341
|
+
if (entry) {
|
|
342
|
+
entries.push({
|
|
343
|
+
runId: run.id,
|
|
344
|
+
timestamp: run.createdAt,
|
|
345
|
+
report: vortexEntryToEvalReport(entry),
|
|
346
|
+
totalEvalsInRun: run.entries.length
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return entries;
|
|
351
|
+
}
|
|
352
|
+
async listEvalReportsBulk(opts) {
|
|
353
|
+
const per = opts?.perEval ?? 20;
|
|
354
|
+
const runs = await this.fetchRunsWithEntries(50, opts?.since);
|
|
355
|
+
const grouped = {};
|
|
356
|
+
for (const run of runs) {
|
|
357
|
+
for (const entry of run.entries) {
|
|
358
|
+
if (!grouped[entry.evalName]) grouped[entry.evalName] = [];
|
|
359
|
+
if (grouped[entry.evalName].length < per) {
|
|
360
|
+
grouped[entry.evalName].push({
|
|
361
|
+
runId: run.id,
|
|
362
|
+
timestamp: run.createdAt,
|
|
363
|
+
report: vortexEntryToEvalReport(entry),
|
|
364
|
+
totalEvalsInRun: run.entries.length
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return grouped;
|
|
370
|
+
}
|
|
371
|
+
// ── Live run tracking ──────────────────────────────────────────────
|
|
372
|
+
async *watchRun(signal, options = {}) {
|
|
373
|
+
const POLL_INTERVAL = 1500;
|
|
374
|
+
const POLL_TIMEOUT = 60 * 60 * 1e3;
|
|
375
|
+
const start = Date.now();
|
|
376
|
+
const hasExpectedRun = !!options.runId || !!options.workflowId;
|
|
377
|
+
let baselineRunId = null;
|
|
378
|
+
if (!hasExpectedRun) {
|
|
379
|
+
try {
|
|
380
|
+
const initial = await this.fetchJson(`/v1/evals/bot/${this.botId}/runs`, {
|
|
381
|
+
limit: "1"
|
|
382
|
+
});
|
|
383
|
+
baselineRunId = initial.runs[0]?.id ?? null;
|
|
384
|
+
} catch {
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const evalMeta = await this.loadEvalMeta();
|
|
388
|
+
const startedEvals = /* @__PURE__ */ new Set();
|
|
389
|
+
const seenTurns = /* @__PURE__ */ new Set();
|
|
390
|
+
const startedTurns = /* @__PURE__ */ new Set();
|
|
391
|
+
const finalizedEvals = /* @__PURE__ */ new Set();
|
|
392
|
+
while (!signal?.aborted && Date.now() - start < POLL_TIMEOUT) {
|
|
393
|
+
try {
|
|
394
|
+
const data = await this.fetchJson(`/v1/evals/bot/${this.botId}/runs`, {
|
|
395
|
+
limit: hasExpectedRun ? "20" : "1"
|
|
396
|
+
});
|
|
397
|
+
let latest = null;
|
|
398
|
+
let raw = null;
|
|
399
|
+
for (const candidate of data.runs) {
|
|
400
|
+
if (options.runId && candidate.id !== options.runId) continue;
|
|
401
|
+
if (options.workflowId && candidate.metadata?.workflowId !== options.workflowId) {
|
|
402
|
+
const candidateRaw = await this.fetchJson(`/v1/evals/runs/${candidate.id}`);
|
|
403
|
+
if (!runMatchesWatchOptions(candidateRaw, options)) continue;
|
|
404
|
+
raw = candidateRaw;
|
|
405
|
+
}
|
|
406
|
+
latest = candidate;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
if (!latest || !hasExpectedRun && latest.id === baselineRunId) {
|
|
410
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
raw ??= await this.fetchJson(`/v1/evals/runs/${latest.id}`);
|
|
414
|
+
const terminal = raw.status === "completed" || raw.status === "failed";
|
|
415
|
+
for (let i = 0; i < raw.entries.length; i++) {
|
|
416
|
+
const entry = raw.entries[i];
|
|
417
|
+
const evalReport = vortexEntryToEvalReport(entry);
|
|
418
|
+
const meta = evalMeta.get(entry.evalName);
|
|
419
|
+
const totalTurns = meta?.totalTurns ?? evalReport.turns.length;
|
|
420
|
+
if (!startedEvals.has(entry.evalName)) {
|
|
421
|
+
startedEvals.add(entry.evalName);
|
|
422
|
+
yield { type: "eval_start", evalName: entry.evalName, index: i, totalTurns };
|
|
423
|
+
}
|
|
424
|
+
for (const turn of evalReport.turns) {
|
|
425
|
+
const turnKey = `${entry.evalName}::${turn.turnIndex}`;
|
|
426
|
+
if (seenTurns.has(turnKey)) continue;
|
|
427
|
+
seenTurns.add(turnKey);
|
|
428
|
+
yield {
|
|
429
|
+
type: "turn_complete",
|
|
430
|
+
evalName: entry.evalName,
|
|
431
|
+
evalIndex: i,
|
|
432
|
+
turnIndex: turn.turnIndex,
|
|
433
|
+
totalTurns,
|
|
434
|
+
turnReport: turn
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
if (entry.passed != null && !finalizedEvals.has(entry.evalName)) {
|
|
438
|
+
finalizedEvals.add(entry.evalName);
|
|
439
|
+
yield { type: "eval_complete", evalName: entry.evalName, index: i, report: evalReport };
|
|
440
|
+
} else if (entry.passed == null && !terminal) {
|
|
441
|
+
const nextTurnIndex = evalReport.turns.length;
|
|
442
|
+
const startKey = `${entry.evalName}::${nextTurnIndex}`;
|
|
443
|
+
if (nextTurnIndex < totalTurns && !startedTurns.has(startKey)) {
|
|
444
|
+
startedTurns.add(startKey);
|
|
445
|
+
yield {
|
|
446
|
+
type: "turn_start",
|
|
447
|
+
evalName: entry.evalName,
|
|
448
|
+
evalIndex: i,
|
|
449
|
+
turnIndex: nextTurnIndex,
|
|
450
|
+
totalTurns,
|
|
451
|
+
userMessage: meta?.userMessages[nextTurnIndex] ?? ""
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
if (terminal) {
|
|
457
|
+
const finalReport = vortexRunToReport(raw);
|
|
458
|
+
for (let i = 0; i < finalReport.evals.length; i++) {
|
|
459
|
+
const evalReport = finalReport.evals[i];
|
|
460
|
+
if (finalizedEvals.has(evalReport.name)) continue;
|
|
461
|
+
finalizedEvals.add(evalReport.name);
|
|
462
|
+
yield { type: "eval_complete", evalName: evalReport.name, index: i, report: evalReport };
|
|
463
|
+
}
|
|
464
|
+
yield {
|
|
465
|
+
type: "suite_complete",
|
|
466
|
+
report: finalReport,
|
|
467
|
+
error: raw.status === "failed" ? "Eval run failed" : void 0
|
|
468
|
+
};
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
} catch {
|
|
472
|
+
}
|
|
473
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL));
|
|
474
|
+
}
|
|
475
|
+
if (!signal?.aborted) {
|
|
476
|
+
yield {
|
|
477
|
+
type: "suite_complete",
|
|
478
|
+
report: {
|
|
479
|
+
id: "timeout",
|
|
480
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
481
|
+
evals: [],
|
|
482
|
+
passed: 0,
|
|
483
|
+
failed: 0,
|
|
484
|
+
total: 0,
|
|
485
|
+
duration: 0
|
|
486
|
+
},
|
|
487
|
+
error: "Eval run timed out"
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
// ── Runner state ───────────────────────────────────────────────────
|
|
492
|
+
async getRunnerState() {
|
|
493
|
+
try {
|
|
494
|
+
const data = await this.fetchJson(`/v1/evals/bot/${this.botId}/runs`, {
|
|
495
|
+
status: "running",
|
|
496
|
+
limit: "1"
|
|
497
|
+
});
|
|
498
|
+
const active = data.runs[0] ?? null;
|
|
499
|
+
return { running: !!active, runId: active?.id ?? null };
|
|
500
|
+
} catch {
|
|
501
|
+
return { running: false, runId: null };
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// ── Internal helpers ───────────────────────────────────────────────
|
|
505
|
+
async getEvalDefinitions() {
|
|
506
|
+
if (!this._loadEvalDefinitions) return [];
|
|
507
|
+
return this._loadEvalDefinitions();
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* evalName → { totalTurns, userMessages } from the eval definitions, so
|
|
511
|
+
* watchRun can report `totalTurns` and synthesize `turn_start` events (with
|
|
512
|
+
* the turn's user message) for the in-flight turn — matching the dev SSE
|
|
513
|
+
* experience where the pending turn is visible before its results land.
|
|
514
|
+
*/
|
|
515
|
+
async loadEvalMeta() {
|
|
516
|
+
try {
|
|
517
|
+
const defs = await this.getEvalDefinitions();
|
|
518
|
+
return new Map(
|
|
519
|
+
defs.map((d) => [
|
|
520
|
+
d.name,
|
|
521
|
+
{ totalTurns: d.conversation.length, userMessages: d.conversation.map((t) => t.user ?? "") }
|
|
522
|
+
])
|
|
523
|
+
);
|
|
524
|
+
} catch {
|
|
525
|
+
return /* @__PURE__ */ new Map();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async fetchRunsWithEntries(limit, since) {
|
|
529
|
+
const data = await this.fetchJson(`/v1/evals/bot/${this.botId}/runs`, {
|
|
530
|
+
limit: String(limit)
|
|
531
|
+
});
|
|
532
|
+
let runs = data.runs;
|
|
533
|
+
if (since) {
|
|
534
|
+
runs = runs.filter((r) => new Date(r.createdAt).getTime() >= since);
|
|
535
|
+
}
|
|
536
|
+
return mapWithConcurrency(runs, 8, (r) => this.fetchJson(`/v1/evals/runs/${r.id}`));
|
|
537
|
+
}
|
|
538
|
+
async fetch(path, init) {
|
|
539
|
+
const target = new URL(`${this.url}${path}`);
|
|
540
|
+
const headers = new Headers(init?.headers);
|
|
541
|
+
if (this.token) headers.set("Authorization", `Bearer ${this.token}`);
|
|
542
|
+
return globalThis.fetch(target, { ...init, headers });
|
|
543
|
+
}
|
|
544
|
+
async fetchJson(path, params) {
|
|
545
|
+
const target = new URL(`${this.url}${path}`);
|
|
546
|
+
if (params) {
|
|
547
|
+
for (const [k, v] of Object.entries(params)) target.searchParams.set(k, v);
|
|
548
|
+
}
|
|
549
|
+
const headers = new Headers();
|
|
550
|
+
if (this.token) headers.set("Authorization", `Bearer ${this.token}`);
|
|
551
|
+
const res = await globalThis.fetch(target, { headers });
|
|
552
|
+
if (!res.ok) {
|
|
553
|
+
const text = await res.text().catch(() => "");
|
|
554
|
+
throw new Error(`Vortex ${res.status}: ${text}`);
|
|
555
|
+
}
|
|
556
|
+
return res.json();
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
export {
|
|
560
|
+
VortexEvalStore
|
|
561
|
+
};
|
|
562
|
+
//# sourceMappingURL=vortex-eval-store.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/stores/vortex-eval-store.ts"],
|
|
4
|
+
"sourcesContent": ["import type { EvalDefinition } from '../definition'\nimport type { EvalFilter, EvalProgressEvent, EvalReport, EvalRunReport, TurnReport, GraderResult } from '../types'\nimport type { EvalStore, EvalSummary, EvalRunSummary, EvalReportHistoryEntry, EvalWatchOptions } from './eval-store'\nimport type { EvalDefinitionLoader } from './eval-definition-loader'\n\n// \u2500\u2500 Vortex response types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface VortexEvalRun {\n id: string\n botId: string\n workspaceId: string\n evalManifestId: string\n status: 'pending' | 'running' | 'completed' | 'failed'\n triggerType: string\n startedAt: string | null\n completedAt: string | null\n createdAt: string\n updatedAt: string\n metadata?: Record<string, unknown> | null\n}\n\ninterface VortexEvalResult {\n id: string\n evalEntryId: string\n turnIndex: number\n graderName: string\n passed: boolean\n score: number | null\n evidence: Record<string, unknown>\n userMessage: string\n botResponse: string\n botDurationMs: number | null\n graderDurationMs: number | null\n createdAt: string\n}\n\ninterface VortexEvalEntry {\n id: string\n evalRunId: string\n evalName: string\n evalType: 'capability' | 'regression'\n description: string\n tags: string[]\n /** null while the entry is in-progress (per-turn lifecycle); set on finalize. */\n passed: boolean | null\n durationMs: number | null\n error: string | null\n createdAt: string\n results: VortexEvalResult[]\n}\n\n/** A single grader result row, as posted to Vortex. */\ninterface VortexResultInput {\n turnIndex: number\n graderName: string\n passed: boolean\n evidence: { expected: string; actual: string }\n userMessage: string\n botResponse: string\n botDurationMs: number\n graderDurationMs: number\n}\n\ntype VortexRunWithEntries = VortexEvalRun & { entries: VortexEvalEntry[] }\n\nconst OUTCOME_TURN_INDEX = -1\n\n// \u2500\u2500 Config \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface VortexEvalStoreConfig {\n url: string\n botId: string\n workspaceId?: string\n token?: string\n evalManifestId?: string | (() => string | undefined)\n loadEvalDefinitions?: EvalDefinitionLoader\n}\n\n// \u2500\u2500 Transformers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction vortexEntryToEvalReport(entry: VortexEvalEntry): EvalReport {\n const outcomeAssertions: GraderResult[] = entry.results\n .filter((r) => r.turnIndex === OUTCOME_TURN_INDEX)\n .map((r) => ({\n assertion: r.graderName,\n pass: r.passed,\n expected: (r.evidence?.expected as string) ?? '',\n actual: (r.evidence?.actual as string) ?? '',\n }))\n\n const resultsByTurn = new Map<number, VortexEvalResult[]>()\n for (const r of entry.results) {\n if (r.turnIndex === OUTCOME_TURN_INDEX) continue\n const existing = resultsByTurn.get(r.turnIndex) ?? []\n existing.push(r)\n resultsByTurn.set(r.turnIndex, existing)\n }\n\n const turns: TurnReport[] = [...resultsByTurn.entries()]\n .sort(([a], [b]) => a - b)\n .map(([turnIndex, results]) => {\n const assertions: GraderResult[] = results.map((r) => ({\n assertion: r.graderName,\n pass: r.passed,\n expected: (r.evidence?.expected as string) ?? '',\n actual: (r.evidence?.actual as string) ?? '',\n }))\n const first = results[0]!\n return {\n turnIndex,\n userMessage: first.userMessage,\n botResponse: first.botResponse,\n assertions,\n pass: assertions.every((a) => a.pass),\n botDuration: first.botDurationMs ?? 0,\n evalDuration: first.graderDurationMs ?? 0,\n }\n })\n\n return {\n name: entry.evalName,\n description: entry.description || undefined,\n type: entry.evalType,\n tags: entry.tags,\n turns,\n outcomeAssertions,\n pass: entry.passed ?? false,\n duration: entry.durationMs ?? 0,\n error: entry.error ?? undefined,\n }\n}\n\n/** Run an async fn over items with bounded concurrency, preserving input order. */\nasync function mapWithConcurrency<T, R>(items: T[], concurrency: number, fn: (item: T) => Promise<R>): Promise<R[]> {\n const results = new Array<R>(items.length)\n let next = 0\n const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {\n while (next < items.length) {\n const i = next++\n results[i] = await fn(items[i]!)\n }\n })\n await Promise.all(workers)\n return results\n}\n\n/** Map one turn's assertions to Vortex result rows (shared by batch + per-turn writes). */\nfunction turnToResultRows(turn: TurnReport): VortexResultInput[] {\n return graderResultsToRows(turn.assertions, {\n turnIndex: turn.turnIndex,\n userMessage: turn.userMessage,\n botResponse: turn.botResponse,\n botDurationMs: turn.botDuration,\n graderDurationMs: turn.evalDuration,\n })\n}\n\nfunction outcomeToResultRows(assertions: GraderResult[]): VortexResultInput[] {\n return graderResultsToRows(assertions, {\n turnIndex: OUTCOME_TURN_INDEX,\n userMessage: '',\n botResponse: '',\n botDurationMs: 0,\n graderDurationMs: 0,\n })\n}\n\nfunction graderResultsToRows(\n assertions: GraderResult[],\n context: Omit<VortexResultInput, 'graderName' | 'passed' | 'evidence'>\n): VortexResultInput[] {\n return assertions.map((assertion) => ({\n ...context,\n graderName: assertion.assertion,\n passed: assertion.pass,\n evidence: { expected: assertion.expected, actual: assertion.actual },\n }))\n}\n\nfunction runMatchesWatchOptions(run: VortexEvalRun, options: EvalWatchOptions): boolean {\n if (options.runId && run.id !== options.runId) return false\n if (options.workflowId && run.metadata?.workflowId !== options.workflowId) return false\n return true\n}\n\nfunction vortexRunToReport(run: VortexRunWithEntries): EvalRunReport {\n const evals = run.entries.map(vortexEntryToEvalReport)\n return {\n id: run.id,\n timestamp: run.createdAt,\n evals,\n passed: evals.filter((e) => e.pass).length,\n failed: evals.filter((e) => !e.pass).length,\n total: evals.length,\n duration: evals.reduce((s, e) => s + e.duration, 0),\n }\n}\n\nfunction vortexRunToSummary(run: VortexEvalRun & { entries?: VortexEvalEntry[] }): EvalRunSummary {\n const startMs = run.startedAt ? new Date(run.startedAt).getTime() : 0\n const endMs = run.completedAt ? new Date(run.completedAt).getTime() : 0\n\n if (run.entries) {\n const evals = run.entries.map(vortexEntryToEvalReport)\n return {\n id: run.id,\n timestamp: run.createdAt,\n passed: evals.filter((e) => e.pass).length,\n failed: evals.filter((e) => !e.pass).length,\n total: evals.length,\n duration: evals.reduce((s, e) => s + e.duration, 0),\n botDuration: evals.reduce((s, e) => e.turns.reduce((ts, t) => ts + t.botDuration, s), 0),\n evalDuration: evals.reduce((s, e) => e.turns.reduce((ts, t) => ts + t.evalDuration, s), 0),\n evalNames: evals.map((e) => e.name),\n aborted: false,\n }\n }\n\n return {\n id: run.id,\n timestamp: run.createdAt,\n passed: 0,\n failed: 0,\n total: 0,\n duration: startMs && endMs ? endMs - startMs : 0,\n botDuration: 0,\n evalDuration: 0,\n evalNames: [],\n aborted: false,\n }\n}\n\n// \u2500\u2500 VortexEvalStore \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class VortexEvalStore implements EvalStore {\n private url: string\n private botId: string\n private workspaceId?: string\n private token?: string\n private _evalManifestId?: string | (() => string | undefined)\n private _loadEvalDefinitions?: EvalDefinitionLoader\n\n constructor(config: VortexEvalStoreConfig) {\n this.url = config.url.replace(/\\/$/, '')\n this.botId = config.botId\n if (config.workspaceId) this.workspaceId = config.workspaceId\n if (config.token) this.token = config.token\n this._evalManifestId = config.evalManifestId\n this._loadEvalDefinitions = config.loadEvalDefinitions\n }\n\n // \u2500\u2500 Eval definitions (from Files API manifest) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async listEvals(filter?: EvalFilter): Promise<EvalSummary[]> {\n const defs = await this.getEvalDefinitions()\n const filtered = defs.filter((e) => {\n if (filter?.names && !filter.names.includes(e.name)) return false\n if (filter?.type && e.type !== filter.type) return false\n if (filter?.tags && !filter.tags.every((tag) => e.tags?.includes(tag))) return false\n return true\n })\n return filtered.map((e) => ({\n name: e.name,\n description: e.description,\n tags: e.tags || [],\n type: e.type || 'capability',\n turnCount: e.conversation.length,\n hasOutcome: !!e.outcome,\n }))\n }\n\n async getEval(name: string): Promise<EvalDefinition | null> {\n const defs = await this.getEvalDefinitions()\n return defs.find((e) => e.name === name) ?? null\n }\n\n // \u2500\u2500 Run lifecycle \u2014 write \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async createRun(runType = 'scheduled', metadata?: Record<string, unknown>): Promise<string> {\n if (!this.workspaceId) {\n throw new Error('workspaceId is required to create Vortex eval runs')\n }\n\n const triggerType = runType === 'manual' ? 'manual' : 'scheduled'\n const res = await this.fetch(`/v1/evals/bot/${this.botId}/runs`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n evalManifestId:\n (typeof this._evalManifestId === 'function' ? this._evalManifestId() : this._evalManifestId) ?? '',\n workspaceId: this.workspaceId,\n triggerType,\n ...(metadata ? { metadata } : {}),\n }),\n })\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex create run failed: ${res.status} ${text}`)\n }\n\n const { id } = (await res.json()) as { id: string }\n return id\n }\n\n async addRunResults(runId: string, evalReport: EvalReport): Promise<void> {\n const entry = {\n evalName: evalReport.name,\n evalType: evalReport.type ?? 'capability',\n description: evalReport.description ?? '',\n tags: evalReport.tags ?? [],\n passed: evalReport.pass,\n durationMs: evalReport.duration,\n error: evalReport.error,\n results: [...evalReport.turns.flatMap(turnToResultRows), ...outcomeToResultRows(evalReport.outcomeAssertions)],\n }\n\n const res = await this.fetch(`/v1/evals/runs/${runId}/entries`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ entries: [entry] }),\n })\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex ingest entries failed: ${res.status} ${text}`)\n }\n }\n\n async completeRun(runId: string, report: EvalRunReport): Promise<void> {\n const hasRunError = report.aborted === true || report.evals.some((e) => e.error !== undefined)\n const res = await this.fetch(`/v1/evals/runs/${runId}/entries`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n entries: [],\n completed: !hasRunError,\n failed: hasRunError,\n }),\n })\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex complete run failed: ${res.status} ${text}`)\n }\n }\n\n // \u2500\u2500 Per-turn incremental lifecycle (Option B) \u2500\u2500\n //\n // The eval-runner workflow builds each entry up as the eval runs (rather than\n // writing it whole at the end), so the dev console can stream turn-by-turn\n // progress. This is the prod ingestion path and requires Vortex to provide:\n // startEntry POST /runs/{runId}/entries \u2192 { entries:[{ id }] } (passed:null, in-progress)\n // appendTurnResults POST /runs/{runId}/entries/{entryId}/results (idempotent upsert by turnIndex+graderName)\n // finalizeEntry PATCH /runs/{runId}/entries/{entryId} (sets the verdict)\n // markRunComplete POST /runs/{runId}/complete (status \u2192 completed|failed)\n\n /** Create an in-progress entry (metadata only, no results) and return its server id. */\n async startEntry(\n runId: string,\n meta: { evalName: string; evalType?: 'capability' | 'regression'; description?: string; tags?: string[] }\n ): Promise<string> {\n const res = await this.fetch(`/v1/evals/runs/${runId}/entries`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n entries: [\n {\n evalName: meta.evalName,\n evalType: meta.evalType ?? 'capability',\n description: meta.description ?? '',\n tags: meta.tags ?? [],\n },\n ],\n }),\n })\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex start entry failed: ${res.status} ${text}`)\n }\n\n const data = (await res.json()) as { entries?: { id: string }[]; id?: string }\n const entryId = data.entries?.[0]?.id ?? data.id\n if (!entryId) {\n throw new Error('Vortex start entry returned no entry id')\n }\n return entryId\n }\n\n /** Append one turn's grader results to an existing entry. */\n async appendTurnResults(runId: string, entryId: string, turn: TurnReport): Promise<void> {\n const res = await this.fetch(`/v1/evals/runs/${runId}/entries/${entryId}/results`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ results: turnToResultRows(turn) }),\n })\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex append results failed: ${res.status} ${text}`)\n }\n }\n\n /** Append outcome-level grader results to an existing entry. */\n async appendOutcomeResults(runId: string, entryId: string, assertions: GraderResult[]): Promise<void> {\n if (assertions.length === 0) return\n\n const res = await this.fetch(`/v1/evals/runs/${runId}/entries/${entryId}/results`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ results: outcomeToResultRows(assertions) }),\n })\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex append results failed: ${res.status} ${text}`)\n }\n }\n\n /** Set the verdict on an entry once its eval finishes. */\n async finalizeEntry(\n runId: string,\n entryId: string,\n verdict: { passed: boolean; durationMs?: number; error?: string }\n ): Promise<void> {\n const res = await this.fetch(`/v1/evals/runs/${runId}/entries/${entryId}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n passed: verdict.passed,\n ...(verdict.durationMs !== undefined ? { durationMs: verdict.durationMs } : {}),\n ...(verdict.error !== undefined ? { error: verdict.error } : {}),\n }),\n })\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex finalize entry failed: ${res.status} ${text}`)\n }\n }\n\n /** Flip the run to its terminal status (replaces completeRun's empty-entries hack). */\n async markRunComplete(runId: string, opts: { failed?: boolean } = {}): Promise<void> {\n const res = await this.fetch(`/v1/evals/runs/${runId}/complete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ...(opts.failed !== undefined ? { failed: opts.failed } : {}) }),\n })\n\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex complete run failed: ${res.status} ${text}`)\n }\n }\n\n // \u2500\u2500 Run results \u2014 read \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async loadRunResult(runId: string): Promise<EvalRunReport | null> {\n const data = await this.fetchJson<VortexRunWithEntries>(`/v1/evals/runs/${runId}`)\n return vortexRunToReport(data)\n }\n\n async getLatestRun(): Promise<EvalRunReport | null> {\n const data = await this.fetchJson<{ runs: VortexEvalRun[] }>(`/v1/evals/bot/${this.botId}/runs`, { limit: '1' })\n if (!data.runs.length) return null\n const latest = data.runs[0]!\n return this.loadRunResult(latest.id)\n }\n\n async listRunSummaries(opts?: { limit?: number; since?: number }): Promise<EvalRunSummary[]> {\n // Vortex's list endpoint returns runs WITHOUT entries, so summary counts\n // would all be zero \u2014 which the heatmap renders as \"No evals\". Pull each\n // run's entries to compute real pass/fail/total. N+1, bounded by the list\n // size; a counts field on the list endpoint would let us drop the per-run\n // fetch.\n const runs = await this.fetchRunsWithEntries(opts?.limit ?? 50, opts?.since)\n return runs.map(vortexRunToSummary)\n }\n\n async listEvalReportsByName(\n evalName: string,\n opts?: { limit?: number; since?: number }\n ): Promise<EvalReportHistoryEntry[]> {\n const limit = opts?.limit ?? 20\n const runs = await this.fetchRunsWithEntries(50, opts?.since)\n\n const entries: EvalReportHistoryEntry[] = []\n for (const run of runs) {\n if (entries.length >= limit) break\n const entry = run.entries.find((e) => e.evalName === evalName)\n if (entry) {\n entries.push({\n runId: run.id,\n timestamp: run.createdAt,\n report: vortexEntryToEvalReport(entry),\n totalEvalsInRun: run.entries.length,\n })\n }\n }\n return entries\n }\n\n async listEvalReportsBulk(opts?: {\n perEval?: number\n since?: number\n }): Promise<Record<string, EvalReportHistoryEntry[]>> {\n const per = opts?.perEval ?? 20\n const runs = await this.fetchRunsWithEntries(50, opts?.since)\n\n const grouped: Record<string, EvalReportHistoryEntry[]> = {}\n for (const run of runs) {\n for (const entry of run.entries) {\n if (!grouped[entry.evalName]) grouped[entry.evalName] = []\n if (grouped[entry.evalName]!.length < per) {\n grouped[entry.evalName]!.push({\n runId: run.id,\n timestamp: run.createdAt,\n report: vortexEntryToEvalReport(entry),\n totalEvalsInRun: run.entries.length,\n })\n }\n }\n }\n return grouped\n }\n\n // \u2500\u2500 Live run tracking \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async *watchRun(signal?: AbortSignal, options: EvalWatchOptions = {}): AsyncIterable<EvalProgressEvent> {\n // Poll fast enough that batched turns don't arrive in a single visible\n // burst \u2014 the prod path can't push per-event like the dev SSE stream, so a\n // tighter cadence is what keeps it feeling live.\n const POLL_INTERVAL = 1_500\n const POLL_TIMEOUT = 60 * 60 * 1_000 // 60min\n const start = Date.now()\n\n // Capture the run that's already the latest before this invocation begins.\n // The eval-runner workflow now creates its Vortex run UP FRONT (before\n // running the suite), so a genuinely new run appears almost immediately \u2014\n // but until it does, the \"latest\" run is the PREVIOUS one. Reporting that\n // would surface a stale, mismatched run (the runner shows eval \"A\" while\n // watchRun reports the prior run of eval \"B\", and the per-eval replay\n // filter renders \"no results\"). Only act on a run whose id differs from\n // this baseline.\n const hasExpectedRun = !!options.runId || !!options.workflowId\n let baselineRunId: string | null = null\n if (!hasExpectedRun) {\n try {\n const initial = await this.fetchJson<{ runs: VortexEvalRun[] }>(`/v1/evals/bot/${this.botId}/runs`, {\n limit: '1',\n })\n baselineRunId = initial.runs[0]?.id ?? null\n } catch {\n // Couldn't read the baseline; fall back to reporting the first completed run.\n }\n }\n\n // Diff the run on every poll and stream progress: `eval_start` when an\n // entry first appears, `turn_start` for the next in-flight turn (synthesized\n // from the definition so the pending-turn UI shows before results land),\n // `turn_complete` as each turn's results arrive, and `eval_complete` once\n // the entry is finalized (verdict set \u2192 `passed` is non-null).\n // `suite_complete` fires when the run reaches a terminal status. (Depends on\n // the per-turn lifecycle endpoints; see the write methods above.)\n const evalMeta = await this.loadEvalMeta()\n const startedEvals = new Set<string>()\n const seenTurns = new Set<string>()\n const startedTurns = new Set<string>()\n const finalizedEvals = new Set<string>()\n\n while (!signal?.aborted && Date.now() - start < POLL_TIMEOUT) {\n try {\n const data = await this.fetchJson<{ runs: VortexEvalRun[] }>(`/v1/evals/bot/${this.botId}/runs`, {\n limit: hasExpectedRun ? '20' : '1',\n })\n let latest: VortexEvalRun | null = null\n let raw: VortexRunWithEntries | null = null\n for (const candidate of data.runs) {\n if (options.runId && candidate.id !== options.runId) continue\n if (options.workflowId && candidate.metadata?.workflowId !== options.workflowId) {\n const candidateRaw = await this.fetchJson<VortexRunWithEntries>(`/v1/evals/runs/${candidate.id}`)\n if (!runMatchesWatchOptions(candidateRaw, options)) continue\n raw = candidateRaw\n }\n latest = candidate\n break\n }\n\n // Still seeing the pre-existing run (or none yet) \u2014 the new run hasn't\n // been created. Wait and retry.\n if (!latest || (!hasExpectedRun && latest.id === baselineRunId)) {\n await new Promise((r) => setTimeout(r, POLL_INTERVAL))\n continue\n }\n\n raw ??= await this.fetchJson<VortexRunWithEntries>(`/v1/evals/runs/${latest.id}`)\n const terminal = raw.status === 'completed' || raw.status === 'failed'\n\n for (let i = 0; i < raw.entries.length; i++) {\n const entry = raw.entries[i]!\n const evalReport = vortexEntryToEvalReport(entry)\n const meta = evalMeta.get(entry.evalName)\n const totalTurns = meta?.totalTurns ?? evalReport.turns.length\n\n if (!startedEvals.has(entry.evalName)) {\n startedEvals.add(entry.evalName)\n yield { type: 'eval_start', evalName: entry.evalName, index: i, totalTurns }\n }\n for (const turn of evalReport.turns) {\n const turnKey = `${entry.evalName}::${turn.turnIndex}`\n if (seenTurns.has(turnKey)) continue\n seenTurns.add(turnKey)\n yield {\n type: 'turn_complete',\n evalName: entry.evalName,\n evalIndex: i,\n turnIndex: turn.turnIndex,\n totalTurns,\n turnReport: turn,\n }\n }\n // Verdict set (passed non-null) \u2192 the eval is done.\n if (entry.passed != null && !finalizedEvals.has(entry.evalName)) {\n finalizedEvals.add(entry.evalName)\n yield { type: 'eval_complete', evalName: entry.evalName, index: i, report: evalReport }\n } else if (entry.passed == null && !terminal) {\n // Still running: announce the next expected turn so the UI renders\n // the in-flight turn (its user message + spinner) instead of jumping\n // straight from completed turns to the next completed turn. Results\n // land per-turn, so the next index is simply the completed count.\n const nextTurnIndex = evalReport.turns.length\n const startKey = `${entry.evalName}::${nextTurnIndex}`\n if (nextTurnIndex < totalTurns && !startedTurns.has(startKey)) {\n startedTurns.add(startKey)\n yield {\n type: 'turn_start',\n evalName: entry.evalName,\n evalIndex: i,\n turnIndex: nextTurnIndex,\n totalTurns,\n userMessage: meta?.userMessages[nextTurnIndex] ?? '',\n }\n }\n }\n }\n\n if (terminal) {\n const finalReport = vortexRunToReport(raw)\n // Flush any eval that never received an explicit verdict (e.g. it\n // errored out before finalizeEntry was called).\n for (let i = 0; i < finalReport.evals.length; i++) {\n const evalReport = finalReport.evals[i]!\n if (finalizedEvals.has(evalReport.name)) continue\n finalizedEvals.add(evalReport.name)\n yield { type: 'eval_complete', evalName: evalReport.name, index: i, report: evalReport }\n }\n yield {\n type: 'suite_complete',\n report: finalReport,\n error: raw.status === 'failed' ? 'Eval run failed' : undefined,\n }\n return\n }\n } catch {\n // Poll failed, retry next interval\n }\n\n // Settled-but-not-terminal poll (or a failed poll): wait before the next.\n // The \"no new run yet\" branch above already slept and continued, so this\n // is the only other delay point.\n await new Promise((r) => setTimeout(r, POLL_INTERVAL))\n }\n\n if (!signal?.aborted) {\n yield {\n type: 'suite_complete',\n report: {\n id: 'timeout',\n timestamp: new Date().toISOString(),\n evals: [],\n passed: 0,\n failed: 0,\n total: 0,\n duration: 0,\n },\n error: 'Eval run timed out',\n }\n }\n }\n\n // \u2500\u2500 Runner state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getRunnerState(): Promise<{ running: boolean; runId: string | null }> {\n try {\n const data = await this.fetchJson<{ runs: VortexEvalRun[] }>(`/v1/evals/bot/${this.botId}/runs`, {\n status: 'running',\n limit: '1',\n })\n const active = data.runs[0] ?? null\n return { running: !!active, runId: active?.id ?? null }\n } catch {\n return { running: false, runId: null }\n }\n }\n\n // \u2500\u2500 Internal helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async getEvalDefinitions(): Promise<EvalDefinition[]> {\n if (!this._loadEvalDefinitions) return []\n return this._loadEvalDefinitions()\n }\n\n /**\n * evalName \u2192 { totalTurns, userMessages } from the eval definitions, so\n * watchRun can report `totalTurns` and synthesize `turn_start` events (with\n * the turn's user message) for the in-flight turn \u2014 matching the dev SSE\n * experience where the pending turn is visible before its results land.\n */\n private async loadEvalMeta(): Promise<Map<string, { totalTurns: number; userMessages: string[] }>> {\n try {\n const defs = await this.getEvalDefinitions()\n return new Map(\n defs.map((d) => [\n d.name,\n { totalTurns: d.conversation.length, userMessages: d.conversation.map((t) => t.user ?? '') },\n ])\n )\n } catch {\n return new Map()\n }\n }\n\n private async fetchRunsWithEntries(limit: number, since?: number): Promise<VortexRunWithEntries[]> {\n const data = await this.fetchJson<{ runs: VortexEvalRun[] }>(`/v1/evals/bot/${this.botId}/runs`, {\n limit: String(limit),\n })\n\n let runs = data.runs\n if (since) {\n runs = runs.filter((r) => new Date(r.createdAt).getTime() >= since)\n }\n\n // The list endpoint omits entries, so fetch each run's detail. Bounded\n // concurrency keeps a wide `since` window from opening hundreds of sockets.\n return mapWithConcurrency(runs, 8, (r) => this.fetchJson<VortexRunWithEntries>(`/v1/evals/runs/${r.id}`))\n }\n\n private async fetch(path: string, init?: RequestInit): Promise<Response> {\n const target = new URL(`${this.url}${path}`)\n const headers = new Headers(init?.headers)\n if (this.token) headers.set('Authorization', `Bearer ${this.token}`)\n return globalThis.fetch(target, { ...init, headers })\n }\n\n private async fetchJson<T>(path: string, params?: Record<string, string>): Promise<T> {\n const target = new URL(`${this.url}${path}`)\n if (params) {\n for (const [k, v] of Object.entries(params)) target.searchParams.set(k, v)\n }\n const headers = new Headers()\n if (this.token) headers.set('Authorization', `Bearer ${this.token}`)\n\n const res = await globalThis.fetch(target, { headers })\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw new Error(`Vortex ${res.status}: ${text}`)\n }\n return res.json() as Promise<T>\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|