@deepagents/evals 7.0.2 → 7.1.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.
@@ -1,12 +1,135 @@
1
1
  // packages/evals/src/store/index.ts
2
2
  import { mkdirSync } from "node:fs";
3
3
  import { dirname } from "node:path";
4
- import { DatabaseSync } from "node:sqlite";
4
+ import {
5
+ DatabaseSync
6
+ } from "node:sqlite";
5
7
 
6
8
  // packages/evals/src/store/ddl.sqlite.sql
7
9
  var ddl_sqlite_default = "PRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA foreign_keys = ON;\n\nCREATE TABLE IF NOT EXISTS suites (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n);\n\nCREATE TABLE IF NOT EXISTS runs (\n id TEXT PRIMARY KEY,\n suite_id TEXT NOT NULL,\n name TEXT NOT NULL,\n model TEXT NOT NULL,\n config TEXT,\n started_at INTEGER NOT NULL,\n finished_at INTEGER,\n status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed')),\n summary TEXT,\n FOREIGN KEY (suite_id) REFERENCES suites(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_runs_suite_id ON runs(suite_id);\nCREATE INDEX IF NOT EXISTS idx_runs_started_at ON runs(started_at);\n\nCREATE TABLE IF NOT EXISTS cases (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL,\n idx INTEGER NOT NULL,\n input TEXT NOT NULL,\n output TEXT,\n expected TEXT,\n latency_ms INTEGER,\n tokens_in INTEGER,\n tokens_out INTEGER,\n error TEXT,\n FOREIGN KEY (run_id) REFERENCES runs(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_cases_run_id ON cases(run_id);\n\nCREATE TABLE IF NOT EXISTS scores (\n id TEXT PRIMARY KEY,\n case_id TEXT NOT NULL,\n scorer_name TEXT NOT NULL,\n score REAL NOT NULL,\n reason TEXT,\n FOREIGN KEY (case_id) REFERENCES cases(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_scores_case_id ON scores(case_id);\n\nCREATE TABLE IF NOT EXISTS prompts (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n content TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n);\n\nCREATE INDEX IF NOT EXISTS idx_prompts_created_at ON prompts(created_at);\n";
8
10
 
9
11
  // packages/evals/src/store/index.ts
12
+ var RUN_STATUSES = ["running", "completed", "failed"];
13
+ function isRunStatus(value) {
14
+ return RUN_STATUSES.some((status) => status === value);
15
+ }
16
+ function matchesColumn(value, type) {
17
+ switch (type) {
18
+ case "text":
19
+ return typeof value === "string";
20
+ case "text?":
21
+ return value === null || typeof value === "string";
22
+ case "number":
23
+ return typeof value === "number";
24
+ case "number?":
25
+ return value === null || typeof value === "number";
26
+ }
27
+ }
28
+ function mismatchedColumns(row, columns) {
29
+ return Object.entries(columns).filter(([name, type]) => !matchesColumn(row[name], type)).map(([name]) => name);
30
+ }
31
+ function hasColumns(row, columns) {
32
+ return mismatchedColumns(row, columns).length === 0;
33
+ }
34
+ function readRow(row, columns, sql) {
35
+ if (hasColumns(row, columns)) return row;
36
+ throw new TypeError(
37
+ `Unexpected values in columns ${mismatchedColumns(row, columns).join(", ")} of a row from: ${oneLine(sql)}`
38
+ );
39
+ }
40
+ function oneLine(sql) {
41
+ return sql.replace(/\s+/g, " ").trim();
42
+ }
43
+ function isRecord(value) {
44
+ return typeof value === "object" && value !== null && !Array.isArray(value);
45
+ }
46
+ function isRunSummary(value) {
47
+ return isRecord(value) && typeof value.totalCases === "number" && typeof value.passCount === "number" && typeof value.failCount === "number" && typeof value.totalLatencyMs === "number" && typeof value.totalTokensIn === "number" && typeof value.totalTokensOut === "number" && isRecord(value.meanScores) && Object.values(value.meanScores).every((score) => typeof score === "number");
48
+ }
49
+ function parseJson(text, guard, source) {
50
+ const value = JSON.parse(text);
51
+ if (guard(value)) return value;
52
+ throw new TypeError(`Unexpected JSON in ${source}`);
53
+ }
54
+ var suiteColumns = {
55
+ id: "text",
56
+ name: "text",
57
+ created_at: "number"
58
+ };
59
+ var runColumns = {
60
+ id: "text",
61
+ suite_id: "text",
62
+ name: "text",
63
+ model: "text",
64
+ config: "text?",
65
+ started_at: "number",
66
+ finished_at: "number?",
67
+ status: "text",
68
+ summary: "text?"
69
+ };
70
+ var caseColumns = {
71
+ id: "text",
72
+ run_id: "text",
73
+ idx: "number",
74
+ input: "text",
75
+ output: "text?",
76
+ expected: "text?",
77
+ latency_ms: "number?",
78
+ tokens_in: "number?",
79
+ tokens_out: "number?",
80
+ error: "text?"
81
+ };
82
+ var promptColumns = {
83
+ id: "text",
84
+ name: "text",
85
+ version: "number",
86
+ content: "text",
87
+ created_at: "number"
88
+ };
89
+ function toSuite(row) {
90
+ return { id: row.id, name: row.name, created_at: row.created_at };
91
+ }
92
+ function toRun(row) {
93
+ if (!isRunStatus(row.status)) {
94
+ throw new TypeError(`Unexpected status "${row.status}" of run ${row.id}`);
95
+ }
96
+ return {
97
+ id: row.id,
98
+ suite_id: row.suite_id,
99
+ name: row.name,
100
+ model: row.model,
101
+ config: row.config ? parseJson(row.config, isRecord, `config of run ${row.id}`) : null,
102
+ started_at: row.started_at,
103
+ finished_at: row.finished_at,
104
+ status: row.status,
105
+ summary: row.summary ? parseJson(row.summary, isRunSummary, `summary of run ${row.id}`) : null
106
+ };
107
+ }
108
+ function toCase(row) {
109
+ const input = JSON.parse(row.input);
110
+ const expected = row.expected ? JSON.parse(row.expected) : null;
111
+ return {
112
+ id: row.id,
113
+ run_id: row.run_id,
114
+ idx: row.idx,
115
+ input,
116
+ output: row.output,
117
+ expected,
118
+ latency_ms: row.latency_ms,
119
+ tokens_in: row.tokens_in,
120
+ tokens_out: row.tokens_out,
121
+ error: row.error
122
+ };
123
+ }
124
+ function toPrompt(row) {
125
+ return {
126
+ id: row.id,
127
+ name: row.name,
128
+ version: row.version,
129
+ content: row.content,
130
+ created_at: row.created_at
131
+ };
132
+ }
10
133
  var RunStore = class {
11
134
  #db;
12
135
  #statements = /* @__PURE__ */ new Map();
@@ -18,6 +141,21 @@ var RunStore = class {
18
141
  }
19
142
  return stmt;
20
143
  }
144
+ #get(columns, sql, ...params) {
145
+ const row = this.#stmt(sql).get(...params);
146
+ return row === void 0 ? void 0 : readRow(row, columns, sql);
147
+ }
148
+ /** Reads an aggregate query, which returns exactly one row. */
149
+ #one(columns, sql, ...params) {
150
+ const row = this.#get(columns, sql, ...params);
151
+ if (row === void 0) {
152
+ throw new Error(`Expected one row from: ${oneLine(sql)}`);
153
+ }
154
+ return row;
155
+ }
156
+ #all(columns, sql, ...params) {
157
+ return this.#stmt(sql).all(...params).map((row) => readRow(row, columns, sql));
158
+ }
21
159
  #transaction(fn) {
22
160
  this.#db.exec("BEGIN TRANSACTION");
23
161
  try {
@@ -45,7 +183,7 @@ var RunStore = class {
45
183
  );
46
184
  }
47
185
  #migratePromptsTableIfNeeded() {
48
- const columns = this.#stmt("PRAGMA table_info(prompts)").all();
186
+ const columns = this.#all({ name: "text" }, "PRAGMA table_info(prompts)");
49
187
  if (columns.length === 0) return;
50
188
  if (columns.some((column) => column.name === "version")) return;
51
189
  this.#transaction(() => {
@@ -75,13 +213,17 @@ var RunStore = class {
75
213
  });
76
214
  }
77
215
  #migrateRunsTableToSuiteRequired() {
78
- const runColumns = this.#stmt("PRAGMA table_info(runs)").all();
79
- if (runColumns.length === 0) return;
80
- const suiteColumn = runColumns.find((column) => column.name === "suite_id");
216
+ const columns = this.#all(
217
+ { name: "text", notnull: "number" },
218
+ "PRAGMA table_info(runs)"
219
+ );
220
+ if (columns.length === 0) return;
221
+ const suiteColumn = columns.find((column) => column.name === "suite_id");
81
222
  const hasNonNullSuite = suiteColumn?.notnull === 1;
82
- const runForeignKeys = this.#stmt(
223
+ const runForeignKeys = this.#all(
224
+ { from: "text", on_delete: "text", table: "text" },
83
225
  "PRAGMA foreign_key_list(runs)"
84
- ).all();
226
+ );
85
227
  const suiteForeignKey = runForeignKeys.find(
86
228
  (fk) => fk.from === "suite_id" && fk.table === "suites"
87
229
  );
@@ -130,8 +272,12 @@ var RunStore = class {
130
272
  return { id, name, created_at: now };
131
273
  }
132
274
  getSuite(id) {
133
- const row = this.#stmt("SELECT * FROM suites WHERE id = ?").get(id);
134
- return row ?? void 0;
275
+ const row = this.#get(
276
+ suiteColumns,
277
+ "SELECT * FROM suites WHERE id = ?",
278
+ id
279
+ );
280
+ return row ? toSuite(row) : void 0;
135
281
  }
136
282
  renameSuite(id, name) {
137
283
  this.#stmt("UPDATE suites SET name = ? WHERE id = ?").run(name, id);
@@ -191,77 +337,45 @@ var RunStore = class {
191
337
  });
192
338
  }
