@deepagents/evals 7.1.1 → 7.1.3
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/evaluate/index.d.ts +11 -5
- package/dist/evaluate/index.d.ts.map +1 -1
- package/dist/evaluate/index.js +25 -18
- package/dist/evaluate/index.js.map +3 -3
- package/dist/index.js +86 -154
- package/dist/index.js.map +2 -2
- package/dist/scorers/index.d.ts.map +1 -1
- package/dist/scorers/index.js +8 -5
- package/dist/scorers/index.js.map +2 -2
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +61 -131
- package/dist/store/index.js.map +2 -2
- package/package.json +13 -2
package/dist/index.js
CHANGED
|
@@ -363,6 +363,7 @@ function dataset(source) {
|
|
|
363
363
|
|
|
364
364
|
// packages/evals/src/scorers/index.ts
|
|
365
365
|
import OpenAI from "openai";
|
|
366
|
+
import pMemoize from "p-memoize";
|
|
366
367
|
var exactMatch = async ({ output, expected }) => {
|
|
367
368
|
const exp = expected == null ? "" : String(expected);
|
|
368
369
|
if (output === exp) return { score: 1 };
|
|
@@ -484,10 +485,14 @@ var LlmJudge = class _LlmJudge {
|
|
|
484
485
|
#model;
|
|
485
486
|
#choices;
|
|
486
487
|
#criterion;
|
|
488
|
+
/** The configured client, or one OpenAI client made on first use. */
|
|
487
489
|
#client;
|
|
488
490
|
constructor(config, choices, criterion) {
|
|
489
491
|
this.#model = config.model;
|
|
490
|
-
|
|
492
|
+
const configured = config.client;
|
|
493
|
+
this.#client = configured ? async () => configured : pMemoize(
|
|
494
|
+
async () => new OpenAI({ apiKey: process.env["OPENAI_API_KEY"] })
|
|
495
|
+
);
|
|
491
496
|
this.#choices = choices;
|
|
492
497
|
this.#criterion = criterion;
|
|
493
498
|
}
|
|
@@ -520,10 +525,8 @@ var LlmJudge = class _LlmJudge {
|
|
|
520
525
|
return (args) => this.#score(args);
|
|
521
526
|
}
|
|
522
527
|
async #score({ input, output, expected }) {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
});
|
|
526
|
-
const response = await this.#client.chat.completions.create({
|
|
528
|
+
const client = await this.#client();
|
|
529
|
+
const response = await client.chat.completions.create({
|
|
527
530
|
model: this.#model,
|
|
528
531
|
messages: [
|
|
529
532
|
{
|
|
@@ -645,6 +648,8 @@ function weighted(config) {
|
|
|
645
648
|
}
|
|
646
649
|
|
|
647
650
|
// packages/evals/src/store/index.ts
|
|
651
|
+
import { Declaration } from "@zukhruf/sqlite-drift";
|
|
652
|
+
import { transaction } from "@zukhruf/sqlite-transaction";
|
|
648
653
|
import { mkdirSync } from "node:fs";
|
|
649
654
|
import { dirname } from "node:path";
|
|
650
655
|
import {
|
|
@@ -652,9 +657,10 @@ import {
|
|
|
652
657
|
} from "node:sqlite";
|
|
653
658
|
|
|
654
659
|
// packages/evals/src/store/ddl.sqlite.sql
|
|
655
|
-
var ddl_sqlite_default = "
|
|
660
|
+
var ddl_sqlite_default = "CREATE 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,\n version INTEGER NOT NULL DEFAULT 1,\n content TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),\n UNIQUE(name, version)\n);\n\nCREATE INDEX IF NOT EXISTS idx_prompts_created_at ON prompts(created_at);\nCREATE INDEX IF NOT EXISTS idx_prompts_name_version ON prompts(name, version DESC);\n";
|
|
656
661
|
|
|
657
662
|
// packages/evals/src/store/index.ts
|
|
663
|
+
var schema = new Declaration([ddl_sqlite_default], { rows: "keep" });
|
|
658
664
|
var RUN_STATUSES = ["running", "completed", "failed"];
|
|
659
665
|
function isRunStatus(value) {
|
|
660
666
|
return RUN_STATUSES.some((status) => status === value);
|
|
@@ -802,20 +808,6 @@ var RunStore = class {
|
|
|
802
808
|
#all(columns, sql2, ...params) {
|
|
803
809
|
return this.#stmt(sql2).all(...params).map((row) => readRow(row, columns, sql2));
|
|
804
810
|
}
|
|
805
|
-
#transaction(fn) {
|
|
806
|
-
this.#db.exec("BEGIN TRANSACTION");
|
|
807
|
-
try {
|
|
808
|
-
const result = fn();
|
|
809
|
-
this.#db.exec("COMMIT");
|
|
810
|
-
return result;
|
|
811
|
-
} catch (error) {
|
|
812
|
-
try {
|
|
813
|
-
this.#db.exec("ROLLBACK");
|
|
814
|
-
} catch {
|
|
815
|
-
}
|
|
816
|
-
throw error;
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
811
|
constructor(pathOrDb) {
|
|
820
812
|
if (pathOrDb instanceof DatabaseSync) {
|
|
821
813
|
this.#db = pathOrDb;
|
|
@@ -824,93 +816,22 @@ var RunStore = class {
|
|
|
824
816
|
mkdirSync(dirname(dbPath), { recursive: true });
|
|
825
817
|
this.#db = new DatabaseSync(dbPath);
|
|
826
818
|
}
|
|
827
|
-
this.#db.exec(
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
819
|
+
this.#db.exec(`
|
|
820
|
+
PRAGMA journal_mode = WAL;
|
|
821
|
+
PRAGMA synchronous = NORMAL;
|
|
822
|
+
PRAGMA foreign_keys = ON;
|
|
823
|
+
PRAGMA busy_timeout = 5000;
|
|
824
|
+
`);
|
|
825
|
+
const runsTable = this.#get(
|
|
826
|
+
{ name: "text" },
|
|
827
|
+
"SELECT name FROM sqlite_schema WHERE type = 'table' AND name = 'runs'"
|
|
832
828
|
);
|
|
833
|
-
|
|
834
|
-
#migratePromptsTableIfNeeded() {
|
|
835
|
-
const columns = this.#all({ name: "text" }, "PRAGMA table_info(prompts)");
|
|
836
|
-
if (columns.length === 0) return;
|
|
837
|
-
if (columns.some((column) => column.name === "version")) return;
|
|
838
|
-
this.#transaction(() => {
|
|
839
|
-
this.#db.exec("ALTER TABLE prompts RENAME TO prompts_legacy");
|
|
840
|
-
this.#db.exec(`
|
|
841
|
-
CREATE TABLE prompts (
|
|
842
|
-
id TEXT PRIMARY KEY,
|
|
843
|
-
name TEXT NOT NULL,
|
|
844
|
-
version INTEGER NOT NULL,
|
|
845
|
-
content TEXT NOT NULL,
|
|
846
|
-
created_at INTEGER NOT NULL DEFAULT (unixepoch() * 1000),
|
|
847
|
-
UNIQUE(name, version)
|
|
848
|
-
)
|
|
849
|
-
`);
|
|
850
|
-
this.#db.exec(`
|
|
851
|
-
INSERT INTO prompts (id, name, version, content, created_at)
|
|
852
|
-
SELECT id, name, 1, content, created_at
|
|
853
|
-
FROM prompts_legacy
|
|
854
|
-
`);
|
|
855
|
-
this.#db.exec("DROP TABLE prompts_legacy");
|
|
829
|
+
if (runsTable !== void 0) {
|
|
856
830
|
this.#db.exec(
|
|
857
|
-
"
|
|
831
|
+
"DELETE FROM runs WHERE suite_id IS NULL OR NOT EXISTS (SELECT 1 FROM suites WHERE suites.id = runs.suite_id)"
|
|
858
832
|
);
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
);
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
#migrateRunsTableToSuiteRequired() {
|
|
865
|
-
const columns = this.#all(
|
|
866
|
-
{ name: "text", notnull: "number" },
|
|
867
|
-
"PRAGMA table_info(runs)"
|
|
868
|
-
);
|
|
869
|
-
if (columns.length === 0) return;
|
|
870
|
-
const suiteColumn = columns.find((column) => column.name === "suite_id");
|
|
871
|
-
const hasNonNullSuite = suiteColumn?.notnull === 1;
|
|
872
|
-
const runForeignKeys = this.#all(
|
|
873
|
-
{ from: "text", on_delete: "text", table: "text" },
|
|
874
|
-
"PRAGMA foreign_key_list(runs)"
|
|
875
|
-
);
|
|
876
|
-
const suiteForeignKey = runForeignKeys.find(
|
|
877
|
-
(fk) => fk.from === "suite_id" && fk.table === "suites"
|
|
878
|
-
);
|
|
879
|
-
const hasCascadeDelete = suiteForeignKey?.on_delete === "CASCADE";
|
|
880
|
-
if (hasNonNullSuite && hasCascadeDelete) return;
|
|
881
|
-
this.#statements.clear();
|
|
882
|
-
this.#transaction(() => {
|
|
883
|
-
this.#db.exec(`
|
|
884
|
-
CREATE TABLE runs_next (
|
|
885
|
-
id TEXT PRIMARY KEY,
|
|
886
|
-
suite_id TEXT NOT NULL,
|
|
887
|
-
name TEXT NOT NULL,
|
|
888
|
-
model TEXT NOT NULL,
|
|
889
|
-
config TEXT,
|
|
890
|
-
started_at INTEGER NOT NULL,
|
|
891
|
-
finished_at INTEGER,
|
|
892
|
-
status TEXT NOT NULL DEFAULT 'running' CHECK(status IN ('running', 'completed', 'failed')),
|
|
893
|
-
summary TEXT,
|
|
894
|
-
FOREIGN KEY (suite_id) REFERENCES suites(id) ON DELETE CASCADE
|
|
895
|
-
)
|
|
896
|
-
`);
|
|
897
|
-
this.#db.exec("DELETE FROM runs WHERE suite_id IS NULL");
|
|
898
|
-
this.#db.exec(`
|
|
899
|
-
INSERT INTO runs_next (id, suite_id, name, model, config, started_at, finished_at, status, summary)
|
|
900
|
-
SELECT r.id, r.suite_id, r.name, r.model, r.config, r.started_at, r.finished_at, r.status, r.summary
|
|
901
|
-
FROM runs r
|
|
902
|
-
JOIN suites s ON s.id = r.suite_id
|
|
903
|
-
`);
|
|
904
|
-
this.#db.exec("DROP TABLE runs");
|
|
905
|
-
this.#db.exec("ALTER TABLE runs_next RENAME TO runs");
|
|
906
|
-
this.#db.exec(
|
|
907
|
-
"CREATE INDEX IF NOT EXISTS idx_runs_suite_id ON runs(suite_id)"
|
|
908
|
-
);
|
|
909
|
-
this.#db.exec(
|
|
910
|
-
"CREATE INDEX IF NOT EXISTS idx_runs_started_at ON runs(started_at)"
|
|
911
|
-
);
|
|
912
|
-
});
|
|
913
|
-
this.#statements.clear();
|
|
833
|
+
}
|
|
834
|
+
schema.apply(this.#db);
|
|
914
835
|
}
|
|
915
836
|
createSuite(name) {
|
|
916
837
|
const id = crypto.randomUUID();
|
|
@@ -955,35 +876,43 @@ var RunStore = class {
|
|
|
955
876
|
).run(Date.now(), status, summary ? JSON.stringify(summary) : null, runId);
|
|
956
877
|
}
|
|
957
878
|
saveCases(cases) {
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
stmt.run(
|
|
964
|
-
c.id,
|
|
965
|
-
c.run_id,
|
|
966
|
-
c.idx,
|
|
967
|
-
JSON.stringify(c.input),
|
|
968
|
-
c.output,
|
|
969
|
-
c.expected != null ? JSON.stringify(c.expected) : null,
|
|
970
|
-
c.latency_ms,
|
|
971
|
-
c.tokens_in,
|
|
972
|
-
c.tokens_out,
|
|
973
|
-
c.error ?? null
|
|
879
|
+
transaction(
|
|
880
|
+
this.#db,
|
|
881
|
+
() => {
|
|
882
|
+
const stmt = this.#stmt(
|
|
883
|
+
"INSERT INTO cases (id, run_id, idx, input, output, expected, latency_ms, tokens_in, tokens_out, error) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
974
884
|
);
|
|
975
|
-
|
|
976
|
-
|
|
885
|
+
for (const c of cases) {
|
|
886
|
+
stmt.run(
|
|
887
|
+
c.id,
|
|
888
|
+
c.run_id,
|
|
889
|
+
c.idx,
|
|
890
|
+
JSON.stringify(c.input),
|
|
891
|
+
c.output,
|
|
892
|
+
c.expected != null ? JSON.stringify(c.expected) : null,
|
|
893
|
+
c.latency_ms,
|
|
894
|
+
c.tokens_in,
|
|
895
|
+
c.tokens_out,
|
|
896
|
+
c.error ?? null
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
},
|
|
900
|
+
{ mode: "deferred" }
|
|
901
|
+
);
|
|
977
902
|
}
|
|
978
903
|
saveScores(scores) {
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
904
|
+
transaction(
|
|
905
|
+
this.#db,
|
|
906
|
+
() => {
|
|
907
|
+
const stmt = this.#stmt(
|
|
908
|
+
"INSERT INTO scores (id, case_id, scorer_name, score, reason) VALUES (?, ?, ?, ?, ?)"
|
|
909
|
+
);
|
|
910
|
+
for (const s of scores) {
|
|
911
|
+
stmt.run(s.id, s.case_id, s.scorer_name, s.score, s.reason ?? null);
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
{ mode: "deferred" }
|
|
915
|
+
);
|
|
987
916
|
}
|
|
988
917
|
getRun(runId) {
|
|
989
918
|
const row = this.#get(runColumns, "SELECT * FROM runs WHERE id = ?", runId);
|
|
@@ -1151,12 +1080,16 @@ var RunStore = class {
|
|
|
1151
1080
|
this.#stmt("DELETE FROM prompts WHERE id = ?").run(id);
|
|
1152
1081
|
}
|
|
1153
1082
|
resetRun(id) {
|
|
1154
|
-
|
|
1155
|
-
this.#
|
|
1156
|
-
|
|
1157
|
-
"
|
|
1158
|
-
|
|
1159
|
-
|
|
1083
|
+
transaction(
|
|
1084
|
+
this.#db,
|
|
1085
|
+
() => {
|
|
1086
|
+
this.#stmt("DELETE FROM cases WHERE run_id = ?").run(id);
|
|
1087
|
+
this.#stmt(
|
|
1088
|
+
"UPDATE runs SET status = ?, started_at = ?, finished_at = NULL, summary = NULL WHERE id = ?"
|
|
1089
|
+
).run("running", Date.now(), id);
|
|
1090
|
+
},
|
|
1091
|
+
{ mode: "deferred" }
|
|
1092
|
+
);
|
|
1160
1093
|
}
|
|
1161
1094
|
deleteRun(id) {
|
|
1162
1095
|
this.#stmt("DELETE FROM runs WHERE id = ?").run(id);
|
|
@@ -2184,45 +2117,44 @@ function resolveFailedIndexes(store, suiteName, model, threshold) {
|
|
|
2184
2117
|
);
|
|
2185
2118
|
return new Set(failingCases.map((c) => c.idx));
|
|
2186
2119
|
}
|
|
2187
|
-
var
|
|
2120
|
+
var EVERY_CASE = { selection: { type: "all" }, shouldAssert: false };
|
|
2121
|
+
var EvalBuilder = class _EvalBuilder {
|
|
2188
2122
|
#plan;
|
|
2189
|
-
#
|
|
2190
|
-
|
|
2191
|
-
constructor(plan) {
|
|
2123
|
+
#run;
|
|
2124
|
+
constructor(plan, run) {
|
|
2192
2125
|
this.#plan = plan;
|
|
2126
|
+
this.#run = run;
|
|
2193
2127
|
}
|
|
2194
|
-
#
|
|
2195
|
-
if (this.#selection.type !== "all") {
|
|
2128
|
+
#select(selection) {
|
|
2129
|
+
if (this.#run.selection.type !== "all") {
|
|
2196
2130
|
throw new Error(
|
|
2197
|
-
`Cannot combine .${this.#selection.type}() with .${selection.type}()`
|
|
2131
|
+
`Cannot combine .${this.#run.selection.type}() with .${selection.type}()`
|
|
2198
2132
|
);
|
|
2199
2133
|
}
|
|
2200
|
-
this.#
|
|
2201
|
-
return this;
|
|
2134
|
+
return new _EvalBuilder(this.#plan, { ...this.#run, selection });
|
|
2202
2135
|
}
|
|
2203
2136
|
failed() {
|
|
2204
|
-
return this.#
|
|
2137
|
+
return this.#select({ type: "failed" });
|
|
2205
2138
|
}
|
|
2206
2139
|
cases(spec) {
|
|
2207
2140
|
const { indexes } = parseRecordSelection(spec);
|
|
2208
|
-
return this.#
|
|
2141
|
+
return this.#select({ type: "cases", indexes });
|
|
2209
2142
|
}
|
|
2210
2143
|
sample(count) {
|
|
2211
2144
|
if (count < 1) {
|
|
2212
2145
|
throw new Error("Sample count must be >= 1");
|
|
2213
2146
|
}
|
|
2214
|
-
return this.#
|
|
2147
|
+
return this.#select({ type: "sample", count });
|
|
2215
2148
|
}
|
|
2216
2149
|
assert() {
|
|
2217
|
-
this.#shouldAssert
|
|
2218
|
-
return this;
|
|
2150
|
+
return new _EvalBuilder(this.#plan, { ...this.#run, shouldAssert: true });
|
|
2219
2151
|
}
|
|
2220
2152
|
then(onfulfilled, onrejected) {
|
|
2221
2153
|
return this.#execute().then(onfulfilled, onrejected);
|
|
2222
2154
|
}
|
|
2223
2155
|
async #execute() {
|
|
2224
|
-
const result = await this.#plan.run(this.#selection);
|
|
2225
|
-
if (this.#shouldAssert && this.#plan.hasFailures(result)) {
|
|
2156
|
+
const result = await this.#plan.run(this.#run.selection);
|
|
2157
|
+
if (this.#run.shouldAssert && this.#plan.hasFailures(result)) {
|
|
2226
2158
|
throw new EvalAssertionError(result);
|
|
2227
2159
|
}
|
|
2228
2160
|
return result;
|
|
@@ -2288,9 +2220,9 @@ var EachModelEval = class {
|
|
|
2288
2220
|
};
|
|
2289
2221
|
function evaluate(options) {
|
|
2290
2222
|
if ("models" in options) {
|
|
2291
|
-
return new EvalBuilder(new EachModelEval(options));
|
|
2223
|
+
return new EvalBuilder(new EachModelEval(options), EVERY_CASE);
|
|
2292
2224
|
}
|
|
2293
|
-
return new EvalBuilder(new SingleModelEval(options));
|
|
2225
|
+
return new EvalBuilder(new SingleModelEval(options), EVERY_CASE);
|
|
2294
2226
|
}
|
|
2295
2227
|
function wireReporters(reporters) {
|
|
2296
2228
|
const emitter = new EvalEmitter();
|