Skip to content

Commit cc785b2

Browse files
committed
feat(core): support flatter eval imports
1 parent e36d111 commit cc785b2

13 files changed

Lines changed: 1484 additions & 217 deletions

File tree

‎apps/web/src/content/docs/docs/evaluation/eval-files.mdx‎

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ other suites. Raw case files are reusable data inputs, not a second runnable
1717
experiment format.
1818

1919
- A **task suite** is eval YAML that owns task context: `workspace`, shared
20-
`input`, shared `assertions`, and test cases. It can run directly or be
21-
imported with `type: suite`.
20+
`input`, shared `assertions`, fixtures, graders, and test cases. It can run
21+
directly or be imported through `imports.suites`.
2222
- A **raw case file** is a YAML/JSONL array, directory, or glob of cases. Import
23-
it with `tests: ./cases.yaml`, string shorthand, or `type: tests`; parent
23+
it with `imports.tests`, `tests: ./cases.yaml`, or string shorthand; parent
2424
suite context applies because raw cases do not carry their own suite context.
2525
- A **wrapper eval** is eval YAML that imports one or more suites with
26-
`type: suite` and binds runtime policy in its inline `experiment:` block.
26+
`imports.suites` and binds runtime policy in its inline `experiment:` block.
2727
Wrapper evals can live anywhere in the repo. A wrapper that imports suites
28-
with `type: suite` must not define parent `workspace`; imported suites own
28+
with `imports.suites` must not define parent `workspace`; imported suites own
2929
task environment. Machine-local existing workspace paths belong in CLI flags
3030
or `config.local.yaml`, not eval YAML.
3131

