@eigenpal/cli 0.4.9 → 0.4.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -211,6 +211,25 @@
|
|
|
211
211
|
|
|
212
212
|
`@` path syntax (`--code @file.js`, `--inputs items=@arr.json`) matches `curl -d @file` / `gh --body-file -`. Bare paths and `-` (stdin) work too.
|
|
213
213
|
|
|
214
|
+
- 4137eb8: custom-script evaluator: the YAML now carries the entire
|
|
215
|
+
`function scoreScript(expected, actual) { ... }` declaration in a single
|
|
216
|
+
`function` field. Previous formats (`code`, then `body`) wrapped the
|
|
217
|
+
text implicitly; the new shape stores the function declaration verbatim
|
|
218
|
+
so the YAML matches the dashboard editor 1-for-1.
|
|
219
|
+
|
|
220
|
+
`workflow validate` and `workflow evaluators validate` flag YAML where
|
|
221
|
+
the function fails to parse, the signature does not match
|
|
222
|
+
`function scoreScript(expected, actual)` exactly, or the body contains
|
|
223
|
+
neither `return` nor `throw` — same checks the dashboard runs at form
|
|
224
|
+
save. The function must return a number in [0, 1]; throws are caught
|
|
225
|
+
and scored as 0.
|
|
226
|
+
|
|
227
|
+
Existing evaluator YAML must be ported by hand: rename `body` (or
|
|
228
|
+
`code`) to `function`, and add the `function scoreScript(expected,
|
|
229
|
+
actual) { ... }` declaration around your statements. Parameter order is
|
|
230
|
+
load-bearing — swapping `expected` and `actual` would silently invert
|
|
231
|
+
every score.
|
|
232
|
+
|
|
214
233
|
- 3da0612: All list commands now support `--limit` / `--offset` pagination consistently.
|
|
215
234
|
- CLI: `workflow execution list`, `workflow version list`, and `workflow step-type list` previously had no `--offset` (or no pagination at all). All six list commands now accept `--limit`/`--offset` with defaults of 50/0.
|
|
216
235
|
- Server: `GET /api/workflows/:id/versions` now reads `?limit=` and `?offset=` query params (defaults 50/0; max limit 100). The `listVersions` repo method gained an optional `{ limit, offset }` argument.
|
|
@@ -283,6 +302,11 @@
|
|
|
283
302
|
|
|
284
303
|
Interactive profiles are unchanged: when you log in via `eigenpal auth login`, the profile stores both apiKey and baseUrl, and every subsequent command derives baseUrl from that profile. Set `EIGENPAL_PROFILE=<name>` to switch profiles per-shell without modifying state.
|
|
285
304
|
|
|
305
|
+
- 542fe0e: CLI: surface evaluator entry-level fields (`name`, `description`, `weight`) so agents and humans can discover the optional `description` field on every evaluator.
|
|
306
|
+
- `workflow evaluator-type get <type>` now emits `entrySchema` alongside `configSchema`. Inspect with `eigenpal workflow evaluator-type get llm-judge | jq '.entrySchema'`.
|
|
307
|
+
- The skill reference now renders a "Common entry fields" table per type and a "Writing descriptions for stakeholders" section explaining the convention: each evaluator's `description` should be a one-sentence, plain-language summary aimed at non-technical reviewers in the dashboard.
|
|
308
|
+
- `init` templates `pdf-extraction` and `text-classification` now ship a `description:` line on their evaluator entries.
|
|
309
|
+
|
|
286
310
|
- d41ee8b: CLI: fix `eigenpal status` error UX + harden `formatCliError` for real-world connection failures.
|
|
287
311
|
- `eigenpal status` against an unreachable base URL no longer mis-diagnoses the failure as an auth problem. Previously, `EIGENPAL_BASE_URL=http://localhost:9999 eigenpal status` printed "Authentication check failed; try `eigenpal auth login`" — the wrong rabbit hole. The status command now rethrows non-auth errors (connection refused, DNS NXDOMAIN, timeout, HTML responses, 5xx) so `formatCliError` upstream can surface the right hint: `Could not connect to the server at <url> ... Set EIGENPAL_BASE_URL or pass --base-url <url>.` Only real 401/403 responses are treated as auth failures.
|
|
288
312
|
- `formatCliError` now detects connection failures wrapped in any Error shape — bare `Error('ENOTFOUND ...')`, `Error` with `code: 'ECONNREFUSED'`, and `TypeError('fetch failed')` whose `.cause` carries the underlying error. Previously only `TypeError` whose message contained `fetch`/`URL` was recognized, so `getaddrinfo`-style errors fell through to the generic raw-message branch and lost the URL context.
|
package/dist/cli.js
CHANGED
|
@@ -23327,6 +23327,141 @@ var init_dataset_archive = __esm(() => {
|
|
|
23327
23327
|
};
|
|
23328
23328
|
});
|
|
23329
23329
|
|
|
23330
|
+
// ../types/src/eval/score-function.ts
|
|
23331
|
+
function stripLeadingTrivia(text) {
|
|
23332
|
+
let i = 0;
|
|
23333
|
+
while (i < text.length) {
|
|
23334
|
+
const ch = text[i];
|
|
23335
|
+
if (ch === " " || ch === "\t" || ch === `
|
|
23336
|
+
` || ch === "\r" || ch === "\v" || ch === "\f") {
|
|
23337
|
+
i++;
|
|
23338
|
+
continue;
|
|
23339
|
+
}
|
|
23340
|
+
if (ch === "/" && text[i + 1] === "/") {
|
|
23341
|
+
const nl = text.indexOf(`
|
|
23342
|
+
`, i + 2);
|
|
23343
|
+
if (nl === -1)
|
|
23344
|
+
return "";
|
|
23345
|
+
i = nl + 1;
|
|
23346
|
+
continue;
|
|
23347
|
+
}
|
|
23348
|
+
if (ch === "/" && text[i + 1] === "*") {
|
|
23349
|
+
const end = text.indexOf("*/", i + 2);
|
|
23350
|
+
if (end === -1)
|
|
23351
|
+
break;
|
|
23352
|
+
i = end + 2;
|
|
23353
|
+
continue;
|
|
23354
|
+
}
|
|
23355
|
+
break;
|
|
23356
|
+
}
|
|
23357
|
+
return text.slice(i);
|
|
23358
|
+
}
|
|
23359
|
+
function hasValidScoreSignature(fn) {
|
|
23360
|
+
return STRICT_HEADER_REGEX.test(stripLeadingTrivia(fn));
|
|
23361
|
+
}
|
|
23362
|
+
function isParseableScoreFunction(fn) {
|
|
23363
|
+
try {
|
|
23364
|
+
new Function(fn);
|
|
23365
|
+
return true;
|
|
23366
|
+
} catch {
|
|
23367
|
+
return false;
|
|
23368
|
+
}
|
|
23369
|
+
}
|
|
23370
|
+
function getScoreFunctionRuntimeIssue(fn) {
|
|
23371
|
+
if (/\basync\s+function\s+scoreScript\b/.test(fn)) {
|
|
23372
|
+
return "scoreScript must be synchronous (drop the `async` keyword — the sandbox calls it sync, so a Promise return scores 0)";
|
|
23373
|
+
}
|
|
23374
|
+
if (/\bimport\s*\(/.test(fn)) {
|
|
23375
|
+
return "dynamic `import(...)` is not supported in the score sandbox";
|
|
23376
|
+
}
|
|
23377
|
+
if (/\brequire\s*\(/.test(fn)) {
|
|
23378
|
+
return "`require(...)` is not available in the score sandbox (no Node module system)";
|
|
23379
|
+
}
|
|
23380
|
+
return null;
|
|
23381
|
+
}
|
|
23382
|
+
function extractScoreFunctionBody(fn) {
|
|
23383
|
+
const headerMatch = fn.match(ANY_HEADER_REGEX);
|
|
23384
|
+
if (!headerMatch || headerMatch.index === undefined) {
|
|
23385
|
+
return { body: fn, wrapperOk: false };
|
|
23386
|
+
}
|
|
23387
|
+
const start = headerMatch.index + headerMatch[0].length;
|
|
23388
|
+
let depth = 1;
|
|
23389
|
+
let i = start;
|
|
23390
|
+
let inLine = false;
|
|
23391
|
+
let inBlock = false;
|
|
23392
|
+
let strQuote = null;
|
|
23393
|
+
while (i < fn.length) {
|
|
23394
|
+
const ch = fn[i];
|
|
23395
|
+
const next = fn[i + 1];
|
|
23396
|
+
if (inLine) {
|
|
23397
|
+
if (ch === `
|
|
23398
|
+
`)
|
|
23399
|
+
inLine = false;
|
|
23400
|
+
} else if (inBlock) {
|
|
23401
|
+
if (ch === "*" && next === "/") {
|
|
23402
|
+
inBlock = false;
|
|
23403
|
+
i++;
|
|
23404
|
+
}
|
|
23405
|
+
} else if (strQuote) {
|
|
23406
|
+
if (ch === "\\")
|
|
23407
|
+
i++;
|
|
23408
|
+
else if (ch === strQuote)
|
|
23409
|
+
strQuote = null;
|
|
23410
|
+
} else if (ch === "/" && next === "/") {
|
|
23411
|
+
inLine = true;
|
|
23412
|
+
i++;
|
|
23413
|
+
} else if (ch === "/" && next === "*") {
|
|
23414
|
+
inBlock = true;
|
|
23415
|
+
i++;
|
|
23416
|
+
} else if (ch === '"' || ch === "'" || ch === "`") {
|
|
23417
|
+
strQuote = ch;
|
|
23418
|
+
} else if (ch === "{") {
|
|
23419
|
+
depth++;
|
|
23420
|
+
} else if (ch === "}") {
|
|
23421
|
+
depth--;
|
|
23422
|
+
if (depth === 0) {
|
|
23423
|
+
const inner = fn.slice(start, i).replace(/^\n+|\n+$/g, "");
|
|
23424
|
+
return { body: inner, wrapperOk: true };
|
|
23425
|
+
}
|
|
23426
|
+
}
|
|
23427
|
+
i++;
|
|
23428
|
+
}
|
|
23429
|
+
return { body: fn.slice(start), wrapperOk: false };
|
|
23430
|
+
}
|
|
23431
|
+
function defaultScoreFunction() {
|
|
23432
|
+
return `/**
|
|
23433
|
+
* Score the workflow output against the example's expected output.
|
|
23434
|
+
*
|
|
23435
|
+
* @param {Expected} expected the example's expected output
|
|
23436
|
+
* @param {Actual} actual the workflow's actual output
|
|
23437
|
+
* @returns {number} a score in [0, 1]; throws are caught and scored as 0
|
|
23438
|
+
*/
|
|
23439
|
+
function scoreScript(expected, actual) {
|
|
23440
|
+
// Return a number in [0, 1]; throws are caught and scored as 0.
|
|
23441
|
+
return JSON.stringify(actual) === JSON.stringify(expected) ? 1 : 0;
|
|
23442
|
+
}
|
|
23443
|
+
`;
|
|
23444
|
+
}
|
|
23445
|
+
function wrapBodyAsScoreFunction(body) {
|
|
23446
|
+
const trimmed = body.replace(/^\n+|\n+$/g, "");
|
|
23447
|
+
return `/**
|
|
23448
|
+
* Score the workflow output against the example's expected output.
|
|
23449
|
+
*
|
|
23450
|
+
* @param {Expected} expected the example's expected output
|
|
23451
|
+
* @param {Actual} actual the workflow's actual output
|
|
23452
|
+
* @returns {number} a score in [0, 1]; throws are caught and scored as 0
|
|
23453
|
+
*/
|
|
23454
|
+
function scoreScript(expected, actual) {
|
|
23455
|
+
${trimmed}
|
|
23456
|
+
}
|
|
23457
|
+
`;
|
|
23458
|
+
}
|
|
23459
|
+
var STRICT_HEADER_REGEX, ANY_HEADER_REGEX;
|
|
23460
|
+
var init_score_function = __esm(() => {
|
|
23461
|
+
STRICT_HEADER_REGEX = /^(?:async\s+)?function\s+scoreScript\s*\(\s*expected\s*,\s*actual\s*\)\s*\{/;
|
|
23462
|
+
ANY_HEADER_REGEX = /function\s+scoreScript\s*\([^)]*\)\s*\{/;
|
|
23463
|
+
});
|
|
23464
|
+
|
|
23330
23465
|
// ../types/src/eval/evaluator-config.ts
|
|
23331
23466
|
function combineEvalScores(results) {
|
|
23332
23467
|
let numerator = 0;
|
|
@@ -23341,9 +23476,10 @@ function combineEvalScores(results) {
|
|
|
23341
23476
|
return null;
|
|
23342
23477
|
return numerator / denominator;
|
|
23343
23478
|
}
|
|
23344
|
-
var EvaluatorTypeSchema, ScoreInUnitInterval, LLM_JUDGE_PROMPT_MIN_LENGTH = 10, ExactDiffConfigSchema, JudgeModeSchema, LabelsRecord, LlmJudgeConfigSchema, CustomScriptConfigSchema, EvaluatorBaseEntrySchema, EvaluatorConfigSchema, EvalConfigYamlSchema, EvaluatorResultSchema;
|
|
23479
|
+
var EvaluatorTypeSchema, ScoreInUnitInterval, LLM_JUDGE_PROMPT_MIN_LENGTH = 10, ExactDiffConfigSchema, JudgeModeSchema, LabelsRecord, LlmJudgeConfigSchema, CUSTOM_SCRIPT_CONTRACT, CUSTOM_SCRIPT_MAX_BYTES = 1e4, CUSTOM_SCRIPT_MAX_TIMEOUT_MS = 30000, CUSTOM_SCRIPT_MAX_MEMORY_MB = 50, CUSTOM_SCRIPT_FUNCTION_DESCRIPTION, CustomScriptConfigSchema, EvaluatorBaseEntrySchema, EvaluatorConfigSchema, EvalConfigYamlSchema, EvaluatorResultSchema;
|
|
23345
23480
|
var init_evaluator_config = __esm(() => {
|
|
23346
23481
|
init_zod();
|
|
23482
|
+
init_score_function();
|
|
23347
23483
|
EvaluatorTypeSchema = exports_external.enum(["exact-diff", "llm-judge", "custom-script"]);
|
|
23348
23484
|
ScoreInUnitInterval = exports_external.number().min(0, { message: "score must be >= 0" }).max(1, { message: "score must be <= 1" });
|
|
23349
23485
|
ExactDiffConfigSchema = exports_external.object({
|
|
@@ -23372,14 +23508,41 @@ var init_evaluator_config = __esm(() => {
|
|
|
23372
23508
|
}
|
|
23373
23509
|
}
|
|
23374
23510
|
});
|
|
23511
|
+
CUSTOM_SCRIPT_CONTRACT = {
|
|
23512
|
+
expected: "the example's expected output",
|
|
23513
|
+
actual: "the workflow's actual output",
|
|
23514
|
+
returns: "a number in [0, 1]",
|
|
23515
|
+
throws: "caught and scored as 0"
|
|
23516
|
+
};
|
|
23517
|
+
CUSTOM_SCRIPT_FUNCTION_DESCRIPTION = `Full JavaScript declaration of \`function scoreScript(expected, actual) { ... }\`. ` + `Receives \`expected\` (${CUSTOM_SCRIPT_CONTRACT.expected}) ` + `and \`actual\` (${CUSTOM_SCRIPT_CONTRACT.actual}), returns ${CUSTOM_SCRIPT_CONTRACT.returns}. ` + `Throws are ${CUSTOM_SCRIPT_CONTRACT.throws}.`;
|
|
23375
23518
|
CustomScriptConfigSchema = exports_external.object({
|
|
23376
|
-
|
|
23377
|
-
|
|
23378
|
-
|
|
23519
|
+
function: exports_external.string().min(1, { message: "required" }).max(CUSTOM_SCRIPT_MAX_BYTES, {
|
|
23520
|
+
message: `must be ≤ ${CUSTOM_SCRIPT_MAX_BYTES} bytes`
|
|
23521
|
+
}).describe(CUSTOM_SCRIPT_FUNCTION_DESCRIPTION).refine(isParseableScoreFunction, {
|
|
23522
|
+
message: "must parse as JavaScript"
|
|
23523
|
+
}).refine(hasValidScoreSignature, {
|
|
23524
|
+
message: "must declare `function scoreScript(expected, actual) { ... }` (parameter names + order matter)"
|
|
23525
|
+
}).refine((fn) => {
|
|
23526
|
+
const { body, wrapperOk } = extractScoreFunctionBody(fn);
|
|
23527
|
+
return wrapperOk && /\b(return|throw)\b/.test(body);
|
|
23528
|
+
}, {
|
|
23529
|
+
message: "function body must `return` a number in [0, 1] (or `throw` to score 0)"
|
|
23530
|
+
}).superRefine((fn, ctx) => {
|
|
23531
|
+
const issue2 = getScoreFunctionRuntimeIssue(fn);
|
|
23532
|
+
if (issue2)
|
|
23533
|
+
ctx.addIssue({ code: exports_external.ZodIssueCode.custom, message: issue2 });
|
|
23534
|
+
}),
|
|
23535
|
+
timeoutMs: exports_external.number().int().positive().max(CUSTOM_SCRIPT_MAX_TIMEOUT_MS, {
|
|
23536
|
+
message: `must be ≤ ${CUSTOM_SCRIPT_MAX_TIMEOUT_MS} ms`
|
|
23537
|
+
}).default(5000).describe("Maximum wall-clock time the script can run before it's killed and the run is marked failed."),
|
|
23538
|
+
memoryLimitMb: exports_external.number().int().positive().max(CUSTOM_SCRIPT_MAX_MEMORY_MB, {
|
|
23539
|
+
message: `must be ≤ ${CUSTOM_SCRIPT_MAX_MEMORY_MB} MB`
|
|
23540
|
+
}).default(10).describe("Maximum memory the sandbox may allocate. Increase if the script processes large arrays or strings."),
|
|
23379
23541
|
passThreshold: ScoreInUnitInterval.default(1).describe("Minimum script score this evaluator must reach for a run to pass.")
|
|
23380
23542
|
});
|
|
23381
23543
|
EvaluatorBaseEntrySchema = exports_external.object({
|
|
23382
23544
|
name: exports_external.string().min(1).max(64).regex(/^[a-z0-9-]+$/, "name must be lowercase kebab-case").describe("Identifier used in eval results and the weighted-mean formula. Use a short kebab-case slug like header-exact or covenants-recall."),
|
|
23545
|
+
description: exports_external.string().trim().max(500).optional().describe("One-sentence explanation, in business language, of what this evaluator measures and why a stakeholder should care. Shown in the dashboard to non-technical reviewers (legal, ops, finance) who never see the YAML or judge prompt. Skip implementation detail (model names, thresholds, dot-paths); those live in `config`."),
|
|
23383
23546
|
weight: exports_external.number().min(0).default(1).describe("Relative importance in the weighted mean. The overall score is Σ(weightᵢ × scoreᵢ) / Σweight. Set to 0 to score-only without affecting overall.")
|
|
23384
23547
|
});
|
|
23385
23548
|
EvaluatorConfigSchema = exports_external.discriminatedUnion("type", [
|
|
@@ -23569,6 +23732,7 @@ var init_eval = __esm(() => {
|
|
|
23569
23732
|
init_dataset_archive();
|
|
23570
23733
|
init_evaluator_config();
|
|
23571
23734
|
init_judge();
|
|
23735
|
+
init_score_function();
|
|
23572
23736
|
init_workflow_tool_metadata();
|
|
23573
23737
|
});
|
|
23574
23738
|
|
|
@@ -23637,6 +23801,7 @@ var init_paths = __esm(() => {
|
|
|
23637
23801
|
// ../types/src/index.ts
|
|
23638
23802
|
var exports_src = {};
|
|
23639
23803
|
__export(exports_src, {
|
|
23804
|
+
wrapBodyAsScoreFunction: () => wrapBodyAsScoreFunction,
|
|
23640
23805
|
workflowTriggerConfigSchema: () => workflowTriggerConfigSchema,
|
|
23641
23806
|
valuesMatch: () => valuesMatch,
|
|
23642
23807
|
validateStepConfig: () => validateStepConfig,
|
|
@@ -23667,10 +23832,12 @@ __export(exports_src, {
|
|
|
23667
23832
|
isValidJudgeModel: () => isValidJudgeModel,
|
|
23668
23833
|
isTerminalExecutionStatus: () => isTerminalExecutionStatus,
|
|
23669
23834
|
isStepCategory: () => isStepCategory,
|
|
23835
|
+
isParseableScoreFunction: () => isParseableScoreFunction,
|
|
23670
23836
|
isOutputSchemaUnknown: () => isOutputSchemaUnknown,
|
|
23671
23837
|
isNonSuccessTerminalExecutionStatus: () => isNonSuccessTerminalExecutionStatus,
|
|
23672
23838
|
isFilePathDescriptor: () => isFilePathDescriptor,
|
|
23673
23839
|
isFileOutput: () => isFileOutput,
|
|
23840
|
+
hasValidScoreSignature: () => hasValidScoreSignature,
|
|
23674
23841
|
hasTriggerMethod: () => hasTriggerMethod,
|
|
23675
23842
|
goldenFileSatisfiedInResult: () => goldenFileSatisfiedInResult,
|
|
23676
23843
|
getTriggerMethods: () => getTriggerMethods,
|
|
@@ -23678,6 +23845,7 @@ __export(exports_src, {
|
|
|
23678
23845
|
getStepSchema: () => getStepSchema,
|
|
23679
23846
|
getStepOutputSchema: () => getStepOutputSchema,
|
|
23680
23847
|
getStepConfigSchema: () => getStepConfigSchema,
|
|
23848
|
+
getScoreFunctionRuntimeIssue: () => getScoreFunctionRuntimeIssue,
|
|
23681
23849
|
getRequiredOutputKeys: () => getRequiredOutputKeys,
|
|
23682
23850
|
getProcessorSchemas: () => getProcessorSchemas,
|
|
23683
23851
|
getParserCategory: () => getParserCategory,
|
|
@@ -23693,11 +23861,13 @@ __export(exports_src, {
|
|
|
23693
23861
|
formatDiffValue: () => formatDiffValue,
|
|
23694
23862
|
fileFieldNamesFromJsonSchema: () => fileFieldNamesFromJsonSchema,
|
|
23695
23863
|
fieldStemFromGoldenName: () => fieldStemFromGoldenName,
|
|
23864
|
+
extractScoreFunctionBody: () => extractScoreFunctionBody,
|
|
23696
23865
|
extractFieldMetadata: () => extractFieldMetadata,
|
|
23697
23866
|
executionWallClockMs: () => executionWallClockMs,
|
|
23698
23867
|
evalInputKey: () => evalInputKey,
|
|
23699
23868
|
evalExpectedKey: () => evalExpectedKey,
|
|
23700
23869
|
errorHintFor: () => errorHintFor,
|
|
23870
|
+
defaultScoreFunction: () => defaultScoreFunction,
|
|
23701
23871
|
compareOutput: () => compareOutput,
|
|
23702
23872
|
compareExpectedValues: () => compareExpectedValues,
|
|
23703
23873
|
combineEvalScores: () => combineEvalScores,
|
|
@@ -23892,6 +24062,10 @@ __export(exports_src, {
|
|
|
23892
24062
|
ControlApprovalOutputSchema: () => ControlApprovalOutputSchema,
|
|
23893
24063
|
ControlApprovalConfigSchema: () => ControlApprovalConfigSchema,
|
|
23894
24064
|
ConnectorSchema: () => ConnectorSchema,
|
|
24065
|
+
CUSTOM_SCRIPT_MAX_TIMEOUT_MS: () => CUSTOM_SCRIPT_MAX_TIMEOUT_MS,
|
|
24066
|
+
CUSTOM_SCRIPT_MAX_MEMORY_MB: () => CUSTOM_SCRIPT_MAX_MEMORY_MB,
|
|
24067
|
+
CUSTOM_SCRIPT_MAX_BYTES: () => CUSTOM_SCRIPT_MAX_BYTES,
|
|
24068
|
+
CUSTOM_SCRIPT_CONTRACT: () => CUSTOM_SCRIPT_CONTRACT,
|
|
23895
24069
|
BoundingRegionSchema: () => BoundingRegionSchema,
|
|
23896
24070
|
BlockStepSchema: () => BlockStepSchema,
|
|
23897
24071
|
BaseEntitySchema: () => BaseEntitySchema,
|
|
@@ -143406,7 +143580,7 @@ import { fileURLToPath as fileURLToPath6 } from "url";
|
|
|
143406
143580
|
// package.json
|
|
143407
143581
|
var package_default = {
|
|
143408
143582
|
name: "@eigenpal/cli",
|
|
143409
|
-
version: "0.4.
|
|
143583
|
+
version: "0.4.10",
|
|
143410
143584
|
description: "Eigenpal CLI — author and run document workflows from your terminal. Agent-ready.",
|
|
143411
143585
|
keywords: [
|
|
143412
143586
|
"eigenpal",
|
|
@@ -146149,26 +146323,23 @@ function getExampleNames(dir, exampleNamesFilter) {
|
|
|
146149
146323
|
function readInput(exampleDir) {
|
|
146150
146324
|
const argsPath = join6(exampleDir, "input", "arguments.json");
|
|
146151
146325
|
const inputDir = join6(exampleDir, "input");
|
|
146152
|
-
const
|
|
146326
|
+
const scalars = JSON.parse(readFileSync5(argsPath, "utf-8"));
|
|
146327
|
+
const files = [];
|
|
146153
146328
|
for (const entry of readdirSync3(inputDir)) {
|
|
146154
146329
|
const full = join6(inputDir, entry);
|
|
146155
146330
|
if (!statSync2(full).isDirectory())
|
|
146156
146331
|
continue;
|
|
146157
|
-
const
|
|
146158
|
-
|
|
146159
|
-
|
|
146160
|
-
|
|
146161
|
-
const buf = readFileSync5(join6(full, filename));
|
|
146162
|
-
const mime = guessMimeType(filename);
|
|
146163
|
-
return {
|
|
146164
|
-
_inline: buf.toString("base64"),
|
|
146332
|
+
const filenames = readdirSync3(full).filter((f) => statSync2(join6(full, f)).isFile()).sort();
|
|
146333
|
+
for (const filename of filenames) {
|
|
146334
|
+
files.push({
|
|
146335
|
+
argument: entry,
|
|
146165
146336
|
filename,
|
|
146166
|
-
|
|
146167
|
-
|
|
146168
|
-
|
|
146169
|
-
|
|
146337
|
+
content: readFileSync5(join6(full, filename)),
|
|
146338
|
+
mimeType: guessMimeType(filename)
|
|
146339
|
+
});
|
|
146340
|
+
}
|
|
146170
146341
|
}
|
|
146171
|
-
return
|
|
146342
|
+
return { scalars, files };
|
|
146172
146343
|
}
|
|
146173
146344
|
function readOverrides(exampleDir) {
|
|
146174
146345
|
const metaPath = join6(exampleDir, "meta.json");
|
|
@@ -146182,8 +146353,10 @@ function readOverrides(exampleDir) {
|
|
|
146182
146353
|
return null;
|
|
146183
146354
|
}
|
|
146184
146355
|
function buildExamplePayload(exampleDir) {
|
|
146356
|
+
const { scalars, files } = readInput(exampleDir);
|
|
146185
146357
|
return {
|
|
146186
|
-
|
|
146358
|
+
scalars,
|
|
146359
|
+
files,
|
|
146187
146360
|
overrides: readOverrides(exampleDir)
|
|
146188
146361
|
};
|
|
146189
146362
|
}
|
|
@@ -146665,11 +146838,19 @@ async function runExec(client, dir, workflowIdOrSlug, exampleNames, options8 = {
|
|
|
146665
146838
|
progress.setRunning(index);
|
|
146666
146839
|
else
|
|
146667
146840
|
console.log(`${ui.dim("→")} ${name}`);
|
|
146668
|
-
const
|
|
146669
|
-
|
|
146670
|
-
|
|
146671
|
-
|
|
146672
|
-
|
|
146841
|
+
const form = new FormData;
|
|
146842
|
+
const sidecar = { trigger: "cli" };
|
|
146843
|
+
if (Object.keys(example.scalars).length > 0)
|
|
146844
|
+
sidecar.input = example.scalars;
|
|
146845
|
+
if (example.overrides)
|
|
146846
|
+
sidecar.overrides = example.overrides;
|
|
146847
|
+
form.append("_json", JSON.stringify(sidecar));
|
|
146848
|
+
for (const file2 of example.files) {
|
|
146849
|
+
form.append(file2.argument, new Blob([file2.content], {
|
|
146850
|
+
type: file2.mimeType ?? "application/octet-stream"
|
|
146851
|
+
}), file2.filename);
|
|
146852
|
+
}
|
|
146853
|
+
const res = await client.postFormData(`/api/v1/workflows/${workflowId}/run`, form);
|
|
146673
146854
|
if (typeof res?.executionId !== "string") {
|
|
146674
146855
|
throw new Error("Run API did not return executionId");
|
|
146675
146856
|
}
|
|
@@ -147464,7 +147645,7 @@ var EVALUATOR_REGISTRY = {
|
|
|
147464
147645
|
"custom-script": {
|
|
147465
147646
|
type: "custom-script",
|
|
147466
147647
|
name: "Custom Script",
|
|
147467
|
-
description: "
|
|
147648
|
+
description: "Score the workflow output with sandboxed JavaScript. Authors a `function scoreScript(expected, actual) { ... }` declaration that returns a number in [0, 1].",
|
|
147468
147649
|
configSchema: CustomScriptConfigSchema
|
|
147469
147650
|
}
|
|
147470
147651
|
};
|
|
@@ -147506,6 +147687,7 @@ function registerEvaluatorTypeCommands(parent) {
|
|
|
147506
147687
|
type: def.type,
|
|
147507
147688
|
name: def.name,
|
|
147508
147689
|
description: def.description,
|
|
147690
|
+
entrySchema: z12.toJSONSchema(EvaluatorBaseEntrySchema, { target: "draft-7" }),
|
|
147509
147691
|
configSchema: z12.toJSONSchema(def.configSchema, { target: "draft-7" })
|
|
147510
147692
|
};
|
|
147511
147693
|
const text = JSON.stringify(result, null, 2);
|
|
@@ -148057,7 +148239,7 @@ flag would otherwise shadow it. Validation failures come back as
|
|
|
148057
148239
|
error51(`Cannot update workflow ${ui.bold(workflowId)} — pass --bump <patch|minor|major> or --set-version <X.Y.Z>, or add a top-level \`version: X.Y.Z\` to ${filePath}.`);
|
|
148058
148240
|
process.exit(1);
|
|
148059
148241
|
}
|
|
148060
|
-
const yamlToSend = cliWantsRewrite ?
|
|
148242
|
+
const yamlToSend = cliWantsRewrite ? spliceWorkflowVersion2(yamlOnDisk, nextVersion) : yamlOnDisk;
|
|
148061
148243
|
const result = await client.post(`/api/v1/workflows/${workflowId}/versions`, {
|
|
148062
148244
|
yaml: yamlToSend,
|
|
148063
148245
|
version: nextVersion
|
|
@@ -148071,7 +148253,7 @@ flag would otherwise shadow it. Validation failures come back as
|
|
|
148071
148253
|
if (opts.bump) {
|
|
148072
148254
|
throw new Error("--bump requires --workflow-id (need a current version to bump from). For a new workflow, use --set-version <X.Y.Z> or omit both.");
|
|
148073
148255
|
}
|
|
148074
|
-
const yamlToSend = opts.setVersion && !extractWorkflowVersion(yamlOnDisk) ?
|
|
148256
|
+
const yamlToSend = opts.setVersion && !extractWorkflowVersion(yamlOnDisk) ? spliceWorkflowVersion2(yamlOnDisk, opts.setVersion) : yamlOnDisk;
|
|
148075
148257
|
const result = await client.post("/api/v1/workflows", { yaml: yamlToSend });
|
|
148076
148258
|
if (opts.json) {
|
|
148077
148259
|
printJson(result);
|
|
@@ -148098,7 +148280,7 @@ function bumpSemver(current, level) {
|
|
|
148098
148280
|
return `${major}.${minor + 1}.0`;
|
|
148099
148281
|
return `${major}.${minor}.${patch + 1}`;
|
|
148100
148282
|
}
|
|
148101
|
-
function
|
|
148283
|
+
function spliceWorkflowVersion2(yaml, next) {
|
|
148102
148284
|
if (/^version:\s*['"]?(\d+\.\d+\.\d+)['"]?\s*$/m.test(yaml)) {
|
|
148103
148285
|
return yaml.replace(/^version:.*$/m, `version: ${next}`);
|
|
148104
148286
|
}
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -174,6 +174,12 @@ eigenpal workflow evaluators validate ./evaluators.yaml
|
|
|
174
174
|
eigenpal workflow evaluators push <workflow-id> --file evaluators.yaml
|
|
175
175
|
```
|
|
176
176
|
|
|
177
|
+
Always set `description:` on every evaluator entry. It's a short, plain-language
|
|
178
|
+
sentence (≤500 chars; aim for one line) explaining what the evaluator measures
|
|
179
|
+
and why it matters, written for a non-technical reviewer in the dashboard.
|
|
180
|
+
Skip model names, thresholds, and dot-paths; those belong in `config`. See
|
|
181
|
+
[`reference/evaluators.md`](reference/evaluators.md#writing-descriptions-for-stakeholders).
|
|
182
|
+
|
|
177
183
|
### Build + validate + upload a dataset
|
|
178
184
|
|
|
179
185
|
```bash
|
|
@@ -11,16 +11,41 @@ this is the agent-facing summary.
|
|
|
11
11
|
```yaml
|
|
12
12
|
evaluators: # array of evaluator definitions, ≥1 entry
|
|
13
13
|
- name: ... # required, unique within this file, [a-z0-9][a-z0-9-_]*
|
|
14
|
+
description: ... # optional, ≤500 chars; one-sentence business explanation (see below)
|
|
14
15
|
type: ... # required, one of: exact-diff | llm-judge | custom-script
|
|
15
16
|
config: # required, shape depends on `type`
|
|
16
17
|
passThreshold: 0.95 # 0..1, optional (default 1.0)
|
|
17
18
|
...
|
|
18
19
|
|
|
19
|
-
# OPTIONAL
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
# OPTIONAL workflow-level pass threshold. A run passes when its weighted-mean
|
|
21
|
+
# score across evaluators clears this number. Defaults to 0.7.
|
|
22
|
+
passThreshold: 0.7
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Writing descriptions for stakeholders
|
|
26
|
+
|
|
27
|
+
Every evaluator entry takes an optional `description`. Always set it. The
|
|
28
|
+
dashboard surfaces this string to non-technical reviewers (legal, ops,
|
|
29
|
+
finance) who never see the YAML or the judge prompt. Write it for them:
|
|
30
|
+
|
|
31
|
+
- One short sentence, plain language.
|
|
32
|
+
- Name what the evaluator measures and why a business reader should care.
|
|
33
|
+
- Skip implementation detail (model names, thresholds, dot-paths). Those
|
|
34
|
+
belong in `config`.
|
|
35
|
+
- Keep it under ~120 characters when you can; the schema caps it at 500.
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
# Good. A non-technical reviewer immediately grasps the intent.
|
|
39
|
+
- name: covenants-recall
|
|
40
|
+
description: Checks every covenant from the contract appears in the extraction.
|
|
41
|
+
type: llm-judge
|
|
42
|
+
config: ...
|
|
43
|
+
|
|
44
|
+
# Bad. Restates the config; teaches a stakeholder nothing.
|
|
45
|
+
- name: covenants-recall
|
|
46
|
+
description: llm-judge with passThreshold 0.85 over the covenants array.
|
|
47
|
+
type: llm-judge
|
|
48
|
+
config: ...
|
|
24
49
|
```
|
|
25
50
|
|
|
26
51
|
## Evaluator types
|
|
@@ -47,23 +72,34 @@ judge: # optional shared model defaults for llm-judge evaluators
|
|
|
47
72
|
- name: covenants-recall
|
|
48
73
|
type: llm-judge
|
|
49
74
|
config:
|
|
50
|
-
model: gpt-4o #
|
|
51
|
-
|
|
75
|
+
model: gpt-4o # OPTIONAL — falls back to the workspace default LLM provider
|
|
76
|
+
mode: continuous # continuous | discrete (default: continuous)
|
|
52
77
|
passThreshold: 0.85
|
|
53
|
-
|
|
78
|
+
docLabel: extraction # what the judge calls the workflow output (default: "output")
|
|
79
|
+
promptExtension: |
|
|
54
80
|
Score how well the actual output captures every covenant in the
|
|
55
|
-
expected output
|
|
56
|
-
|
|
57
|
-
```
|
|
81
|
+
expected output. Penalise missing covenants; do not penalise
|
|
82
|
+
over-extraction. Be strict on covenants that change loan economics.
|
|
58
83
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
# discrete mode — the judge picks a label and the score table maps to a number
|
|
85
|
+
- name: tone-check
|
|
86
|
+
type: llm-judge
|
|
87
|
+
config:
|
|
88
|
+
mode: discrete
|
|
89
|
+
passThreshold: 0.7
|
|
90
|
+
labels:
|
|
91
|
+
excellent: 1.0
|
|
92
|
+
acceptable: 0.7
|
|
93
|
+
off-tone: 0.0
|
|
94
|
+
promptExtension: |
|
|
95
|
+
Pick the label that best matches the tone of the response.
|
|
96
|
+
```
|
|
64
97
|
|
|
65
|
-
|
|
66
|
-
|
|
98
|
+
`promptExtension` is the only required field — it's appended to the harness
|
|
99
|
+
prompt and tells the judge what "good" looks like for this workflow. The
|
|
100
|
+
harness handles JSON parsing, retries, and feeds the judge `actual`,
|
|
101
|
+
`expected`, and example metadata. In `discrete` mode the judge MUST return
|
|
102
|
+
one of the keys in `labels`; the harness then looks up the score.
|
|
67
103
|
|
|
68
104
|
### `custom-script` — JavaScript in the sandbox
|
|
69
105
|
|
|
@@ -72,16 +108,38 @@ It expects the model to return JSON with at least `score` (0..1) and
|
|
|
72
108
|
type: custom-script
|
|
73
109
|
config:
|
|
74
110
|
passThreshold: 1.0
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
111
|
+
function: |
|
|
112
|
+
/**
|
|
113
|
+
* @param {Expected} expected the example's expected output
|
|
114
|
+
* @param {Actual} actual the workflow's actual output
|
|
115
|
+
* @returns {number} a score in [0, 1]; throws are caught and scored as 0
|
|
116
|
+
*/
|
|
117
|
+
function scoreScript(expected, actual) {
|
|
118
|
+
const a = actual.totalAmount ?? 0;
|
|
119
|
+
const e = expected.totalAmount ?? 0;
|
|
120
|
+
return Math.abs(a - e) <= 0.01 ? 1 : 0;
|
|
121
|
+
}
|
|
80
122
|
```
|
|
81
123
|
|
|
82
124
|
Same WASM sandbox as `transform.script` (5s wall clock, 10 MB heap, no
|
|
83
|
-
network).
|
|
84
|
-
|
|
125
|
+
network). The YAML stores the **whole** `function scoreScript(expected,
|
|
126
|
+
actual) { ... }` declaration verbatim — same text the dashboard editor
|
|
127
|
+
shows. The JSDoc preamble is optional but recommended: it pulls the
|
|
128
|
+
workflow's output schema into IntelliSense for `expected.…` /
|
|
129
|
+
`actual.…` in the dashboard.
|
|
130
|
+
|
|
131
|
+
The signature shape is **locked**:
|
|
132
|
+
|
|
133
|
+
- Function name MUST be `scoreScript`.
|
|
134
|
+
- Parameters MUST be `(expected, actual)` in that order. Swapping them
|
|
135
|
+
would silently invert every score.
|
|
136
|
+
- Body MUST `return` a number in `[0, 1]`, or `throw` (which is caught
|
|
137
|
+
and scored as 0).
|
|
138
|
+
|
|
139
|
+
`workflow validate` and `workflow evaluators validate` flag YAML where
|
|
140
|
+
the function fails to parse, the signature doesn't match, or the body
|
|
141
|
+
contains neither `return` nor `throw` — so a hand-edited config errors
|
|
142
|
+
before it reaches the worker.
|
|
85
143
|
|
|
86
144
|
## Pass / fail vs score
|
|
87
145
|
|
|
@@ -160,9 +218,11 @@ process — `--out` writes the file directly from the server's storage.
|
|
|
160
218
|
LLM-judge scores have variance. To reduce flake:
|
|
161
219
|
|
|
162
220
|
- Pin a specific model id (`gpt-4o-2024-08-06` rather than `gpt-4o`).
|
|
163
|
-
-
|
|
164
|
-
- Keep the prompt narrow — score one dimension per evaluator. Use
|
|
221
|
+
- Keep `promptExtension` narrow — score one dimension per evaluator. Use
|
|
165
222
|
multiple judges instead of one omnibus prompt.
|
|
223
|
+
- Prefer `mode: discrete` with a small label set when the judgement is
|
|
224
|
+
categorical (good / acceptable / bad) — it eliminates the model's
|
|
225
|
+
free-form numeric drift.
|
|
166
226
|
- For high-stakes thresholds, run the experiment ≥3 times and average.
|
|
167
227
|
|
|
168
228
|
<!-- GENERATED:EVALUATOR_CATALOG START -->
|
|
@@ -177,6 +237,14 @@ _Generated from `EvalConfigYamlSchema` in `@eigenpal/types/src/eval/evaluator-co
|
|
|
177
237
|
| `evaluators` | array<object> | no | `[]` | |
|
|
178
238
|
| `passThreshold` | number | no | `0.7` | |
|
|
179
239
|
|
|
240
|
+
### Common entry fields (every evaluator type)
|
|
241
|
+
|
|
242
|
+
| Field | Type | Required | Default | Description |
|
|
243
|
+
| --- | --- | --- | --- | --- |
|
|
244
|
+
| `name` | string | yes | | Identifier used in eval results and the weighted-mean formula. Use a short kebab-case slug like header-exact or covenants-recall. |
|
|
245
|
+
| `description` | string | no | | One-sentence explanation, in business language, of what this evaluator measures and why a stakeholder should care. Shown in the dashboard to non-technical reviewers (legal, ops, finance) who never see the YAML or judge prompt. Skip implementation detail (model names, thresholds, dot-paths); those live in `config`. |
|
|
246
|
+
| `weight` | number | no | `1` | Relative importance in the weighted mean. The overall score is Σ(weightᵢ × scoreᵢ) / Σweight. Set to 0 to score-only without affecting overall. |
|
|
247
|
+
|
|
180
248
|
### `exact-diff` — JSON deep-diff against expected output
|
|
181
249
|
|
|
182
250
|
| Field | Type | Required | Default | Description |
|
|
@@ -203,7 +271,7 @@ _Generated from `EvalConfigYamlSchema` in `@eigenpal/types/src/eval/evaluator-co
|
|
|
203
271
|
|
|
204
272
|
| Field | Type | Required | Default | Description |
|
|
205
273
|
| --- | --- | --- | --- | --- |
|
|
206
|
-
| `
|
|
274
|
+
| `function` | string | yes | | Full JavaScript declaration of `function scoreScript(expected, actual) { ... }`. Receives `expected` (the example's expected output) and `actual` (the workflow's actual output), returns a number in [0, 1]. Throws are caught and scored as 0. |
|
|
207
275
|
| `timeoutMs` | integer | no | `5000` | Maximum wall-clock time the script can run before it's killed and the run is marked failed. |
|
|
208
276
|
| `memoryLimitMb` | integer | no | `10` | Maximum memory the sandbox may allocate. Increase if the script processes large arrays or strings. |
|
|
209
277
|
| `passThreshold` | number | no | `1` | Minimum script score this evaluator must reach for a run to pass. |
|