1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//! The thing being benchmarked: the host's agent harness, behind the [`Harness`] trait, plus
//! [`RunArtifacts`] — everything a single run produced EXCEPT scoring — and the structured [`ToolCall`]
//! the artifacts carry.
//!
//! `eval-core` knows nothing about HOW a run happens (which LLM, which tools, which world). The host
//! implements [`Harness`] over its own `World` + `Setup`, and the generic runner ([`crate::run_eval`])
//! drives it: build a fresh world per case, run the instruction against it, then hand the resulting
//! world AND the run's [`RunArtifacts`] to a [`Scorer`](crate::Scorer). The harness owns its own backend
//! and reports its own token count — there is deliberately no backend/token-counting trait in this crate.
//!
//! For the common case — scoring what the agent DID (tool calls, params, final text/number) rather than
//! a custom world — a host can skip [`Harness`]/[`Scorer`](crate::Scorer)/`World`/`Setup` entirely and implement the
//! one-method [`Agent`] trait, authoring [`Expectation`](crate::expect::Expectation) predicates and
//! calling [`run_suite`](crate::run_suite). See that trait + the `calculator` example.
use Value;
use crateEvalError;
/// One tool call the agent made: the tool's `name` and the structured `args` it was invoked with.
///
/// `args` is opaque JSON so any argument shape round-trips and the built-in
/// [`expect::Expectation`](crate::expect) assertions can subset-match against it. The report keeps a
/// *display* string per call (see [`CaseOutcome::tool_calls`](crate::report::CaseOutcome::tool_calls));
/// the runner derives those from these structured calls via [`ToolCall::display`], so saved JSON / the
/// `--json` path / the HTML report stay shape-compatible.
/// Everything a single run produced EXCEPT scoring. The [`Harness`] (or [`Agent`]) fills this in and
/// returns it; the generic runner copies it onto the case's [`CaseOutcome`](crate::report::CaseOutcome)
/// and also hands it to the [`Scorer`](crate::Scorer) so built-in assertions can inspect what the agent
/// did.
///
/// Every field is optional/empty by default, so a minimal harness can return `RunArtifacts::default()`
/// and a richer one can populate as much diagnostic detail as it has.
///
/// `#[non_exhaustive]`: new diagnostic fields can be added without a breaking change. Within `eval-core`
/// it is still constructed with struct literals / `..Default::default()`; external crates build it via
/// the builder methods (e.g. `RunArtifacts::new().with_final_text(...)`).
/// The thing being benchmarked: the host's agent harness. The host implements this over its own world.
///
/// The runner calls [`setup`](Harness::setup) once per case to build a FRESH world, then
/// [`run`](Harness::run) once against that world. Implementations should be deterministic given the same
/// `Setup` (the eval forces a deterministic configuration where it can) so runs are comparable.
///
/// Method calls are isolated behind `catch_unwind` by the runner, so a panic inside `setup`/`run` fails
/// only the offending case; implementors need not catch their own panics.
///
/// For the common "score what the agent DID, against `()` world + the built-in
/// [`Expectation`](crate::expect::Expectation)s" case, prefer the simpler [`Agent`] trait +
/// [`run_suite`](crate::run_suite) — no `World`/`Setup`/`Scorer` to implement.
/// The easy path: implement this single method for your harness — run ONE prompt, return what the agent
/// did — and the framework does the rest.
///
/// Pair an `Agent` with the built-in [`Expectation`](crate::expect::Expectation) assertions and call
/// [`run_suite`](crate::run_suite): no `World`, no `Setup`, no [`Scorer`](crate::Scorer) impl. Internally
/// an adapter ([`AgentHarness`](crate::AgentHarness)) wraps the agent as a `Harness<World = (), Setup =
/// ()>`, and [`BuiltinScorer`](crate::BuiltinScorer) scores the returned [`RunArtifacts`].
///
/// This is "pytest for agents": a test case is a prompt + [`Expectation`](crate::expect::Expectation)s,
/// scored over the universal [`RunArtifacts`] with no user-implemented scorer.