@ikarmic/forge-eval 0.0.0-stage → 4.4.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/LICENSE +21 -0
- package/README.md +95 -2
- package/dist/bin/forge-eval.d.ts +3 -0
- package/dist/bin/forge-eval.d.ts.map +1 -0
- package/dist/bin/forge-eval.js +138 -0
- package/dist/bin/forge-eval.js.map +1 -0
- package/dist/graders/behavior.d.ts +57 -0
- package/dist/graders/behavior.d.ts.map +1 -0
- package/dist/graders/behavior.js +154 -0
- package/dist/graders/behavior.js.map +1 -0
- package/dist/graders/content.d.ts +59 -0
- package/dist/graders/content.d.ts.map +1 -0
- package/dist/graders/content.js +135 -0
- package/dist/graders/content.js.map +1 -0
- package/dist/graders/fs.d.ts +24 -0
- package/dist/graders/fs.d.ts.map +1 -0
- package/dist/graders/fs.js +134 -0
- package/dist/graders/fs.js.map +1 -0
- package/dist/graders/index.d.ts +15 -0
- package/dist/graders/index.d.ts.map +1 -0
- package/dist/graders/index.js +19 -0
- package/dist/graders/index.js.map +1 -0
- package/dist/graders/metrics.d.ts +59 -0
- package/dist/graders/metrics.d.ts.map +1 -0
- package/dist/graders/metrics.js +70 -0
- package/dist/graders/metrics.js.map +1 -0
- package/dist/graders/schema.d.ts +87 -0
- package/dist/graders/schema.d.ts.map +1 -0
- package/dist/graders/schema.js +157 -0
- package/dist/graders/schema.js.map +1 -0
- package/dist/graders/shell.d.ts +36 -0
- package/dist/graders/shell.d.ts.map +1 -0
- package/dist/graders/shell.js +72 -0
- package/dist/graders/shell.js.map +1 -0
- package/dist/graders/text.d.ts +28 -0
- package/dist/graders/text.d.ts.map +1 -0
- package/dist/graders/text.js +70 -0
- package/dist/graders/text.js.map +1 -0
- package/dist/graders/types.d.ts +76 -0
- package/dist/graders/types.d.ts.map +1 -0
- package/dist/graders/types.js +2 -0
- package/dist/graders/types.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/report.d.ts +39 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +120 -0
- package/dist/report.js.map +1 -0
- package/dist/runner.d.ts +59 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +207 -0
- package/dist/runner.js.map +1 -0
- package/dist/tasks/edit.remove-section.d.ts +15 -0
- package/dist/tasks/edit.remove-section.d.ts.map +1 -0
- package/dist/tasks/edit.remove-section.js +31 -0
- package/dist/tasks/edit.remove-section.js.map +1 -0
- package/dist/tasks/edit.rename-symbol.d.ts +16 -0
- package/dist/tasks/edit.rename-symbol.d.ts.map +1 -0
- package/dist/tasks/edit.rename-symbol.js +34 -0
- package/dist/tasks/edit.rename-symbol.js.map +1 -0
- package/dist/tasks/edit.replace-greeting.d.ts +16 -0
- package/dist/tasks/edit.replace-greeting.d.ts.map +1 -0
- package/dist/tasks/edit.replace-greeting.js +29 -0
- package/dist/tasks/edit.replace-greeting.js.map +1 -0
- package/dist/tasks/edit.write-hello.d.ts +15 -0
- package/dist/tasks/edit.write-hello.d.ts.map +1 -0
- package/dist/tasks/edit.write-hello.js +22 -0
- package/dist/tasks/edit.write-hello.js.map +1 -0
- package/dist/tasks/index.d.ts +9 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +34 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/multi.refactor-move.d.ts +16 -0
- package/dist/tasks/multi.refactor-move.d.ts.map +1 -0
- package/dist/tasks/multi.refactor-move.js +37 -0
- package/dist/tasks/multi.refactor-move.js.map +1 -0
- package/dist/tasks/multi.run-and-report.d.ts +16 -0
- package/dist/tasks/multi.run-and-report.d.ts.map +1 -0
- package/dist/tasks/multi.run-and-report.js +26 -0
- package/dist/tasks/multi.run-and-report.js.map +1 -0
- package/dist/tasks/multi.summarize.d.ts +16 -0
- package/dist/tasks/multi.summarize.d.ts.map +1 -0
- package/dist/tasks/multi.summarize.js +35 -0
- package/dist/tasks/multi.summarize.js.map +1 -0
- package/dist/tasks/read.list-files.d.ts +15 -0
- package/dist/tasks/read.list-files.d.ts.map +1 -0
- package/dist/tasks/read.list-files.js +28 -0
- package/dist/tasks/read.list-files.js.map +1 -0
- package/dist/tasks/read.package-version.d.ts +15 -0
- package/dist/tasks/read.package-version.d.ts.map +1 -0
- package/dist/tasks/read.package-version.js +25 -0
- package/dist/tasks/read.package-version.js.map +1 -0
- package/dist/tasks/read.search-todos.d.ts +16 -0
- package/dist/tasks/read.search-todos.d.ts.map +1 -0
- package/dist/tasks/read.search-todos.js +30 -0
- package/dist/tasks/read.search-todos.js.map +1 -0
- package/package.json +57 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 IKarmic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ikarmic/forge-eval
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Eval harness for [IKarmic AI Forge](https://github.com/ikarmicai/ikarmic-forge). Runs agent
|
|
4
|
+
tasks against isolated workspaces and grades the results with deterministic checks:
|
|
5
|
+
no API keys needed to *grade*, only to *run*.
|
|
6
|
+
|
|
7
|
+
## What's in the box
|
|
8
|
+
|
|
9
|
+
- **5 free graders**: `regex`, `contains`, `equals`, `file-exists`, `command-runs`.
|
|
10
|
+
Deterministic, no LLM-as-judge, no API spend.
|
|
11
|
+
- **10 curated tasks** spanning three categories:
|
|
12
|
+
- **read**: list / read / search style tasks
|
|
13
|
+
- **edit**: write / modify / delete style tasks
|
|
14
|
+
- **multi-step**: tasks that need search + edit, run + report, or read + summarize
|
|
15
|
+
- **Per-task isolated cwd**: every task runs in its own temp directory so tasks can't
|
|
16
|
+
affect each other or your real project.
|
|
17
|
+
- **CLI**: `forge-eval run [--model X] [--task Y] [--json]` plus
|
|
18
|
+
`forge-eval list` for browsing the task catalog.
|
|
19
|
+
- **Markdown + JSON reports** so you can diff runs over time.
|
|
20
|
+
|
|
21
|
+
## Install / build
|
|
22
|
+
|
|
23
|
+
This package lives in the Forge monorepo. From the repo root:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm -r build
|
|
27
|
+
pnpm --filter @ikarmic/forge-eval test
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## CLI usage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Run the full suite against whatever model FORGE_DEFAULT_* resolves to.
|
|
34
|
+
pnpm forge-eval run
|
|
35
|
+
|
|
36
|
+
# Run a single task by id.
|
|
37
|
+
pnpm forge-eval run --task read-package-version
|
|
38
|
+
|
|
39
|
+
# Pin a specific model.
|
|
40
|
+
pnpm forge-eval run --model anthropic/claude-3-5-haiku-latest
|
|
41
|
+
|
|
42
|
+
# JSON output (one event per line) for piping into scripts.
|
|
43
|
+
pnpm forge-eval run --json | tee run.jsonl
|
|
44
|
+
|
|
45
|
+
# Browse the task catalog.
|
|
46
|
+
pnpm forge-eval list
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The CLI exits 0 if all tasks passed, 1 if any failed, 2 if the run couldn't start
|
|
50
|
+
(missing model, bad task id, etc.).
|
|
51
|
+
|
|
52
|
+
## Task authoring
|
|
53
|
+
|
|
54
|
+
A task is just an object: setup writes fixture files into a temp dir, the agent runs
|
|
55
|
+
against that dir, then graders inspect the result + filesystem.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { defineTask, regex, fileExists, commandRuns } from '@ikarmic/forge-eval';
|
|
59
|
+
|
|
60
|
+
export default defineTask({
|
|
61
|
+
id: 'my-task',
|
|
62
|
+
name: 'My task',
|
|
63
|
+
category: 'edit',
|
|
64
|
+
description: 'Does X then Y',
|
|
65
|
+
task: 'Write "hi" to greeting.txt',
|
|
66
|
+
setup: async (cwd) => {
|
|
67
|
+
await fs.writeFile(path.join(cwd, '.gitkeep'), '');
|
|
68
|
+
},
|
|
69
|
+
graders: [
|
|
70
|
+
fileExists('greeting.txt'),
|
|
71
|
+
commandRuns({ command: 'node -e "require(\'fs\').readFileSync(\'greeting.txt\',\'utf8\')"', contains: 'hi' }),
|
|
72
|
+
],
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Drop the task file in `src/tasks/your-task.ts`, register it in `src/tasks/index.ts`, done.
|
|
77
|
+
|
|
78
|
+
## Grader reference
|
|
79
|
+
|
|
80
|
+
| Grader | What it checks |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `regex(pattern, opts?)` | `result.finalText` matches a regex (and optionally case-insensitive / multiline) |
|
|
83
|
+
| `contains(substring)` | `result.finalText` contains a substring (case-sensitive) |
|
|
84
|
+
| `equals(text)` | `result.finalText.trim()` equals `text.trim()` |
|
|
85
|
+
| `fileExists(relPath)` | A file exists at `cwd/relPath` |
|
|
86
|
+
| `commandRuns(opts)` | Runs a shell command via `child_process.exec` in `cwd`; checks exit code 0 + optional stdout predicate |
|
|
87
|
+
|
|
88
|
+
All graders receive `{ cwd, task, result, toolInvocations }` so you can also write
|
|
89
|
+
custom graders against `result.messages` or the recorded tool calls.
|
|
90
|
+
|
|
91
|
+
## Limitations
|
|
92
|
+
|
|
93
|
+
- Free graders only: no LLM-as-judge in v1 (that needs API keys per task).
|
|
94
|
+
- 10 tasks in v1: the catalog is intentionally small. More tasks are welcome
|
|
95
|
+
via PR.
|
|
96
|
+
- Tasks assume a writable temp dir and a working `node` on PATH (for `command-runs`).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forge-eval.d.ts","sourceRoot":"","sources":["../../src/bin/forge-eval.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import { findTask, TASKS } from '../tasks/index.js';
|
|
4
|
+
import { formatJsonReport, formatMarkdownReport, formatTerminalSummary } from '../report.js';
|
|
5
|
+
import { resolveModel, runSuite } from '../runner.js';
|
|
6
|
+
function parseArgs(argv) {
|
|
7
|
+
const args = argv.slice(2);
|
|
8
|
+
const out = {
|
|
9
|
+
command: 'run',
|
|
10
|
+
json: false,
|
|
11
|
+
markdown: false,
|
|
12
|
+
help: false,
|
|
13
|
+
};
|
|
14
|
+
const takeNext = (flag) => {
|
|
15
|
+
const v = args.shift();
|
|
16
|
+
if (v === undefined || v.startsWith('--')) {
|
|
17
|
+
throw new Error(`${flag} requires a value`);
|
|
18
|
+
}
|
|
19
|
+
return v;
|
|
20
|
+
};
|
|
21
|
+
// Subcommand.
|
|
22
|
+
if (args[0] === 'list') {
|
|
23
|
+
args.shift();
|
|
24
|
+
out.command = 'list';
|
|
25
|
+
}
|
|
26
|
+
else if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') {
|
|
27
|
+
args.shift();
|
|
28
|
+
out.command = 'help';
|
|
29
|
+
out.help = true;
|
|
30
|
+
}
|
|
31
|
+
else if (args[0] === 'run') {
|
|
32
|
+
args.shift();
|
|
33
|
+
out.command = 'run';
|
|
34
|
+
}
|
|
35
|
+
while (args.length > 0) {
|
|
36
|
+
const a = args.shift();
|
|
37
|
+
if (a === '--model') {
|
|
38
|
+
out.model = takeNext('--model');
|
|
39
|
+
}
|
|
40
|
+
else if (a === '--task') {
|
|
41
|
+
out.task = takeNext('--task');
|
|
42
|
+
}
|
|
43
|
+
else if (a === '--json') {
|
|
44
|
+
out.json = true;
|
|
45
|
+
}
|
|
46
|
+
else if (a === '--markdown') {
|
|
47
|
+
out.markdown = true;
|
|
48
|
+
}
|
|
49
|
+
else if (a === '--help' || a === '-h') {
|
|
50
|
+
out.help = true;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
throw new Error(`Unknown argument: ${a}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
function printHelp() {
|
|
59
|
+
process.stdout.write(`forge-eval: run Forge agent tasks and grade the results.\n\n` +
|
|
60
|
+
`Usage:\n` +
|
|
61
|
+
` forge-eval run [--model <provider>/<model>] [--task <id>] [--json | --markdown]\n` +
|
|
62
|
+
` forge-eval list\n` +
|
|
63
|
+
` forge-eval help\n\n` +
|
|
64
|
+
`Flags:\n` +
|
|
65
|
+
` --model <p>/<m> Override the model. Defaults to FORGE_DEFAULT_MODEL / provider env vars.\n` +
|
|
66
|
+
` --task <id> Run only the named task (see \`forge-eval list\`).\n` +
|
|
67
|
+
` --json Emit a single JSON report on stdout.\n` +
|
|
68
|
+
` --markdown Emit a markdown report on stdout (in addition to terminal summary).\n\n` +
|
|
69
|
+
`Exit codes: 0 = all passed, 1 = any failed, 2 = run couldn't start.\n`);
|
|
70
|
+
}
|
|
71
|
+
function printList() {
|
|
72
|
+
process.stdout.write(`# Tasks (${TASKS.length})\n\n`);
|
|
73
|
+
for (const t of TASKS) {
|
|
74
|
+
process.stdout.write(`## ${t.id}: ${t.name} [${t.category}]\n${t.description}\n\n`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function main() {
|
|
78
|
+
const args = parseArgs(process.argv);
|
|
79
|
+
if (args.command === 'help' || args.help) {
|
|
80
|
+
printHelp();
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (args.command === 'list') {
|
|
84
|
+
printList();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// command === 'run' from here on
|
|
88
|
+
// Task lookup first: it doesn't need a model, so we can fail fast on a
|
|
89
|
+
// bogus --task id before bothering to resolve credentials.
|
|
90
|
+
let tasks;
|
|
91
|
+
if (args.task) {
|
|
92
|
+
const t = findTask(args.task);
|
|
93
|
+
if (!t) {
|
|
94
|
+
process.stderr.write(`forge-eval: unknown task id "${args.task}". Run \`forge-eval list\` to see options.\n`);
|
|
95
|
+
process.exit(2);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
tasks = [t];
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
tasks = TASKS;
|
|
102
|
+
}
|
|
103
|
+
let model;
|
|
104
|
+
try {
|
|
105
|
+
model = resolveModel(args.model);
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
process.stderr.write(`forge-eval: ${err.message}\n`);
|
|
109
|
+
process.exit(2);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!args.json) {
|
|
113
|
+
process.stderr.write(`forge-eval: running ${tasks.length} task(s) against ${model.provider}/${model.model}...\n`);
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const suite = await runSuite(tasks, model);
|
|
117
|
+
if (args.json) {
|
|
118
|
+
const report = formatJsonReport(suite);
|
|
119
|
+
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
process.stdout.write('\n' + formatTerminalSummary(suite) + '\n');
|
|
123
|
+
if (args.markdown) {
|
|
124
|
+
process.stdout.write('\n' + formatMarkdownReport(suite) + '\n');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
process.exit(suite.summary.failed === 0 ? 0 : 1);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
process.stderr.write(`forge-eval: ${err.message}\n`);
|
|
131
|
+
process.exit(2);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
main().catch((err) => {
|
|
135
|
+
process.stderr.write(`forge-eval: ${err.message}\n`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
});
|
|
138
|
+
//# sourceMappingURL=forge-eval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forge-eval.js","sourceRoot":"","sources":["../../src/bin/forge-eval.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Grader } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Grader that passes when the agent called a given tool at least `min` times
|
|
4
|
+
* during the run. Use this to assert the model took the right path (vs. just
|
|
5
|
+
* guessing in its final text).
|
|
6
|
+
*/
|
|
7
|
+
export declare function toolWasCalled(name: string, min?: number): Grader;
|
|
8
|
+
/**
|
|
9
|
+
* Grader that passes when the agent never called a given tool. Useful for
|
|
10
|
+
* "the agent should have done this without writing a file" tasks.
|
|
11
|
+
*/
|
|
12
|
+
export declare function toolNotCalled(name: string): Grader;
|
|
13
|
+
/**
|
|
14
|
+
* Grader that passes when `finalText` is non-empty. Catches "model went
|
|
15
|
+
* silent" runs that some graders above would also catch indirectly.
|
|
16
|
+
*/
|
|
17
|
+
export declare function finalTextNonEmpty(): Grader;
|
|
18
|
+
/**
|
|
19
|
+
* Grader that passes when the agent finished with `reason === 'completed'`.
|
|
20
|
+
* Catches runs that aborted early (max_iterations, error, etc.): even if
|
|
21
|
+
* the other graders happened to pass.
|
|
22
|
+
*/
|
|
23
|
+
export declare function completed(): Grader;
|
|
24
|
+
/**
|
|
25
|
+
* Options for {@link toolCount}.
|
|
26
|
+
*
|
|
27
|
+
* At least one of `min` / `max` / `exact` must be provided. If
|
|
28
|
+
* multiple are passed they're AND-ed together: `exact` is the
|
|
29
|
+
* strongest constraint.
|
|
30
|
+
*/
|
|
31
|
+
export interface ToolCountOptions {
|
|
32
|
+
min?: number;
|
|
33
|
+
max?: number;
|
|
34
|
+
exact?: number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Grader that passes when the call count for `name` falls within
|
|
38
|
+
* `min` / `max` (inclusive). Use this to assert a budget
|
|
39
|
+
* ("didn't spam the read tool") or a minimum ("read at least once
|
|
40
|
+
* before guessing").
|
|
41
|
+
*/
|
|
42
|
+
export declare function toolCount(name: string, options?: ToolCountOptions): Grader;
|
|
43
|
+
/**
|
|
44
|
+
* Grader that passes when at least one call to `name` had
|
|
45
|
+
* `predicate(args)` return true. Use this to assert the agent
|
|
46
|
+
* invoked a tool with the right arguments (not just the right tool
|
|
47
|
+
* name): e.g. "the agent called `edit_file` with a non-empty diff"
|
|
48
|
+
* vs. "the agent called `edit_file` 5 times on autopilot".
|
|
49
|
+
*/
|
|
50
|
+
export declare function toolCalledWith(name: string, predicate: (args: unknown) => boolean): Grader;
|
|
51
|
+
/**
|
|
52
|
+
* Grader that passes when no tool call during the run returned an
|
|
53
|
+
* error. Useful as a baseline guard: any task that says "agent did
|
|
54
|
+
* something" should also assert it didn't fall over partway.
|
|
55
|
+
*/
|
|
56
|
+
export declare function noToolErrors(): Grader;
|
|
57
|
+
//# sourceMappingURL=behavior.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"behavior.d.ts","sourceRoot":"","sources":["../../src/graders/behavior.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAA+B,MAAM,YAAY,CAAC;AAEtE;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAI,GAAG,MAAM,CAO3D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAO1C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAMlC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAEhB;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CA0B9E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,GACpC,MAAM,CAsBR;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAcrC"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grader that passes when the agent called a given tool at least `min` times
|
|
3
|
+
* during the run. Use this to assert the model took the right path (vs. just
|
|
4
|
+
* guessing in its final text).
|
|
5
|
+
*/
|
|
6
|
+
export function toolWasCalled(name, min = 1) {
|
|
7
|
+
return (ctx) => {
|
|
8
|
+
const calls = ctx.toolInvocations.filter((t) => t.name === name).length;
|
|
9
|
+
return calls >= min
|
|
10
|
+
? { passed: true, message: `tool ${name} called ${calls}x` }
|
|
11
|
+
: { passed: false, message: `tool ${name} called ${calls}x (need ${min})` };
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Grader that passes when the agent never called a given tool. Useful for
|
|
16
|
+
* "the agent should have done this without writing a file" tasks.
|
|
17
|
+
*/
|
|
18
|
+
export function toolNotCalled(name) {
|
|
19
|
+
return (ctx) => {
|
|
20
|
+
const calls = ctx.toolInvocations.filter((t) => t.name === name).length;
|
|
21
|
+
return calls === 0
|
|
22
|
+
? { passed: true, message: `tool ${name} was not called` }
|
|
23
|
+
: { passed: false, message: `tool ${name} was called ${calls}x but should not have been` };
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Grader that passes when `finalText` is non-empty. Catches "model went
|
|
28
|
+
* silent" runs that some graders above would also catch indirectly.
|
|
29
|
+
*/
|
|
30
|
+
export function finalTextNonEmpty() {
|
|
31
|
+
return (ctx) => {
|
|
32
|
+
const text = ctx.result.finalText?.trim() ?? '';
|
|
33
|
+
return text.length > 0
|
|
34
|
+
? { passed: true, message: 'finalText is non-empty' }
|
|
35
|
+
: { passed: false, message: 'finalText is empty' };
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Grader that passes when the agent finished with `reason === 'completed'`.
|
|
40
|
+
* Catches runs that aborted early (max_iterations, error, etc.): even if
|
|
41
|
+
* the other graders happened to pass.
|
|
42
|
+
*/
|
|
43
|
+
export function completed() {
|
|
44
|
+
return (ctx) => {
|
|
45
|
+
return ctx.result.reason === 'completed'
|
|
46
|
+
? { passed: true, message: `reason: completed` }
|
|
47
|
+
: { passed: false, message: `reason: ${ctx.result.reason}` };
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Grader that passes when the call count for `name` falls within
|
|
52
|
+
* `min` / `max` (inclusive). Use this to assert a budget
|
|
53
|
+
* ("didn't spam the read tool") or a minimum ("read at least once
|
|
54
|
+
* before guessing").
|
|
55
|
+
*/
|
|
56
|
+
export function toolCount(name, options = {}) {
|
|
57
|
+
if (options.min === undefined && options.max === undefined && options.exact === undefined) {
|
|
58
|
+
throw new Error('toolCount requires at least one of min/max/exact');
|
|
59
|
+
}
|
|
60
|
+
const { min = 0, max = Number.POSITIVE_INFINITY, exact } = options;
|
|
61
|
+
const effectiveMin = exact ?? min;
|
|
62
|
+
const effectiveMax = exact ?? max;
|
|
63
|
+
return (ctx) => {
|
|
64
|
+
const calls = ctx.toolInvocations.filter((t) => t.name === name).length;
|
|
65
|
+
if (calls < effectiveMin) {
|
|
66
|
+
return {
|
|
67
|
+
passed: false,
|
|
68
|
+
message: `tool ${name} called ${calls}x (need at least ${effectiveMin})`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (calls > effectiveMax) {
|
|
72
|
+
return {
|
|
73
|
+
passed: false,
|
|
74
|
+
message: `tool ${name} called ${calls}x (at most ${effectiveMax})`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
passed: true,
|
|
79
|
+
message: `tool ${name} called ${calls}x (in [${effectiveMin}, ${effectiveMax}])`,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Grader that passes when at least one call to `name` had
|
|
85
|
+
* `predicate(args)` return true. Use this to assert the agent
|
|
86
|
+
* invoked a tool with the right arguments (not just the right tool
|
|
87
|
+
* name): e.g. "the agent called `edit_file` with a non-empty diff"
|
|
88
|
+
* vs. "the agent called `edit_file` 5 times on autopilot".
|
|
89
|
+
*/
|
|
90
|
+
export function toolCalledWith(name, predicate) {
|
|
91
|
+
return (ctx) => {
|
|
92
|
+
const calls = ctx.toolInvocations.filter((t) => t.name === name);
|
|
93
|
+
if (calls.length === 0) {
|
|
94
|
+
return { passed: false, message: `tool ${name} was never called` };
|
|
95
|
+
}
|
|
96
|
+
const matching = calls.filter((t) => predicate(t.args));
|
|
97
|
+
if (matching.length > 0) {
|
|
98
|
+
return {
|
|
99
|
+
passed: true,
|
|
100
|
+
message: `tool ${name} called with matching args ${matching.length}x of ${calls.length}`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
passed: false,
|
|
105
|
+
message: `tool ${name} called ${calls.length}x but no call's args matched predicate`,
|
|
106
|
+
details: `observed arg samples: ${calls
|
|
107
|
+
.slice(0, 3)
|
|
108
|
+
.map((c) => summarizeArgs(c.args))
|
|
109
|
+
.join(' | ')}`,
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Grader that passes when no tool call during the run returned an
|
|
115
|
+
* error. Useful as a baseline guard: any task that says "agent did
|
|
116
|
+
* something" should also assert it didn't fall over partway.
|
|
117
|
+
*/
|
|
118
|
+
export function noToolErrors() {
|
|
119
|
+
return (ctx) => {
|
|
120
|
+
const errs = ctx.toolInvocations.filter((t) => t.isError);
|
|
121
|
+
if (errs.length === 0) {
|
|
122
|
+
return { passed: true, message: 'no tool errors' };
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
passed: false,
|
|
126
|
+
message: `${errs.length} tool call(s) errored`,
|
|
127
|
+
details: errs
|
|
128
|
+
.map((e) => ` - ${e.name}: ${shortError(e.result)}`)
|
|
129
|
+
.join('\n'),
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function shortError(result) {
|
|
134
|
+
if (result instanceof Error)
|
|
135
|
+
return result.message;
|
|
136
|
+
if (typeof result === 'string')
|
|
137
|
+
return result;
|
|
138
|
+
try {
|
|
139
|
+
return JSON.stringify(result).slice(0, 200);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return '<unserializable>';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function summarizeArgs(args) {
|
|
146
|
+
try {
|
|
147
|
+
const s = JSON.stringify(args);
|
|
148
|
+
return s.length > 80 ? s.slice(0, 77) + '…' : s;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return '<unserializable>';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=behavior.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"behavior.js","sourceRoot":"","sources":["../../src/graders/behavior.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured content extraction graders. v2.13.
|
|
3
|
+
*
|
|
4
|
+
* Helpers for tasks where the agent's final text is expected to
|
|
5
|
+
* contain extractable structured content (a JSON object, a fenced
|
|
6
|
+
* code block in a specific language). These complement the
|
|
7
|
+
* full-schema graders in `./schema.ts`: where `jsonSchema` requires a
|
|
8
|
+
* Zod schema, `jsonContains` accepts a plain predicate so you don't
|
|
9
|
+
* need to write a schema for "the response is roughly shaped like X".
|
|
10
|
+
*/
|
|
11
|
+
import type { Grader } from './types.js';
|
|
12
|
+
/** Options for {@link languageBlock}. */
|
|
13
|
+
export interface LanguageBlockOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Truncate the extracted block content in failure details to this
|
|
16
|
+
* many characters. Default 320.
|
|
17
|
+
*/
|
|
18
|
+
maxDetailChars?: number;
|
|
19
|
+
/**
|
|
20
|
+
* When true (default), the grader fails when the fenced block is
|
|
21
|
+
* missing. When false, a missing block is acceptable and yields a
|
|
22
|
+
* pass.
|
|
23
|
+
*/
|
|
24
|
+
requireBlock?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Grader that extracts the first <code>```<lang> ... ```</code>
|
|
28
|
+
* fenced block from `finalText` and passes when `predicate(block)`
|
|
29
|
+
* returns true. Language matching is case-insensitive.
|
|
30
|
+
*
|
|
31
|
+
* Use this for tasks like "agent produced a runnable Python snippet
|
|
32
|
+
* with a `solve()` function" without parsing the whole response
|
|
33
|
+
* manually.
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* graders: [
|
|
37
|
+
* languageBlock('ts', (code) => /export function/.test(code)),
|
|
38
|
+
* ]
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function languageBlock(lang: string, predicate: (code: string) => boolean, options?: LanguageBlockOptions): Grader;
|
|
42
|
+
/**
|
|
43
|
+
* Predicate-driven JSON shape check. Parses `finalText` as JSON: raw
|
|
44
|
+
* text first, then a single fenced block: then calls
|
|
45
|
+
* `predicate(value)`. Passes when the predicate accepts.
|
|
46
|
+
*
|
|
47
|
+
* Use this instead of {@link jsonSchema} when the field set is loose,
|
|
48
|
+
* you want to assert a single property, or you don't want a Zod
|
|
49
|
+
* dependency in this task's grading.
|
|
50
|
+
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* jsonContains((v) => {
|
|
53
|
+
* return v && typeof v === 'object' &&
|
|
54
|
+
* 'name' in v && 'price' in v && (v as any).price > 0;
|
|
55
|
+
* })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function jsonContains(predicate: (value: unknown) => boolean): Grader;
|
|
59
|
+
//# sourceMappingURL=content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/graders/content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,MAAM,EAA+B,MAAM,YAAY,CAAC;AAEtE,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,EACpC,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAuBR;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,MAAM,CAmB3E"}
|