193
339
  getRun(runId) {
194
- const row = this.#stmt("SELECT * FROM runs WHERE id = ?").get(runId);
195
- if (!row) return void 0;
196
- return {
197
- id: row.id,
198
- suite_id: row.suite_id,
199
- name: row.name,
200
- model: row.model,
201
- config: row.config ? JSON.parse(row.config) : null,
202
- started_at: row.started_at,
203
- finished_at: row.finished_at,
204
- status: row.status,
205
- summary: row.summary ? JSON.parse(row.summary) : null
206
- };
340
+ const row = this.#get(runColumns, "SELECT * FROM runs WHERE id = ?", runId);
341
+ return row ? toRun(row) : void 0;
207
342
  }
208
343
  listRuns(suiteId) {
209
- const sql = suiteId ? "SELECT * FROM runs WHERE suite_id = ? ORDER BY started_at" : "SELECT * FROM runs ORDER BY started_at";
210
- const rows = suiteId ? this.#stmt(sql).all(suiteId) : this.#stmt(sql).all();
211
- return rows.map((row) => ({
212
- id: row.id,
213
- suite_id: row.suite_id,
214
- name: row.name,
215
- model: row.model,
216
- config: row.config ? JSON.parse(row.config) : null,
217
- started_at: row.started_at,
218
- finished_at: row.finished_at,
219
- status: row.status,
220
- summary: row.summary ? JSON.parse(row.summary) : null
221
- }));
344
+ const rows = suiteId ? this.#all(
345
+ runColumns,
346
+ "SELECT * FROM runs WHERE suite_id = ? ORDER BY started_at",
347
+ suiteId
348
+ ) : this.#all(runColumns, "SELECT * FROM runs ORDER BY started_at");
349
+ return rows.map(toRun);
222
350
  }
223
351
  getCases(runId) {
224
- const rows = this.#stmt(
225
- "SELECT * FROM cases WHERE run_id = ? ORDER BY idx"
226
- ).all(runId);
227
- return rows.map((row) => ({
228
- id: row.id,
229
- run_id: row.run_id,
230
- idx: row.idx,
231
- input: JSON.parse(row.input),
232
- output: row.output,
233
- expected: row.expected ? JSON.parse(row.expected) : null,
234
- latency_ms: row.latency_ms,
235
- tokens_in: row.tokens_in,
236
- tokens_out: row.tokens_out,
237
- error: row.error
238
- }));
352
+ return this.#all(
353
+ caseColumns,
354
+ "SELECT * FROM cases WHERE run_id = ? ORDER BY idx",
355
+ runId
356
+ ).map(toCase);
239
357
  }
240
358
  getFailingCases(runId, threshold = 0.5) {
241
- const rows = this.#stmt(
359
+ const rows = this.#all(
360
+ {
361
+ ...caseColumns,
362
+ scorer_name: "text",
363
+ score: "number",
364
+ score_reason: "text?"
365
+ },
242
366
  `SELECT c.*, s.scorer_name, s.score, s.reason as score_reason
243
367
  FROM cases c
244
368
  JOIN scores s ON s.case_id = c.id
245
369
  WHERE c.run_id = ? AND s.score < ?
246
- ORDER BY c.idx`
247
- ).all(runId, threshold);
370
+ ORDER BY c.idx`,
371
+ runId,
372
+ threshold
373
+ );
248
374
  const caseMap = /* @__PURE__ */ new Map();
249
375
  for (const row of rows) {
250
376
  let c = caseMap.get(row.id);
251
377
  if (!c) {
252
- c = {
253
- id: row.id,
254
- run_id: row.run_id,
255
- idx: row.idx,
256
- input: JSON.parse(row.input),
257
- output: row.output,
258
- expected: row.expected ? JSON.parse(row.expected) : null,
259
- latency_ms: row.latency_ms,
260
- tokens_in: row.tokens_in,
261
- tokens_out: row.tokens_out,
262
- error: row.error,
263
- scores: []
264
- };
378
+ c = { ...toCase(row), scores: [] };
265
379
  caseMap.set(row.id, c);
266
380
  }
267
381
  c.scores.push({
@@ -273,33 +387,44 @@ var RunStore = class {
273
387
  return Array.from(caseMap.values());
274
388
  }
275
389
  getRunSummary(runId, threshold = 0.5) {
276
- const totals = this.#stmt(
390
+ const totals = this.#one(
391
+ {
392
+ totalCases: "number",
393
+ totalLatencyMs: "number",
394
+ totalTokensIn: "number",
395
+ totalTokensOut: "number"
396
+ },
277
397
  `SELECT
278
398
  COUNT(DISTINCT c.id) as totalCases,
279
399
  COALESCE(SUM(c.latency_ms), 0) as totalLatencyMs,
280
400
  COALESCE(SUM(c.tokens_in), 0) as totalTokensIn,
281
401
  COALESCE(SUM(c.tokens_out), 0) as totalTokensOut
282
- FROM cases c WHERE c.run_id = ?`
283
- ).get(runId);
284
- const scorerMeans = this.#stmt(
402
+ FROM cases c WHERE c.run_id = ?`,
403
+ runId
404
+ );
405
+ const scorerMeans = this.#all(
406
+ { scorer_name: "text", meanScore: "number" },
285
407
  `SELECT s.scorer_name, AVG(s.score) as meanScore
286
408
  FROM scores s
287
409
  JOIN cases c ON c.id = s.case_id
288
410
  WHERE c.run_id = ?
289
- GROUP BY s.scorer_name`
290
- ).all(runId);
411
+ GROUP BY s.scorer_name`,
412
+ runId
413
+ );
291
414
  const meanScores = {};
292
415
  for (const row of scorerMeans) {
293
416
  meanScores[row.scorer_name] = row.meanScore;
294
417
  }
295
- const passFail = this.#stmt(
418
+ const passFail = this.#all(
419
+ { id: "text", minScore: "number" },
296
420
  `SELECT c.id,
297
421
  MIN(s.score) as minScore
298
422
  FROM cases c
299
423
  JOIN scores s ON s.case_id = c.id
300
424
  WHERE c.run_id = ?
301
- GROUP BY c.id`
302
- ).all(runId);
425
+ GROUP BY c.id`,
426
+ runId
427
+ );
303
428
  let passCount = 0;
304
429
  let failCount = 0;
305
430
  for (const row of passFail) {
@@ -317,71 +442,61 @@ var RunStore = class {
317
442
  };
318
443
  }
319
444
  findSuiteByName(name) {
320
- const row = this.#stmt(
321
- "SELECT * FROM suites WHERE name = ? ORDER BY created_at DESC LIMIT 1"
322
- ).get(name);
323
- return row ?? void 0;
445
+ const row = this.#get(
446
+ suiteColumns,
447
+ "SELECT * FROM suites WHERE name = ? ORDER BY created_at DESC LIMIT 1",
448
+ name
449
+ );
450
+ return row ? toSuite(row) : void 0;
324
451
  }
325
452
  getLatestCompletedRun(suiteId, model) {
326
- const sql = model ? "SELECT * FROM runs WHERE suite_id = ? AND status = ? AND model = ? ORDER BY started_at DESC LIMIT 1" : "SELECT * FROM runs WHERE suite_id = ? AND status = ? ORDER BY started_at DESC LIMIT 1";
327
- const row = model ? this.#stmt(sql).get(suiteId, "completed", model) : this.#stmt(sql).get(suiteId, "completed");
328
- if (!row) return void 0;
329
- return {
330
- id: row.id,
331
- suite_id: row.suite_id,
332
- name: row.name,
333
- model: row.model,
334
- config: row.config ? JSON.parse(row.config) : null,
335
- started_at: row.started_at,
336
- finished_at: row.finished_at,
337
- status: row.status,
338
- summary: row.summary ? JSON.parse(row.summary) : null
339
- };
453
+ const row = model ? this.#get(
454
+ runColumns,
455
+ "SELECT * FROM runs WHERE suite_id = ? AND status = ? AND model = ? ORDER BY started_at DESC LIMIT 1",
456
+ suiteId,
457
+ "completed",
458
+ model
459
+ ) : this.#get(
460
+ runColumns,
461
+ "SELECT * FROM runs WHERE suite_id = ? AND status = ? ORDER BY started_at DESC LIMIT 1",
462
+ suiteId,
463
+ "completed"
464
+ );
465
+ return row ? toRun(row) : void 0;
340
466
  }
341
467
  listSuites() {
342
- const rows = this.#stmt(
468
+ return this.#all(
469
+ suiteColumns,
343
470
  "SELECT * FROM suites ORDER BY created_at DESC"
344
- ).all();
345
- return rows.map((row) => ({
346
- id: row.id,
347
- name: row.name,
348
- created_at: row.created_at
349
- }));
471
+ ).map(toSuite);
350
472
  }
351
473
  createPrompt(name, content) {
352
474
  const id = crypto.randomUUID();
353
475
  const now = Date.now();
354
- const latest = this.#stmt(
355
- "SELECT MAX(version) as latestVersion FROM prompts WHERE name = ?"
356
- ).get(name);
357
- const version = (latest?.latestVersion ?? 0) + 1;
476
+ const { latestVersion } = this.#one(
477
+ { latestVersion: "number?" },
478
+ "SELECT MAX(version) as latestVersion FROM prompts WHERE name = ?",
479
+ name
480
+ );
481
+ const version = (latestVersion ?? 0) + 1;
358
482
  this.#stmt(
359
483
  "INSERT INTO prompts (id, name, version, content, created_at) VALUES (?, ?, ?, ?, ?)"
360
484
  ).run(id, name, version, content, now);
361
485
  return { id, name, version, content, created_at: now };
362
486
  }
363
487
  listPrompts() {
364
- const rows = this.#stmt(
488
+ return this.#all(
489
+ promptColumns,
365
490
  "SELECT * FROM prompts ORDER BY name COLLATE NOCASE ASC, version DESC"
366
- ).all();
367
- return rows.map((row) => ({
368
- id: row.id,
369
- name: row.name,
370
- version: row.version,
371
- content: row.content,
372
- created_at: row.created_at
373
- }));
491
+ ).map(toPrompt);
374
492
  }
375
493
  getPrompt(id) {
376
- const row = this.#stmt("SELECT * FROM prompts WHERE id = ?").get(id);
377
- if (!row) return void 0;
378
- return {
379
- id: row.id,
380
- name: row.name,
381
- version: row.version,
382
- content: row.content,
383
- created_at: row.created_at
384
- };
494
+ const row = this.#get(
495
+ promptColumns,
496
+ "SELECT * FROM prompts WHERE id = ?",
497
+ id
498
+ );
499
+ return row ? toPrompt(row) : void 0;
385
500
  }
