@erdoai/cli 0.55.1 → 0.55.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +113 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -362,11 +362,17 @@ var ErdoClient = class {
|
|
|
362
362
|
`/v1/evals/suites/${encodeURIComponent(slug)}/cases/${encodeURIComponent(caseName)}`
|
|
363
363
|
);
|
|
364
364
|
}
|
|
365
|
-
runEvalSuite(slug, concurrency) {
|
|
365
|
+
runEvalSuite(slug, concurrency, commitSha, frontendCommitSha, backendCommitSha, source) {
|
|
366
366
|
return this.request(
|
|
367
367
|
"POST",
|
|
368
368
|
`/v1/evals/suites/${encodeURIComponent(slug)}/run`,
|
|
369
|
-
{
|
|
369
|
+
{
|
|
370
|
+
concurrency: concurrency ?? 0,
|
|
371
|
+
commit_sha: commitSha,
|
|
372
|
+
frontend_commit_sha: frontendCommitSha,
|
|
373
|
+
backend_commit_sha: backendCommitSha,
|
|
374
|
+
source
|
|
375
|
+
}
|
|
370
376
|
);
|
|
371
377
|
}
|
|
372
378
|
getEvalRun(runID) {
|
|
@@ -375,6 +381,14 @@ var ErdoClient = class {
|
|
|
375
381
|
`/v1/evals/runs/${encodeURIComponent(runID)}`
|
|
376
382
|
);
|
|
377
383
|
}
|
|
384
|
+
getEvalWidgetCanary() {
|
|
385
|
+
return this.request("GET", "/v1/evals/widget-canary");
|
|
386
|
+
}
|
|
387
|
+
configureEvalWidgetCanary(widget) {
|
|
388
|
+
return this.request("PUT", "/v1/evals/widget-canary", {
|
|
389
|
+
widget
|
|
390
|
+
});
|
|
391
|
+
}
|
|
378
392
|
// --- projects ---
|
|
379
393
|
listProjects(params) {
|
|
380
394
|
const q = new URLSearchParams();
|
|
@@ -1384,6 +1398,20 @@ function summariseResults(results, cases = []) {
|
|
|
1384
1398
|
}
|
|
1385
1399
|
}
|
|
1386
1400
|
}
|
|
1401
|
+
function printEvalRunAttribution(run) {
|
|
1402
|
+
console.log(`source: ${run.triggered_by}`);
|
|
1403
|
+
if (run.commit_sha) console.log(`commit: ${run.commit_sha}`);
|
|
1404
|
+
if (run.frontend_commit_sha) console.log(`frontend commit: ${run.frontend_commit_sha}`);
|
|
1405
|
+
if (run.backend_commit_sha) console.log(`backend commit: ${run.backend_commit_sha}`);
|
|
1406
|
+
}
|
|
1407
|
+
function compactEvalRunAttribution(run) {
|
|
1408
|
+
const revisions = [
|
|
1409
|
+
run.commit_sha ? `commit ${run.commit_sha.slice(0, 12)}` : void 0,
|
|
1410
|
+
run.frontend_commit_sha ? `frontend ${run.frontend_commit_sha.slice(0, 12)}` : void 0,
|
|
1411
|
+
run.backend_commit_sha ? `backend ${run.backend_commit_sha.slice(0, 12)}` : void 0
|
|
1412
|
+
].filter((part) => part !== void 0);
|
|
1413
|
+
return [run.triggered_by, ...revisions].join(" ");
|
|
1414
|
+
}
|
|
1387
1415
|
var program = new Command();
|
|
1388
1416
|
program.name("erdo").description("Erdo CLI").version(pkg.version).option("--org <idOrSlug>", "org to target for this command (overrides the active org)").option("--project <uuid>", "project context for this command (must belong to the active org)");
|
|
1389
1417
|
program.hook("preAction", (thisCommand) => {
|
|
@@ -2228,18 +2256,41 @@ function caseScoringPayload(opts) {
|
|
|
2228
2256
|
}
|
|
2229
2257
|
return out;
|
|
2230
2258
|
}
|
|
2231
|
-
|
|
2259
|
+
function parseEvalThreshold(value) {
|
|
2260
|
+
if (!/^(?:\d+(?:\.\d*)?|\.\d+)$/.test(value.trim())) {
|
|
2261
|
+
throw new Error("threshold must be greater than 0 and at most 5");
|
|
2262
|
+
}
|
|
2263
|
+
const threshold = Number(value);
|
|
2264
|
+
if (!Number.isFinite(threshold) || threshold <= 0 || threshold > 5) {
|
|
2265
|
+
throw new Error("threshold must be greater than 0 and at most 5");
|
|
2266
|
+
}
|
|
2267
|
+
return threshold;
|
|
2268
|
+
}
|
|
2269
|
+
function parseStrictBoolean(value) {
|
|
2270
|
+
switch (value.trim().toLowerCase()) {
|
|
2271
|
+
case "true":
|
|
2272
|
+
case "1":
|
|
2273
|
+
case "yes":
|
|
2274
|
+
return true;
|
|
2275
|
+
case "false":
|
|
2276
|
+
case "0":
|
|
2277
|
+
case "no":
|
|
2278
|
+
return false;
|
|
2279
|
+
default:
|
|
2280
|
+
throw new Error("value must be true or false");
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
evalCmd.command("update <slug>").description("Update a suite's settings (only the flags you pass change)").option("--name <name>", "new name").option("--description <text>", "new description").option("--agent <key>", "repoint the agent under test, e.g. erdo.data-question-answerer").option("--judge <model>", "new judge model").option("--threshold <n>", "new pass threshold >0 and <=5", parseEvalThreshold).option("--evaluate-artifact <bool>", "judge the rendered page (true/false)").option("--cron <bool>", "include in the daily cron (true/false)").action(
|
|
2232
2284
|
async (slug, opts) => {
|
|
2233
2285
|
try {
|
|
2234
|
-
const parseBool = (v) => v === void 0 ? void 0 : v === "true" || v === "1" || v === "yes";
|
|
2235
2286
|
const res = await new ErdoClient().updateEvalSuite(slug, {
|
|
2236
2287
|
name: opts.name,
|
|
2237
2288
|
description: opts.description,
|
|
2238
2289
|
agent_key: opts.agent,
|
|
2239
2290
|
judge_model: opts.judge,
|
|
2240
2291
|
pass_threshold: opts.threshold,
|
|
2241
|
-
evaluate_artifact:
|
|
2242
|
-
cron_enabled:
|
|
2292
|
+
evaluate_artifact: opts.evaluateArtifact === void 0 ? void 0 : parseStrictBoolean(opts.evaluateArtifact),
|
|
2293
|
+
cron_enabled: opts.cron === void 0 ? void 0 : parseStrictBoolean(opts.cron)
|
|
2243
2294
|
});
|
|
2244
2295
|
print(res);
|
|
2245
2296
|
} catch (e) {
|
|
@@ -2247,7 +2298,7 @@ evalCmd.command("update <slug>").description("Update a suite's settings (only th
|
|
|
2247
2298
|
}
|
|
2248
2299
|
}
|
|
2249
2300
|
);
|
|
2250
|
-
evalCmd.command("create <name>").description("Create a suite (in the active org)").requiredOption("--agent <key>", "agent under test, e.g. erdo.artifact-builder").option("--description <text>", "description").option("--judge <model>", "judge model").option("--threshold <n>", "pass threshold 0
|
|
2301
|
+
evalCmd.command("create <name>").description("Create a suite (in the active org)").requiredOption("--agent <key>", "agent under test, e.g. erdo.artifact-builder").option("--description <text>", "description").option("--judge <model>", "judge model").option("--threshold <n>", "pass threshold >0 and <=5", parseEvalThreshold).option("--evaluate-artifact", "judge the rendered page (visual suite)").option("--no-cron", "exclude from the daily cron (recommended for artifact suites)").requiredOption("--case <json>", "first case: {name,input,rubric,tags?}", (v, acc) => {
|
|
2251
2302
|
acc.push(v);
|
|
2252
2303
|
return acc;
|
|
2253
2304
|
}, []).action(
|
|
@@ -2271,7 +2322,7 @@ evalCmd.command("create <name>").description("Create a suite (in the active org)
|
|
|
2271
2322
|
}
|
|
2272
2323
|
}
|
|
2273
2324
|
);
|
|
2274
|
-
evalCmd.command("run <slug>").description("Run a suite; --watch polls until it completes").option("-w, --watch", "poll until the run finishes and print results").option("-c, --concurrency <n>", "parallel cases", (v) => parseInt(v, 10)).action(async (slug, opts) => {
|
|
2325
|
+
evalCmd.command("run <slug>").description("Run a suite; --watch polls until it completes").option("-w, --watch", "poll until the run finishes and print results").option("-c, --concurrency <n>", "parallel cases", (v) => parseInt(v, 10)).option("--commit-sha <sha>", "full 40-character Git commit SHA to associate with the run").option("--frontend-commit-sha <sha>", "frontend revision in the evaluated production snapshot").option("--backend-commit-sha <sha>", "backend revision in the evaluated production snapshot").option("--source <source>", "staff-only run source: post_deploy, nightly, or rollback").action(async (slug, opts) => {
|
|
2275
2326
|
try {
|
|
2276
2327
|
const api = new ErdoClient();
|
|
2277
2328
|
const { suite } = await api.getEvalSuite(slug);
|
|
@@ -2287,7 +2338,14 @@ evalCmd.command("run <slug>").description("Run a suite; --watch polls until it c
|
|
|
2287
2338
|
);
|
|
2288
2339
|
process.exit(1);
|
|
2289
2340
|
}
|
|
2290
|
-
const { run_id } = await api.runEvalSuite(
|
|
2341
|
+
const { run_id } = await api.runEvalSuite(
|
|
2342
|
+
slug,
|
|
2343
|
+
opts.concurrency,
|
|
2344
|
+
opts.commitSha,
|
|
2345
|
+
opts.frontendCommitSha,
|
|
2346
|
+
opts.backendCommitSha,
|
|
2347
|
+
opts.source
|
|
2348
|
+
);
|
|
2291
2349
|
console.log(`run_id: ${run_id}`);
|
|
2292
2350
|
if (!opts.watch) return;
|
|
2293
2351
|
for (; ; ) {
|
|
@@ -2301,6 +2359,7 @@ evalCmd.command("run <slug>").description("Run a suite; --watch polls until it c
|
|
|
2301
2359
|
`
|
|
2302
2360
|
${run.status}: ${run.passed_cases}/${run.total_cases} passed, avg ${run.avg_score.toFixed(2)}`
|
|
2303
2361
|
);
|
|
2362
|
+
printEvalRunAttribution(run);
|
|
2304
2363
|
summariseResults(results, cases);
|
|
2305
2364
|
if (run.status !== "completed" || run.passed_cases < run.total_cases) {
|
|
2306
2365
|
process.exitCode = 1;
|
|
@@ -2322,6 +2381,7 @@ evalCmd.command("results <runId>").description("Show a run's results").option("-
|
|
|
2322
2381
|
console.log(
|
|
2323
2382
|
`${run.status}: ${run.passed_cases}/${run.total_cases} passed, avg ${run.avg_score.toFixed(2)}`
|
|
2324
2383
|
);
|
|
2384
|
+
printEvalRunAttribution(run);
|
|
2325
2385
|
summariseResults(results, cases);
|
|
2326
2386
|
} catch (e) {
|
|
2327
2387
|
fail(e);
|
|
@@ -2332,9 +2392,41 @@ evalCmd.command("runs").description("List recent runs").option("-s, --suite <slu
|
|
|
2332
2392
|
const { runs } = await new ErdoClient().listEvalRuns(opts.suite, opts.limit);
|
|
2333
2393
|
for (const r of runs) {
|
|
2334
2394
|
console.log(
|
|
2335
|
-
`${r.id} ${r.status} ${r.passed_cases}/${r.total_cases} avg ${r.avg_score.toFixed(2)}`
|
|
2395
|
+
`${r.id} ${r.status} ${r.passed_cases}/${r.total_cases} avg ${r.avg_score.toFixed(2)} ${compactEvalRunAttribution(r)}`
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
} catch (e) {
|
|
2399
|
+
fail(e);
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2402
|
+
var evalCanaryCmd = evalCmd.command("canary").description("Show or configure the current organization's deployed-experience widget canary (staff only)");
|
|
2403
|
+
evalCanaryCmd.command("show").description("Show the widget designated as this organization's eval canary").action(async () => {
|
|
2404
|
+
try {
|
|
2405
|
+
print(await new ErdoClient().getEvalWidgetCanary());
|
|
2406
|
+
} catch (e) {
|
|
2407
|
+
fail(e);
|
|
2408
|
+
}
|
|
2409
|
+
});
|
|
2410
|
+
evalCanaryCmd.command("set <widget>").description("Designate a widget by unique name or public key in the pinned organization").action(async (widget) => {
|
|
2411
|
+
try {
|
|
2412
|
+
if (!process.env.ERDO_ORG) {
|
|
2413
|
+
throw new Error(
|
|
2414
|
+
"Refusing to change an eval canary without a pinned org. Re-run as: erdo --org <id|slug> eval canary set <widget-name-or-public-key>"
|
|
2336
2415
|
);
|
|
2337
2416
|
}
|
|
2417
|
+
print(await new ErdoClient().configureEvalWidgetCanary(widget));
|
|
2418
|
+
} catch (e) {
|
|
2419
|
+
fail(e);
|
|
2420
|
+
}
|
|
2421
|
+
});
|
|
2422
|
+
evalCanaryCmd.command("clear").description("Clear the pinned organization's eval widget canary").action(async () => {
|
|
2423
|
+
try {
|
|
2424
|
+
if (!process.env.ERDO_ORG) {
|
|
2425
|
+
throw new Error(
|
|
2426
|
+
"Refusing to clear an eval canary without a pinned org. Re-run as: erdo --org <id|slug> eval canary clear"
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
print(await new ErdoClient().configureEvalWidgetCanary(""));
|
|
2338
2430
|
} catch (e) {
|
|
2339
2431
|
fail(e);
|
|
2340
2432
|
}
|
|
@@ -2345,6 +2437,9 @@ caseCmd.command("add <slug>").description("Add a case to a suite").requiredOptio
|
|
|
2345
2437
|
"a turn to run BEFORE input, same thread/agent (repeatable; e.g. 'create a voice widget for X')",
|
|
2346
2438
|
collect,
|
|
2347
2439
|
[]
|
|
2440
|
+
).option(
|
|
2441
|
+
"--target-type <type>",
|
|
2442
|
+
"closed deployed target: live_page_fleet, widget_fleet_live, widget_text_canary, widget_voice_canary, or widget_video_canary"
|
|
2348
2443
|
).option("--rubric <json>", 'LLM rubric: JSON array of {"criterion","weight"}').option("--evaluator <json>", "evaluator JSON {type,weight,...} (repeatable)", collect, []).option("--tags <csv>", "comma-separated tags").action(
|
|
2349
2444
|
async (slug, opts) => {
|
|
2350
2445
|
try {
|
|
@@ -2352,6 +2447,7 @@ caseCmd.command("add <slug>").description("Add a case to a suite").requiredOptio
|
|
|
2352
2447
|
name: opts.name,
|
|
2353
2448
|
input: opts.input,
|
|
2354
2449
|
setup_messages: opts.setup,
|
|
2450
|
+
target_type: opts.targetType,
|
|
2355
2451
|
...caseScoringPayload(opts),
|
|
2356
2452
|
tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : []
|
|
2357
2453
|
});
|
|
@@ -2361,13 +2457,19 @@ caseCmd.command("add <slug>").description("Add a case to a suite").requiredOptio
|
|
|
2361
2457
|
}
|
|
2362
2458
|
}
|
|
2363
2459
|
);
|
|
2364
|
-
caseCmd.command("update <slug> <caseName>").description("Replace a case's brief, scoring, and tags").requiredOption("--input <brief>", "replacement brief/prompt (the evaluated turn)").option("--setup <msg>", "replacement pre-turn run before input (repeatable; omit
|
|
2460
|
+
caseCmd.command("update <slug> <caseName>").description("Replace a case's brief, scoring, and tags").requiredOption("--input <brief>", "replacement brief/prompt (the evaluated turn)").option("--setup <msg>", "replacement pre-turn run before input (repeatable; omit to preserve)", collect, []).option(
|
|
2461
|
+
"--target-type <type>",
|
|
2462
|
+
"replacement deployed target: live_page_fleet, widget_fleet_live, widget_text_canary, widget_voice_canary, or widget_video_canary (omit to preserve)"
|
|
2463
|
+
).option("--clear-setup", "remove all existing setup turns").option("--clear-target", "remove the deployed target and return to ordinary agent execution").option("--rubric <json>", "replacement LLM rubric JSON").option("--evaluator <json>", "evaluator JSON {type,weight,...} (repeatable)", collect, []).option("--tags <csv>", "comma-separated tags").action(
|
|
2365
2464
|
async (slug, caseName, opts) => {
|
|
2366
2465
|
try {
|
|
2367
2466
|
const res = await new ErdoClient().updateEvalCase(slug, caseName, {
|
|
2368
2467
|
name: caseName,
|
|
2369
2468
|
input: opts.input,
|
|
2370
2469
|
setup_messages: opts.setup,
|
|
2470
|
+
target_type: opts.targetType,
|
|
2471
|
+
clear_setup: !!opts.clearSetup,
|
|
2472
|
+
clear_target: !!opts.clearTarget,
|
|
2371
2473
|
...caseScoringPayload(opts),
|
|
2372
2474
|
tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : []
|
|
2373
2475
|
});
|