@@ -61,25 +61,29 @@ A wrapper eval stays ordinary eval YAML while choosing runtime policy:
6161
```yaml
6262
# experiments/refunds-codex.eval.yaml
6363
experiment:
64-
name: refunds-codex
6564
target: codex-gpt5
6665
repeat:
6766
count: 2
6867
strategy: pass_all
6968

69+
imports:
70+
suites:
71+
- path: ../evals/suites/refunds.eval.yaml
72+
tests:
73+
- path: ../evals/cases/refund-smoke.cases.yaml
74+
7075
tests:
71-
- include: ../evals/suites/refunds.eval.yaml
72-
type: suite
73-
- include: ../evals/cases/refund-smoke.cases.yaml
74-
type: tests
76+
- id: local-edge-case
77+
input: Can a final-sale item be refunded after damage in transit?
78+
expected_output: Explain the final-sale exception for damaged transit.
7579
```
7680
7781
The `experiments/` directory in that example is optional and user-owned. AgentV
7882
does not infer behavior from the path; the wrapper runs because it is eval YAML
7983
with an inline `experiment:` block. The wrapper owns runtime policy only. Put
8084
workspace setup in imported child suites. Parent workspace-affecting fields,
8185
including top-level `workspace`, are for parent-owned raw cases, including
82-
cases imported with `type: tests`. Runtime workspace path overrides belong in
86+
cases imported with `imports.tests`. Runtime workspace path overrides belong in
8387
CLI flags or `.agentv/config.local.yaml`; repos, hooks, templates, Docker
8488
config, env checks, and isolation belong in top-level or case-level
8589
`workspace`.
@@ -112,7 +116,8 @@ tests:
112116
| `category` | Optional slash-delimited analytics taxonomy path. Overrides the category derived from the eval file path. |
113117
| `experiment` | Runtime policy (`target`, `targets`, `workers`, `repeat`, `threshold`, `timeout_seconds`, `budget_usd`, etc.) |
114118
| `workspace` | Suite-level task environment — inline object or string path to an [external workspace file](/docs/guides/workspace-pool/#external-workspace-config). Repo entries declare identity and checkout pins; acquisition is covered in [Workspace Architecture](/docs/guides/workspace-architecture/#repo-provenance-vs-acquisition). |
115-
| `tests` | Array of individual tests, include entries, or a string path to an external file or directory. Tests and include entries may use scoped `run:` overrides for `threshold`, `repeat`, `timeout_seconds`, and `budget_usd`. |
119+
| `imports` | Optional import groups. `imports.suites` imports full child eval suites with their task context. `imports.tests` imports raw test rows into this file's context. Import entries may use scoped `run:` overrides for `threshold`, `repeat`, `timeout_seconds`, and `budget_usd`. |
120+
| `tests` | Inline raw tests or a string path to an external raw-case file or directory. Legacy `tests[].include` entries still load with a migration warning; prefer `imports.suites` or `imports.tests`. |
116121
| `assertions` | Suite-level graders appended to each test unless `execution.skip_defaults: true` is set on the test |
117122
| `input` | Suite-level input messages prepended to each test's input unless `execution.skip_defaults: true` is set on the test |
118123

@@ -332,13 +337,26 @@ use direct paths, directories, or globs:
332337
```yaml
333338
tests:
334339
- ./cases/*.cases.yaml
335-
- include: ./suites/*.eval.yaml
336-
type: suite
337340
```
338341

339-
String shorthand is raw-case-only. Import reusable task suites with object
340-
entries using `include:` and `type: suite`; use `type: tests` when you want to
341-
drop suite context and import only raw cases.
342+
String shorthand is raw-case-only. Import reusable task suites through
343+
`imports.suites`; use `imports.tests` when you want to drop suite context and
344+
import only raw cases into the parent context:
345+
346+
```yaml
347+
imports:
348+
suites:
349+
- path: ./suites/*.eval.yaml
350+
tests:
351+
- path: ./cases/regression.jsonl
352+
353+
tests:
354+
- id: local-edge-case
355+
input: ...
356+
```
357+
358+
Legacy `tests[].include` entries still load with a migration warning for older
359+
eval files, but new evals should use `imports.suites` or `imports.tests`.
342360

343361
### Raw Cases as Directory Paths
344362

‎apps/web/src/content/docs/docs/evaluation/experiments.mdx‎

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -64,75 +64,81 @@ experiment:
6464
workers: 2
6565
6666
tests:
67-
- include: ../evals/suites/refunds.eval.yaml
68-
type: suite
69-
- include: ../evals/cases/refund-smoke.cases.yaml
70-
type: tests
67+
- id: local-edge-case
68+
input: Check a damaged final-sale refund.
69+
70+
imports:
71+
suites:
72+
- path: ../evals/suites/refunds.eval.yaml
73+
tests:
74+
- path: ../evals/cases/refund-smoke.cases.yaml
7175
```
7276

7377
The `experiments/` folder is optional and user-owned. AgentV does not scan it
7478
for special files or infer runtime behavior from the path; the same wrapper eval
7579
could live under `evals/wrappers/`, `benchmarks/`, or beside the suite it runs.
7680

77-
## Tests Imports
81+
## Suite And Test Imports
7882

79-
Use `tests[]` for composition, imports, and selection.
83+
Use `imports.suites` for full child suites and `imports.tests` for raw test
84+
rows. Inline `tests` remain raw cases owned by the current file.
8085

8186
```yaml
87+
imports:
88+
suites:
89+
- path: evals/support/*.eval.yaml
90+
select:
91+
test_ids:
92+
- refund-*
93+
- missing-order-date
94+
tags: regression
95+
metadata:
96+
priority: high
97+
run:
98+
threshold: 1.0
99+
repeat:
100+
count: 2
101+
strategy: pass_all
102+
tests:
103+
- path: cases/*.cases.yaml
104+
- path: cases/regression.jsonl
105+
82106
tests:
83-
- include: evals/support/*.eval.yaml
84-
type: suite
85-
select:
86-
test_ids:
87-
- refund-*
88-
- missing-order-date
89-
tags: regression
90-
metadata:
91-
priority: high
92-
run:
93-
threshold: 1.0
94-
repeat:
95-
count: 2
96-
strategy: pass_all
97-
- include: cases/*.cases.yaml
98-
type: tests
99-
- include: cases/regression.jsonl
100-
type: tests
101107
- cases/smoke/*.cases.yaml
102108
```
103109

104-
`type: suite` preserves the imported suite's task contract: metadata,
110+
`imports.suites` preserves the imported suite's task contract: metadata,
105111
`workspace`, shared `input`, shared `assertions`, and tests. The parent eval
106112
still owns the single run bundle and runtime policy. Child suite
107113
`experiment:` blocks are ignored when imported; use parent `experiment:` for
108-
run policy and `tests[].run` for scoped threshold, repeat, timeout, or budget
114+
run policy and import `run:` for scoped threshold, repeat, timeout, or budget
109115
overrides.
110116

111-
A parent eval that imports any `type: suite` entry must not define top-level
117+
A parent eval that imports any `imports.suites` entry must not define top-level
112118
`workspace`. Imported suites own task environment. If the parent should provide
113-
workspace context, import raw cases with `type: tests` or shorthand paths
119+
workspace context, import raw cases with `imports.tests` or shorthand paths
114120
instead of importing an eval suite.
115121

116-
`type: tests` imports only raw test entries. It intentionally drops shared
122+
`imports.tests` imports only raw test entries. It intentionally drops shared
117123
context from an imported eval suite, so parent suite fields apply to those raw
118124
cases.
119125

120-
`tests[].select.test_ids` filters imported test IDs with glob patterns.
121-
`tests[].select.tags` filters each imported case's effective `metadata.tags`.
126+
Import `select.test_ids` filters imported test IDs with glob patterns.
127+
Import `select.tags` filters each imported case's effective `metadata.tags`.
122128
Effective case tags are suite-first and deduped:
123129
`suite.tags + suite.metadata.tags + test.metadata.tags`. Top-level suite `tags`
124130
still remain suite identity metadata for discovery and reporting; selection reads
125-
the merged case metadata view. `tests[].select.metadata` filters case metadata by
131+
the merged case metadata view. Import `select.metadata` filters case metadata by
126132
key/value, where selector values may be scalars or lists. Globbed include paths
127133
are resolved in deterministic path order, then test order.
128134

129135
String-valued `tests` and string entries inside `tests[]` are raw-case import
130-
shorthand. They are equivalent to `include` with `type: tests` and may point at
136+
shorthand. They are equivalent to `imports.tests` and may point at
131137
raw case files, directories, or globs. Importing another eval suite must use
132-
object form with `include:` and `type: suite`.
138+
`imports.suites`.
133139

134-
Suite imports are resolved as a deterministic include graph. Circular `type:
135-
suite` imports fail validation with the import chain; raw-case shorthand does
140+
Suite imports are resolved as a deterministic include graph. Circular
141+
`imports.suites` imports fail validation with the import chain; raw-case shorthand does
136142
not recursively load suite runtime blocks.
137143

138144
Imported suite rows keep their source suite metadata in `run_manifest.jsonl`. Use each
@@ -145,7 +151,7 @@ Use scoped `run:` blocks for result interpretation and scheduling policies that
145151
vary by include group or test case. Precedence is:
146152

147153
```text
148-
test.run > tests[].run > parent experiment
154+
test.run > import run > parent experiment
149155
```
150156

151157
```yaml
@@ -156,26 +162,26 @@ experiment:
156162
count: 3
157163
strategy: pass_at_k
158164
159-
tests:
160-
- include: ./evals/flaky-agentic/**/*.eval.yaml
161-
type: suite
162-
select:
163-
tags: [agentic]
164-
run:
165-
repeat:
166-
count: 3
167-
strategy: pass_at_k
168-
169-
- include: ./evals/regression/**/*.eval.yaml
170-
type: suite
171-
select:
172-
tags: [must-pass]
173-
run:
174-
threshold: 1.0
175-
repeat:
176-
count: 2
177-
strategy: pass_all
165+
imports:
166+
suites:
167+
- path: ./evals/flaky-agentic/**/*.eval.yaml
168+
select:
169+
tags: [agentic]
170+
run:
171+
repeat:
172+
count: 3
173+
strategy: pass_at_k
174+
175+
- path: ./evals/regression/**/*.eval.yaml
176+
select:
177+
tags: [must-pass]
178+
run:
179+
threshold: 1.0
180+
repeat:
181+
count: 2
182+
strategy: pass_all
178183
184+
tests:
179185
- id: critical-case
180186
input: "..."
181187
criteria: Must pass exactly

‎docs/adr/0006-separate-experiments-from-eval-definitions.md‎

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -159,17 +159,22 @@ multi-file command such as `agentv eval a.eval.yaml b.eval.yaml` remains a batch
159159
of independent eval-suite runs, rather than one implicit wrapper experiment with
160160
shared mutable setup.
161161

162-
## Tests Import Surface
162+
## Suite And Test Import Surface
163163

164-
`tests:` is the only composition, import, and selection surface.
164+
`imports.suites`, `imports.tests`, and inline `tests` are the authored
165+
composition surfaces.
165166

166-
`include:` accepts direct paths and glob patterns. Include entries are
167-
structurally identified by the `include` field, so their `type:` field can use
168-
the ordinary AgentV wire-format discriminator without ambiguity:
167+
`imports.suites` imports full child eval suites. `imports.tests` imports raw
168+
case rows into the parent file's task context. Inline `tests` are raw cases
169+
owned by the current file. Legacy `tests[].include` entries remain a migration
170+
path for existing eval files, but new evals should use the flatter import
171+
groups.
169172

170-
- `include: **/*.eval.yaml` imports eval suites.
171-
- `include: **/*.cases.yaml` imports raw cases.
172-
- `include: **/*.jsonl` imports raw cases.
173+
`path:` accepts direct paths and glob patterns:
174+
175+
- `imports.suites[].path: **/*.eval.yaml` imports eval suites.
176+
- `imports.tests[].path: **/*.cases.yaml` imports raw cases.
177+
- `imports.tests[].path: **/*.jsonl` imports raw cases.
173178

174179
`select:` filters imported cases with the same general selection shape used by
175180
old experiment suite selection. `select.test_ids` filters by test id and maps
@@ -216,29 +221,29 @@ In that example, `case-1` matches `select.tags: cargowise`.
216221
Imported tests run in deterministic order: resolved path first, then the test
217222
order inside each resolved source.
218223

219-
`type: suite` preserves the imported suite task contract. That includes suite
224+
`imports.suites` preserves the imported suite task contract. That includes suite
220225
metadata, `workspace`, shared `input`, shared `assertions`, and tests. The
221226
parent eval still owns one run bundle and one runtime policy. Child suite
222-
`experiment:` blocks are ignored when imported with `type: suite`; they do not
227+
`experiment:` blocks are ignored when imported through `imports.suites`; they do not
223228
fall back into the parent run. Scoped runtime overrides that the parent wants
224-
to apply to imported tests live in `tests[].run`.
229+
to apply to imported tests live on the import entry's `run:` block.
225230

226-
A parent eval that imports any child eval suite with `type: suite` must not
231+
A parent eval that imports any child eval suite with `imports.suites` must not
227232
define parent `workspace`. The wrapper owns runtime policy, not task
228233
environment. Imported child suites keep their own `workspace`, including
229234
`workspace.repos[]`, templates, hooks, and isolation. Existing local workspace
230235
paths are machine-local bindings supplied through CLI flags or
231236
`config.local.yaml`. If the parent should own workspace context, import raw cases
232-
with `type: tests` or shorthand paths instead of importing an eval suite.
237+
with `imports.tests` or shorthand paths instead of importing an eval suite.
233238

234-
`type: tests` imports only raw test entries. It intentionally drops shared
239+
`imports.tests` imports only raw test entries. It intentionally drops shared
235240
suite context such as workspace, shared input, and shared assertions. Use this
236241
mode only when the imported file is a case corpus or when dropping suite context
237242
is the desired behavior.
238243

239244
Suite files still support raw-case shorthand imports. A string-valued `tests`
240-
field or a string entry inside the `tests` list is equivalent to an include
241-
entry with `type: tests`:
245+
field or a string entry inside the `tests` list is equivalent to an
246+
`imports.tests` entry:
242247

243248
```yaml
244249
tests: ./cases.yaml
@@ -247,16 +252,13 @@ tests: ./cases.yaml
247252
```yaml
248253
tests:
249254
- ./cases/*.cases.yaml
250-
- include: ./suites/*.eval.yaml
251-
type: suite
252255
```
253256

254257
The shorthand is only for raw case files or directories. Importing another eval
255-
suite must use object form with `include:` and `type: suite`, so authors cannot
256-
accidentally drop or preserve suite context without saying which behavior they
257-
want.
258+
suite must use `imports.suites`, so authors cannot accidentally drop or
259+
preserve suite context without saying which behavior they want.
258260

259-
Do not use `import:` or `kind:` for `tests:` include entries.
261+
Do not use `import:` or `kind:` for import entries.
260262

261263
Parent suite-level task fields should not silently override imported suite task
262264
fields. Explicit override syntax can be considered later if a concrete use case

0 commit comments

Comments
 (0)