386
501
  deletePrompt(id) {
387
502
  this.#stmt("DELETE FROM prompts WHERE id = ?").run(id);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/store/index.ts", "../../src/store/ddl.sqlite.sql"],
4
- "sourcesContent": ["import { mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\n\nimport DDL from './ddl.sqlite.sql';\n\nexport interface SuiteRow {\n id: string;\n name: string;\n created_at: number;\n}\n\nexport interface RunRow {\n id: string;\n suite_id: string;\n name: string;\n model: string;\n config: Record<string, unknown> | null;\n started_at: number;\n finished_at: number | null;\n status: 'running' | 'completed' | 'failed';\n summary: RunSummary | null;\n}\n\nexport interface CaseRow {\n id: string;\n run_id: string;\n idx: number;\n input: unknown;\n output: string | null;\n expected: unknown | null;\n latency_ms: number;\n tokens_in: number;\n tokens_out: number;\n error: string | null;\n}\n\nexport interface CaseWithScores extends CaseRow {\n scores: Array<{ scorer_name: string; score: number; reason: string | null }>;\n}\n\nexport interface ScoreRow {\n id: string;\n case_id: string;\n scorer_name: string;\n score: number;\n reason: string | null;\n}\n\nexport interface RunSummary {\n totalCases: number;\n passCount: number;\n failCount: number;\n meanScores: Record<string, number>;\n totalLatencyMs: number;\n totalTokensIn: number;\n totalTokensOut: number;\n}\n\nexport interface PromptRow {\n id: string;\n name: string;\n version: number;\n content: string;\n created_at: number;\n}\n\nexport interface CaseData {\n id: string;\n run_id: string;\n idx: number;\n input: unknown;\n output: string | null;\n expected?: unknown;\n latency_ms: number;\n tokens_in: number;\n tokens_out: number;\n error?: string;\n}\n\nexport interface ScoreData {\n id: string;\n case_id: string;\n scorer_name: string;\n score: number;\n reason?: string;\n}\n\nexport class RunStore {\n #db: DatabaseSync;\n #statements = new Map<string, ReturnType<DatabaseSync['prepare']>>();\n\n #stmt(sql: string): ReturnType<DatabaseSync['prepare']> {\n let stmt = this.#statements.get(sql);\n if (!stmt) {\n stmt = this.#db.prepare(sql);\n this.#statements.set(sql, stmt);\n }\n return stmt;\n }\n\n #transaction<T>(fn: () => T): T {\n this.#db.exec('BEGIN TRANSACTION');\n try {\n const result = fn();\n this.#db.exec('COMMIT');\n return result;\n } catch (error) {\n this.#db.exec('ROLLBACK');\n throw error;\n }\n }\n\n constructor(pathOrDb?: string | DatabaseSync) {\n if (pathOrDb instanceof DatabaseSync) {\n this.#db = pathOrDb;\n } else {\n const dbPath = pathOrDb ?? '.evals/store.db';\n mkdirSync(dirname(dbPath), { recursive: true });\n this.#db = new DatabaseSync(dbPath);\n }\n this.#db.exec(DDL);\n this.#migrateRunsTableToSuiteRequired();\n this.#migratePromptsTableIfNeeded();\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_prompts_name_version ON prompts(name, version DESC)',\n );\n }\n\n #migratePromptsTableIfNeeded(): void {\n const columns = this.#stmt('PRAGMA table_info(prompts)').all() as Array<{\n name: string;\n }>;\n\n if (columns.length === 0) return;\n if (columns.some((column) => column.name === 'version')) return;\n\n this.#transaction(() => {\n this.#db.exec('ALTER TABLE prompts RENAME TO prompts_legacy');\n this.#db.exec(`\n CREATE TABLE prompts (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n version INTEGER NOT NULL,\n content TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),\n UNIQUE(name, version)\n )\n `);\n this.#db.exec(`\n INSERT INTO prompts (id, name, version, content, created_at)\n SELECT id, name, 1, content, created_at\n FROM prompts_legacy\n `);\n this.#db.exec('DROP TABLE prompts_legacy');\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_prompts_created_at ON prompts(created_at)',\n );\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_prompts_name_version ON prompts(name, version DESC)',\n );\n });\n }\n\n #migrateRunsTableToSuiteRequired(): void {\n const runColumns = this.#stmt('PRAGMA table_info(runs)').all() as Array<{\n name: string;\n notnull: number;\n }>;\n\n if (runColumns.length === 0) return;\n\n const suiteColumn = runColumns.find((column) => column.name === 'suite_id');\n const hasNonNullSuite = suiteColumn?.notnull === 1;\n\n const runForeignKeys = this.#stmt(\n 'PRAGMA foreign_key_list(runs)',\n ).all() as Array<{\n from: string;\n on_delete: string;\n table: string;\n }>;\n const suiteForeignKey = runForeignKeys.find(\n (fk) => fk.from === 'suite_id' && fk.table === 'suites',\n );\n const hasCascadeDelete = suiteForeignKey?.on_delete === 'CASCADE';\n\n if (hasNonNullSuite && hasCascadeDelete) return;\n\n this.#statements.clear();\n this.#transaction(() => {\n this.#db.exec(`\n CREATE TABLE runs_next (\n id TEXT PRIMARY KEY,\n suite_id TEXT NOT NULL,\n name TEXT NOT NULL,\n model TEXT NOT NULL,\n config TEXT,\n started_at INTEGER NOT NULL,\n finished_at INTEGER,\n status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed')),\n summary TEXT,\n FOREIGN KEY (suite_id) REFERENCES suites(id) ON DELETE CASCADE\n )\n `);\n\n // Drop legacy orphaned runs that do not belong to a suite.\n this.#db.exec('DELETE FROM runs WHERE suite_id IS NULL');\n\n this.#db.exec(`\n INSERT INTO runs_next (id, suite_id, name, model, config, started_at, finished_at, status, summary)\n SELECT r.id, r.suite_id, r.name, r.model, r.config, r.started_at, r.finished_at, r.status, r.summary\n FROM runs r\n JOIN suites s ON s.id = r.suite_id\n `);\n\n this.#db.exec('DROP TABLE runs');\n this.#db.exec('ALTER TABLE runs_next RENAME TO runs');\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_runs_suite_id ON runs(suite_id)',\n );\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_runs_started_at ON runs(started_at)',\n );\n });\n this.#statements.clear();\n }\n\n createSuite(name: string): SuiteRow {\n const id = crypto.randomUUID();\n const now = Date.now();\n this.#stmt(\n 'INSERT INTO suites (id, name, created_at) VALUES (?, ?, ?)',\n ).run(id, name, now);\n return { id, name, created_at: now };\n }\n\n getSuite(id: string): SuiteRow | undefined {\n const row = this.#stmt('SELECT * FROM suites WHERE id = ?').get(id) as\n | { id: string; name: string; created_at: number }\n | undefined;\n return row ?? undefined;\n }\n\n renameSuite(id: string, name: string): void {\n this.#stmt('UPDATE suites SET name = ? WHERE id = ?').run(name, id);\n }\n\n renameRun(id: string, name: string): void {\n this.#stmt('UPDATE runs SET name = ? WHERE id = ?').run(name, id);\n }\n\n createRun(run: {\n suite_id: string;\n name: string;\n model: string;\n config?: Record<string, unknown>;\n }): string {\n const id = crypto.randomUUID();\n const now = Date.now();\n this.#stmt(\n 'INSERT INTO runs (id, suite_id, name, model, config, started_at) VALUES (?, ?, ?, ?, ?, ?)',\n ).run(\n id,\n run.suite_id,\n run.name,\n run.model,\n run.config ? JSON.stringify(run.config) : null,\n now,\n );\n return id;\n }\n\n finishRun(\n runId: string,\n status: 'completed' | 'failed',\n summary?: RunSummary,\n ): void {\n this.#stmt(\n 'UPDATE runs SET finished_at = ?, status = ?, summary = ? WHERE id = ?',\n ).run(Date.now(), status, summary ? JSON.stringify(summary) : null, runId);\n }\n\n saveCases(cases: CaseData[]): void {\n this.#transaction(() => {\n const stmt = this.#stmt(\n 'INSERT INTO cases (id, run_id, idx, input, output, expected, latency_ms, tokens_in, tokens_out, error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',\n );\n for (const c of cases) {\n stmt.run(\n c.id,\n c.run_id,\n c.idx,\n JSON.stringify(c.input),\n c.output,\n c.expected != null ? JSON.stringify(c.expected) : null,\n c.latency_ms,\n c.tokens_in,\n c.tokens_out,\n c.error ?? null,\n );\n }\n });\n }\n\n saveScores(scores: ScoreData[]): void {\n this.#transaction(() => {\n const stmt = this.#stmt(\n 'INSERT INTO scores (id, case_id, scorer_name, score, reason) VALUES (?, ?, ?, ?, ?)',\n );\n for (const s of scores) {\n stmt.run(s.id, s.case_id, s.scorer_name, s.score, s.reason ?? null);\n }\n });\n }\n\n getRun(runId: string): RunRow | undefined {\n const row = this.#stmt('SELECT * FROM runs WHERE id = ?').get(runId) as\n | {\n id: string;\n suite_id: string;\n name: string;\n model: string;\n config: string | null;\n started_at: number;\n finished_at: number | null;\n status: string;\n summary: string | null;\n }\n | undefined;\n if (!row) return undefined;\n return {\n id: row.id,\n suite_id: row.suite_id,\n name: row.name,\n model: row.model,\n config: row.config ? JSON.parse(row.config) : null,\n started_at: row.started_at,\n finished_at: row.finished_at,\n status: row.status as RunRow['status'],\n summary: row.summary ? JSON.parse(row.summary) : null,\n };\n }\n\n listRuns(suiteId?: string): RunRow[] {\n const sql = suiteId\n ? 'SELECT * FROM runs WHERE suite_id = ? ORDER BY started_at'\n : 'SELECT * FROM runs ORDER BY started_at';\n const rows = (\n suiteId ? this.#stmt(sql).all(suiteId) : this.#stmt(sql).all()\n ) as Array<{\n id: string;\n suite_id: string;\n name: string;\n model: string;\n config: string | null;\n started_at: number;\n finished_at: number | null;\n status: string;\n summary: string | null;\n }>;\n return rows.map((row) => ({\n id: row.id,\n suite_id: row.suite_id,\n name: row.name,\n model: row.model,\n config: row.config ? JSON.parse(row.config) : null,\n started_at: row.started_at,\n finished_at: row.finished_at,\n status: row.status as RunRow['status'],\n summary: row.summary ? JSON.parse(row.summary) : null,\n }));\n }\n\n getCases(runId: string): CaseRow[] {\n const rows = this.#stmt(\n 'SELECT * FROM cases WHERE run_id = ? ORDER BY idx',\n ).all(runId) as Array<{\n id: string;\n run_id: string;\n idx: number;\n input: string;\n output: string | null;\n expected: string | null;\n latency_ms: number;\n tokens_in: number;\n tokens_out: number;\n error: string | null;\n }>;\n return rows.map((row) => ({\n id: row.id,\n run_id: row.run_id,\n idx: row.idx,\n input: JSON.parse(row.input),\n output: row.output,\n expected: row.expected ? JSON.parse(row.expected) : null,\n latency_ms: row.latency_ms,\n tokens_in: row.tokens_in,\n tokens_out: row.tokens_out,\n error: row.error,\n }));\n }\n\n getFailingCases(runId: string, threshold = 0.5): CaseWithScores[] {\n const rows = this.#stmt(\n `SELECT c.*, s.scorer_name, s.score, s.reason as score_reason\n FROM cases c\n JOIN scores s ON s.case_id = c.id\n WHERE c.run_id = ? AND s.score < ?\n ORDER BY c.idx`,\n ).all(runId, threshold) as Array<{\n id: string;\n run_id: string;\n idx: number;\n input: string;\n output: string | null;\n expected: string | null;\n latency_ms: number;\n tokens_in: number;\n tokens_out: number;\n error: string | null;\n scorer_name: string;\n score: number;\n score_reason: string | null;\n }>;\n\n const caseMap = new Map<string, CaseWithScores>();\n for (const row of rows) {\n let c = caseMap.get(row.id);\n if (!c) {\n c = {\n id: row.id,\n run_id: row.run_id,\n idx: row.idx,\n input: JSON.parse(row.input),\n output: row.output,\n expected: row.expected ? JSON.parse(row.expected) : null,\n latency_ms: row.latency_ms,\n tokens_in: row.tokens_in,\n tokens_out: row.tokens_out,\n error: row.error,\n scores: [],\n };\n caseMap.set(row.id, c);\n }\n c.scores.push({\n scorer_name: row.scorer_name,\n score: row.score,\n reason: row.score_reason,\n });\n }\n return Array.from(caseMap.values());\n }\n\n getRunSummary(runId: string, threshold = 0.5): RunSummary {\n const totals = this.#stmt(\n `SELECT\n COUNT(DISTINCT c.id) as totalCases,\n COALESCE(SUM(c.latency_ms), 0) as totalLatencyMs,\n COALESCE(SUM(c.tokens_in), 0) as totalTokensIn,\n COALESCE(SUM(c.tokens_out), 0) as totalTokensOut\n FROM cases c WHERE c.run_id = ?`,\n ).get(runId) as {\n totalCases: number;\n totalLatencyMs: number;\n totalTokensIn: number;\n totalTokensOut: number;\n };\n\n const scorerMeans = this.#stmt(\n `SELECT s.scorer_name, AVG(s.score) as meanScore\n FROM scores s\n JOIN cases c ON c.id = s.case_id\n WHERE c.run_id = ?\n GROUP BY s.scorer_name`,\n ).all(runId) as Array<{ scorer_name: string; meanScore: number }>;\n\n const meanScores: Record<string, number> = {};\n for (const row of scorerMeans) {\n meanScores[row.scorer_name] = row.meanScore;\n }\n\n const passFail = this.#stmt(\n `SELECT c.id,\n MIN(s.score) as minScore\n FROM cases c\n JOIN scores s ON s.case_id = c.id\n WHERE c.run_id = ?\n GROUP BY c.id`,\n ).all(runId) as Array<{ id: string; minScore: number }>;\n\n let passCount = 0;\n let failCount = 0;\n for (const row of passFail) {\n if (row.minScore >= threshold) passCount++;\n else failCount++;\n }\n\n return {\n totalCases: totals.totalCases,\n passCount,\n failCount,\n meanScores,\n totalLatencyMs: totals.totalLatencyMs,\n totalTokensIn: totals.totalTokensIn,\n totalTokensOut: totals.totalTokensOut,\n };\n }\n\n findSuiteByName(name: string): SuiteRow | undefined {\n const row = this.#stmt(\n 'SELECT * FROM suites WHERE name = ? ORDER BY created_at DESC LIMIT 1',\n ).get(name) as { id: string; name: string; created_at: number } | undefined;\n return row ?? undefined;\n }\n\n getLatestCompletedRun(suiteId: string, model?: string): RunRow | undefined {\n const sql = model\n ? 'SELECT * FROM runs WHERE suite_id = ? AND status = ? AND model = ? ORDER BY started_at DESC LIMIT 1'\n : 'SELECT * FROM runs WHERE suite_id = ? AND status = ? ORDER BY started_at DESC LIMIT 1';\n const row = (\n model\n ? this.#stmt(sql).get(suiteId, 'completed', model)\n : this.#stmt(sql).get(suiteId, 'completed')\n ) as\n | {\n id: string;\n suite_id: string;\n name: string;\n model: string;\n config: string | null;\n started_at: number;\n finished_at: number | null;\n status: string;\n summary: string | null;\n }\n | undefined;\n if (!row) return undefined;\n return {\n id: row.id,\n suite_id: row.suite_id,\n name: row.name,\n model: row.model,\n config: row.config ? JSON.parse(row.config) : null,\n started_at: row.started_at,\n finished_at: row.finished_at,\n status: row.status as RunRow['status'],\n summary: row.summary ? JSON.parse(row.summary) : null,\n };\n }\n\n listSuites(): SuiteRow[] {\n const rows = this.#stmt(\n 'SELECT * FROM suites ORDER BY created_at DESC',\n ).all() as Array<{ id: string; name: string; created_at: number }>;\n return rows.map((row) => ({\n id: row.id,\n name: row.name,\n created_at: row.created_at,\n }));\n }\n\n createPrompt(name: string, content: string): PromptRow {\n const id = crypto.randomUUID();\n const now = Date.now();\n\n const latest = this.#stmt(\n 'SELECT MAX(version) as latestVersion FROM prompts WHERE name = ?',\n ).get(name) as { latestVersion: number | null } | undefined;\n const version = (latest?.latestVersion ?? 0) + 1;\n\n this.#stmt(\n 'INSERT INTO prompts (id, name, version, content, created_at) VALUES (?, ?, ?, ?, ?)',\n ).run(id, name, version, content, now);\n return { id, name, version, content, created_at: now };\n }\n\n listPrompts(): PromptRow[] {\n const rows = this.#stmt(\n 'SELECT * FROM prompts ORDER BY name COLLATE NOCASE ASC, version DESC',\n ).all() as Array<{\n id: string;\n name: string;\n version: number;\n content: string;\n created_at: number;\n }>;\n return rows.map((row) => ({\n id: row.id,\n name: row.name,\n version: row.version,\n content: row.content,\n created_at: row.created_at,\n }));\n }\n\n getPrompt(id: string): PromptRow | undefined {\n const row = this.#stmt('SELECT * FROM prompts WHERE id = ?').get(id) as\n | {\n id: string;\n name: string;\n version: number;\n content: string;\n created_at: number;\n }\n | undefined;\n if (!row) return undefined;\n return {\n id: row.id,\n name: row.name,\n version: row.version,\n content: row.content,\n created_at: row.created_at,\n };\n }\n\n deletePrompt(id: string): void {\n this.#stmt('DELETE FROM prompts WHERE id = ?').run(id);\n }\n\n resetRun(id: string): void {\n this.#transaction(() => {\n this.#stmt('DELETE FROM cases WHERE run_id = ?').run(id);\n this.#stmt(\n 'UPDATE runs SET status = ?, started_at = ?, finished_at = NULL, summary = NULL WHERE id = ?',\n ).run('running', Date.now(), id);\n });\n }\n\n deleteRun(id: string): void {\n this.#stmt('DELETE FROM runs WHERE id = ?').run(id);\n }\n\n deleteSuite(id: string): void {\n this.#stmt('DELETE FROM suites WHERE id = ?').run(id);\n }\n}\n", "PRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA foreign_keys = ON;\n\nCREATE TABLE IF NOT EXISTS suites (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n);\n\nCREATE TABLE IF NOT EXISTS runs (\n id TEXT PRIMARY KEY,\n suite_id TEXT NOT NULL,\n name TEXT NOT NULL,\n model TEXT NOT NULL,\n config TEXT,\n started_at INTEGER NOT NULL,\n finished_at INTEGER,\n status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed')),\n summary TEXT,\n FOREIGN KEY (suite_id) REFERENCES suites(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_runs_suite_id ON runs(suite_id);\nCREATE INDEX IF NOT EXISTS idx_runs_started_at ON runs(started_at);\n\nCREATE TABLE IF NOT EXISTS cases (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL,\n idx INTEGER NOT NULL,\n input TEXT NOT NULL,\n output TEXT,\n expected TEXT,\n latency_ms INTEGER,\n tokens_in INTEGER,\n tokens_out INTEGER,\n error TEXT,\n FOREIGN KEY (run_id) REFERENCES runs(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_cases_run_id ON cases(run_id);\n\nCREATE TABLE IF NOT EXISTS scores (\n id TEXT PRIMARY KEY,\n case_id TEXT NOT NULL,\n scorer_name TEXT NOT NULL,\n score REAL NOT NULL,\n reason TEXT,\n FOREIGN KEY (case_id) REFERENCES cases(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_scores_case_id ON scores(case_id);\n\nCREATE TABLE IF NOT EXISTS prompts (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n content TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n);\n\nCREATE INDEX IF NOT EXISTS idx_prompts_created_at ON prompts(created_at);\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport {\n DatabaseSync,\n type SQLInputValue,\n type SQLOutputValue,\n} from 'node:sqlite';\n\nimport DDL from './ddl.sqlite.sql';\n\nexport interface SuiteRow {\n id: string;\n name: string;\n created_at: number;\n}\n\nexport interface RunRow {\n id: string;\n suite_id: string;\n name: string;\n model: string;\n config: Record<string, unknown> | null;\n started_at: number;\n finished_at: number | null;\n status: RunStatus;\n summary: RunSummary | null;\n}\n\nexport interface CaseRow {\n id: string;\n run_id: string;\n idx: number;\n input: unknown;\n output: string | null;\n expected: unknown | null;\n latency_ms: number | null;\n tokens_in: number | null;\n tokens_out: number | null;\n error: string | null;\n}\n\nexport interface CaseWithScores extends CaseRow {\n scores: Array<{ scorer_name: string; score: number; reason: string | null }>;\n}\n\nexport interface ScoreRow {\n id: string;\n case_id: string;\n scorer_name: string;\n score: number;\n reason: string | null;\n}\n\nexport interface RunSummary {\n totalCases: number;\n passCount: number;\n failCount: number;\n meanScores: Record<string, number>;\n totalLatencyMs: number;\n totalTokensIn: number;\n totalTokensOut: number;\n}\n\nexport interface PromptRow {\n id: string;\n name: string;\n version: number;\n content: string;\n created_at: number;\n}\n\nexport interface CaseData {\n id: string;\n run_id: string;\n idx: number;\n input: unknown;\n output: string | null;\n expected?: unknown;\n latency_ms: number;\n tokens_in: number;\n tokens_out: number;\n error?: string;\n}\n\nexport interface ScoreData {\n id: string;\n case_id: string;\n scorer_name: string;\n score: number;\n reason?: string;\n}\n\nconst RUN_STATUSES = ['running', 'completed', 'failed'] as const;\n\ntype RunStatus = (typeof RUN_STATUSES)[number];\n\nfunction isRunStatus(value: string): value is RunStatus {\n return RUN_STATUSES.some((status) => status === value);\n}\n\ntype SqlRow = Record<string, SQLOutputValue>;\n\n/** What node:sqlite returns for each kind of column the store reads. */\ninterface ColumnTypes {\n text: string;\n 'text?': string | null;\n number: number;\n 'number?': number | null;\n}\n\ntype ColumnType = keyof ColumnTypes;\n\ntype Columns = Readonly<Record<string, ColumnType>>;\n\ntype RowOf<C extends Columns> = { -readonly [K in keyof C]: ColumnTypes[C[K]] };\n\nfunction matchesColumn(\n value: SQLOutputValue | undefined,\n type: ColumnType,\n): boolean {\n switch (type) {\n case 'text':\n return typeof value === 'string';\n case 'text?':\n return value === null || typeof value === 'string';\n case 'number':\n return typeof value === 'number';\n case 'number?':\n return value === null || typeof value === 'number';\n }\n}\n\nfunction mismatchedColumns(row: SqlRow, columns: Columns): string[] {\n return Object.entries(columns)\n .filter(([name, type]) => !matchesColumn(row[name], type))\n .map(([name]) => name);\n}\n\nfunction hasColumns<C extends Columns>(\n row: SqlRow,\n columns: C,\n): row is SqlRow & RowOf<C> {\n return mismatchedColumns(row, columns).length === 0;\n}\n\nfunction readRow<C extends Columns>(\n row: SqlRow,\n columns: C,\n sql: string,\n): RowOf<C> {\n if (hasColumns(row, columns)) return row;\n throw new TypeError(\n `Unexpected values in columns ${mismatchedColumns(row, columns).join(', ')} of a row from: ${oneLine(sql)}`,\n );\n}\n\nfunction oneLine(sql: string): string {\n return sql.replace(/\\s+/g, ' ').trim();\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isRunSummary(value: unknown): value is RunSummary {\n return (\n isRecord(value) &&\n typeof value.totalCases === 'number' &&\n typeof value.passCount === 'number' &&\n typeof value.failCount === 'number' &&\n typeof value.totalLatencyMs === 'number' &&\n typeof value.totalTokensIn === 'number' &&\n typeof value.totalTokensOut === 'number' &&\n isRecord(value.meanScores) &&\n Object.values(value.meanScores).every((score) => typeof score === 'number')\n );\n}\n\nfunction parseJson<T>(\n text: string,\n guard: (value: unknown) => value is T,\n source: string,\n): T {\n const value: unknown = JSON.parse(text);\n if (guard(value)) return value;\n throw new TypeError(`Unexpected JSON in ${source}`);\n}\n\nconst suiteColumns = {\n id: 'text',\n name: 'text',\n created_at: 'number',\n} as const;\n\nconst runColumns = {\n id: 'text',\n suite_id: 'text',\n name: 'text',\n model: 'text',\n config: 'text?',\n started_at: 'number',\n finished_at: 'number?',\n status: 'text',\n summary: 'text?',\n} as const;\n\nconst caseColumns = {\n id: 'text',\n run_id: 'text',\n idx: 'number',\n input: 'text',\n output: 'text?',\n expected: 'text?',\n latency_ms: 'number?',\n tokens_in: 'number?',\n tokens_out: 'number?',\n error: 'text?',\n} as const;\n\nconst promptColumns = {\n id: 'text',\n name: 'text',\n version: 'number',\n content: 'text',\n created_at: 'number',\n} as const;\n\nfunction toSuite(row: RowOf<typeof suiteColumns>): SuiteRow {\n return { id: row.id, name: row.name, created_at: row.created_at };\n}\n\nfunction toRun(row: RowOf<typeof runColumns>): RunRow {\n if (!isRunStatus(row.status)) {\n throw new TypeError(`Unexpected status \"${row.status}\" of run ${row.id}`);\n }\n return {\n id: row.id,\n suite_id: row.suite_id,\n name: row.name,\n model: row.model,\n config: row.config\n ? parseJson(row.config, isRecord, `config of run ${row.id}`)\n : null,\n started_at: row.started_at,\n finished_at: row.finished_at,\n status: row.status,\n summary: row.summary\n ? parseJson(row.summary, isRunSummary, `summary of run ${row.id}`)\n : null,\n };\n}\n\nfunction toCase(row: RowOf<typeof caseColumns>): CaseRow {\n const input: unknown = JSON.parse(row.input);\n const expected: unknown = row.expected ? JSON.parse(row.expected) : null;\n return {\n id: row.id,\n run_id: row.run_id,\n idx: row.idx,\n input,\n output: row.output,\n expected,\n latency_ms: row.latency_ms,\n tokens_in: row.tokens_in,\n tokens_out: row.tokens_out,\n error: row.error,\n };\n}\n\nfunction toPrompt(row: RowOf<typeof promptColumns>): PromptRow {\n return {\n id: row.id,\n name: row.name,\n version: row.version,\n content: row.content,\n created_at: row.created_at,\n };\n}\n\nexport class RunStore {\n #db: DatabaseSync;\n #statements = new Map<string, ReturnType<DatabaseSync['prepare']>>();\n\n #stmt(sql: string): ReturnType<DatabaseSync['prepare']> {\n let stmt = this.#statements.get(sql);\n if (!stmt) {\n stmt = this.#db.prepare(sql);\n this.#statements.set(sql, stmt);\n }\n return stmt;\n }\n\n #get<const C extends Columns>(\n columns: C,\n sql: string,\n ...params: SQLInputValue[]\n ): RowOf<C> | undefined {\n const row = this.#stmt(sql).get(...params);\n return row === undefined ? undefined : readRow(row, columns, sql);\n }\n\n /** Reads an aggregate query, which returns exactly one row. */\n #one<const C extends Columns>(\n columns: C,\n sql: string,\n ...params: SQLInputValue[]\n ): RowOf<C> {\n const row = this.#get(columns, sql, ...params);\n if (row === undefined) {\n throw new Error(`Expected one row from: ${oneLine(sql)}`);\n }\n return row;\n }\n\n #all<const C extends Columns>(\n columns: C,\n sql: string,\n ...params: SQLInputValue[]\n ): RowOf<C>[] {\n return this.#stmt(sql)\n .all(...params)\n .map((row) => readRow(row, columns, sql));\n }\n\n #transaction<T>(fn: () => T): T {\n this.#db.exec('BEGIN TRANSACTION');\n try {\n const result = fn();\n this.#db.exec('COMMIT');\n return result;\n } catch (error) {\n this.#db.exec('ROLLBACK');\n throw error;\n }\n }\n\n constructor(pathOrDb?: string | DatabaseSync) {\n if (pathOrDb instanceof DatabaseSync) {\n this.#db = pathOrDb;\n } else {\n const dbPath = pathOrDb ?? '.evals/store.db';\n mkdirSync(dirname(dbPath), { recursive: true });\n this.#db = new DatabaseSync(dbPath);\n }\n this.#db.exec(DDL);\n this.#migrateRunsTableToSuiteRequired();\n this.#migratePromptsTableIfNeeded();\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_prompts_name_version ON prompts(name, version DESC)',\n );\n }\n\n #migratePromptsTableIfNeeded(): void {\n const columns = this.#all({ name: 'text' }, 'PRAGMA table_info(prompts)');\n\n if (columns.length === 0) return;\n if (columns.some((column) => column.name === 'version')) return;\n\n this.#transaction(() => {\n this.#db.exec('ALTER TABLE prompts RENAME TO prompts_legacy');\n this.#db.exec(`\n CREATE TABLE prompts (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n version INTEGER NOT NULL,\n content TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),\n UNIQUE(name, version)\n )\n `);\n this.#db.exec(`\n INSERT INTO prompts (id, name, version, content, created_at)\n SELECT id, name, 1, content, created_at\n FROM prompts_legacy\n `);\n this.#db.exec('DROP TABLE prompts_legacy');\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_prompts_created_at ON prompts(created_at)',\n );\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_prompts_name_version ON prompts(name, version DESC)',\n );\n });\n }\n\n #migrateRunsTableToSuiteRequired(): void {\n const columns = this.#all(\n { name: 'text', notnull: 'number' },\n 'PRAGMA table_info(runs)',\n );\n\n if (columns.length === 0) return;\n\n const suiteColumn = columns.find((column) => column.name === 'suite_id');\n const hasNonNullSuite = suiteColumn?.notnull === 1;\n\n const runForeignKeys = this.#all(\n { from: 'text', on_delete: 'text', table: 'text' },\n 'PRAGMA foreign_key_list(runs)',\n );\n const suiteForeignKey = runForeignKeys.find(\n (fk) => fk.from === 'suite_id' && fk.table === 'suites',\n );\n const hasCascadeDelete = suiteForeignKey?.on_delete === 'CASCADE';\n\n if (hasNonNullSuite && hasCascadeDelete) return;\n\n this.#statements.clear();\n this.#transaction(() => {\n this.#db.exec(`\n CREATE TABLE runs_next (\n id TEXT PRIMARY KEY,\n suite_id TEXT NOT NULL,\n name TEXT NOT NULL,\n model TEXT NOT NULL,\n config TEXT,\n started_at INTEGER NOT NULL,\n finished_at INTEGER,\n status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed')),\n summary TEXT,\n FOREIGN KEY (suite_id) REFERENCES suites(id) ON DELETE CASCADE\n )\n `);\n\n // Drop legacy orphaned runs that do not belong to a suite.\n this.#db.exec('DELETE FROM runs WHERE suite_id IS NULL');\n\n this.#db.exec(`\n INSERT INTO runs_next (id, suite_id, name, model, config, started_at, finished_at, status, summary)\n SELECT r.id, r.suite_id, r.name, r.model, r.config, r.started_at, r.finished_at, r.status, r.summary\n FROM runs r\n JOIN suites s ON s.id = r.suite_id\n `);\n\n this.#db.exec('DROP TABLE runs');\n this.#db.exec('ALTER TABLE runs_next RENAME TO runs');\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_runs_suite_id ON runs(suite_id)',\n );\n this.#db.exec(\n 'CREATE INDEX IF NOT EXISTS idx_runs_started_at ON runs(started_at)',\n );\n });\n this.#statements.clear();\n }\n\n createSuite(name: string): SuiteRow {\n const id = crypto.randomUUID();\n const now = Date.now();\n this.#stmt(\n 'INSERT INTO suites (id, name, created_at) VALUES (?, ?, ?)',\n ).run(id, name, now);\n return { id, name, created_at: now };\n }\n\n getSuite(id: string): SuiteRow | undefined {\n const row = this.#get(\n suiteColumns,\n 'SELECT * FROM suites WHERE id = ?',\n id,\n );\n return row ? toSuite(row) : undefined;\n }\n\n renameSuite(id: string, name: string): void {\n this.#stmt('UPDATE suites SET name = ? WHERE id = ?').run(name, id);\n }\n\n renameRun(id: string, name: string): void {\n this.#stmt('UPDATE runs SET name = ? WHERE id = ?').run(name, id);\n }\n\n createRun(run: {\n suite_id: string;\n name: string;\n model: string;\n config?: Record<string, unknown>;\n }): string {\n const id = crypto.randomUUID();\n const now = Date.now();\n this.#stmt(\n 'INSERT INTO runs (id, suite_id, name, model, config, started_at) VALUES (?, ?, ?, ?, ?, ?)',\n ).run(\n id,\n run.suite_id,\n run.name,\n run.model,\n run.config ? JSON.stringify(run.config) : null,\n now,\n );\n return id;\n }\n\n finishRun(\n runId: string,\n status: 'completed' | 'failed',\n summary?: RunSummary,\n ): void {\n this.#stmt(\n 'UPDATE runs SET finished_at = ?, status = ?, summary = ? WHERE id = ?',\n ).run(Date.now(), status, summary ? JSON.stringify(summary) : null, runId);\n }\n\n saveCases(cases: CaseData[]): void {\n this.#transaction(() => {\n const stmt = this.#stmt(\n 'INSERT INTO cases (id, run_id, idx, input, output, expected, latency_ms, tokens_in, tokens_out, error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',\n );\n for (const c of cases) {\n stmt.run(\n c.id,\n c.run_id,\n c.idx,\n JSON.stringify(c.input),\n c.output,\n c.expected != null ? JSON.stringify(c.expected) : null,\n c.latency_ms,\n c.tokens_in,\n c.tokens_out,\n c.error ?? null,\n );\n }\n });\n }\n\n saveScores(scores: ScoreData[]): void {\n this.#transaction(() => {\n const stmt = this.#stmt(\n 'INSERT INTO scores (id, case_id, scorer_name, score, reason) VALUES (?, ?, ?, ?, ?)',\n );\n for (const s of scores) {\n stmt.run(s.id, s.case_id, s.scorer_name, s.score, s.reason ?? null);\n }\n });\n }\n\n getRun(runId: string): RunRow | undefined {\n const row = this.#get(runColumns, 'SELECT * FROM runs WHERE id = ?', runId);\n return row ? toRun(row) : undefined;\n }\n\n listRuns(suiteId?: string): RunRow[] {\n const rows = suiteId\n ? this.#all(\n runColumns,\n 'SELECT * FROM runs WHERE suite_id = ? ORDER BY started_at',\n suiteId,\n )\n : this.#all(runColumns, 'SELECT * FROM runs ORDER BY started_at');\n return rows.map(toRun);\n }\n\n getCases(runId: string): CaseRow[] {\n return this.#all(\n caseColumns,\n 'SELECT * FROM cases WHERE run_id = ? ORDER BY idx',\n runId,\n ).map(toCase);\n }\n\n getFailingCases(runId: string, threshold = 0.5): CaseWithScores[] {\n const rows = this.#all(\n {\n ...caseColumns,\n scorer_name: 'text',\n score: 'number',\n score_reason: 'text?',\n },\n `SELECT c.*, s.scorer_name, s.score, s.reason as score_reason\n FROM cases c\n JOIN scores s ON s.case_id = c.id\n WHERE c.run_id = ? AND s.score < ?\n ORDER BY c.idx`,\n runId,\n threshold,\n );\n\n const caseMap = new Map<string, CaseWithScores>();\n for (const row of rows) {\n let c = caseMap.get(row.id);\n if (!c) {\n c = { ...toCase(row), scores: [] };\n caseMap.set(row.id, c);\n }\n c.scores.push({\n scorer_name: row.scorer_name,\n score: row.score,\n reason: row.score_reason,\n });\n }\n return Array.from(caseMap.values());\n }\n\n getRunSummary(runId: string, threshold = 0.5): RunSummary {\n const totals = this.#one(\n {\n totalCases: 'number',\n totalLatencyMs: 'number',\n totalTokensIn: 'number',\n totalTokensOut: 'number',\n },\n `SELECT\n COUNT(DISTINCT c.id) as totalCases,\n COALESCE(SUM(c.latency_ms), 0) as totalLatencyMs,\n COALESCE(SUM(c.tokens_in), 0) as totalTokensIn,\n COALESCE(SUM(c.tokens_out), 0) as totalTokensOut\n FROM cases c WHERE c.run_id = ?`,\n runId,\n );\n\n const scorerMeans = this.#all(\n { scorer_name: 'text', meanScore: 'number' },\n `SELECT s.scorer_name, AVG(s.score) as meanScore\n FROM scores s\n JOIN cases c ON c.id = s.case_id\n WHERE c.run_id = ?\n GROUP BY s.scorer_name`,\n runId,\n );\n\n const meanScores: Record<string, number> = {};\n for (const row of scorerMeans) {\n meanScores[row.scorer_name] = row.meanScore;\n }\n\n const passFail = this.#all(\n { id: 'text', minScore: 'number' },\n `SELECT c.id,\n MIN(s.score) as minScore\n FROM cases c\n JOIN scores s ON s.case_id = c.id\n WHERE c.run_id = ?\n GROUP BY c.id`,\n runId,\n );\n\n let passCount = 0;\n let failCount = 0;\n for (const row of passFail) {\n if (row.minScore >= threshold) passCount++;\n else failCount++;\n }\n\n return {\n totalCases: totals.totalCases,\n passCount,\n failCount,\n meanScores,\n totalLatencyMs: totals.totalLatencyMs,\n totalTokensIn: totals.totalTokensIn,\n totalTokensOut: totals.totalTokensOut,\n };\n }\n\n findSuiteByName(name: string): SuiteRow | undefined {\n const row = this.#get(\n suiteColumns,\n 'SELECT * FROM suites WHERE name = ? ORDER BY created_at DESC LIMIT 1',\n name,\n );\n return row ? toSuite(row) : undefined;\n }\n\n getLatestCompletedRun(suiteId: string, model?: string): RunRow | undefined {\n const row = model\n ? this.#get(\n runColumns,\n 'SELECT * FROM runs WHERE suite_id = ? AND status = ? AND model = ? ORDER BY started_at DESC LIMIT 1',\n suiteId,\n 'completed',\n model,\n )\n : this.#get(\n runColumns,\n 'SELECT * FROM runs WHERE suite_id = ? AND status = ? ORDER BY started_at DESC LIMIT 1',\n suiteId,\n 'completed',\n );\n return row ? toRun(row) : undefined;\n }\n\n listSuites(): SuiteRow[] {\n return this.#all(\n suiteColumns,\n 'SELECT * FROM suites ORDER BY created_at DESC',\n ).map(toSuite);\n }\n\n createPrompt(name: string, content: string): PromptRow {\n const id = crypto.randomUUID();\n const now = Date.now();\n\n const { latestVersion } = this.#one(\n { latestVersion: 'number?' },\n 'SELECT MAX(version) as latestVersion FROM prompts WHERE name = ?',\n name,\n );\n const version = (latestVersion ?? 0) + 1;\n\n this.#stmt(\n 'INSERT INTO prompts (id, name, version, content, created_at) VALUES (?, ?, ?, ?, ?)',\n ).run(id, name, version, content, now);\n return { id, name, version, content, created_at: now };\n }\n\n listPrompts(): PromptRow[] {\n return this.#all(\n promptColumns,\n 'SELECT * FROM prompts ORDER BY name COLLATE NOCASE ASC, version DESC',\n ).map(toPrompt);\n }\n\n getPrompt(id: string): PromptRow | undefined {\n const row = this.#get(\n promptColumns,\n 'SELECT * FROM prompts WHERE id = ?',\n id,\n );\n return row ? toPrompt(row) : undefined;\n }\n\n deletePrompt(id: string): void {\n this.#stmt('DELETE FROM prompts WHERE id = ?').run(id);\n }\n\n resetRun(id: string): void {\n this.#transaction(() => {\n this.#stmt('DELETE FROM cases WHERE run_id = ?').run(id);\n this.#stmt(\n 'UPDATE runs SET status = ?, started_at = ?, finished_at = NULL, summary = NULL WHERE id = ?',\n ).run('running', Date.now(), id);\n });\n }\n\n deleteRun(id: string): void {\n this.#stmt('DELETE FROM runs WHERE id = ?').run(id);\n }\n\n deleteSuite(id: string): void {\n this.#stmt('DELETE FROM suites WHERE id = ?').run(id);\n }\n}\n", "PRAGMA journal_mode = WAL;\nPRAGMA synchronous = NORMAL;\nPRAGMA foreign_keys = ON;\n\nCREATE TABLE IF NOT EXISTS suites (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n);\n\nCREATE TABLE IF NOT EXISTS runs (\n id TEXT PRIMARY KEY,\n suite_id TEXT NOT NULL,\n name TEXT NOT NULL,\n model TEXT NOT NULL,\n config TEXT,\n started_at INTEGER NOT NULL,\n finished_at INTEGER,\n status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed')),\n summary TEXT,\n FOREIGN KEY (suite_id) REFERENCES suites(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_runs_suite_id ON runs(suite_id);\nCREATE INDEX IF NOT EXISTS idx_runs_started_at ON runs(started_at);\n\nCREATE TABLE IF NOT EXISTS cases (\n id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL,\n idx INTEGER NOT NULL,\n input TEXT NOT NULL,\n output TEXT,\n expected TEXT,\n latency_ms INTEGER,\n tokens_in INTEGER,\n tokens_out INTEGER,\n error TEXT,\n FOREIGN KEY (run_id) REFERENCES runs(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_cases_run_id ON cases(run_id);\n\nCREATE TABLE IF NOT EXISTS scores (\n id TEXT PRIMARY KEY,\n case_id TEXT NOT NULL,\n scorer_name TEXT NOT NULL,\n score REAL NOT NULL,\n reason TEXT,\n FOREIGN KEY (case_id) REFERENCES cases(id) ON DELETE CASCADE\n);\n\nCREATE INDEX IF NOT EXISTS idx_scores_case_id ON scores(case_id);\n\nCREATE TABLE IF NOT EXISTS prompts (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n content TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000)\n);\n\nCREATE INDEX IF NOT EXISTS idx_prompts_created_at ON prompts(created_at);\n"],
5
+ "mappings": ";AAAA,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB;AAAA,EACE;AAAA,OAGK;;;ACNP;;;AD4FA,IAAM,eAAe,CAAC,WAAW,aAAa,QAAQ;AAItD,SAAS,YAAY,OAAmC;AACtD,SAAO,aAAa,KAAK,CAAC,WAAW,WAAW,KAAK;AACvD;AAkBA,SAAS,cACP,OACA,MACS;AACT,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,OAAO,UAAU;AAAA,IAC1B,KAAK;AACH,aAAO,UAAU,QAAQ,OAAO,UAAU;AAAA,IAC5C,KAAK;AACH,aAAO,OAAO,UAAU;AAAA,IAC1B,KAAK;AACH,aAAO,UAAU,QAAQ,OAAO,UAAU;AAAA,EAC9C;AACF;AAEA,SAAS,kBAAkB,KAAa,SAA4B;AAClE,SAAO,OAAO,QAAQ,OAAO,EAC1B,OAAO,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,cAAc,IAAI,IAAI,GAAG,IAAI,CAAC,EACxD,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AACzB;AAEA,SAAS,WACP,KACA,SAC0B;AAC1B,SAAO,kBAAkB,KAAK,OAAO,EAAE,WAAW;AACpD;AAEA,SAAS,QACP,KACA,SACA,KACU;AACV,MAAI,WAAW,KAAK,OAAO,EAAG,QAAO;AACrC,QAAM,IAAI;AAAA,IACR,gCAAgC,kBAAkB,KAAK,OAAO,EAAE,KAAK,IAAI,CAAC,mBAAmB,QAAQ,GAAG,CAAC;AAAA,EAC3G;AACF;AAEA,SAAS,QAAQ,KAAqB;AACpC,SAAO,IAAI,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACvC;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,aAAa,OAAqC;AACzD,SACE,SAAS,KAAK,KACd,OAAO,MAAM,eAAe,YAC5B,OAAO,MAAM,cAAc,YAC3B,OAAO,MAAM,cAAc,YAC3B,OAAO,MAAM,mBAAmB,YAChC,OAAO,MAAM,kBAAkB,YAC/B,OAAO,MAAM,mBAAmB,YAChC,SAAS,MAAM,UAAU,KACzB,OAAO,OAAO,MAAM,UAAU,EAAE,MAAM,CAAC,UAAU,OAAO,UAAU,QAAQ;AAE9E;AAEA,SAAS,UACP,MACA,OACA,QACG;AACH,QAAM,QAAiB,KAAK,MAAM,IAAI;AACtC,MAAI,MAAM,KAAK,EAAG,QAAO;AACzB,QAAM,IAAI,UAAU,sBAAsB,MAAM,EAAE;AACpD;AAEA,IAAM,eAAe;AAAA,EACnB,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,YAAY;AACd;AAEA,IAAM,aAAa;AAAA,EACjB,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,SAAS;AACX;AAEA,IAAM,cAAc;AAAA,EAClB,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,KAAK;AAAA,EACL,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,OAAO;AACT;AAEA,IAAM,gBAAgB;AAAA,EACpB,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AACd;AAEA,SAAS,QAAQ,KAA2C;AAC1D,SAAO,EAAE,IAAI,IAAI,IAAI,MAAM,IAAI,MAAM,YAAY,IAAI,WAAW;AAClE;AAEA,SAAS,MAAM,KAAuC;AACpD,MAAI,CAAC,YAAY,IAAI,MAAM,GAAG;AAC5B,UAAM,IAAI,UAAU,sBAAsB,IAAI,MAAM,YAAY,IAAI,EAAE,EAAE;AAAA,EAC1E;AACA,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,UAAU,IAAI;AAAA,IACd,MAAM,IAAI;AAAA,IACV,OAAO,IAAI;AAAA,IACX,QAAQ,IAAI,SACR,UAAU,IAAI,QAAQ,UAAU,iBAAiB,IAAI,EAAE,EAAE,IACzD;AAAA,IACJ,YAAY,IAAI;AAAA,IAChB,aAAa,IAAI;AAAA,IACjB,QAAQ,IAAI;AAAA,IACZ,SAAS,IAAI,UACT,UAAU,IAAI,SAAS,cAAc,kBAAkB,IAAI,EAAE,EAAE,IAC/D;AAAA,EACN;AACF;AAEA,SAAS,OAAO,KAAyC;AACvD,QAAM,QAAiB,KAAK,MAAM,IAAI,KAAK;AAC3C,QAAM,WAAoB,IAAI,WAAW,KAAK,MAAM,IAAI,QAAQ,IAAI;AACpE,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,QAAQ,IAAI;AAAA,IACZ,KAAK,IAAI;AAAA,IACT;AAAA,IACA,QAAQ,IAAI;AAAA,IACZ;AAAA,IACA,YAAY,IAAI;AAAA,IAChB,WAAW,IAAI;AAAA,IACf,YAAY,IAAI;AAAA,IAChB,OAAO,IAAI;AAAA,EACb;AACF;AAEA,SAAS,SAAS,KAA6C;AAC7D,SAAO;AAAA,IACL,IAAI,IAAI;AAAA,IACR,MAAM,IAAI;AAAA,IACV,SAAS,IAAI;AAAA,IACb,SAAS,IAAI;AAAA,IACb,YAAY,IAAI;AAAA,EAClB;AACF;AAEO,IAAM,WAAN,MAAe;AAAA,EACpB;AAAA,EACA,cAAc,oBAAI,IAAiD;AAAA,EAEnE,MAAM,KAAkD;AACtD,QAAI,OAAO,KAAK,YAAY,IAAI,GAAG;AACnC,QAAI,CAAC,MAAM;AACT,aAAO,KAAK,IAAI,QAAQ,GAAG;AAC3B,WAAK,YAAY,IAAI,KAAK,IAAI;AAAA,IAChC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,KACE,SACA,QACG,QACmB;AACtB,UAAM,MAAM,KAAK,MAAM,GAAG,EAAE,IAAI,GAAG,MAAM;AACzC,WAAO,QAAQ,SAAY,SAAY,QAAQ,KAAK,SAAS,GAAG;AAAA,EAClE;AAAA;AAAA,EAGA,KACE,SACA,QACG,QACO;AACV,UAAM,MAAM,KAAK,KAAK,SAAS,KAAK,GAAG,MAAM;AAC7C,QAAI,QAAQ,QAAW;AACrB,YAAM,IAAI,MAAM,0BAA0B,QAAQ,GAAG,CAAC,EAAE;AAAA,IAC1D;AACA,WAAO;AAAA,EACT;AAAA,EAEA,KACE,SACA,QACG,QACS;AACZ,WAAO,KAAK,MAAM,GAAG,EAClB,IAAI,GAAG,MAAM,EACb,IAAI,CAAC,QAAQ,QAAQ,KAAK,SAAS,GAAG,CAAC;AAAA,EAC5C;AAAA,EAEA,aAAgB,IAAgB;AAC9B,SAAK,IAAI,KAAK,mBAAmB;AACjC,QAAI;AACF,YAAM,SAAS,GAAG;AAClB,WAAK,IAAI,KAAK,QAAQ;AACtB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,WAAK,IAAI,KAAK,UAAU;AACxB,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,YAAY,UAAkC;AAC5C,QAAI,oBAAoB,cAAc;AACpC,WAAK,MAAM;AAAA,IACb,OAAO;AACL,YAAM,SAAS,YAAY;AAC3B,gBAAU,QAAQ,MAAM,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,WAAK,MAAM,IAAI,aAAa,MAAM;AAAA,IACpC;AACA,SAAK,IAAI,KAAK,kBAAG;AACjB,SAAK,iCAAiC;AACtC,SAAK,6BAA6B;AAClC,SAAK,IAAI;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAAA,EAEA,+BAAqC;AACnC,UAAM,UAAU,KAAK,KAAK,EAAE,MAAM,OAAO,GAAG,4BAA4B;AAExE,QAAI,QAAQ,WAAW,EAAG;AAC1B,QAAI,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,SAAS,EAAG;AAEzD,SAAK,aAAa,MAAM;AACtB,WAAK,IAAI,KAAK,8CAA8C;AAC5D,WAAK,IAAI,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OASb;AACD,WAAK,IAAI,KAAK;AAAA;AAAA;AAAA;AAAA,OAIb;AACD,WAAK,IAAI,KAAK,2BAA2B;AACzC,WAAK,IAAI;AAAA,QACP;AAAA,MACF;AACA,WAAK,IAAI;AAAA,QACP;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,mCAAyC;AACvC,UAAM,UAAU,KAAK;AAAA,MACnB,EAAE,MAAM,QAAQ,SAAS,SAAS;AAAA,MAClC;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW,EAAG;AAE1B,UAAM,cAAc,QAAQ,KAAK,CAAC,WAAW,OAAO,SAAS,UAAU;AACvE,UAAM,kBAAkB,aAAa,YAAY;AAEjD,UAAM,iBAAiB,KAAK;AAAA,MAC1B,EAAE,MAAM,QAAQ,WAAW,QAAQ,OAAO,OAAO;AAAA,MACjD;AAAA,IACF;AACA,UAAM,kBAAkB,eAAe;AAAA,MACrC,CAAC,OAAO,GAAG,SAAS,cAAc,GAAG,UAAU;AAAA,IACjD;AACA,UAAM,mBAAmB,iBAAiB,cAAc;AAExD,QAAI,mBAAmB,iBAAkB;AAEzC,SAAK,YAAY,MAAM;AACvB,SAAK,aAAa,MAAM;AACtB,WAAK,IAAI,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAab;AAGD,WAAK,IAAI,KAAK,yCAAyC;AAEvD,WAAK,IAAI,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,OAKb;AAED,WAAK,IAAI,KAAK,iBAAiB;AAC/B,WAAK,IAAI,KAAK,sCAAsC;AACpD,WAAK,IAAI;AAAA,QACP;AAAA,MACF;AACA,WAAK,IAAI;AAAA,QACP;AAAA,MACF;AAAA,IACF,CAAC;AACD,SAAK,YAAY,MAAM;AAAA,EACzB;AAAA,EAEA,YAAY,MAAwB;AAClC,UAAM,KAAK,OAAO,WAAW;AAC7B,UAAM,MAAM,KAAK,IAAI;AACrB,SAAK;AAAA,MACH;AAAA,IACF,EAAE,IAAI,IAAI,MAAM,GAAG;AACnB,WAAO,EAAE,IAAI,MAAM,YAAY,IAAI;AAAA,EACrC;AAAA,EAEA,SAAS,IAAkC;AACzC,UAAM,MAAM,KAAK;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO,MAAM,QAAQ,GAAG,IAAI;AAAA,EAC9B;AAAA,EAEA,YAAY,IAAY,MAAoB;AAC1C,SAAK,MAAM,yCAAyC,EAAE,IAAI,MAAM,EAAE;AAAA,EACpE;AAAA,EAEA,UAAU,IAAY,MAAoB;AACxC,SAAK,MAAM,uCAAuC,EAAE,IAAI,MAAM,EAAE;AAAA,EAClE;AAAA,EAEA,UAAU,KAKC;AACT,UAAM,KAAK,OAAO,WAAW;AAC7B,UAAM,MAAM,KAAK,IAAI;AACrB,SAAK;AAAA,MACH;AAAA,IACF,EAAE;AAAA,MACA;AAAA,MACA,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI;AAAA,MACJ,IAAI,SAAS,KAAK,UAAU,IAAI,MAAM,IAAI;AAAA,MAC1C;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UACE,OACA,QACA,SACM;AACN,SAAK;AAAA,MACH;AAAA,IACF,EAAE,IAAI,KAAK,IAAI,GAAG,QAAQ,UAAU,KAAK,UAAU,OAAO,IAAI,MAAM,KAAK;AAAA,EAC3E;AAAA,EAEA,UAAU,OAAyB;AACjC,SAAK,aAAa,MAAM;AACtB,YAAM,OAAO,KAAK;AAAA,QAChB;AAAA,MACF;AACA,iBAAW,KAAK,OAAO;AACrB,aAAK;AAAA,UACH,EAAE;AAAA,UACF,EAAE;AAAA,UACF,EAAE;AAAA,UACF,KAAK,UAAU,EAAE,KAAK;AAAA,UACtB,EAAE;AAAA,UACF,EAAE,YAAY,OAAO,KAAK,UAAU,EAAE,QAAQ,IAAI;AAAA,UAClD,EAAE;AAAA,UACF,EAAE;AAAA,UACF,EAAE;AAAA,UACF,EAAE,SAAS;AAAA,QACb;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,WAAW,QAA2B;AACpC,SAAK,aAAa,MAAM;AACtB,YAAM,OAAO,KAAK;AAAA,QAChB;AAAA,MACF;AACA,iBAAW,KAAK,QAAQ;AACtB,aAAK,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,UAAU,IAAI;AAAA,MACpE;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,OAAmC;AACxC,UAAM,MAAM,KAAK,KAAK,YAAY,mCAAmC,KAAK;AAC1E,WAAO,MAAM,MAAM,GAAG,IAAI;AAAA,EAC5B;AAAA,EAEA,SAAS,SAA4B;AACnC,UAAM,OAAO,UACT,KAAK;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,IACF,IACA,KAAK,KAAK,YAAY,wCAAwC;AAClE,WAAO,KAAK,IAAI,KAAK;AAAA,EACvB;AAAA,EAEA,SAAS,OAA0B;AACjC,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,IAAI,MAAM;AAAA,EACd;AAAA,EAEA,gBAAgB,OAAe,YAAY,KAAuB;AAChE,UAAM,OAAO,KAAK;AAAA,MAChB;AAAA,QACE,GAAG;AAAA,QACH,aAAa;AAAA,QACb,OAAO;AAAA,QACP,cAAc;AAAA,MAChB;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA;AAAA,MACA;AAAA,IACF;AAEA,UAAM,UAAU,oBAAI,IAA4B;AAChD,eAAW,OAAO,MAAM;AACtB,UAAI,IAAI,QAAQ,IAAI,IAAI,EAAE;AAC1B,UAAI,CAAC,GAAG;AACN,YAAI,EAAE,GAAG,OAAO,GAAG,GAAG,QAAQ,CAAC,EAAE;AACjC,gBAAQ,IAAI,IAAI,IAAI,CAAC;AAAA,MACvB;AACA,QAAE,OAAO,KAAK;AAAA,QACZ,aAAa,IAAI;AAAA,QACjB,OAAO,IAAI;AAAA,QACX,QAAQ,IAAI;AAAA,MACd,CAAC;AAAA,IACH;AACA,WAAO,MAAM,KAAK,QAAQ,OAAO,CAAC;AAAA,EACpC;AAAA,EAEA,cAAc,OAAe,YAAY,KAAiB;AACxD,UAAM,SAAS,KAAK;AAAA,MAClB;AAAA,QACE,YAAY;AAAA,QACZ,gBAAgB;AAAA,QAChB,eAAe;AAAA,QACf,gBAAgB;AAAA,MAClB;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA;AAAA,IACF;AAEA,UAAM,cAAc,KAAK;AAAA,MACvB,EAAE,aAAa,QAAQ,WAAW,SAAS;AAAA,MAC3C;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA;AAAA,IACF;AAEA,UAAM,aAAqC,CAAC;AAC5C,eAAW,OAAO,aAAa;AAC7B,iBAAW,IAAI,WAAW,IAAI,IAAI;AAAA,IACpC;AAEA,UAAM,WAAW,KAAK;AAAA,MACpB,EAAE,IAAI,QAAQ,UAAU,SAAS;AAAA,MACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA;AAAA,IACF;AAEA,QAAI,YAAY;AAChB,QAAI,YAAY;AAChB,eAAW,OAAO,UAAU;AAC1B,UAAI,IAAI,YAAY,UAAW;AAAA,UAC1B;AAAA,IACP;AAEA,WAAO;AAAA,MACL,YAAY,OAAO;AAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB,OAAO;AAAA,MACvB,eAAe,OAAO;AAAA,MACtB,gBAAgB,OAAO;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,gBAAgB,MAAoC;AAClD,UAAM,MAAM,KAAK;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO,MAAM,QAAQ,GAAG,IAAI;AAAA,EAC9B;AAAA,EAEA,sBAAsB,SAAiB,OAAoC;AACzE,UAAM,MAAM,QACR,KAAK;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IACA,KAAK;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACJ,WAAO,MAAM,MAAM,GAAG,IAAI;AAAA,EAC5B;AAAA,EAEA,aAAyB;AACvB,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,IACF,EAAE,IAAI,OAAO;AAAA,EACf;AAAA,EAEA,aAAa,MAAc,SAA4B;AACrD,UAAM,KAAK,OAAO,WAAW;AAC7B,UAAM,MAAM,KAAK,IAAI;AAErB,UAAM,EAAE,cAAc,IAAI,KAAK;AAAA,MAC7B,EAAE,eAAe,UAAU;AAAA,MAC3B;AAAA,MACA;AAAA,IACF;AACA,UAAM,WAAW,iBAAiB,KAAK;AAEvC,SAAK;AAAA,MACH;AAAA,IACF,EAAE,IAAI,IAAI,MAAM,SAAS,SAAS,GAAG;AACrC,WAAO,EAAE,IAAI,MAAM,SAAS,SAAS,YAAY,IAAI;AAAA,EACvD;AAAA,EAEA,cAA2B;AACzB,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,IACF,EAAE,IAAI,QAAQ;AAAA,EAChB;AAAA,EAEA,UAAU,IAAmC;AAC3C,UAAM,MAAM,KAAK;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,WAAO,MAAM,SAAS,GAAG,IAAI;AAAA,EAC/B;AAAA,EAEA,aAAa,IAAkB;AAC7B,SAAK,MAAM,kCAAkC,EAAE,IAAI,EAAE;AAAA,EACvD;AAAA,EAEA,SAAS,IAAkB;AACzB,SAAK,aAAa,MAAM;AACtB,WAAK,MAAM,oCAAoC,EAAE,IAAI,EAAE;AACvD,WAAK;AAAA,QACH;AAAA,MACF,EAAE,IAAI,WAAW,KAAK,IAAI,GAAG,EAAE;AAAA,IACjC,CAAC;AAAA,EACH;AAAA,EAEA,UAAU,IAAkB;AAC1B,SAAK,MAAM,+BAA+B,EAAE,IAAI,EAAE;AAAA,EACpD;AAAA,EAEA,YAAY,IAAkB;AAC5B,SAAK,MAAM,iCAAiC,EAAE,IAAI,EAAE;AAAA,EACtD;AACF;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepagents/evals",
3
- "version": "7.0.2",
3
+ "version": "7.1.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",