@theokit/sdk 4.4.2 → 4.5.1
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/CHANGELOG.md +21 -0
- package/README.md +12 -2
- package/claude-template/dot-claude/skills/theokit-eval/SKILL.md +35 -0
- package/dist/eval.cjs +289 -11
- package/dist/eval.cjs.map +1 -1
- package/dist/eval.d.cts +1 -0
- package/dist/eval.d.ts +1 -0
- package/dist/eval.js +288 -12
- package/dist/eval.js.map +1 -1
- package/dist/interactive/index.d.cts +12 -0
- package/dist/interactive/types.d.cts +85 -0
- package/dist/internal/eval/assert.d.ts +26 -0
- package/dist/internal/eval/trials.d.ts +19 -0
- package/dist/internal/scorers/levenshtein.d.ts +11 -0
- package/dist/scorers.d.ts +57 -0
- package/dist/types/eval.d.ts +45 -0
- package/docs/error-codes.md +157 -0
- package/docs/harness-capability-map.md +260 -0
- package/package.json +13 -12
package/dist/eval.d.cts
CHANGED
|
@@ -31,6 +31,7 @@ export declare class Eval {
|
|
|
31
31
|
*/
|
|
32
32
|
run(runOpts?: EvalRunOptions): Promise<EvalRun>;
|
|
33
33
|
}
|
|
34
|
+
export { assertEval, EvalThresholdError } from "./internal/eval/assert.js";
|
|
34
35
|
export { captureArtifact } from "./internal/eval/code-runner.js";
|
|
35
36
|
export { EvalAlreadyRunningError } from "./internal/eval/single-flight.js";
|
|
36
37
|
export { JsonlParseError, loadJsonl } from "./internal/persistence/jsonl.js";
|
package/dist/eval.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export declare class Eval {
|
|
|
31
31
|
*/
|
|
32
32
|
run(runOpts?: EvalRunOptions): Promise<EvalRun>;
|
|
33
33
|
}
|
|
34
|
+
export { assertEval, EvalThresholdError } from "./internal/eval/assert.js";
|
|
34
35
|
export { captureArtifact } from "./internal/eval/code-runner.js";
|
|
35
36
|
export { EvalAlreadyRunningError } from "./internal/eval/single-flight.js";
|
|
36
37
|
export { JsonlParseError, loadJsonl } from "./internal/persistence/jsonl.js";
|
package/dist/eval.js
CHANGED
|
@@ -7253,11 +7253,11 @@ function truncateToBudget(sources, maxTokens) {
|
|
|
7253
7253
|
if (total <= maxTokens) {
|
|
7254
7254
|
return sources.map((src) => ({ name: src.name, tokens: src.tokens }));
|
|
7255
7255
|
}
|
|
7256
|
-
const
|
|
7257
|
-
const remaining = Math.max(0, maxTokens -
|
|
7256
|
+
const floor2 = Math.min(MIN_SOURCE_TOKENS, Math.floor(maxTokens / sources.length));
|
|
7257
|
+
const remaining = Math.max(0, maxTokens - floor2 * sources.length);
|
|
7258
7258
|
return sources.map((src) => {
|
|
7259
7259
|
const proportional = total === 0 ? 0 : Math.floor(src.tokens.length / total * remaining);
|
|
7260
|
-
const allotted = Math.min(src.tokens.length,
|
|
7260
|
+
const allotted = Math.min(src.tokens.length, floor2 + proportional);
|
|
7261
7261
|
return { name: src.name, tokens: src.tokens.slice(0, allotted) };
|
|
7262
7262
|
});
|
|
7263
7263
|
}
|
|
@@ -18245,6 +18245,87 @@ function startEvalRunSpan(attrs) {
|
|
|
18245
18245
|
};
|
|
18246
18246
|
}
|
|
18247
18247
|
|
|
18248
|
+
// src/internal/eval/trials.ts
|
|
18249
|
+
var TRIAL_INDEX_KEY = "__evalRowIndex";
|
|
18250
|
+
var TRIAL_NUM_KEY = "__evalTrial";
|
|
18251
|
+
function expandForTrials(entries, trials) {
|
|
18252
|
+
const out = [];
|
|
18253
|
+
for (let i = 0; i < entries.length; i += 1) {
|
|
18254
|
+
const entry = entries[i];
|
|
18255
|
+
if (entry === void 0) continue;
|
|
18256
|
+
for (let t = 0; t < trials; t += 1) {
|
|
18257
|
+
out.push({
|
|
18258
|
+
...entry,
|
|
18259
|
+
metadata: { ...entry.metadata ?? {}, [TRIAL_INDEX_KEY]: i, [TRIAL_NUM_KEY]: t }
|
|
18260
|
+
});
|
|
18261
|
+
}
|
|
18262
|
+
}
|
|
18263
|
+
return out;
|
|
18264
|
+
}
|
|
18265
|
+
function stripReserved(metadata) {
|
|
18266
|
+
if (metadata === void 0) return void 0;
|
|
18267
|
+
const { [TRIAL_INDEX_KEY]: _idx, [TRIAL_NUM_KEY]: _trial, ...rest } = metadata;
|
|
18268
|
+
return Object.keys(rest).length > 0 ? rest : void 0;
|
|
18269
|
+
}
|
|
18270
|
+
function sumDefined(values) {
|
|
18271
|
+
const present = values.filter((v) => typeof v === "number");
|
|
18272
|
+
return present.length > 0 ? present.reduce((acc, v) => acc + v, 0) : void 0;
|
|
18273
|
+
}
|
|
18274
|
+
function meanScoresByScorer(group, trials) {
|
|
18275
|
+
const names = [];
|
|
18276
|
+
const sums = /* @__PURE__ */ new Map();
|
|
18277
|
+
for (const row of group) {
|
|
18278
|
+
for (const s of row.scores) {
|
|
18279
|
+
if (!sums.has(s.name)) {
|
|
18280
|
+
names.push(s.name);
|
|
18281
|
+
sums.set(s.name, 0);
|
|
18282
|
+
}
|
|
18283
|
+
sums.set(s.name, (sums.get(s.name) ?? 0) + s.score);
|
|
18284
|
+
}
|
|
18285
|
+
}
|
|
18286
|
+
return names.map((name) => ({
|
|
18287
|
+
name,
|
|
18288
|
+
score: (sums.get(name) ?? 0) / trials,
|
|
18289
|
+
reason: `mean of ${trials} trials`
|
|
18290
|
+
}));
|
|
18291
|
+
}
|
|
18292
|
+
function collapseGroup(index, group, trials) {
|
|
18293
|
+
const first = group[0];
|
|
18294
|
+
const scores = meanScoresByScorer(group, trials);
|
|
18295
|
+
const allErrored = group.every((r) => r.error !== void 0);
|
|
18296
|
+
const meanScore = allErrored || scores.length === 0 ? 0 : scores.reduce((acc, s) => acc + s.score, 0) / scores.length;
|
|
18297
|
+
const firstOk = group.find((r) => r.error === void 0);
|
|
18298
|
+
const errorMsg = allErrored ? group.find((r) => r.error !== void 0)?.error : void 0;
|
|
18299
|
+
const metadata = stripReserved(first.metadata);
|
|
18300
|
+
const tokensIn = sumDefined(group.map((r) => r.tokensIn));
|
|
18301
|
+
const tokensOut = sumDefined(group.map((r) => r.tokensOut));
|
|
18302
|
+
return {
|
|
18303
|
+
index,
|
|
18304
|
+
input: first.input,
|
|
18305
|
+
output: firstOk?.output ?? first.output,
|
|
18306
|
+
...first.expected !== void 0 ? { expected: first.expected } : {},
|
|
18307
|
+
scores,
|
|
18308
|
+
meanScore,
|
|
18309
|
+
durationMs: group.reduce((acc, r) => acc + r.durationMs, 0),
|
|
18310
|
+
...tokensIn !== void 0 ? { tokensIn } : {},
|
|
18311
|
+
...tokensOut !== void 0 ? { tokensOut } : {},
|
|
18312
|
+
...errorMsg !== void 0 ? { error: errorMsg } : {},
|
|
18313
|
+
...metadata !== void 0 ? { metadata } : {},
|
|
18314
|
+
trialCount: trials
|
|
18315
|
+
};
|
|
18316
|
+
}
|
|
18317
|
+
function collapseTrials(rows, trials) {
|
|
18318
|
+
const groups = /* @__PURE__ */ new Map();
|
|
18319
|
+
for (const row of rows) {
|
|
18320
|
+
const meta = row.metadata;
|
|
18321
|
+
const origIdx = typeof meta?.[TRIAL_INDEX_KEY] === "number" ? meta[TRIAL_INDEX_KEY] : row.index;
|
|
18322
|
+
const arr = groups.get(origIdx) ?? [];
|
|
18323
|
+
arr.push(row);
|
|
18324
|
+
groups.set(origIdx, arr);
|
|
18325
|
+
}
|
|
18326
|
+
return [...groups.entries()].sort((a, b) => a[0] - b[0]).map(([origIdx, group]) => collapseGroup(origIdx, group, trials));
|
|
18327
|
+
}
|
|
18328
|
+
|
|
18248
18329
|
// src/internal/eval/runner.ts
|
|
18249
18330
|
function safeHook(fn) {
|
|
18250
18331
|
try {
|
|
@@ -18453,7 +18534,9 @@ async function runEval(options, runOpts) {
|
|
|
18453
18534
|
const id = randomUUID();
|
|
18454
18535
|
const startedAt = Date.now();
|
|
18455
18536
|
const entries = await materializeDataset(options.dataset);
|
|
18456
|
-
const
|
|
18537
|
+
const materialized = entries.map((e) => ({ ...e }));
|
|
18538
|
+
const trials = options.trials ?? 1;
|
|
18539
|
+
const indexed = trials > 1 ? expandForTrials(materialized, trials) : materialized;
|
|
18457
18540
|
const scorers = normalizeScorers(options.scorers);
|
|
18458
18541
|
const concurrency = options.concurrency ?? 4;
|
|
18459
18542
|
const signal = runOpts?.signal;
|
|
@@ -18485,7 +18568,8 @@ async function runEval(options, runOpts) {
|
|
|
18485
18568
|
const batchOpts = makeAgentForBatch(options.agent, indexed);
|
|
18486
18569
|
rows = await runRowsViaBatch(indexed, batchOpts, scorers, concurrency, signal, onRow, sink);
|
|
18487
18570
|
}
|
|
18488
|
-
const
|
|
18571
|
+
const finalRows = trials > 1 ? collapseTrials(rows, trials) : rows;
|
|
18572
|
+
const aggregate = computeAggregate(finalRows);
|
|
18489
18573
|
const endedAt = Date.now();
|
|
18490
18574
|
const run = {
|
|
18491
18575
|
id,
|
|
@@ -18494,7 +18578,7 @@ async function runEval(options, runOpts) {
|
|
|
18494
18578
|
endedAt,
|
|
18495
18579
|
durationMs: endedAt - startedAt,
|
|
18496
18580
|
aggregate,
|
|
18497
|
-
rows,
|
|
18581
|
+
rows: finalRows,
|
|
18498
18582
|
...options.metadata !== void 0 ? { metadata: options.metadata } : {}
|
|
18499
18583
|
};
|
|
18500
18584
|
safeHook(() => hooks?.afterRun?.(run));
|
|
@@ -18509,6 +18593,59 @@ async function runEval(options, runOpts) {
|
|
|
18509
18593
|
}
|
|
18510
18594
|
}
|
|
18511
18595
|
|
|
18596
|
+
// src/internal/eval/assert.ts
|
|
18597
|
+
var EvalThresholdError = class extends Error {
|
|
18598
|
+
name = "EvalThresholdError";
|
|
18599
|
+
/** The eval's name (`EvalRun.name`). */
|
|
18600
|
+
evalName;
|
|
18601
|
+
/** Every unmet threshold, in check order. */
|
|
18602
|
+
failures;
|
|
18603
|
+
constructor(evalName, failures) {
|
|
18604
|
+
const lines = failures.map(
|
|
18605
|
+
(f) => ` - ${f.metric}: required ${f.threshold}, got ${Number.isNaN(f.actual) ? "n/a (scorer absent)" : f.actual}`
|
|
18606
|
+
);
|
|
18607
|
+
super(
|
|
18608
|
+
`Eval "${evalName}" failed ${failures.length} threshold${failures.length === 1 ? "" : "s"}:
|
|
18609
|
+
${lines.join("\n")}`
|
|
18610
|
+
);
|
|
18611
|
+
this.evalName = evalName;
|
|
18612
|
+
this.failures = failures;
|
|
18613
|
+
}
|
|
18614
|
+
};
|
|
18615
|
+
function floor(metric, actual, threshold) {
|
|
18616
|
+
if (threshold === void 0 || actual >= threshold) return void 0;
|
|
18617
|
+
return { metric, threshold, actual };
|
|
18618
|
+
}
|
|
18619
|
+
function perScorerFailures(perScorer, thresholds) {
|
|
18620
|
+
if (thresholds === void 0) return [];
|
|
18621
|
+
const out = [];
|
|
18622
|
+
for (const [name, min] of Object.entries(thresholds)) {
|
|
18623
|
+
const stats = perScorer[name];
|
|
18624
|
+
if (stats === void 0 || stats.mean < min) {
|
|
18625
|
+
out.push({
|
|
18626
|
+
metric: `perScorer.${name}`,
|
|
18627
|
+
threshold: min,
|
|
18628
|
+
actual: stats === void 0 ? Number.NaN : stats.mean
|
|
18629
|
+
});
|
|
18630
|
+
}
|
|
18631
|
+
}
|
|
18632
|
+
return out;
|
|
18633
|
+
}
|
|
18634
|
+
function assertEval(run, thresholds) {
|
|
18635
|
+
const a = run.aggregate;
|
|
18636
|
+
const errorRatio = a.totalRows > 0 ? a.errorRows / a.totalRows : 0;
|
|
18637
|
+
const ceiling = thresholds.maxErrorRatio !== void 0 && errorRatio > thresholds.maxErrorRatio ? { metric: "errorRatio", threshold: thresholds.maxErrorRatio, actual: errorRatio } : void 0;
|
|
18638
|
+
const failures = [
|
|
18639
|
+
floor("meanScore", a.meanScore, thresholds.minMeanScore),
|
|
18640
|
+
floor("passRatio", a.passRatio, thresholds.minPassRatio),
|
|
18641
|
+
ceiling,
|
|
18642
|
+
...perScorerFailures(a.perScorer, thresholds.perScorer)
|
|
18643
|
+
].filter((f) => f !== void 0);
|
|
18644
|
+
if (failures.length > 0) {
|
|
18645
|
+
throw new EvalThresholdError(run.name, failures);
|
|
18646
|
+
}
|
|
18647
|
+
}
|
|
18648
|
+
|
|
18512
18649
|
// src/sandbox/shell-escape.ts
|
|
18513
18650
|
function shellEscapePosix(arg) {
|
|
18514
18651
|
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
@@ -18528,6 +18665,27 @@ async function captureArtifact(sandbox, repoDir) {
|
|
|
18528
18665
|
return { diff, applies: check.exitCode === 0 };
|
|
18529
18666
|
}
|
|
18530
18667
|
|
|
18668
|
+
// src/internal/scorers/levenshtein.ts
|
|
18669
|
+
var LEVENSHTEIN_MAX_LEN = 4e3;
|
|
18670
|
+
function nextRow(prev, rowIndex, ai, b) {
|
|
18671
|
+
const curr = [rowIndex];
|
|
18672
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
18673
|
+
const cost = ai === b.charCodeAt(j - 1) ? 0 : 1;
|
|
18674
|
+
curr[j] = Math.min((prev[j] ?? 0) + 1, (curr[j - 1] ?? 0) + 1, (prev[j - 1] ?? 0) + cost);
|
|
18675
|
+
}
|
|
18676
|
+
return curr;
|
|
18677
|
+
}
|
|
18678
|
+
function levenshteinDistance(a, b) {
|
|
18679
|
+
const m = a.length;
|
|
18680
|
+
const n = b.length;
|
|
18681
|
+
if (m === 0) return n;
|
|
18682
|
+
if (n === 0) return m;
|
|
18683
|
+
let prev = [];
|
|
18684
|
+
for (let j = 0; j <= n; j += 1) prev[j] = j;
|
|
18685
|
+
for (let i = 1; i <= m; i += 1) prev = nextRow(prev, i, a.charCodeAt(i - 1), b);
|
|
18686
|
+
return prev[n] ?? 0;
|
|
18687
|
+
}
|
|
18688
|
+
|
|
18531
18689
|
// src/internal/scorers/llm-judge.ts
|
|
18532
18690
|
init_agent_factory_registry();
|
|
18533
18691
|
function buildPrompt(subject, criteria, rubric, expected) {
|
|
@@ -18688,15 +18846,80 @@ var LocalSandbox = class extends SandboxBackend {
|
|
|
18688
18846
|
|
|
18689
18847
|
// src/scorers.ts
|
|
18690
18848
|
var JSON_SHAPE_MAX_BYTES = 1e6;
|
|
18849
|
+
function cosineSimilarity(a, b) {
|
|
18850
|
+
const n = Math.min(a.length, b.length);
|
|
18851
|
+
let dot = 0;
|
|
18852
|
+
let na = 0;
|
|
18853
|
+
let nb = 0;
|
|
18854
|
+
for (let i = 0; i < n; i += 1) {
|
|
18855
|
+
const av = a[i] ?? 0;
|
|
18856
|
+
const bv = b[i] ?? 0;
|
|
18857
|
+
dot += av * bv;
|
|
18858
|
+
na += av * av;
|
|
18859
|
+
nb += bv * bv;
|
|
18860
|
+
}
|
|
18861
|
+
if (na === 0 || nb === 0) return 0;
|
|
18862
|
+
return dot / (Math.sqrt(na) * Math.sqrt(nb));
|
|
18863
|
+
}
|
|
18864
|
+
function normalizeStringInputs(output, expected, caseSensitive) {
|
|
18865
|
+
if (typeof expected !== "string") return { score: 0, reason: "expected_not_string" };
|
|
18866
|
+
if (expected.length === 0) return { score: 0, reason: "expected_empty" };
|
|
18867
|
+
return {
|
|
18868
|
+
o: caseSensitive ? output : output.toLowerCase(),
|
|
18869
|
+
e: caseSensitive ? expected : expected.toLowerCase()
|
|
18870
|
+
};
|
|
18871
|
+
}
|
|
18872
|
+
function scoreLevenshtein(output, expected, caseSensitive, threshold) {
|
|
18873
|
+
const norm = normalizeStringInputs(output, expected, caseSensitive);
|
|
18874
|
+
if (!("o" in norm)) return norm;
|
|
18875
|
+
const { o, e } = norm;
|
|
18876
|
+
if (o.length > LEVENSHTEIN_MAX_LEN || e.length > LEVENSHTEIN_MAX_LEN) {
|
|
18877
|
+
return { score: 0, reason: "input_too_large" };
|
|
18878
|
+
}
|
|
18879
|
+
const sim = 1 - levenshteinDistance(o, e) / Math.max(o.length, e.length, 1);
|
|
18880
|
+
if (threshold === void 0) return { score: sim };
|
|
18881
|
+
return sim >= threshold ? { score: 1 } : { score: 0, reason: `sim=${sim.toFixed(3)}` };
|
|
18882
|
+
}
|
|
18883
|
+
function toFiniteNumber2(value) {
|
|
18884
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
18885
|
+
if (value === void 0 || value === null) return void 0;
|
|
18886
|
+
const n = Number(String(value).trim());
|
|
18887
|
+
return Number.isFinite(n) ? n : void 0;
|
|
18888
|
+
}
|
|
18889
|
+
function scoreNumericDiff(output, expected, tolerance) {
|
|
18890
|
+
const o = toFiniteNumber2(output);
|
|
18891
|
+
if (o === void 0) return { score: 0, reason: "output_not_numeric" };
|
|
18892
|
+
const e = toFiniteNumber2(expected);
|
|
18893
|
+
if (e === void 0) return { score: 0, reason: "expected_not_numeric" };
|
|
18894
|
+
if (tolerance !== void 0) {
|
|
18895
|
+
return Math.abs(o - e) <= tolerance ? { score: 1 } : { score: 0, reason: `abs_diff=${Math.abs(o - e)}` };
|
|
18896
|
+
}
|
|
18897
|
+
const denom = Math.max(Math.abs(o), Math.abs(e));
|
|
18898
|
+
if (denom === 0) return { score: 1 };
|
|
18899
|
+
return { score: Math.max(0, 1 - Math.abs(o - e) / denom) };
|
|
18900
|
+
}
|
|
18901
|
+
function embedderCreateOptions(opts) {
|
|
18902
|
+
return {
|
|
18903
|
+
...opts.model !== void 0 ? { model: opts.model } : {},
|
|
18904
|
+
...opts.apiKey !== void 0 ? { apiKey: opts.apiKey } : {},
|
|
18905
|
+
...opts.baseUrl !== void 0 ? { baseUrl: opts.baseUrl } : {}
|
|
18906
|
+
};
|
|
18907
|
+
}
|
|
18908
|
+
function makeDefaultEmbedder(opts) {
|
|
18909
|
+
let runtime;
|
|
18910
|
+
return (texts) => {
|
|
18911
|
+
if (runtime === void 0) {
|
|
18912
|
+
runtime = openRouterMemoryEmbeddingProviderAdapter.create(embedderCreateOptions(opts));
|
|
18913
|
+
}
|
|
18914
|
+
return runtime.then((rt) => rt.embed(texts));
|
|
18915
|
+
};
|
|
18916
|
+
}
|
|
18691
18917
|
function makeStringScorer(name, caseSensitive, compare) {
|
|
18692
18918
|
return {
|
|
18693
18919
|
name,
|
|
18694
18920
|
score: (output, expected) => {
|
|
18695
|
-
|
|
18696
|
-
|
|
18697
|
-
const o = caseSensitive ? output : output.toLowerCase();
|
|
18698
|
-
const e = caseSensitive ? expected : expected.toLowerCase();
|
|
18699
|
-
return compare(o, e);
|
|
18921
|
+
const norm = normalizeStringInputs(output, expected, caseSensitive);
|
|
18922
|
+
return "o" in norm ? compare(norm.o, norm.e) : norm;
|
|
18700
18923
|
}
|
|
18701
18924
|
};
|
|
18702
18925
|
}
|
|
@@ -18740,6 +18963,56 @@ var Scorers = {
|
|
|
18740
18963
|
}
|
|
18741
18964
|
};
|
|
18742
18965
|
},
|
|
18966
|
+
/**
|
|
18967
|
+
* SE41 — normalized Levenshtein similarity: `1 - editDistance / max(len)`.
|
|
18968
|
+
* Deterministic (no LLM), so it always runs in CI. `threshold` binarizes.
|
|
18969
|
+
*
|
|
18970
|
+
* Refuses empty/non-string `expected` (EC-1 parity) and caps input at
|
|
18971
|
+
* {@link LEVENSHTEIN_MAX_LEN} chars to bound the O(n*m) cost on adversarial output.
|
|
18972
|
+
*/
|
|
18973
|
+
levenshtein(opts = {}) {
|
|
18974
|
+
const caseSensitive = opts.caseSensitive ?? false;
|
|
18975
|
+
const { threshold } = opts;
|
|
18976
|
+
return {
|
|
18977
|
+
name: threshold !== void 0 ? `levenshtein(>=${threshold})` : "levenshtein",
|
|
18978
|
+
score: (output, expected) => scoreLevenshtein(output, expected, caseSensitive, threshold)
|
|
18979
|
+
};
|
|
18980
|
+
},
|
|
18981
|
+
/**
|
|
18982
|
+
* SE41 — numeric closeness. Parses `output` and `expected` as numbers and
|
|
18983
|
+
* scores continuous relative closeness `1 - |o-e| / max(|o|,|e|)` (both 0 ⇒ 1),
|
|
18984
|
+
* or a binary pass when `tolerance` is set. Deterministic (no LLM).
|
|
18985
|
+
*/
|
|
18986
|
+
numericDiff(opts = {}) {
|
|
18987
|
+
const { tolerance } = opts;
|
|
18988
|
+
return {
|
|
18989
|
+
name: "numeric-diff",
|
|
18990
|
+
score: (output, expected) => scoreNumericDiff(output, expected, tolerance)
|
|
18991
|
+
};
|
|
18992
|
+
},
|
|
18993
|
+
/**
|
|
18994
|
+
* SE41 — semantic similarity via embeddings: cosine of `embed(output)` vs
|
|
18995
|
+
* `embed(expected)`, clamped to `[0, 1]` (negatives → 0). `threshold` binarizes.
|
|
18996
|
+
*
|
|
18997
|
+
* By default routes through OpenRouter's embeddings endpoint
|
|
18998
|
+
* (`OPENROUTER_API_KEY`); inject `embed` to use another provider or to test
|
|
18999
|
+
* deterministically. Each scored row costs one embeddings call.
|
|
19000
|
+
*/
|
|
19001
|
+
embeddingSimilarity(opts) {
|
|
19002
|
+
const { threshold } = opts;
|
|
19003
|
+
const embed = opts.embed ?? makeDefaultEmbedder(opts);
|
|
19004
|
+
return {
|
|
19005
|
+
name: threshold !== void 0 ? `embedding-similarity(>=${threshold})` : "embedding-similarity",
|
|
19006
|
+
score: async (output, expected) => {
|
|
19007
|
+
if (typeof expected !== "string") return { score: 0, reason: "expected_not_string" };
|
|
19008
|
+
const [vo, ve] = await embed([output, expected]);
|
|
19009
|
+
if (vo === void 0 || ve === void 0) return { score: 0, reason: "embed_failed" };
|
|
19010
|
+
const score = Math.max(0, cosineSimilarity(vo, ve));
|
|
19011
|
+
if (threshold === void 0) return { score };
|
|
19012
|
+
return score >= threshold ? { score: 1 } : { score: 0, reason: `cos=${score.toFixed(3)}` };
|
|
19013
|
+
}
|
|
19014
|
+
};
|
|
19015
|
+
},
|
|
18743
19016
|
/**
|
|
18744
19017
|
* Parse `output` as JSON and validate against a Zod schema.
|
|
18745
19018
|
*
|
|
@@ -18845,6 +19118,9 @@ var EvalOptionsSchema = z.object({
|
|
|
18845
19118
|
// EC-3: concurrency MUST be a finite integer in [1, 64]. Bare `z.number()`
|
|
18846
19119
|
// would let Infinity / 0 / negatives slip through.
|
|
18847
19120
|
concurrency: z.number().int("concurrency must be an integer").min(1, "concurrency must be >= 1").max(64, "concurrency must be <= 64").optional(),
|
|
19121
|
+
// SE41: trials MUST be a finite integer in [1, 100]. Guards against 0
|
|
19122
|
+
// (no-op run) and unbounded fanout that would DoS the provider.
|
|
19123
|
+
trials: z.number().int("trials must be an integer").min(1, "trials must be >= 1").max(100, "trials must be <= 100").optional(),
|
|
18848
19124
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
18849
19125
|
hooks: z.object({
|
|
18850
19126
|
beforeRun: z.function().optional(),
|
|
@@ -18875,6 +19151,6 @@ var Eval = class _Eval {
|
|
|
18875
19151
|
}
|
|
18876
19152
|
};
|
|
18877
19153
|
|
|
18878
|
-
export { Eval, EvalAlreadyRunningError, JsonlParseError, Scorers, captureArtifact, loadJsonl };
|
|
19154
|
+
export { Eval, EvalAlreadyRunningError, EvalThresholdError, JsonlParseError, Scorers, assertEval, captureArtifact, loadJsonl };
|
|
18879
19155
|
//# sourceMappingURL=eval.js.map
|
|
18880
19156
|
//# sourceMappingURL=eval.js.map
|