@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.cjs
CHANGED
|
@@ -3126,6 +3126,20 @@ var init_dist3 = __esm({
|
|
|
3126
3126
|
};
|
|
3127
3127
|
}
|
|
3128
3128
|
});
|
|
3129
|
+
|
|
3130
|
+
// src/assertions/validators/types.ts
|
|
3131
|
+
var SnapshotSanitizers = {
|
|
3132
|
+
/** Replaces Unix timestamps (seconds and milliseconds) with a stable placeholder */
|
|
3133
|
+
TIMESTAMP: "timestamp",
|
|
3134
|
+
/** Replaces UUID v1-v5 strings with a stable placeholder */
|
|
3135
|
+
UUID: "uuid",
|
|
3136
|
+
/** Replaces ISO 8601 date/datetime strings with a stable placeholder */
|
|
3137
|
+
ISO_DATE: "iso-date",
|
|
3138
|
+
/** Replaces MongoDB ObjectId strings with a stable placeholder */
|
|
3139
|
+
OBJECT_ID: "objectId",
|
|
3140
|
+
/** Replaces JWT tokens with a stable placeholder */
|
|
3141
|
+
JWT: "jwt"
|
|
3142
|
+
};
|
|
3129
3143
|
var MCPHostCapabilitiesSchema = zod.z.object({
|
|
3130
3144
|
sampling: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
3131
3145
|
roots: zod.z.object({
|
|
@@ -4411,7 +4425,7 @@ function escapeHtml(text) {
|
|
|
4411
4425
|
|
|
4412
4426
|
// package.json
|
|
4413
4427
|
var package_default = {
|
|
4414
|
-
version: "1.0
|
|
4428
|
+
version: "1.1.0"};
|
|
4415
4429
|
|
|
4416
4430
|
// src/mcp/clientFactory.ts
|
|
4417
4431
|
function getRetryAfterDelayMs(err) {
|
|
@@ -5935,20 +5949,6 @@ async function validateJudge(response, config) {
|
|
|
5935
5949
|
}
|
|
5936
5950
|
}
|
|
5937
5951
|
|
|
5938
|
-
// src/assertions/validators/types.ts
|
|
5939
|
-
var SnapshotSanitizers = {
|
|
5940
|
-
/** Replaces Unix timestamps (seconds and milliseconds) with a stable placeholder */
|
|
5941
|
-
TIMESTAMP: "timestamp",
|
|
5942
|
-
/** Replaces UUID v1-v5 strings with a stable placeholder */
|
|
5943
|
-
UUID: "uuid",
|
|
5944
|
-
/** Replaces ISO 8601 date/datetime strings with a stable placeholder */
|
|
5945
|
-
ISO_DATE: "iso-date",
|
|
5946
|
-
/** Replaces MongoDB ObjectId strings with a stable placeholder */
|
|
5947
|
-
OBJECT_ID: "objectId",
|
|
5948
|
-
/** Replaces JWT tokens with a stable placeholder */
|
|
5949
|
-
JWT: "jwt"
|
|
5950
|
-
};
|
|
5951
|
-
|
|
5952
5952
|
// src/mcp/fixtures/mcpFixture.ts
|
|
5953
5953
|
var DEFAULT_CALL_TIMEOUT_MS = 3e4;
|
|
5954
5954
|
function withCallTimeout(promise, ms, opName) {
|
|
@@ -6994,6 +6994,7 @@ function parseStreamJson(stdout) {
|
|
|
6994
6994
|
const lines = stdout.split("\n").filter((line) => line.trim().length > 0);
|
|
6995
6995
|
const toolCalls = [];
|
|
6996
6996
|
const textParts = [];
|
|
6997
|
+
let usage;
|
|
6997
6998
|
const conversationHistory = [];
|
|
6998
6999
|
for (const line of lines) {
|
|
6999
7000
|
let event;
|
|
@@ -7026,16 +7027,28 @@ function parseStreamJson(stdout) {
|
|
|
7026
7027
|
}
|
|
7027
7028
|
}
|
|
7028
7029
|
}
|
|
7029
|
-
if (event.type === "result"
|
|
7030
|
-
if (textParts.length === 0) {
|
|
7030
|
+
if (event.type === "result") {
|
|
7031
|
+
if (typeof event.result === "string" && textParts.length === 0) {
|
|
7031
7032
|
textParts.push(event.result);
|
|
7032
7033
|
}
|
|
7034
|
+
if (event.usage) {
|
|
7035
|
+
usage = {
|
|
7036
|
+
inputTokens: event.usage.input_tokens ?? 0,
|
|
7037
|
+
outputTokens: event.usage.output_tokens ?? 0,
|
|
7038
|
+
totalCostUsd: event.total_cost_usd ?? 0,
|
|
7039
|
+
durationMs: event.duration_ms ?? 0,
|
|
7040
|
+
durationApiMs: event.duration_api_ms,
|
|
7041
|
+
cacheReadInputTokens: event.usage.cache_read_input_tokens,
|
|
7042
|
+
cacheCreationInputTokens: event.usage.cache_creation_input_tokens
|
|
7043
|
+
};
|
|
7044
|
+
}
|
|
7033
7045
|
}
|
|
7034
7046
|
if (event.type === "result" && event.is_error === true) {
|
|
7035
7047
|
return {
|
|
7036
7048
|
success: false,
|
|
7037
7049
|
toolCalls,
|
|
7038
|
-
error: typeof event.result === "string" ? event.result : "CLI host reported an error"
|
|
7050
|
+
error: typeof event.result === "string" ? event.result : "CLI host reported an error",
|
|
7051
|
+
usage
|
|
7039
7052
|
};
|
|
7040
7053
|
}
|
|
7041
7054
|
}
|
|
@@ -7047,7 +7060,8 @@ function parseStreamJson(stdout) {
|
|
|
7047
7060
|
success: true,
|
|
7048
7061
|
toolCalls,
|
|
7049
7062
|
response: response || void 0,
|
|
7050
|
-
conversationHistory: conversationHistory.length > 0 ? conversationHistory : void 0
|
|
7063
|
+
conversationHistory: conversationHistory.length > 0 ? conversationHistory : void 0,
|
|
7064
|
+
usage
|
|
7051
7065
|
};
|
|
7052
7066
|
}
|
|
7053
7067
|
function createJsonParser(paths) {
|
|
@@ -7293,6 +7307,216 @@ function buildBaselinePassMap(baseline) {
|
|
|
7293
7307
|
}
|
|
7294
7308
|
return map;
|
|
7295
7309
|
}
|
|
7310
|
+
var KIND_DIRS = {
|
|
7311
|
+
"eval-runner-result": "eval-runs",
|
|
7312
|
+
"reporter-run": "reporter-runs",
|
|
7313
|
+
"eval-run-comparison": "comparisons/eval-runs",
|
|
7314
|
+
"server-comparison": "comparisons/servers"
|
|
7315
|
+
};
|
|
7316
|
+
function createEvalResultStore(config) {
|
|
7317
|
+
if (config.provider === "file") {
|
|
7318
|
+
return new FileEvalResultStore(config);
|
|
7319
|
+
}
|
|
7320
|
+
return new GCSEvalResultStore(config);
|
|
7321
|
+
}
|
|
7322
|
+
function resolveEvalResultStore(store) {
|
|
7323
|
+
return isEvalResultStore(store) ? store : createEvalResultStore(store);
|
|
7324
|
+
}
|
|
7325
|
+
function isEvalResultStore(value) {
|
|
7326
|
+
return typeof value === "object" && value !== null && "saveArtifact" in value && "loadArtifact" in value && "loadLatestArtifact" in value && "listArtifacts" in value;
|
|
7327
|
+
}
|
|
7328
|
+
function createStoredEvalArtifact(options) {
|
|
7329
|
+
const createdAt = options.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
7330
|
+
return {
|
|
7331
|
+
schemaVersion: 1,
|
|
7332
|
+
kind: options.kind,
|
|
7333
|
+
id: options.id ?? createDefaultArtifactId(createdAt),
|
|
7334
|
+
createdAt,
|
|
7335
|
+
metadata: {
|
|
7336
|
+
...defaultEnvironmentMetadata(),
|
|
7337
|
+
...options.metadata ?? {}
|
|
7338
|
+
},
|
|
7339
|
+
data: options.data
|
|
7340
|
+
};
|
|
7341
|
+
}
|
|
7342
|
+
function createDefaultArtifactId(timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
7343
|
+
const safeTimestamp = timestamp.replace(/[:.]/g, "-");
|
|
7344
|
+
const runNumber = process.env.GITHUB_RUN_NUMBER;
|
|
7345
|
+
const sha = process.env.GITHUB_SHA?.slice(0, 12);
|
|
7346
|
+
const suffix = runNumber ?? sha;
|
|
7347
|
+
return suffix ? `${safeTimestamp}-${suffix}` : safeTimestamp;
|
|
7348
|
+
}
|
|
7349
|
+
function defaultEnvironmentMetadata() {
|
|
7350
|
+
return {
|
|
7351
|
+
...process.env.GITHUB_SHA !== void 0 && {
|
|
7352
|
+
gitHash: process.env.GITHUB_SHA
|
|
7353
|
+
},
|
|
7354
|
+
...process.env.GITHUB_REF_NAME !== void 0 && {
|
|
7355
|
+
branch: process.env.GITHUB_REF_NAME
|
|
7356
|
+
},
|
|
7357
|
+
...process.env.GITHUB_RUN_NUMBER !== void 0 && {
|
|
7358
|
+
runNumber: process.env.GITHUB_RUN_NUMBER
|
|
7359
|
+
},
|
|
7360
|
+
...process.env.GITHUB_EVENT_NAME !== void 0 && {
|
|
7361
|
+
trigger: process.env.GITHUB_EVENT_NAME
|
|
7362
|
+
}
|
|
7363
|
+
};
|
|
7364
|
+
}
|
|
7365
|
+
var FileEvalResultStore = class {
|
|
7366
|
+
dir;
|
|
7367
|
+
constructor(config) {
|
|
7368
|
+
this.dir = config.dir;
|
|
7369
|
+
}
|
|
7370
|
+
async saveArtifact(artifact) {
|
|
7371
|
+
const artifactDir = path2.join(this.dir, KIND_DIRS[artifact.kind]);
|
|
7372
|
+
await fs$1.mkdir(artifactDir, { recursive: true });
|
|
7373
|
+
const serialized = JSON.stringify(artifact, null, 2);
|
|
7374
|
+
await fs$1.writeFile(
|
|
7375
|
+
path2.join(artifactDir, `${artifact.id}.json`),
|
|
7376
|
+
serialized,
|
|
7377
|
+
"utf8"
|
|
7378
|
+
);
|
|
7379
|
+
await fs$1.writeFile(path2.join(artifactDir, "latest.json"), serialized, "utf8");
|
|
7380
|
+
}
|
|
7381
|
+
async loadArtifact(kind, id) {
|
|
7382
|
+
const raw = await fs$1.readFile(
|
|
7383
|
+
path2.join(this.dir, KIND_DIRS[kind], `${id}.json`),
|
|
7384
|
+
"utf8"
|
|
7385
|
+
);
|
|
7386
|
+
return JSON.parse(raw);
|
|
7387
|
+
}
|
|
7388
|
+
async loadLatestArtifact(kind) {
|
|
7389
|
+
try {
|
|
7390
|
+
const raw = await fs$1.readFile(
|
|
7391
|
+
path2.join(this.dir, KIND_DIRS[kind], "latest.json"),
|
|
7392
|
+
"utf8"
|
|
7393
|
+
);
|
|
7394
|
+
return JSON.parse(raw);
|
|
7395
|
+
} catch (error) {
|
|
7396
|
+
if (isMissingFileError(error)) {
|
|
7397
|
+
return null;
|
|
7398
|
+
}
|
|
7399
|
+
throw error;
|
|
7400
|
+
}
|
|
7401
|
+
}
|
|
7402
|
+
async listArtifacts(kind, options = {}) {
|
|
7403
|
+
let files;
|
|
7404
|
+
try {
|
|
7405
|
+
files = await fs$1.readdir(path2.join(this.dir, KIND_DIRS[kind]));
|
|
7406
|
+
} catch (error) {
|
|
7407
|
+
if (isMissingFileError(error)) {
|
|
7408
|
+
return [];
|
|
7409
|
+
}
|
|
7410
|
+
throw error;
|
|
7411
|
+
}
|
|
7412
|
+
const summaries = await Promise.all(
|
|
7413
|
+
files.filter((f) => f.endsWith(".json") && f !== "latest.json").map(async (file) => {
|
|
7414
|
+
const raw = await fs$1.readFile(
|
|
7415
|
+
path2.join(this.dir, KIND_DIRS[kind], file),
|
|
7416
|
+
"utf8"
|
|
7417
|
+
);
|
|
7418
|
+
return toSummary(JSON.parse(raw));
|
|
7419
|
+
})
|
|
7420
|
+
);
|
|
7421
|
+
return summaries.sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, options.limit);
|
|
7422
|
+
}
|
|
7423
|
+
};
|
|
7424
|
+
var GCSEvalResultStore = class {
|
|
7425
|
+
bucketName;
|
|
7426
|
+
prefix;
|
|
7427
|
+
storage;
|
|
7428
|
+
constructor(config) {
|
|
7429
|
+
this.bucketName = config.bucket;
|
|
7430
|
+
this.prefix = trimSlashes(config.prefix ?? "");
|
|
7431
|
+
}
|
|
7432
|
+
async saveArtifact(artifact) {
|
|
7433
|
+
const bucket = await this.getBucket();
|
|
7434
|
+
const serialized = JSON.stringify(artifact, null, 2);
|
|
7435
|
+
await bucket.file(
|
|
7436
|
+
this.objectPath(
|
|
7437
|
+
artifact.kind,
|
|
7438
|
+
`${encodeURIComponent(artifact.id)}.json`
|
|
7439
|
+
)
|
|
7440
|
+
).save(serialized, {
|
|
7441
|
+
contentType: "application/json",
|
|
7442
|
+
resumable: false,
|
|
7443
|
+
validation: false
|
|
7444
|
+
});
|
|
7445
|
+
await bucket.file(this.objectPath(artifact.kind, "latest.json")).save(serialized, {
|
|
7446
|
+
contentType: "application/json",
|
|
7447
|
+
resumable: false,
|
|
7448
|
+
validation: false
|
|
7449
|
+
});
|
|
7450
|
+
}
|
|
7451
|
+
async loadArtifact(kind, id) {
|
|
7452
|
+
const bucket = await this.getBucket();
|
|
7453
|
+
const file = bucket.file(
|
|
7454
|
+
this.objectPath(kind, `${encodeURIComponent(id)}.json`)
|
|
7455
|
+
);
|
|
7456
|
+
const [buffer] = await file.download();
|
|
7457
|
+
return JSON.parse(buffer.toString("utf8"));
|
|
7458
|
+
}
|
|
7459
|
+
async loadLatestArtifact(kind) {
|
|
7460
|
+
const bucket = await this.getBucket();
|
|
7461
|
+
const file = bucket.file(this.objectPath(kind, "latest.json"));
|
|
7462
|
+
const [exists] = await file.exists();
|
|
7463
|
+
if (!exists) return null;
|
|
7464
|
+
const [buffer] = await file.download();
|
|
7465
|
+
return JSON.parse(buffer.toString("utf8"));
|
|
7466
|
+
}
|
|
7467
|
+
async listArtifacts(kind, options = {}) {
|
|
7468
|
+
const bucket = await this.getBucket();
|
|
7469
|
+
const [files] = await bucket.getFiles({
|
|
7470
|
+
prefix: this.objectPath(kind, "")
|
|
7471
|
+
});
|
|
7472
|
+
const summaries = await Promise.all(
|
|
7473
|
+
files.filter(
|
|
7474
|
+
(f) => f.name.endsWith(".json") && !f.name.endsWith("/latest.json")
|
|
7475
|
+
).map(async (file) => {
|
|
7476
|
+
const [buffer] = await file.download();
|
|
7477
|
+
return toSummary(
|
|
7478
|
+
JSON.parse(buffer.toString("utf8"))
|
|
7479
|
+
);
|
|
7480
|
+
})
|
|
7481
|
+
);
|
|
7482
|
+
return summaries.sort((a, b) => b.createdAt.localeCompare(a.createdAt)).slice(0, options.limit);
|
|
7483
|
+
}
|
|
7484
|
+
async getBucket() {
|
|
7485
|
+
if (!this.storage) {
|
|
7486
|
+
const moduleName = "@google-cloud/storage";
|
|
7487
|
+
let Storage;
|
|
7488
|
+
try {
|
|
7489
|
+
const mod = await import(moduleName);
|
|
7490
|
+
Storage = mod.Storage;
|
|
7491
|
+
} catch (error) {
|
|
7492
|
+
throw new Error(
|
|
7493
|
+
`GCS result storage requires the optional \`@google-cloud/storage\` package. Install it and authenticate with Application Default Credentials via GOOGLE_APPLICATION_CREDENTIALS.
|
|
7494
|
+
Original error: ${error instanceof Error ? error.message : String(error)}`
|
|
7495
|
+
);
|
|
7496
|
+
}
|
|
7497
|
+
this.storage = new Storage();
|
|
7498
|
+
}
|
|
7499
|
+
return this.storage.bucket(this.bucketName);
|
|
7500
|
+
}
|
|
7501
|
+
objectPath(kind, filename) {
|
|
7502
|
+
const parts = [this.prefix, KIND_DIRS[kind], filename].filter(Boolean);
|
|
7503
|
+
return parts.join("/");
|
|
7504
|
+
}
|
|
7505
|
+
};
|
|
7506
|
+
function toSummary(artifact) {
|
|
7507
|
+
return {
|
|
7508
|
+
kind: artifact.kind,
|
|
7509
|
+
id: artifact.id,
|
|
7510
|
+
createdAt: artifact.createdAt,
|
|
7511
|
+
metadata: artifact.metadata
|
|
7512
|
+
};
|
|
7513
|
+
}
|
|
7514
|
+
function trimSlashes(value) {
|
|
7515
|
+
return value.replace(/^\/+|\/+$/g, "");
|
|
7516
|
+
}
|
|
7517
|
+
function isMissingFileError(error) {
|
|
7518
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
7519
|
+
}
|
|
7296
7520
|
var execFileAsync = util.promisify(child_process.execFile);
|
|
7297
7521
|
async function execFileNoThrow(file, args) {
|
|
7298
7522
|
try {
|
|
@@ -7339,6 +7563,41 @@ function sumUsage(a, b) {
|
|
|
7339
7563
|
}
|
|
7340
7564
|
|
|
7341
7565
|
// src/evals/evalRunner.ts
|
|
7566
|
+
function createToolOverrideMCP(mcp, variant) {
|
|
7567
|
+
return {
|
|
7568
|
+
...mcp,
|
|
7569
|
+
async listTools() {
|
|
7570
|
+
const tools = await mcp.listTools();
|
|
7571
|
+
const knownToolNames = new Set(tools.map((tool2) => tool2.name));
|
|
7572
|
+
const unknownToolNames = Object.keys(variant.tools).filter(
|
|
7573
|
+
(name15) => !knownToolNames.has(name15)
|
|
7574
|
+
);
|
|
7575
|
+
if (unknownToolNames.length > 0) {
|
|
7576
|
+
throw new Error(
|
|
7577
|
+
`[mcp-server-tester] toolOverrides variant "${variant.id}" references unknown tool(s): ` + unknownToolNames.join(", ")
|
|
7578
|
+
);
|
|
7579
|
+
}
|
|
7580
|
+
return tools.map((tool2) => {
|
|
7581
|
+
const override = variant.tools[tool2.name];
|
|
7582
|
+
if (!override) {
|
|
7583
|
+
return tool2;
|
|
7584
|
+
}
|
|
7585
|
+
return {
|
|
7586
|
+
...tool2,
|
|
7587
|
+
...override.description !== void 0 && {
|
|
7588
|
+
description: override.description
|
|
7589
|
+
},
|
|
7590
|
+
...override.inputSchema !== void 0 && {
|
|
7591
|
+
inputSchema: override.inputSchema
|
|
7592
|
+
}
|
|
7593
|
+
};
|
|
7594
|
+
});
|
|
7595
|
+
},
|
|
7596
|
+
async callTool(name15, args) {
|
|
7597
|
+
return mcp.callTool(name15, args);
|
|
7598
|
+
}
|
|
7599
|
+
};
|
|
7600
|
+
}
|
|
7342
7601
|
async function executeToolCall(evalCase, mcp) {
|
|
7343
7602
|
const mode = evalCase.mode || "direct";
|
|
7344
7603
|
try {
|
|
@@ -7523,9 +7782,12 @@ async function runExpectBlockValidations(expectBlock, response, config) {
|
|
|
7523
7782
|
}
|
|
7524
7783
|
return { expectations: results, toolPrecision, toolRecall };
|
|
7525
7784
|
}
|
|
7526
|
-
function buildRequest(evalCase) {
|
|
7785
|
+
function buildRequest(evalCase, toolOverrideVariantId) {
|
|
7527
7786
|
const request = {};
|
|
7528
7787
|
if (evalCase.description) request.description = evalCase.description;
|
|
7788
|
+
if (toolOverrideVariantId !== void 0) {
|
|
7789
|
+
request.toolOverrideVariantId = toolOverrideVariantId;
|
|
7790
|
+
}
|
|
7529
7791
|
if (evalCase.mode === "mcp_host") {
|
|
7530
7792
|
if (evalCase.scenario) request.scenario = evalCase.scenario;
|
|
7531
7793
|
if (evalCase.mcpHostConfig) {
|
|
@@ -7590,7 +7852,7 @@ async function runSingleIteration(evalCase, context, options) {
|
|
|
7590
7852
|
toolName: evalCase.scenario != null ? "mcp_host" : evalCase.toolName ?? "unknown",
|
|
7591
7853
|
source: "eval",
|
|
7592
7854
|
pass: didCasePass(error, expectationResults),
|
|
7593
|
-
request: buildRequest(evalCase),
|
|
7855
|
+
request: buildRequest(evalCase, options.toolOverrideVariantId),
|
|
7594
7856
|
response,
|
|
7595
7857
|
error,
|
|
7596
7858
|
expectations: expectationResults,
|
|
@@ -7669,7 +7931,8 @@ async function runEvalCase(evalCase, context, options = {}) {
|
|
|
7669
7931
|
authType: context.mcp.authType,
|
|
7670
7932
|
project: context.mcp.project,
|
|
7671
7933
|
durationMs: 0,
|
|
7672
|
-
tags: evalCase.tags
|
|
7934
|
+
tags: evalCase.tags,
|
|
7935
|
+
request: buildRequest(evalCase, options.toolOverrideVariantId)
|
|
7673
7936
|
};
|
|
7674
7937
|
const totalHostUsage = iterationResults.reduce(
|
|
7675
7938
|
(acc, r) => sumUsage(acc, r.hostUsage),
|
|
@@ -7728,11 +7991,15 @@ async function runEvalDataset(options, context) {
|
|
|
7728
7991
|
filterTags,
|
|
7729
7992
|
saveResultsTo,
|
|
7730
7993
|
omitResponsesFromBaseline = true,
|
|
7994
|
+
redactStoredResponses,
|
|
7995
|
+
resultStore,
|
|
7731
7996
|
baselineResultsFrom,
|
|
7997
|
+
toolOverrides,
|
|
7732
7998
|
mcpHostModel,
|
|
7733
7999
|
judgeModel
|
|
7734
8000
|
} = options;
|
|
7735
8001
|
const startTime = Date.now();
|
|
8002
|
+
const effectiveContext = toolOverrides ? { ...context, mcp: createToolOverrideMCP(context.mcp, toolOverrides) } : context;
|
|
7736
8003
|
const allSchemas = {
|
|
7737
8004
|
...dataset.schemas,
|
|
7738
8005
|
...schemas
|
|
@@ -7764,9 +8031,10 @@ async function runEvalDataset(options, context) {
|
|
|
7764
8031
|
}
|
|
7765
8032
|
}
|
|
7766
8033
|
const effectiveCase = withIterations.judgeReps === void 0 && defaultJudgeReps !== void 0 ? { ...withIterations, judgeReps: defaultJudgeReps } : withIterations;
|
|
7767
|
-
const result2 = await runEvalCase(effectiveCase,
|
|
8034
|
+
const result2 = await runEvalCase(effectiveCase, effectiveContext, {
|
|
7768
8035
|
datasetName: dataset.name,
|
|
7769
|
-
schemas: allSchemas
|
|
8036
|
+
schemas: allSchemas,
|
|
8037
|
+
toolOverrideVariantId: toolOverrides?.id
|
|
7770
8038
|
});
|
|
7771
8039
|
if (onCaseComplete) {
|
|
7772
8040
|
await onCaseComplete(result2);
|
|
@@ -7791,6 +8059,9 @@ async function runEvalDataset(options, context) {
|
|
|
7791
8059
|
gitHash,
|
|
7792
8060
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7793
8061
|
packageVersion: package_default.version,
|
|
8062
|
+
...toolOverrides !== void 0 && {
|
|
8063
|
+
toolOverrideVariantId: toolOverrides.id
|
|
8064
|
+
},
|
|
7794
8065
|
...mcpHostModel !== void 0 && { mcpHostModel },
|
|
7795
8066
|
...judgeModel !== void 0 && { judgeModel }
|
|
7796
8067
|
};
|
|
@@ -7809,7 +8080,7 @@ async function runEvalDataset(options, context) {
|
|
|
7809
8080
|
};
|
|
7810
8081
|
if (baselineResultsFrom) {
|
|
7811
8082
|
try {
|
|
7812
|
-
const baseline = await loadBaseline(baselineResultsFrom);
|
|
8083
|
+
const baseline = typeof baselineResultsFrom === "string" ? await loadBaseline(baselineResultsFrom) : await loadStoredBaseline(baselineResultsFrom, resultStore);
|
|
7813
8084
|
const baselinePassRate = baseline.total > 0 ? baseline.passed / baseline.total : 0;
|
|
7814
8085
|
const baselineMap = buildBaselinePassMap(baseline);
|
|
7815
8086
|
const currentCaseIds = result.caseResults.map((cr) => cr.id);
|
|
@@ -7837,7 +8108,7 @@ async function runEvalDataset(options, context) {
|
|
|
7837
8108
|
result.deltaPassRate = result.total > 0 ? result.passed / result.total - baselinePassRate : 0;
|
|
7838
8109
|
} catch (err) {
|
|
7839
8110
|
console.warn(
|
|
7840
|
-
`[mcp-server-tester] Could not load baseline from ${baselineResultsFrom}: ${err instanceof Error ? err.message : String(err)}`
|
|
8111
|
+
`[mcp-server-tester] Could not load baseline from ${formatBaselineRef(baselineResultsFrom)}: ${err instanceof Error ? err.message : String(err)}`
|
|
7841
8112
|
);
|
|
7842
8113
|
}
|
|
7843
8114
|
}
|
|
@@ -7852,9 +8123,26 @@ async function runEvalDataset(options, context) {
|
|
|
7852
8123
|
result.datasetToolF1 = avgPrec + avgRecall > 0 ? 2 * avgPrec * avgRecall / (avgPrec + avgRecall) : 0;
|
|
7853
8124
|
}
|
|
7854
8125
|
if (saveResultsTo) {
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
8126
|
+
if (typeof saveResultsTo === "string") {
|
|
8127
|
+
await saveBaseline(result, saveResultsTo, {
|
|
8128
|
+
omitResponses: omitResponsesFromBaseline
|
|
8129
|
+
});
|
|
8130
|
+
} else {
|
|
8131
|
+
await saveStoredEvalResult(result, saveResultsTo, {
|
|
8132
|
+
resultStore,
|
|
8133
|
+
omitResponses: redactStoredResponses ?? true,
|
|
8134
|
+
metadata: {
|
|
8135
|
+
datasetName: dataset.name,
|
|
8136
|
+
...toolOverrides?.id !== void 0 && {
|
|
8137
|
+
toolOverrideVariantId: toolOverrides.id
|
|
8138
|
+
},
|
|
8139
|
+
...mcpHostModel !== void 0 && { mcpHostModel },
|
|
8140
|
+
...judgeModel !== void 0 && { judgeModel },
|
|
8141
|
+
...gitHash !== void 0 && { gitHash },
|
|
8142
|
+
packageVersion: package_default.version
|
|
8143
|
+
}
|
|
8144
|
+
});
|
|
8145
|
+
}
|
|
7858
8146
|
}
|
|
7859
8147
|
if (context.testInfo) {
|
|
7860
8148
|
await context.testInfo.attach("mcp-test-results", {
|
|
@@ -7868,6 +8156,50 @@ async function runEvalDataset(options, context) {
|
|
|
7868
8156
|
}
|
|
7869
8157
|
return result;
|
|
7870
8158
|
}
|
|
8159
|
+
async function loadStoredBaseline(baselineResultsFrom, resultStore) {
|
|
8160
|
+
if (!resultStore) {
|
|
8161
|
+
throw new Error("resultStore is required for store-backed baselines");
|
|
8162
|
+
}
|
|
8163
|
+
const store = resolveEvalResultStore(resultStore);
|
|
8164
|
+
const artifact = baselineResultsFrom.ref === "latest" ? await store.loadLatestArtifact("eval-runner-result") : await store.loadArtifact(
|
|
8165
|
+
"eval-runner-result",
|
|
8166
|
+
baselineResultsFrom.ref.id
|
|
8167
|
+
);
|
|
8168
|
+
if (!artifact) {
|
|
8169
|
+
throw new Error("No latest eval run artifact found");
|
|
8170
|
+
}
|
|
8171
|
+
return artifact.data;
|
|
8172
|
+
}
|
|
8173
|
+
async function saveStoredEvalResult(result, saveResultsTo, options) {
|
|
8174
|
+
if (!options.resultStore) {
|
|
8175
|
+
throw new Error("resultStore is required for store-backed saves");
|
|
8176
|
+
}
|
|
8177
|
+
const store = resolveEvalResultStore(options.resultStore);
|
|
8178
|
+
const data = options.omitResponses ? omitResponsesFromResult(result) : result;
|
|
8179
|
+
const id = saveResultsTo.ref && saveResultsTo.ref !== "latest" ? saveResultsTo.ref.id : void 0;
|
|
8180
|
+
await store.saveArtifact(
|
|
8181
|
+
createStoredEvalArtifact({
|
|
8182
|
+
kind: "eval-runner-result",
|
|
8183
|
+
id,
|
|
8184
|
+
data,
|
|
8185
|
+
metadata: options.metadata
|
|
8186
|
+
})
|
|
8187
|
+
);
|
|
8188
|
+
}
|
|
8189
|
+
function omitResponsesFromResult(result) {
|
|
8190
|
+
return {
|
|
8191
|
+
...result,
|
|
8192
|
+
caseResults: result.caseResults.map(
|
|
8193
|
+
({ response: _response, ...rest }) => rest
|
|
8194
|
+
)
|
|
8195
|
+
};
|
|
8196
|
+
}
|
|
8197
|
+
function formatBaselineRef(baselineResultsFrom) {
|
|
8198
|
+
if (typeof baselineResultsFrom === "string") {
|
|
8199
|
+
return baselineResultsFrom;
|
|
8200
|
+
}
|
|
8201
|
+
return baselineResultsFrom.ref === "latest" ? "resultStore latest" : `resultStore ${baselineResultsFrom.ref.id}`;
|
|
8202
|
+
}
|
|
7871
8203
|
|
|
7872
8204
|
// src/evals/serverComparison.ts
|
|
7873
8205
|
async function runServerComparison(options, contextA, contextB) {
|
|
@@ -7909,7 +8241,7 @@ async function runServerComparison(options, contextA, contextB) {
|
|
|
7909
8241
|
}
|
|
7910
8242
|
const total = cases.length;
|
|
7911
8243
|
const decidedCases = aWins + bWins + ties;
|
|
7912
|
-
|
|
8244
|
+
const comparison = {
|
|
7913
8245
|
dataset: options.dataset.name,
|
|
7914
8246
|
total,
|
|
7915
8247
|
aWins,
|
|
@@ -7926,6 +8258,326 @@ async function runServerComparison(options, contextA, contextB) {
|
|
|
7926
8258
|
serverBResult: resultB,
|
|
7927
8259
|
durationMs: Date.now() - startTime
|
|
7928
8260
|
};
|
|
8261
|
+
if (options.comparisonStore) {
|
|
8262
|
+
await saveServerComparison({
|
|
8263
|
+
store: options.comparisonStore,
|
|
8264
|
+
comparison,
|
|
8265
|
+
id: options.comparisonId,
|
|
8266
|
+
metadata: {
|
|
8267
|
+
datasetName: options.dataset.name,
|
|
8268
|
+
...options.comparisonMetadata ?? {}
|
|
8269
|
+
},
|
|
8270
|
+
redactStoredResponses: options.redactStoredResponses
|
|
8271
|
+
});
|
|
8272
|
+
}
|
|
8273
|
+
return comparison;
|
|
8274
|
+
}
|
|
8275
|
+
async function saveServerComparison(options) {
|
|
8276
|
+
const store = resolveEvalResultStore(options.store);
|
|
8277
|
+
const data = options.redactStoredResponses ? redactResponses(options.comparison) : options.comparison;
|
|
8278
|
+
const artifact = createStoredEvalArtifact({
|
|
8279
|
+
kind: "server-comparison",
|
|
8280
|
+
id: options.id,
|
|
8281
|
+
data,
|
|
8282
|
+
metadata: {
|
|
8283
|
+
datasetName: options.comparison.dataset,
|
|
8284
|
+
...options.metadata ?? {}
|
|
8285
|
+
}
|
|
8286
|
+
});
|
|
8287
|
+
await store.saveArtifact(artifact);
|
|
8288
|
+
return artifact;
|
|
8289
|
+
}
|
|
8290
|
+
function redactResponses(value) {
|
|
8291
|
+
return JSON.parse(
|
|
8292
|
+
JSON.stringify(
|
|
8293
|
+
value,
|
|
8294
|
+
(key, currentValue) => key === "response" ? void 0 : currentValue
|
|
8295
|
+
)
|
|
8296
|
+
);
|
|
8297
|
+
}
|
|
8298
|
+
|
|
8299
|
+
// src/evals/evalRunComparison.ts
|
|
8300
|
+
function compareEvalRuns(options) {
|
|
8301
|
+
const { baseline, candidate, labels } = options;
|
|
8302
|
+
const candidateMap = new Map(
|
|
8303
|
+
candidate.caseResults.map((result) => [result.id, result])
|
|
8304
|
+
);
|
|
8305
|
+
const cases = [];
|
|
8306
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
8307
|
+
for (const baselineCase of baseline.caseResults) {
|
|
8308
|
+
seenIds.add(baselineCase.id);
|
|
8309
|
+
const candidateCase = candidateMap.get(baselineCase.id);
|
|
8310
|
+
if (!candidateCase) {
|
|
8311
|
+
cases.push({
|
|
8312
|
+
id: baselineCase.id,
|
|
8313
|
+
outcome: "MISSING_FROM_CANDIDATE",
|
|
8314
|
+
baseline: baselineCase
|
|
8315
|
+
});
|
|
8316
|
+
continue;
|
|
8317
|
+
}
|
|
8318
|
+
cases.push({
|
|
8319
|
+
id: baselineCase.id,
|
|
8320
|
+
outcome: compareCaseOutcome(baselineCase.pass, candidateCase.pass),
|
|
8321
|
+
baseline: baselineCase,
|
|
8322
|
+
candidate: candidateCase
|
|
8323
|
+
});
|
|
8324
|
+
}
|
|
8325
|
+
for (const candidateCase of candidate.caseResults) {
|
|
8326
|
+
if (seenIds.has(candidateCase.id)) {
|
|
8327
|
+
continue;
|
|
8328
|
+
}
|
|
8329
|
+
cases.push({
|
|
8330
|
+
id: candidateCase.id,
|
|
8331
|
+
outcome: "MISSING_FROM_BASELINE",
|
|
8332
|
+
candidate: candidateCase
|
|
8333
|
+
});
|
|
8334
|
+
}
|
|
8335
|
+
const baselinePassRate = passRate(baseline);
|
|
8336
|
+
const candidatePassRate = passRate(candidate);
|
|
8337
|
+
return {
|
|
8338
|
+
baselineLabel: labels?.baseline ?? "baseline",
|
|
8339
|
+
candidateLabel: labels?.candidate ?? candidate.metadata?.toolOverrideVariantId ?? "candidate",
|
|
8340
|
+
baselinePassRate,
|
|
8341
|
+
candidatePassRate,
|
|
8342
|
+
deltaPassRate: candidatePassRate - baselinePassRate,
|
|
8343
|
+
...metricDelta(
|
|
8344
|
+
"ToolPrecision",
|
|
8345
|
+
baseline.datasetToolPrecision,
|
|
8346
|
+
candidate.datasetToolPrecision
|
|
8347
|
+
),
|
|
8348
|
+
...metricDelta(
|
|
8349
|
+
"ToolRecall",
|
|
8350
|
+
baseline.datasetToolRecall,
|
|
8351
|
+
candidate.datasetToolRecall
|
|
8352
|
+
),
|
|
8353
|
+
...metricDelta("ToolF1", baseline.datasetToolF1, candidate.datasetToolF1),
|
|
8354
|
+
cases,
|
|
8355
|
+
improvedCases: cases.filter((c) => c.outcome === "IMPROVED"),
|
|
8356
|
+
regressedCases: cases.filter((c) => c.outcome === "REGRESSED"),
|
|
8357
|
+
unchangedPasses: cases.filter((c) => c.outcome === "UNCHANGED_PASS"),
|
|
8358
|
+
unchangedFailures: cases.filter((c) => c.outcome === "UNCHANGED_FAIL"),
|
|
8359
|
+
missingFromBaseline: cases.filter(
|
|
8360
|
+
(c) => c.outcome === "MISSING_FROM_BASELINE"
|
|
8361
|
+
),
|
|
8362
|
+
missingFromCandidate: cases.filter(
|
|
8363
|
+
(c) => c.outcome === "MISSING_FROM_CANDIDATE"
|
|
8364
|
+
)
|
|
8365
|
+
};
|
|
8366
|
+
}
|
|
8367
|
+
async function loadStoredEvalRunnerResult(storeLike, ref) {
|
|
8368
|
+
const store = resolveEvalResultStore(storeLike);
|
|
8369
|
+
const artifact = ref === "latest" ? await store.loadLatestArtifact("eval-runner-result") : await store.loadArtifact(
|
|
8370
|
+
"eval-runner-result",
|
|
8371
|
+
ref.id
|
|
8372
|
+
);
|
|
8373
|
+
if (!artifact) {
|
|
8374
|
+
throw new Error("No latest eval run artifact found");
|
|
8375
|
+
}
|
|
8376
|
+
return artifact;
|
|
8377
|
+
}
|
|
8378
|
+
async function saveEvalRunComparison(options) {
|
|
8379
|
+
const store = resolveEvalResultStore(options.store);
|
|
8380
|
+
const data = options.redactStoredResponses ? redactResponses2(options.comparison) : options.comparison;
|
|
8381
|
+
const artifact = createStoredEvalArtifact({
|
|
8382
|
+
kind: "eval-run-comparison",
|
|
8383
|
+
id: options.id,
|
|
8384
|
+
data,
|
|
8385
|
+
metadata: {
|
|
8386
|
+
labels: {
|
|
8387
|
+
baseline: options.comparison.baselineLabel,
|
|
8388
|
+
candidate: options.comparison.candidateLabel
|
|
8389
|
+
},
|
|
8390
|
+
...options.metadata ?? {}
|
|
8391
|
+
}
|
|
8392
|
+
});
|
|
8393
|
+
await store.saveArtifact(artifact);
|
|
8394
|
+
return artifact;
|
|
8395
|
+
}
|
|
8396
|
+
function compareCaseOutcome(baselinePass, candidatePass) {
|
|
8397
|
+
if (!baselinePass && candidatePass) return "IMPROVED";
|
|
8398
|
+
if (baselinePass && !candidatePass) return "REGRESSED";
|
|
8399
|
+
return baselinePass ? "UNCHANGED_PASS" : "UNCHANGED_FAIL";
|
|
8400
|
+
}
|
|
8401
|
+
function passRate(result) {
|
|
8402
|
+
return result.total > 0 ? result.passed / result.total : 0;
|
|
8403
|
+
}
|
|
8404
|
+
function metricDelta(name15, baselineValue, candidateValue) {
|
|
8405
|
+
const result = {};
|
|
8406
|
+
if (baselineValue !== void 0) {
|
|
8407
|
+
result[`baseline${name15}`] = baselineValue;
|
|
8408
|
+
}
|
|
8409
|
+
if (candidateValue !== void 0) {
|
|
8410
|
+
result[`candidate${name15}`] = candidateValue;
|
|
8411
|
+
}
|
|
8412
|
+
if (baselineValue !== void 0 && candidateValue !== void 0) {
|
|
8413
|
+
result[`delta${name15}`] = candidateValue - baselineValue;
|
|
8414
|
+
}
|
|
8415
|
+
return result;
|
|
8416
|
+
}
|
|
8417
|
+
function redactResponses2(value) {
|
|
8418
|
+
return JSON.parse(
|
|
8419
|
+
JSON.stringify(
|
|
8420
|
+
value,
|
|
8421
|
+
(key, currentValue) => key === "response" ? void 0 : currentValue
|
|
8422
|
+
)
|
|
8423
|
+
);
|
|
8424
|
+
}
|
|
8425
|
+
|
|
8426
|
+
// src/evals/variantExperiment.ts
|
|
8427
|
+
async function runVariantExperiment(options, context) {
|
|
8428
|
+
const metric = options.metric ?? "passRate";
|
|
8429
|
+
const maxRounds = options.maxRounds ?? 1;
|
|
8430
|
+
const minImprovement = options.minImprovement ?? 0;
|
|
8431
|
+
const allowRegressions = options.allowRegressions ?? false;
|
|
8432
|
+
const baseline = await runEvalDataset(
|
|
8433
|
+
buildRunOptions(options, void 0),
|
|
8434
|
+
context
|
|
8435
|
+
);
|
|
8436
|
+
const baselineValue = readMetric(baseline, metric);
|
|
8437
|
+
if (baselineValue === void 0) {
|
|
8438
|
+
throw new Error(
|
|
8439
|
+
`Metric '${metric}' is unavailable: the dataset produced no tool precision/recall data. Add mcp_host cases with toolsTriggered expectations, or use metric 'passRate'.`
|
|
8440
|
+
);
|
|
8441
|
+
}
|
|
8442
|
+
const rounds = [];
|
|
8443
|
+
let bestSoFar;
|
|
8444
|
+
let bestAttempted;
|
|
8445
|
+
let reason = "max-rounds";
|
|
8446
|
+
for (let round = 0; round < maxRounds; round++) {
|
|
8447
|
+
const variants = await gatherVariants(options, {
|
|
8448
|
+
round,
|
|
8449
|
+
baseline,
|
|
8450
|
+
metric,
|
|
8451
|
+
history: rounds,
|
|
8452
|
+
bestSoFar
|
|
8453
|
+
});
|
|
8454
|
+
if (variants.length === 0) {
|
|
8455
|
+
reason = round === 0 ? "no-variants" : "no-improvement";
|
|
8456
|
+
break;
|
|
8457
|
+
}
|
|
8458
|
+
const candidates = [];
|
|
8459
|
+
for (const variant of variants) {
|
|
8460
|
+
const candidate = await scoreVariant(
|
|
8461
|
+
options,
|
|
8462
|
+
context,
|
|
8463
|
+
baseline,
|
|
8464
|
+
baselineValue,
|
|
8465
|
+
metric,
|
|
8466
|
+
allowRegressions,
|
|
8467
|
+
variant
|
|
8468
|
+
);
|
|
8469
|
+
candidates.push(candidate);
|
|
8470
|
+
bestAttempted = pickBetter(bestAttempted, candidate, true);
|
|
8471
|
+
}
|
|
8472
|
+
const roundBest = candidates.reduce(
|
|
8473
|
+
(best, candidate) => pickBetter(best, candidate, false),
|
|
8474
|
+
void 0
|
|
8475
|
+
);
|
|
8476
|
+
rounds.push({ round, candidates, best: roundBest });
|
|
8477
|
+
if (roundBest) {
|
|
8478
|
+
const improvement = roundBest.metricValue - (bestSoFar?.metricValue ?? baselineValue);
|
|
8479
|
+
bestSoFar = pickBetter(bestSoFar, roundBest, false);
|
|
8480
|
+
if (improvement < minImprovement) {
|
|
8481
|
+
reason = "no-improvement";
|
|
8482
|
+
break;
|
|
8483
|
+
}
|
|
8484
|
+
}
|
|
8485
|
+
}
|
|
8486
|
+
const winner = bestSoFar;
|
|
8487
|
+
const proposalSource = winner ?? bestAttempted;
|
|
8488
|
+
const proposal = proposalSource ? buildProposal(metric, baselineValue, proposalSource, winner !== void 0) : void 0;
|
|
8489
|
+
return {
|
|
8490
|
+
metric,
|
|
8491
|
+
baseline,
|
|
8492
|
+
rounds,
|
|
8493
|
+
winner,
|
|
8494
|
+
proposal,
|
|
8495
|
+
converged: true,
|
|
8496
|
+
reason
|
|
8497
|
+
};
|
|
8498
|
+
}
|
|
8499
|
+
async function gatherVariants(options, context) {
|
|
8500
|
+
if (context.round === 0 && options.variants && options.variants.length > 0) {
|
|
8501
|
+
return options.variants;
|
|
8502
|
+
}
|
|
8503
|
+
if (options.proposeVariants) {
|
|
8504
|
+
return options.proposeVariants(context);
|
|
8505
|
+
}
|
|
8506
|
+
return [];
|
|
8507
|
+
}
|
|
8508
|
+
async function scoreVariant(options, context, baseline, baselineValue, metric, allowRegressions, variant) {
|
|
8509
|
+
const result = await runEvalDataset(
|
|
8510
|
+
buildRunOptions(options, variant),
|
|
8511
|
+
context
|
|
8512
|
+
);
|
|
8513
|
+
const comparison = compareEvalRuns({
|
|
8514
|
+
baseline,
|
|
8515
|
+
candidate: result,
|
|
8516
|
+
labels: { candidate: variant.id }
|
|
8517
|
+
});
|
|
8518
|
+
const metricValue = readMetric(result, metric) ?? baselineValue;
|
|
8519
|
+
const disqualified = !allowRegressions && comparison.regressedCases.length > 0;
|
|
8520
|
+
return {
|
|
8521
|
+
variant,
|
|
8522
|
+
result,
|
|
8523
|
+
comparison,
|
|
8524
|
+
metricValue,
|
|
8525
|
+
metricDelta: metricValue - baselineValue,
|
|
8526
|
+
disqualified
|
|
8527
|
+
};
|
|
8528
|
+
}
|
|
8529
|
+
function pickBetter(incumbent, challenger, includeDisqualified) {
|
|
8530
|
+
if (!includeDisqualified && challenger.disqualified) {
|
|
8531
|
+
return incumbent;
|
|
8532
|
+
}
|
|
8533
|
+
if (!incumbent) {
|
|
8534
|
+
return challenger;
|
|
8535
|
+
}
|
|
8536
|
+
return challenger.metricValue > incumbent.metricValue ? challenger : incumbent;
|
|
8537
|
+
}
|
|
8538
|
+
function buildProposal(metric, baselineValue, source, isWinner) {
|
|
8539
|
+
let recommendation;
|
|
8540
|
+
if (isWinner) {
|
|
8541
|
+
recommendation = source.metricDelta > 0 ? "apply" : "inconclusive";
|
|
8542
|
+
} else {
|
|
8543
|
+
recommendation = source.disqualified ? "reject" : "inconclusive";
|
|
8544
|
+
}
|
|
8545
|
+
return {
|
|
8546
|
+
variantId: source.variant.id,
|
|
8547
|
+
metric,
|
|
8548
|
+
baselineValue,
|
|
8549
|
+
candidateValue: source.metricValue,
|
|
8550
|
+
delta: source.metricDelta,
|
|
8551
|
+
toolChanges: source.variant.tools,
|
|
8552
|
+
improvedCaseIds: source.comparison.improvedCases.map((c) => c.id),
|
|
8553
|
+
regressedCaseIds: source.comparison.regressedCases.map((c) => c.id),
|
|
8554
|
+
recommendation
|
|
8555
|
+
};
|
|
8556
|
+
}
|
|
8557
|
+
function readMetric(result, metric) {
|
|
8558
|
+
switch (metric) {
|
|
8559
|
+
case "passRate":
|
|
8560
|
+
return result.total > 0 ? result.passed / result.total : 0;
|
|
8561
|
+
case "toolF1":
|
|
8562
|
+
return result.datasetToolF1;
|
|
8563
|
+
case "toolPrecision":
|
|
8564
|
+
return result.datasetToolPrecision;
|
|
8565
|
+
case "toolRecall":
|
|
8566
|
+
return result.datasetToolRecall;
|
|
8567
|
+
}
|
|
8568
|
+
}
|
|
8569
|
+
function buildRunOptions(options, toolOverrides) {
|
|
8570
|
+
return {
|
|
8571
|
+
dataset: options.dataset,
|
|
8572
|
+
toolOverrides,
|
|
8573
|
+
defaultLlmIterations: options.defaultLlmIterations,
|
|
8574
|
+
defaultJudgeReps: options.defaultJudgeReps,
|
|
8575
|
+
concurrency: options.concurrency,
|
|
8576
|
+
filterTags: options.filterTags,
|
|
8577
|
+
schemas: options.schemas,
|
|
8578
|
+
mcpHostModel: options.mcpHostModel,
|
|
8579
|
+
judgeModel: options.judgeModel
|
|
8580
|
+
};
|
|
7929
8581
|
}
|
|
7930
8582
|
|
|
7931
8583
|
// src/spec/conformanceChecks.ts
|
|
@@ -8105,15 +8757,22 @@ exports.DiscoveryError = DiscoveryError;
|
|
|
8105
8757
|
exports.ENV_VAR_NAMES = ENV_VAR_NAMES;
|
|
8106
8758
|
exports.EvalCaseSchema = EvalCaseSchema;
|
|
8107
8759
|
exports.EvalDatasetSchema = EvalDatasetSchema;
|
|
8760
|
+
exports.FileEvalResultStore = FileEvalResultStore;
|
|
8761
|
+
exports.GCSEvalResultStore = GCSEvalResultStore;
|
|
8108
8762
|
exports.MCPConfigSchema = MCPConfigSchema;
|
|
8109
8763
|
exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
|
|
8110
8764
|
exports.SnapshotSanitizers = SnapshotSanitizers;
|
|
8111
8765
|
exports.clearJudgeRegistry = clearJudgeRegistry;
|
|
8112
8766
|
exports.closeMCPClient = closeMCPClient;
|
|
8767
|
+
exports.compareEvalRuns = compareEvalRuns;
|
|
8768
|
+
exports.createDefaultArtifactId = createDefaultArtifactId;
|
|
8769
|
+
exports.createEvalResultStore = createEvalResultStore;
|
|
8113
8770
|
exports.createJudge = createJudge;
|
|
8114
8771
|
exports.createMCPClientForConfig = createMCPClientForConfig;
|
|
8115
8772
|
exports.createMCPFixture = createMCPFixture;
|
|
8773
|
+
exports.createStoredEvalArtifact = createStoredEvalArtifact;
|
|
8116
8774
|
exports.createTokenAuthHeaders = createTokenAuthHeaders;
|
|
8775
|
+
exports.defaultEnvironmentMetadata = defaultEnvironmentMetadata;
|
|
8117
8776
|
exports.discoverAuthorizationServer = discoverAuthorizationServer;
|
|
8118
8777
|
exports.discoverProtectedResource = discoverProtectedResource;
|
|
8119
8778
|
exports.expect = expect;
|
|
@@ -8124,6 +8783,7 @@ exports.getResponseSizeBytes = getResponseSizeBytes;
|
|
|
8124
8783
|
exports.hasValidTokens = hasValidTokens;
|
|
8125
8784
|
exports.injectTokens = injectTokens;
|
|
8126
8785
|
exports.isBuiltInRubric = isBuiltInRubric;
|
|
8786
|
+
exports.isEvalResultStore = isEvalResultStore;
|
|
8127
8787
|
exports.isHttpConfig = isHttpConfig;
|
|
8128
8788
|
exports.isProviderAvailable = isProviderAvailable;
|
|
8129
8789
|
exports.isStdioConfig = isStdioConfig;
|
|
@@ -8132,6 +8792,7 @@ exports.isTokenExpiringSoon = isTokenExpiringSoon;
|
|
|
8132
8792
|
exports.loadBaseline = loadBaseline;
|
|
8133
8793
|
exports.loadEvalDataset = loadEvalDataset;
|
|
8134
8794
|
exports.loadEvalDatasetFromObject = loadEvalDatasetFromObject;
|
|
8795
|
+
exports.loadStoredEvalRunnerResult = loadStoredEvalRunnerResult;
|
|
8135
8796
|
exports.loadTokens = loadTokens;
|
|
8136
8797
|
exports.loadTokensFromEnv = loadTokensFromEnv;
|
|
8137
8798
|
exports.mcpAuthTest = test2;
|
|
@@ -8142,12 +8803,16 @@ exports.performOAuthSetup = performOAuthSetup;
|
|
|
8142
8803
|
exports.performOAuthSetupIfNeeded = performOAuthSetupIfNeeded;
|
|
8143
8804
|
exports.refreshAccessToken = refreshAccessToken;
|
|
8144
8805
|
exports.registerJudge = registerJudge;
|
|
8806
|
+
exports.resolveEvalResultStore = resolveEvalResultStore;
|
|
8145
8807
|
exports.resolveRubric = resolveRubric;
|
|
8146
8808
|
exports.runConformanceChecks = runConformanceChecks;
|
|
8147
8809
|
exports.runEvalCase = runEvalCase;
|
|
8148
8810
|
exports.runEvalDataset = runEvalDataset;
|
|
8149
8811
|
exports.runServerComparison = runServerComparison;
|
|
8812
|
+
exports.runVariantExperiment = runVariantExperiment;
|
|
8150
8813
|
exports.saveBaseline = saveBaseline;
|
|
8814
|
+
exports.saveEvalRunComparison = saveEvalRunComparison;
|
|
8815
|
+
exports.saveServerComparison = saveServerComparison;
|
|
8151
8816
|
exports.simulateMCPHost = simulateMCPHost;
|
|
8152
8817
|
exports.test = test;
|
|
8153
8818
|
exports.validateAccessToken = validateAccessToken;
|