@gleanwork/mcp-server-tester 1.0.1 → 1.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.
- package/dist/cli/index.js +1 -1
- package/dist/fixtures/mcp.js +1 -1
- package/dist/fixtures/mcp.js.map +1 -1
- package/dist/index-BcUWzQCx.d.cts +4282 -0
- package/dist/index-BcUWzQCx.d.ts +4282 -0
- package/dist/index.cjs +695 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +551 -4365
- package/dist/index.d.ts +551 -4365
- package/dist/index.js +685 -33
- package/dist/index.js.map +1 -1
- package/dist/reporters/mcpReporter.cjs +294 -1
- package/dist/reporters/mcpReporter.cjs.map +1 -1
- package/dist/reporters/mcpReporter.d.cts +426 -3
- package/dist/reporters/mcpReporter.d.ts +426 -3
- package/dist/reporters/mcpReporter.js +294 -1
- package/dist/reporters/mcpReporter.js.map +1 -1
- package/dist/reporters/ui-dist/app.js +1 -1
- package/dist/types/index.cjs +19 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +9 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.js +17 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +8 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
|
-
import { readFile, mkdir, writeFile } from 'fs/promises';
|
|
2
|
+
import { readFile, mkdir, writeFile, readdir } from 'fs/promises';
|
|
3
3
|
import * as path2 from 'path';
|
|
4
|
-
import { dirname } from 'path';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
5
|
import * as z4 from 'zod/v4';
|
|
6
6
|
import { ZodFirstPartyTypeKind } from 'zod/v3';
|
|
7
7
|
import { z } from 'zod';
|
|
@@ -3099,6 +3099,20 @@ var init_dist3 = __esm({
|
|
|
3099
3099
|
};
|
|
3100
3100
|
}
|
|
3101
3101
|
});
|
|
3102
|
+
|
|
3103
|
+
// src/assertions/validators/types.ts
|
|
3104
|
+
var SnapshotSanitizers = {
|
|
3105
|
+
/** Replaces Unix timestamps (seconds and milliseconds) with a stable placeholder */
|
|
3106
|
+
TIMESTAMP: "timestamp",
|
|
3107
|
+
/** Replaces UUID v1-v5 strings with a stable placeholder */
|
|
3108
|
+
UUID: "uuid",
|
|
3109
|
+
/** Replaces ISO 8601 date/datetime strings with a stable placeholder */
|
|
3110
|
+
ISO_DATE: "iso-date",
|
|
3111
|
+
/** Replaces MongoDB ObjectId strings with a stable placeholder */
|
|
3112
|
+
OBJECT_ID: "objectId",
|
|
3113
|
+
/** Replaces JWT tokens with a stable placeholder */
|
|
3114
|
+
JWT: "jwt"
|
|
3115
|
+
};
|
|
3102
3116
|
var MCPHostCapabilitiesSchema = z.object({
|
|
3103
3117
|
sampling: z.record(z.string(), z.unknown()).optional(),
|
|
3104
3118
|
roots: z.object({
|
|
@@ -4384,7 +4398,7 @@ function escapeHtml(text) {
|
|
|
4384
4398
|
|
|
4385
4399
|
// package.json
|
|
4386
4400
|
var package_default = {
|
|
4387
|
-
version: "1.0
|
|
4401
|
+
version: "1.1.0"};
|
|
4388
4402
|
|
|
4389
4403
|
// src/mcp/clientFactory.ts
|
|
4390
4404
|
function getRetryAfterDelayMs(err) {
|
|
@@ -5908,20 +5922,6 @@ async function validateJudge(response, config) {
|
|
|
5908
5922
|
}
|
|
5909
5923
|
}
|
|
5910
5924
|
|
|
5911
|
-
// src/assertions/validators/types.ts
|
|
5912
|
-
var SnapshotSanitizers = {
|
|
5913
|
-
/** Replaces Unix timestamps (seconds and milliseconds) with a stable placeholder */
|
|
5914
|
-
TIMESTAMP: "timestamp",
|
|
5915
|
-
/** Replaces UUID v1-v5 strings with a stable placeholder */
|
|
5916
|
-
UUID: "uuid",
|
|
5917
|
-
/** Replaces ISO 8601 date/datetime strings with a stable placeholder */
|
|
5918
|
-
ISO_DATE: "iso-date",
|
|
5919
|
-
/** Replaces MongoDB ObjectId strings with a stable placeholder */
|
|
5920
|
-
OBJECT_ID: "objectId",
|
|
5921
|
-
/** Replaces JWT tokens with a stable placeholder */
|
|
5922
|
-
JWT: "jwt"
|
|
5923
|
-
};
|
|
5924
|
-
|
|
5925
5925
|
// src/mcp/fixtures/mcpFixture.ts
|
|
5926
5926
|
var DEFAULT_CALL_TIMEOUT_MS = 3e4;
|
|
5927
5927
|
function withCallTimeout(promise, ms, opName) {
|
|
@@ -6967,6 +6967,7 @@ function parseStreamJson(stdout) {
|
|
|
6967
6967
|
const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
|
|
6968
6968
|
const toolCalls = [];
|
|
6969
6969
|
const textParts = [];
|
|
6970
|
+
let usage;
|
|
6970
6971
|
const conversationHistory = [];
|
|
6971
6972
|
for (const line of lines) {
|
|
6972
6973
|
let event;
|
|
@@ -6999,16 +7000,28 @@ function parseStreamJson(stdout) {
|
|
|
6999
7000
|
}
|
|
7000
7001
|
}
|
|
7001
7002
|
}
|
|
7002
|
-
if (event.type === "result"
|
|
7003
|
-
if (textParts.length === 0) {
|
|
7003
|
+
if (event.type === "result") {
|
|
7004
|
+
if (typeof event.result === "string" && textParts.length === 0) {
|
|
7004
7005
|
textParts.push(event.result);
|
|
7005
7006
|
}
|
|
7007
|
+
if (event.usage) {
|
|
7008
|
+
usage = {
|
|
7009
|
+
inputTokens: event.usage.input_tokens ?? 0,
|
|
7010
|
+
outputTokens: event.usage.output_tokens ?? 0,
|
|
7011
|
+
totalCostUsd: event.total_cost_usd ?? 0,
|
|
7012
|
+
durationMs: event.duration_ms ?? 0,
|
|
7013
|
+
durationApiMs: event.duration_api_ms,
|
|
7014
|
+
cacheReadInputTokens: event.usage.cache_read_input_tokens,
|
|
7015
|
+
cacheCreationInputTokens: event.usage.cache_creation_input_tokens
|
|
7016
|
+
};
|
|
7017
|
+
}
|
|
7006
7018
|
}
|
|
7007
7019
|
if (event.type === "result" && event.is_error === true) {
|
|
7008
7020
|
return {
|
|
7009
7021
|
success: false,
|
|
7010
7022
|
toolCalls,
|
|
7011
|
-
error: typeof event.result === "string" ? event.result : "CLI host reported an error"
|
|
7023
|
+
error: typeof event.result === "string" ? event.result : "CLI host reported an error",
|
|
7024
|
+
usage
|
|
7012
7025
|
};
|
|
7013
7026
|
}
|
|
7014
7027
|
}
|
|
@@ -7020,7 +7033,8 @@ function parseStreamJson(stdout) {
|
|
|
7020
7033
|
success: true,
|
|
7021
7034
|
toolCalls,
|
|
7022
7035
|
response: response || void 0,
|
|
7023
|
-
conversationHistory: conversationHistory.length > 0 ? conversationHistory : void 0
|
|
7036
|
+
conversationHistory: conversationHistory.length > 0 ? conversationHistory : void 0,
|
|
7037
|
+
usage
|
|
7024
7038
|
};
|
|
7025
7039
|
}
|
|
7026
7040
|
function createJsonParser(paths) {
|
|
@@ -7266,6 +7280,216 @@ function buildBaselinePassMap(baseline) {
|
|
|
7266
7280
|
}
|
|
7267
7281
|
return map;
|
|
7268
7282
|
}
|
|
7283
|
+
var KIND_DIRS = {
|
|
7284
|
+
"eval-runner-result": "eval-runs",
|
|
7285
|
+
"reporter-run": "reporter-runs",
|
|
7286
|
+
"eval-run-comparison": "comparisons/eval-runs",
|
|
7287
|
+
"server-comparison": "comparisons/servers"
|
|
7288
|
+
};
|
|
7289
|
+
function createEvalResultStore(config) {
|
|
7290
|
+
if (config.provider === "file") {
|
|
7291
|
+
return new FileEvalResultStore(config);
|
|
7292
|
+
}
|
|
7293
|
+
return new GCSEvalResultStore(config);
|
|
7294
|
+
}
|
|
7295
|
+
function resolveEvalResultStore(store) {
|
|
7296
|
+
return isEvalResultStore(store) ? store : createEvalResultStore(store);
|
|
7297
|
+
}
|
|
7298
|
+
function isEvalResultStore(value) {
|
|
7299
|
+
return typeof value === "object" && value !== null && "saveArtifact" in value && "loadArtifact" in value && "loadLatestArtifact" in value && "listArtifacts" in value;
|
|
7300
|
+
}
|
|
7301
|
+
function createStoredEvalArtifact(options) {
|
|
7302
|
+
const createdAt = options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
7303
|
+
return {
|
|
7304
|
+
schemaVersion: 1,
|
|
7305
|
+
kind: options.kind,
|
|
7306
|
+
id: options.id ?? createDefaultArtifactId(createdAt),
|
|
7307
|
+
createdAt,
|
|
7308
|
+
metadata: {
|
|
7309
|
+
...defaultEnvironmentMetadata(),
|
|
7310
|
+
...options.metadata ?? {}
|
|
7311
|
+
},
|
|
7312
|
+
data: options.data
|
|
7313
|
+
};
|
|
7314
|
+
}
|
|
7315
|
+
function createDefaultArtifactId(timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
7316
|
+
const safeTimestamp = timestamp.replace(/[:.]/g, "-");
|
|
7317
|
+
const runNumber = process.env.GITHUB_RUN_NUMBER;
|
|
7318
|
+
const sha = process.env.GITHUB_SHA?.slice(0, 12);
|
|
7319
|
+
const suffix = runNumber ?? sha;
|
|
7320
|
+
return suffix ? `${safeTimestamp}-${suffix}` : safeTimestamp;
|
|
7321
|
+
}
|
|
7322
|
+
function defaultEnvironmentMetadata() {
|
|
7323
|
+
return {
|
|
7324
|
+
...process.env.GITHUB_SHA !== void 0 && {
|
|
7325
|
+
gitHash: process.env.GITHUB_SHA
|
|
7326
|
+
},
|
|
7327
|
+
...process.env.GITHUB_REF_NAME !== void 0 && {
|
|
7328
|
+
branch: process.env.GITHUB_REF_NAME
|
|
7329
|
+
},
|
|
7330
|
+
...process.env.GITHUB_RUN_NUMBER !== void 0 && {
|
|
7331
|
+
runNumber: process.env.GITHUB_RUN_NUMBER
|
|
7332
|
+
},
|
|
7333
|
+
...process.env.GITHUB_EVENT_NAME !== void 0 && {
|
|
7334
|
+
trigger: process.env.GITHUB_EVENT_NAME
|
|
7335
|
+
}
|
|
7336
|
+
};
|
|
7337
|
+
}
|
|
7338
|
+
var FileEvalResultStore = class {
|
|
7339
|
+
dir;
|
|
7340
|
+
constructor(config) {
|
|
7341
|
+
this.dir = config.dir;
|
|
7342
|
+
}
|
|
7343
|
+
async saveArtifact(artifact) {
|
|
7344
|
+
const artifactDir = join(this.dir, KIND_DIRS[artifact.kind]);
|
|
7345
|
+
await mkdir(artifactDir, { recursive: true });
|
|
7346
|
+
const serialized = JSON.stringify(artifact, null, 2);
|
|
7347
|
+
await writeFile(
|
|
7348
|
+
join(artifactDir, `${artifact.id}.json`),
|
|
7349
|
+
serialized,
|
|
7350
|
+
"utf8"
|
|
7351
|
+
);
|
|
7352
|
+
await writeFile(join(artifactDir, "latest.json"), serialized, "utf8");
|
|
7353
|
+
}
|
|
7354
|
+
async loadArtifact(kind, id) {
|
|
7355
|
+
const raw = await readFile(
|
|
7356
|
+
join(this.dir, KIND_DIRS[kind], `${id}.json`),
|
|
7357
|
+
"utf8"
|
|
7358
|
+
);
|
|
7359
|
+
return JSON.parse(raw);
|
|
7360
|
+
}
|
|
7361
|
+
async loadLatestArtifact(kind) {
|
|
7362
|
+
try {
|
|
7363
|
+
const raw = await readFile(
|
|
7364
|
+
join(this.dir, KIND_DIRS[kind], "latest.json"),
|
|
7365
|
+
"utf8"
|
|
7366
|
+
);
|
|
7367
|
+
return JSON.parse(raw);
|
|
7368
|
+
} catch (error) {
|
|
7369
|
+
if (isMissingFileError(error)) {
|
|
7370
|
+
return null;
|
|
7371
|
+
}
|
|
7372
|
+
throw error;
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
async listArtifacts(kind, options = {}) {
|
|
7376
|
+
let files;
|
|
7377
|
+
try {
|
|
7378
|
+
files = await readdir(join(this.dir, KIND_DIRS[kind]));
|
|
7379
|
+
} catch (error) {
|
|
7380
|
+
if (isMissingFileError(error)) {
|
|
7381
|
+
return [];
|
|
7382
|
+
}
|
|
7383
|
+
throw error;
|
|
7384
|
+
}
|
|
7385
|
+
const summaries = await Promise.all(
|
|
7386
|
+
files.filter((f) => f.endsWith(".json") && f !== "latest.json").map(async (file) => {
|
|
7387
|
+
const raw = await readFile(
|
|
7388
|
+
join(this.dir, KIND_DIRS[kind], file),
|
|
7389
|
+
"utf8"
|
|
7390
|
+
);
|
|
7391
|
+
return toSummary(JSON.parse(raw));
|
|
7392
|
+
})
|
|
7393
|
+
);
|
|
7394
|
+
return summaries.sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, options.limit);
|
|
7395
|
+
}
|
|
7396
|
+
};
|
|
7397
|
+
var GCSEvalResultStore = class {
|
|
7398
|
+
bucketName;
|
|
7399
|
+
prefix;
|
|
7400
|
+
storage;
|
|
7401
|
+
constructor(config) {
|
|
7402
|
+
this.bucketName = config.bucket;
|
|
7403
|
+
this.prefix = trimSlashes(config.prefix ?? "");
|
|
7404
|
+
}
|
|
7405
|
+
async saveArtifact(artifact) {
|
|
7406
|
+
const bucket = await this.getBucket();
|
|
7407
|
+
const serialized = JSON.stringify(artifact, null, 2);
|
|
7408
|
+
await bucket.file(
|
|
7409
|
+
this.objectPath(
|
|
7410
|
+
artifact.kind,
|
|
7411
|
+
`${encodeURIComponent(artifact.id)}.json`
|
|
7412
|
+
)
|
|
7413
|
+
).save(serialized, {
|
|
7414
|
+
contentType: "application/json",
|
|
7415
|
+
resumable: false,
|
|
7416
|
+
validation: false
|
|
7417
|
+
});
|
|
7418
|
+
await bucket.file(this.objectPath(artifact.kind, "latest.json")).save(serialized, {
|
|
7419
|
+
contentType: "application/json",
|
|
7420
|
+
resumable: false,
|
|
7421
|
+
validation: false
|
|
7422
|
+
});
|
|
7423
|
+
}
|
|
7424
|
+
async loadArtifact(kind, id) {
|
|
7425
|
+
const bucket = await this.getBucket();
|
|
7426
|
+
const file = bucket.file(
|
|
7427
|
+
this.objectPath(kind, `${encodeURIComponent(id)}.json`)
|
|
7428
|
+
);
|
|
7429
|
+
const [buffer] = await file.download();
|
|
7430
|
+
return JSON.parse(buffer.toString("utf8"));
|
|
7431
|
+
}
|
|
7432
|
+
async loadLatestArtifact(kind) {
|
|
7433
|
+
const bucket = await this.getBucket();
|
|
7434
|
+
const file = bucket.file(this.objectPath(kind, "latest.json"));
|
|
7435
|
+
const [exists] = await file.exists();
|
|
7436
|
+
if (!exists) return null;
|
|
7437
|
+
const [buffer] = await file.download();
|
|
7438
|
+
return JSON.parse(buffer.toString("utf8"));
|
|
7439
|
+
}
|
|
7440
|
+
async listArtifacts(kind, options = {}) {
|
|
7441
|
+
const bucket = await this.getBucket();
|
|
7442
|
+
const [files] = await bucket.getFiles({
|
|
7443
|
+
prefix: this.objectPath(kind, "")
|
|
7444
|
+
});
|
|
7445
|
+
const summaries = await Promise.all(
|
|
7446
|
+
files.filter(
|
|
7447
|
+
(f) => f.name.endsWith(".json") && !f.name.endsWith("/latest.json")
|
|
7448
|
+
).map(async (file) => {
|
|
7449
|
+
const [buffer] = await file.download();
|
|
7450
|
+
return toSummary(
|
|
7451
|
+
JSON.parse(buffer.toString("utf8"))
|
|
7452
|
+
);
|
|
7453
|
+
})
|
|
7454
|
+
);
|
|
7455
|
+
return summaries.sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, options.limit);
|
|
7456
|
+
}
|
|
7457
|
+
async getBucket() {
|
|
7458
|
+
if (!this.storage) {
|
|
7459
|
+
const moduleName = "@google-cloud/storage";
|
|
7460
|
+
let Storage;
|
|
7461
|
+
try {
|
|
7462
|
+
const mod = await import(moduleName);
|
|
7463
|
+
Storage = mod.Storage;
|
|
7464
|
+
} catch (error) {
|
|
7465
|
+
throw new Error(
|
|
7466
|
+
`GCS result storage requires the optional \`@google-cloud/storage\` package. Install it and authenticate with Application Default Credentials via GOOGLE_APPLICATION_CREDENTIALS.
|
|
7467
|
+
Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
7468
|
+
);
|
|
7469
|
+
}
|
|
7470
|
+
this.storage = new Storage();
|
|
7471
|
+
}
|
|
7472
|
+
return this.storage.bucket(this.bucketName);
|
|
7473
|
+
}
|
|
7474
|
+
objectPath(kind, filename) {
|
|
7475
|
+
const parts = [this.prefix, KIND_DIRS[kind], filename].filter(Boolean);
|
|
7476
|
+
return parts.join("/");
|
|
7477
|
+
}
|
|
7478
|
+
};
|
|
7479
|
+
function toSummary(artifact) {
|
|
7480
|
+
return {
|
|
7481
|
+
kind: artifact.kind,
|
|
7482
|
+
id: artifact.id,
|
|
7483
|
+
createdAt: artifact.createdAt,
|
|
7484
|
+
metadata: artifact.metadata
|
|
7485
|
+
};
|
|
7486
|
+
}
|
|
7487
|
+
function trimSlashes(value) {
|
|
7488
|
+
return value.replace(/^\/+|\/+$/g, "");
|
|
7489
|
+
}
|
|
7490
|
+
function isMissingFileError(error) {
|
|
7491
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
7492
|
+
}
|
|
7269
7493
|
var execFileAsync = promisify(execFile);
|
|
7270
7494
|
async function execFileNoThrow(file, args) {
|
|
7271
7495
|
try {
|
|
@@ -7312,6 +7536,41 @@ function sumUsage(a, b) {
|
|
|
7312
7536
|
}
|
|
7313
7537
|
|
|
7314
7538
|
// src/evals/evalRunner.ts
|
|
7539
|
+
function createToolOverrideMCP(mcp, variant) {
|
|
7540
|
+
return {
|
|
7541
|
+
...mcp,
|
|
7542
|
+
async listTools() {
|
|
7543
|
+
const tools = await mcp.listTools();
|
|
7544
|
+
const knownToolNames = new Set(tools.map((tool2) => tool2.name));
|
|
7545
|
+
const unknownToolNames = Object.keys(variant.tools).filter(
|
|
7546
|
+
(name15) => !knownToolNames.has(name15)
|
|
7547
|
+
);
|
|
7548
|
+
if (unknownToolNames.length > 0) {
|
|
7549
|
+
throw new Error(
|
|
7550
|
+
`[mcp-server-tester] toolOverrides variant "${variant.id}" references unknown tool(s): ` + unknownToolNames.join(", ")
|
|
7551
|
+
);
|
|
7552
|
+
}
|
|
7553
|
+
return tools.map((tool2) => {
|
|
7554
|
+
const override = variant.tools[tool2.name];
|
|
7555
|
+
if (!override) {
|
|
7556
|
+
return tool2;
|
|
7557
|
+
}
|
|
7558
|
+
return {
|
|
7559
|
+
...tool2,
|
|
7560
|
+
...override.description !== void 0 && {
|
|
7561
|
+
description: override.description
|
|
7562
|
+
},
|
|
7563
|
+
...override.inputSchema !== void 0 && {
|
|
7564
|
+
inputSchema: override.inputSchema
|
|
7565
|
+
}
|
|
7566
|
+
};
|
|
7567
|
+
});
|
|
7568
|
+
},
|
|
7569
|
+
async callTool(name15, args) {
|
|
7570
|
+
return mcp.callTool(name15, args);
|
|
7571
|
+
}
|
|
7572
|
+
};
|
|
7573
|
+
}
|
|
7315
7574
|
async function executeToolCall(evalCase, mcp) {
|
|
7316
7575
|
const mode = evalCase.mode || "direct";
|
|
7317
7576
|
try {
|
|
@@ -7496,9 +7755,12 @@ async function runExpectBlockValidations(expectBlock, response, config) {
|
|
|
7496
7755
|
}
|
|
7497
7756
|
return { expectations: results, toolPrecision, toolRecall };
|
|
7498
7757
|
}
|
|
7499
|
-
function buildRequest(evalCase) {
|
|
7758
|
+
function buildRequest(evalCase, toolOverrideVariantId) {
|
|
7500
7759
|
const request = {};
|
|
7501
7760
|
if (evalCase.description) request.description = evalCase.description;
|
|
7761
|
+
if (toolOverrideVariantId !== void 0) {
|
|
7762
|
+
request.toolOverrideVariantId = toolOverrideVariantId;
|
|
7763
|
+
}
|
|
7502
7764
|
if (evalCase.mode === "mcp_host") {
|
|
7503
7765
|
if (evalCase.scenario) request.scenario = evalCase.scenario;
|
|
7504
7766
|
if (evalCase.mcpHostConfig) {
|
|
@@ -7563,7 +7825,7 @@ async function runSingleIteration(evalCase, context, options) {
|
|
|
7563
7825
|
toolName: evalCase.scenario != null ? "mcp_host" : evalCase.toolName ?? "unknown",
|
|
7564
7826
|
source: "eval",
|
|
7565
7827
|
pass: didCasePass(error, expectationResults),
|
|
7566
|
-
request: buildRequest(evalCase),
|
|
7828
|
+
request: buildRequest(evalCase, options.toolOverrideVariantId),
|
|
7567
7829
|
response,
|
|
7568
7830
|
error,
|
|
7569
7831
|
expectations: expectationResults,
|
|
@@ -7642,7 +7904,8 @@ async function runEvalCase(evalCase, context, options = {}) {
|
|
|
7642
7904
|
authType: context.mcp.authType,
|
|
7643
7905
|
project: context.mcp.project,
|
|
7644
7906
|
durationMs: 0,
|
|
7645
|
-
tags: evalCase.tags
|
|
7907
|
+
tags: evalCase.tags,
|
|
7908
|
+
request: buildRequest(evalCase, options.toolOverrideVariantId)
|
|
7646
7909
|
};
|
|
7647
7910
|
const totalHostUsage = iterationResults.reduce(
|
|
7648
7911
|
(acc, r) => sumUsage(acc, r.hostUsage),
|
|
@@ -7701,11 +7964,15 @@ async function runEvalDataset(options, context) {
|
|
|
7701
7964
|
filterTags,
|
|
7702
7965
|
saveResultsTo,
|
|
7703
7966
|
omitResponsesFromBaseline = true,
|
|
7967
|
+
redactStoredResponses,
|
|
7968
|
+
resultStore,
|
|
7704
7969
|
baselineResultsFrom,
|
|
7970
|
+
toolOverrides,
|
|
7705
7971
|
mcpHostModel,
|
|
7706
7972
|
judgeModel
|
|
7707
7973
|
} = options;
|
|
7708
7974
|
const startTime = Date.now();
|
|
7975
|
+
const effectiveContext = toolOverrides ? { ...context, mcp: createToolOverrideMCP(context.mcp, toolOverrides) } : context;
|
|
7709
7976
|
const allSchemas = {
|
|
7710
7977
|
...dataset.schemas,
|
|
7711
7978
|
...schemas
|
|
@@ -7737,9 +8004,10 @@ async function runEvalDataset(options, context) {
|
|
|
7737
8004
|
}
|
|
7738
8005
|
}
|
|
7739
8006
|
const effectiveCase = withIterations.judgeReps === void 0 && defaultJudgeReps !== void 0 ? { ...withIterations, judgeReps: defaultJudgeReps } : withIterations;
|
|
7740
|
-
const result2 = await runEvalCase(effectiveCase,
|
|
8007
|
+
const result2 = await runEvalCase(effectiveCase, effectiveContext, {
|
|
7741
8008
|
datasetName: dataset.name,
|
|
7742
|
-
schemas: allSchemas
|
|
8009
|
+
schemas: allSchemas,
|
|
8010
|
+
toolOverrideVariantId: toolOverrides?.id
|
|
7743
8011
|
});
|
|
7744
8012
|
if (onCaseComplete) {
|
|
7745
8013
|
await onCaseComplete(result2);
|
|
@@ -7764,6 +8032,9 @@ async function runEvalDataset(options, context) {
|
|
|
7764
8032
|
gitHash,
|
|
7765
8033
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7766
8034
|
packageVersion: package_default.version,
|
|
8035
|
+
...toolOverrides !== void 0 && {
|
|
8036
|
+
toolOverrideVariantId: toolOverrides.id
|
|
8037
|
+
},
|
|
7767
8038
|
...mcpHostModel !== void 0 && { mcpHostModel },
|
|
7768
8039
|
...judgeModel !== void 0 && { judgeModel }
|
|
7769
8040
|
};
|
|
@@ -7782,7 +8053,7 @@ async function runEvalDataset(options, context) {
|
|
|
7782
8053
|
};
|
|
7783
8054
|
if (baselineResultsFrom) {
|
|
7784
8055
|
try {
|
|
7785
|
-
const baseline = await loadBaseline(baselineResultsFrom);
|
|
8056
|
+
const baseline = typeof baselineResultsFrom === "string" ? await loadBaseline(baselineResultsFrom) : await loadStoredBaseline(baselineResultsFrom, resultStore);
|
|
7786
8057
|
const baselinePassRate = baseline.total > 0 ? baseline.passed / baseline.total : 0;
|
|
7787
8058
|
const baselineMap = buildBaselinePassMap(baseline);
|
|
7788
8059
|
const currentCaseIds = result.caseResults.map((cr) => cr.id);
|
|
@@ -7810,7 +8081,7 @@ async function runEvalDataset(options, context) {
|
|
|
7810
8081
|
result.deltaPassRate = result.total > 0 ? result.passed / result.total - baselinePassRate : 0;
|
|
7811
8082
|
} catch (err) {
|
|
7812
8083
|
console.warn(
|
|
7813
|
-
`[mcp-server-tester] Could not load baseline from ${baselineResultsFrom}: ${err instanceof Error ? err.message : String(err)}`
|
|
8084
|
+
`[mcp-server-tester] Could not load baseline from ${formatBaselineRef(baselineResultsFrom)}: ${err instanceof Error ? err.message : String(err)}`
|
|
7814
8085
|
);
|
|
7815
8086
|
}
|
|
7816
8087
|
}
|
|
@@ -7825,9 +8096,26 @@ async function runEvalDataset(options, context) {
|
|
|
7825
8096
|
result.datasetToolF1 = avgPrec + avgRecall > 0 ? 2 * avgPrec * avgRecall / (avgPrec + avgRecall) : 0;
|
|
7826
8097
|
}
|
|
7827
8098
|
if (saveResultsTo) {
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
8099
|
+
if (typeof saveResultsTo === "string") {
|
|
8100
|
+
await saveBaseline(result, saveResultsTo, {
|
|
8101
|
+
omitResponses: omitResponsesFromBaseline
|
|
8102
|
+
});
|
|
8103
|
+
} else {
|
|
8104
|
+
await saveStoredEvalResult(result, saveResultsTo, {
|
|
8105
|
+
resultStore,
|
|
8106
|
+
omitResponses: redactStoredResponses ?? true,
|
|
8107
|
+
metadata: {
|
|
8108
|
+
datasetName: dataset.name,
|
|
8109
|
+
...toolOverrides?.id !== void 0 && {
|
|
8110
|
+
toolOverrideVariantId: toolOverrides.id
|
|
8111
|
+
},
|
|
8112
|
+
...mcpHostModel !== void 0 && { mcpHostModel },
|
|
8113
|
+
...judgeModel !== void 0 && { judgeModel },
|
|
8114
|
+
...gitHash !== void 0 && { gitHash },
|
|
8115
|
+
packageVersion: package_default.version
|
|
8116
|
+
}
|
|
8117
|
+
});
|
|
8118
|
+
}
|
|
7831
8119
|
}
|
|
7832
8120
|
if (context.testInfo) {
|
|
7833
8121
|
await context.testInfo.attach("mcp-test-results", {
|
|
@@ -7841,6 +8129,50 @@ async function runEvalDataset(options, context) {
|
|
|
7841
8129
|
}
|
|
7842
8130
|
return result;
|
|
7843
8131
|
}
|
|
8132
|
+
async function loadStoredBaseline(baselineResultsFrom, resultStore) {
|
|
8133
|
+
if (!resultStore) {
|
|
8134
|
+
throw new Error("resultStore is required for store-backed baselines");
|
|
8135
|
+
}
|
|
8136
|
+
const store = resolveEvalResultStore(resultStore);
|
|
8137
|
+
const artifact = baselineResultsFrom.ref === "latest" ? await store.loadLatestArtifact("eval-runner-result") : await store.loadArtifact(
|
|
8138
|
+
"eval-runner-result",
|
|
8139
|
+
baselineResultsFrom.ref.id
|
|
8140
|
+
);
|
|
8141
|
+
if (!artifact) {
|
|
8142
|
+
throw new Error("No latest eval run artifact found");
|
|
8143
|
+
}
|
|
8144
|
+
return artifact.data;
|
|
8145
|
+
}
|
|
8146
|
+
async function saveStoredEvalResult(result, saveResultsTo, options) {
|
|
8147
|
+
if (!options.resultStore) {
|
|
8148
|
+
throw new Error("resultStore is required for store-backed saves");
|
|
8149
|
+
}
|
|
8150
|
+
const store = resolveEvalResultStore(options.resultStore);
|
|
8151
|
+
const data = options.omitResponses ? omitResponsesFromResult(result) : result;
|
|
8152
|
+
const id = saveResultsTo.ref && saveResultsTo.ref !== "latest" ? saveResultsTo.ref.id : void 0;
|
|
8153
|
+
await store.saveArtifact(
|
|
8154
|
+
createStoredEvalArtifact({
|
|
8155
|
+
kind: "eval-runner-result",
|
|
8156
|
+
id,
|
|
8157
|
+
data,
|
|
8158
|
+
metadata: options.metadata
|
|
8159
|
+
})
|
|
8160
|
+
);
|
|
8161
|
+
}
|
|
8162
|
+
function omitResponsesFromResult(result) {
|
|
8163
|
+
return {
|
|
8164
|
+
...result,
|
|
8165
|
+
caseResults: result.caseResults.map(
|
|
8166
|
+
({ response: _response, ...rest }) => rest
|
|
8167
|
+
)
|
|
8168
|
+
};
|
|
8169
|
+
}
|
|
8170
|
+
function formatBaselineRef(baselineResultsFrom) {
|
|
8171
|
+
if (typeof baselineResultsFrom === "string") {
|
|
8172
|
+
return baselineResultsFrom;
|
|
8173
|
+
}
|
|
8174
|
+
return baselineResultsFrom.ref === "latest" ? "resultStore latest" : `resultStore ${baselineResultsFrom.ref.id}`;
|
|
8175
|
+
}
|
|
7844
8176
|
|
|
7845
8177
|
// src/evals/serverComparison.ts
|
|
7846
8178
|
async function runServerComparison(options, contextA, contextB) {
|
|
@@ -7882,7 +8214,7 @@ async function runServerComparison(options, contextA, contextB) {
|
|
|
7882
8214
|
}
|
|
7883
8215
|
const total = cases.length;
|
|
7884
8216
|
const decidedCases = aWins + bWins + ties;
|
|
7885
|
-
|
|
8217
|
+
const comparison = {
|
|
7886
8218
|
dataset: options.dataset.name,
|
|
7887
8219
|
total,
|
|
7888
8220
|
aWins,
|
|
@@ -7899,6 +8231,326 @@ async function runServerComparison(options, contextA, contextB) {
|
|
|
7899
8231
|
serverBResult: resultB,
|
|
7900
8232
|
durationMs: Date.now() - startTime
|
|
7901
8233
|
};
|
|
8234
|
+
if (options.comparisonStore) {
|
|
8235
|
+
await saveServerComparison({
|
|
8236
|
+
store: options.comparisonStore,
|
|
8237
|
+
comparison,
|
|
8238
|
+
id: options.comparisonId,
|
|
8239
|
+
metadata: {
|
|
8240
|
+
datasetName: options.dataset.name,
|
|
8241
|
+
...options.comparisonMetadata ?? {}
|
|
8242
|
+
},
|
|
8243
|
+
redactStoredResponses: options.redactStoredResponses
|
|
8244
|
+
});
|
|
8245
|
+
}
|
|
8246
|
+
return comparison;
|
|
8247
|
+
}
|
|
8248
|
+
async function saveServerComparison(options) {
|
|
8249
|
+
const store = resolveEvalResultStore(options.store);
|
|
8250
|
+
const data = options.redactStoredResponses ? redactResponses(options.comparison) : options.comparison;
|
|
8251
|
+
const artifact = createStoredEvalArtifact({
|
|
8252
|
+
kind: "server-comparison",
|
|
8253
|
+
id: options.id,
|
|
8254
|
+
data,
|
|
8255
|
+
metadata: {
|
|
8256
|
+
datasetName: options.comparison.dataset,
|
|
8257
|
+
...options.metadata ?? {}
|
|
8258
|
+
}
|
|
8259
|
+
});
|
|
8260
|
+
await store.saveArtifact(artifact);
|
|
8261
|
+
return artifact;
|
|
8262
|
+
}
|
|
8263
|
+
function redactResponses(value) {
|
|
8264
|
+
return JSON.parse(
|
|
8265
|
+
JSON.stringify(
|
|
8266
|
+
value,
|
|
8267
|
+
(key, currentValue) => key === "response" ? void 0 : currentValue
|
|
8268
|
+
)
|
|
8269
|
+
);
|
|
8270
|
+
}
|
|
8271
|
+
|
|
8272
|
+
// src/evals/evalRunComparison.ts
|
|
8273
|
+
function compareEvalRuns(options) {
|
|
8274
|
+
const { baseline, candidate, labels } = options;
|
|
8275
|
+
const candidateMap = new Map(
|
|
8276
|
+
candidate.caseResults.map((result) => [result.id, result])
|
|
8277
|
+
);
|
|
8278
|
+
const cases = [];
|
|
8279
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
8280
|
+
for (const baselineCase of baseline.caseResults) {
|
|
8281
|
+
seenIds.add(baselineCase.id);
|
|
8282
|
+
const candidateCase = candidateMap.get(baselineCase.id);
|
|
8283
|
+
if (!candidateCase) {
|
|
8284
|
+
cases.push({
|
|
8285
|
+
id: baselineCase.id,
|
|
8286
|
+
outcome: "MISSING_FROM_CANDIDATE",
|
|
8287
|
+
baseline: baselineCase
|
|
8288
|
+
});
|
|
8289
|
+
continue;
|
|
8290
|
+
}
|
|
8291
|
+
cases.push({
|
|
8292
|
+
id: baselineCase.id,
|
|
8293
|
+
outcome: compareCaseOutcome(baselineCase.pass, candidateCase.pass),
|
|
8294
|
+
baseline: baselineCase,
|
|
8295
|
+
candidate: candidateCase
|
|
8296
|
+
});
|
|
8297
|
+
}
|
|
8298
|
+
for (const candidateCase of candidate.caseResults) {
|
|
8299
|
+
if (seenIds.has(candidateCase.id)) {
|
|
8300
|
+
continue;
|
|
8301
|
+
}
|
|
8302
|
+
cases.push({
|
|
8303
|
+
id: candidateCase.id,
|
|
8304
|
+
outcome: "MISSING_FROM_BASELINE",
|
|
8305
|
+
candidate: candidateCase
|
|
8306
|
+
});
|
|
8307
|
+
}
|
|
8308
|
+
const baselinePassRate = passRate(baseline);
|
|
8309
|
+
const candidatePassRate = passRate(candidate);
|
|
8310
|
+
return {
|
|
8311
|
+
baselineLabel: labels?.baseline ?? "baseline",
|
|
8312
|
+
candidateLabel: labels?.candidate ?? candidate.metadata?.toolOverrideVariantId ?? "candidate",
|
|
8313
|
+
baselinePassRate,
|
|
8314
|
+
candidatePassRate,
|
|
8315
|
+
deltaPassRate: candidatePassRate - baselinePassRate,
|
|
8316
|
+
...metricDelta(
|
|
8317
|
+
"ToolPrecision",
|
|
8318
|
+
baseline.datasetToolPrecision,
|
|
8319
|
+
candidate.datasetToolPrecision
|
|
8320
|
+
),
|
|
8321
|
+
...metricDelta(
|
|
8322
|
+
"ToolRecall",
|
|
8323
|
+
baseline.datasetToolRecall,
|
|
8324
|
+
candidate.datasetToolRecall
|
|
8325
|
+
),
|
|
8326
|
+
...metricDelta("ToolF1", baseline.datasetToolF1, candidate.datasetToolF1),
|
|
8327
|
+
cases,
|
|
8328
|
+
improvedCases: cases.filter((c) => c.outcome === "IMPROVED"),
|
|
8329
|
+
regressedCases: cases.filter((c) => c.outcome === "REGRESSED"),
|
|
8330
|
+
unchangedPasses: cases.filter((c) => c.outcome === "UNCHANGED_PASS"),
|
|
8331
|
+
unchangedFailures: cases.filter((c) => c.outcome === "UNCHANGED_FAIL"),
|
|
8332
|
+
missingFromBaseline: cases.filter(
|
|
8333
|
+
(c) => c.outcome === "MISSING_FROM_BASELINE"
|
|
8334
|
+
),
|
|
8335
|
+
missingFromCandidate: cases.filter(
|
|
8336
|
+
(c) => c.outcome === "MISSING_FROM_CANDIDATE"
|
|
8337
|
+
)
|
|
8338
|
+
};
|
|
8339
|
+
}
|
|
8340
|
+
async function loadStoredEvalRunnerResult(storeLike, ref) {
|
|
8341
|
+
const store = resolveEvalResultStore(storeLike);
|
|
8342
|
+
const artifact = ref === "latest" ? await store.loadLatestArtifact("eval-runner-result") : await store.loadArtifact(
|
|
8343
|
+
"eval-runner-result",
|
|
8344
|
+
ref.id
|
|
8345
|
+
);
|
|
8346
|
+
if (!artifact) {
|
|
8347
|
+
throw new Error("No latest eval run artifact found");
|
|
8348
|
+
}
|
|
8349
|
+
return artifact;
|
|
8350
|
+
}
|
|
8351
|
+
async function saveEvalRunComparison(options) {
|
|
8352
|
+
const store = resolveEvalResultStore(options.store);
|
|
8353
|
+
const data = options.redactStoredResponses ? redactResponses2(options.comparison) : options.comparison;
|
|
8354
|
+
const artifact = createStoredEvalArtifact({
|
|
8355
|
+
kind: "eval-run-comparison",
|
|
8356
|
+
id: options.id,
|
|
8357
|
+
data,
|
|
8358
|
+
metadata: {
|
|
8359
|
+
labels: {
|
|
8360
|
+
baseline: options.comparison.baselineLabel,
|
|
8361
|
+
candidate: options.comparison.candidateLabel
|
|
8362
|
+
},
|
|
8363
|
+
...options.metadata ?? {}
|
|
8364
|
+
}
|
|
8365
|
+
});
|
|
8366
|
+
await store.saveArtifact(artifact);
|
|
8367
|
+
return artifact;
|
|
8368
|
+
}
|
|
8369
|
+
function compareCaseOutcome(baselinePass, candidatePass) {
|
|
8370
|
+
if (!baselinePass && candidatePass) return "IMPROVED";
|
|
8371
|
+
if (baselinePass && !candidatePass) return "REGRESSED";
|
|
8372
|
+
return baselinePass ? "UNCHANGED_PASS" : "UNCHANGED_FAIL";
|
|
8373
|
+
}
|
|
8374
|
+
function passRate(result) {
|
|
8375
|
+
return result.total > 0 ? result.passed / result.total : 0;
|
|
8376
|
+
}
|
|
8377
|
+
function metricDelta(name15, baselineValue, candidateValue) {
|
|
8378
|
+
const result = {};
|
|
8379
|
+
if (baselineValue !== void 0) {
|
|
8380
|
+
result[`baseline${name15}`] = baselineValue;
|
|
8381
|
+
}
|
|
8382
|
+
if (candidateValue !== void 0) {
|
|
8383
|
+
result[`candidate${name15}`] = candidateValue;
|
|
8384
|
+
}
|
|
8385
|
+
if (baselineValue !== void 0 && candidateValue !== void 0) {
|
|
8386
|
+
result[`delta${name15}`] = candidateValue - baselineValue;
|
|
8387
|
+
}
|
|
8388
|
+
return result;
|
|
8389
|
+
}
|
|
8390
|
+
function redactResponses2(value) {
|
|
8391
|
+
return JSON.parse(
|
|
8392
|
+
JSON.stringify(
|
|
8393
|
+
value,
|
|
8394
|
+
(key, currentValue) => key === "response" ? void 0 : currentValue
|
|
8395
|
+
)
|
|
8396
|
+
);
|
|
8397
|
+
}
|
|
8398
|
+
|
|
8399
|
+
// src/evals/variantExperiment.ts
|
|
8400
|
+
async function runVariantExperiment(options, context) {
|
|
8401
|
+
const metric = options.metric ?? "passRate";
|
|
8402
|
+
const maxRounds = options.maxRounds ?? 1;
|
|
8403
|
+
const minImprovement = options.minImprovement ?? 0;
|
|
8404
|
+
const allowRegressions = options.allowRegressions ?? false;
|
|
8405
|
+
const baseline = await runEvalDataset(
|
|
8406
|
+
buildRunOptions(options, void 0),
|
|
8407
|
+
context
|
|
8408
|
+
);
|
|
8409
|
+
const baselineValue = readMetric(baseline, metric);
|
|
8410
|
+
if (baselineValue === void 0) {
|
|
8411
|
+
throw new Error(
|
|
8412
|
+
`Metric '${metric}' is unavailable: the dataset produced no tool precision/recall data. Add mcp_host cases with toolsTriggered expectations, or use metric 'passRate'.`
|
|
8413
|
+
);
|
|
8414
|
+
}
|
|
8415
|
+
const rounds = [];
|
|
8416
|
+
let bestSoFar;
|
|
8417
|
+
let bestAttempted;
|
|
8418
|
+
let reason = "max-rounds";
|
|
8419
|
+
for (let round = 0; round < maxRounds; round++) {
|
|
8420
|
+
const variants = await gatherVariants(options, {
|
|
8421
|
+
round,
|
|
8422
|
+
baseline,
|
|
8423
|
+
metric,
|
|
8424
|
+
history: rounds,
|
|
8425
|
+
bestSoFar
|
|
8426
|
+
});
|
|
8427
|
+
if (variants.length === 0) {
|
|
8428
|
+
reason = round === 0 ? "no-variants" : "no-improvement";
|
|
8429
|
+
break;
|
|
8430
|
+
}
|
|
8431
|
+
const candidates = [];
|
|
8432
|
+
for (const variant of variants) {
|
|
8433
|
+
const candidate = await scoreVariant(
|
|
8434
|
+
options,
|
|
8435
|
+
context,
|
|
8436
|
+
baseline,
|
|
8437
|
+
baselineValue,
|
|
8438
|
+
metric,
|
|
8439
|
+
allowRegressions,
|
|
8440
|
+
variant
|
|
8441
|
+
);
|
|
8442
|
+
candidates.push(candidate);
|
|
8443
|
+
bestAttempted = pickBetter(bestAttempted, candidate, true);
|
|
8444
|
+
}
|
|
8445
|
+
const roundBest = candidates.reduce(
|
|
8446
|
+
(best, candidate) => pickBetter(best, candidate, false),
|
|
8447
|
+
void 0
|
|
8448
|
+
);
|
|
8449
|
+
rounds.push({ round, candidates, best: roundBest });
|
|
8450
|
+
if (roundBest) {
|
|
8451
|
+
const improvement = roundBest.metricValue - (bestSoFar?.metricValue ?? baselineValue);
|
|
8452
|
+
bestSoFar = pickBetter(bestSoFar, roundBest, false);
|
|
8453
|
+
if (improvement < minImprovement) {
|
|
8454
|
+
reason = "no-improvement";
|
|
8455
|
+
break;
|
|
8456
|
+
}
|
|
8457
|
+
}
|
|
8458
|
+
}
|
|
8459
|
+
const winner = bestSoFar;
|
|
8460
|
+
const proposalSource = winner ?? bestAttempted;
|
|
8461
|
+
const proposal = proposalSource ? buildProposal(metric, baselineValue, proposalSource, winner !== void 0) : void 0;
|
|
8462
|
+
return {
|
|
8463
|
+
metric,
|
|
8464
|
+
baseline,
|
|
8465
|
+
rounds,
|
|
8466
|
+
winner,
|
|
8467
|
+
proposal,
|
|
8468
|
+
converged: true,
|
|
8469
|
+
reason
|
|
8470
|
+
};
|
|
8471
|
+
}
|
|
8472
|
+
async function gatherVariants(options, context) {
|
|
8473
|
+
if (context.round === 0 && options.variants && options.variants.length > 0) {
|
|
8474
|
+
return options.variants;
|
|
8475
|
+
}
|
|
8476
|
+
if (options.proposeVariants) {
|
|
8477
|
+
return options.proposeVariants(context);
|
|
8478
|
+
}
|
|
8479
|
+
return [];
|
|
8480
|
+
}
|
|
8481
|
+
async function scoreVariant(options, context, baseline, baselineValue, metric, allowRegressions, variant) {
|
|
8482
|
+
const result = await runEvalDataset(
|
|
8483
|
+
buildRunOptions(options, variant),
|
|
8484
|
+
context
|
|
8485
|
+
);
|
|
8486
|
+
const comparison = compareEvalRuns({
|
|
8487
|
+
baseline,
|
|
8488
|
+
candidate: result,
|
|
8489
|
+
labels: { candidate: variant.id }
|
|
8490
|
+
});
|
|
8491
|
+
const metricValue = readMetric(result, metric) ?? baselineValue;
|
|
8492
|
+
const disqualified = !allowRegressions && comparison.regressedCases.length > 0;
|
|
8493
|
+
return {
|
|
8494
|
+
variant,
|
|
8495
|
+
result,
|
|
8496
|
+
comparison,
|
|
8497
|
+
metricValue,
|
|
8498
|
+
metricDelta: metricValue - baselineValue,
|
|
8499
|
+
disqualified
|
|
8500
|
+
};
|
|
8501
|
+
}
|
|
8502
|
+
function pickBetter(incumbent, challenger, includeDisqualified) {
|
|
8503
|
+
if (!includeDisqualified && challenger.disqualified) {
|
|
8504
|
+
return incumbent;
|
|
8505
|
+
}
|
|
8506
|
+
if (!incumbent) {
|
|
8507
|
+
return challenger;
|
|
8508
|
+
}
|
|
8509
|
+
return challenger.metricValue > incumbent.metricValue ? challenger : incumbent;
|
|
8510
|
+
}
|
|
8511
|
+
function buildProposal(metric, baselineValue, source, isWinner) {
|
|
8512
|
+
let recommendation;
|
|
8513
|
+
if (isWinner) {
|
|
8514
|
+
recommendation = source.metricDelta > 0 ? "apply" : "inconclusive";
|
|
8515
|
+
} else {
|
|
8516
|
+
recommendation = source.disqualified ? "reject" : "inconclusive";
|
|
8517
|
+
}
|
|
8518
|
+
return {
|
|
8519
|
+
variantId: source.variant.id,
|
|
8520
|
+
metric,
|
|
8521
|
+
baselineValue,
|
|
8522
|
+
candidateValue: source.metricValue,
|
|
8523
|
+
delta: source.metricDelta,
|
|
8524
|
+
toolChanges: source.variant.tools,
|
|
8525
|
+
improvedCaseIds: source.comparison.improvedCases.map((c) => c.id),
|
|
8526
|
+
regressedCaseIds: source.comparison.regressedCases.map((c) => c.id),
|
|
8527
|
+
recommendation
|
|
8528
|
+
};
|
|
8529
|
+
}
|
|
8530
|
+
function readMetric(result, metric) {
|
|
8531
|
+
switch (metric) {
|
|
8532
|
+
case "passRate":
|
|
8533
|
+
return result.total > 0 ? result.passed / result.total : 0;
|
|
8534
|
+
case "toolF1":
|
|
8535
|
+
return result.datasetToolF1;
|
|
8536
|
+
case "toolPrecision":
|
|
8537
|
+
return result.datasetToolPrecision;
|
|
8538
|
+
case "toolRecall":
|
|
8539
|
+
return result.datasetToolRecall;
|
|
8540
|
+
}
|
|
8541
|
+
}
|
|
8542
|
+
function buildRunOptions(options, toolOverrides) {
|
|
8543
|
+
return {
|
|
8544
|
+
dataset: options.dataset,
|
|
8545
|
+
toolOverrides,
|
|
8546
|
+
defaultLlmIterations: options.defaultLlmIterations,
|
|
8547
|
+
defaultJudgeReps: options.defaultJudgeReps,
|
|
8548
|
+
concurrency: options.concurrency,
|
|
8549
|
+
filterTags: options.filterTags,
|
|
8550
|
+
schemas: options.schemas,
|
|
8551
|
+
mcpHostModel: options.mcpHostModel,
|
|
8552
|
+
judgeModel: options.judgeModel
|
|
8553
|
+
};
|
|
7902
8554
|
}
|
|
7903
8555
|
|
|
7904
8556
|
// src/spec/conformanceChecks.ts
|
|
@@ -8072,6 +8724,6 @@ function formatCapabilities(capabilities) {
|
|
|
8072
8724
|
return parts.length > 0 ? parts.join(", ") : "none declared";
|
|
8073
8725
|
}
|
|
8074
8726
|
|
|
8075
|
-
export { BUILT_IN_RUBRICS, CLIOAuthClient, DiscoveryError, ENV_VAR_NAMES, EvalCaseSchema, EvalDatasetSchema, MCPConfigSchema, MCP_PROTOCOL_VERSION, PlaywrightOAuthClientProvider, SnapshotSanitizers, clearJudgeRegistry, closeMCPClient, createJudge, createMCPClientForConfig, createMCPFixture, createTokenAuthHeaders, discoverAuthorizationServer, discoverProtectedResource, expect, extractText, getMissingDependencyMessage, getRegisteredJudge, getResponseSizeBytes, hasValidTokens, injectTokens, isBuiltInRubric, isHttpConfig, isProviderAvailable, isStdioConfig, isTokenExpired, isTokenExpiringSoon, loadBaseline, loadEvalDataset, loadEvalDatasetFromObject, loadTokens, loadTokensFromEnv, test2 as mcpAuthTest, normalizeToolResponse, normalizeWhitespace, performClientCredentialsFlow, performOAuthSetup, performOAuthSetupIfNeeded, refreshAccessToken, registerJudge, resolveRubric, runConformanceChecks, runEvalCase, runEvalDataset, runServerComparison, saveBaseline, simulateMCPHost, test, validateAccessToken, validateError, validateEvalCase, validateEvalDataset, validateJudge, validateMCPConfig, validatePattern, validateResponse, validateSchema, validateSize, validateText, validateToolCallCount, validateToolCalls };
|
|
8727
|
+
export { BUILT_IN_RUBRICS, CLIOAuthClient, DiscoveryError, ENV_VAR_NAMES, EvalCaseSchema, EvalDatasetSchema, FileEvalResultStore, GCSEvalResultStore, MCPConfigSchema, MCP_PROTOCOL_VERSION, PlaywrightOAuthClientProvider, SnapshotSanitizers, clearJudgeRegistry, closeMCPClient, compareEvalRuns, createDefaultArtifactId, createEvalResultStore, createJudge, createMCPClientForConfig, createMCPFixture, createStoredEvalArtifact, createTokenAuthHeaders, defaultEnvironmentMetadata, discoverAuthorizationServer, discoverProtectedResource, expect, extractText, getMissingDependencyMessage, getRegisteredJudge, getResponseSizeBytes, hasValidTokens, injectTokens, isBuiltInRubric, isEvalResultStore, isHttpConfig, isProviderAvailable, isStdioConfig, isTokenExpired, isTokenExpiringSoon, loadBaseline, loadEvalDataset, loadEvalDatasetFromObject, loadStoredEvalRunnerResult, loadTokens, loadTokensFromEnv, test2 as mcpAuthTest, normalizeToolResponse, normalizeWhitespace, performClientCredentialsFlow, performOAuthSetup, performOAuthSetupIfNeeded, refreshAccessToken, registerJudge, resolveEvalResultStore, resolveRubric, runConformanceChecks, runEvalCase, runEvalDataset, runServerComparison, runVariantExperiment, saveBaseline, saveEvalRunComparison, saveServerComparison, simulateMCPHost, test, validateAccessToken, validateError, validateEvalCase, validateEvalDataset, validateJudge, validateMCPConfig, validatePattern, validateResponse, validateSchema, validateSize, validateText, validateToolCallCount, validateToolCalls };
|
|
8076
8728
|
//# sourceMappingURL=index.js.map
|
|
8077
8729
|
//# sourceMappingURL=index.js.map
|