niceeval 0.4.5 → 0.5.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/docs-site/zh/concepts/adapter.mdx +6 -6
- package/docs-site/zh/concepts/experiment.mdx +7 -7
- package/docs-site/zh/concepts/overview.mdx +1 -1
- package/docs-site/zh/concepts/tier.mdx +6 -6
- package/docs-site/zh/guides/agent-feedback-loop.mdx +63 -20
- package/docs-site/zh/guides/connect-your-agent.mdx +7 -7
- package/docs-site/zh/guides/custom-reports.mdx +243 -0
- package/docs-site/zh/guides/experiments.mdx +3 -3
- package/docs-site/zh/guides/report-components.mdx +252 -0
- package/docs-site/zh/guides/reporters.mdx +1 -1
- package/docs-site/zh/guides/results-data.mdx +224 -0
- package/docs-site/zh/guides/runner.mdx +1 -1
- package/docs-site/zh/guides/sandbox-agent.mdx +2 -2
- package/docs-site/zh/guides/viewing-results.mdx +147 -77
- package/docs-site/zh/guides/write-experiment.mdx +9 -9
- package/docs-site/zh/guides/write-send.mdx +6 -6
- package/docs-site/zh/index.mdx +1 -1
- package/docs-site/zh/introduction.mdx +1 -1
- package/docs-site/zh/reference/cli.mdx +10 -2
- package/docs-site/zh/reference/define-agent.mdx +5 -5
- package/docs-site/zh/reference/define-config.mdx +1 -1
- package/docs-site/zh/reference/define-eval.mdx +3 -3
- package/package.json +3 -2
- package/src/agents/ai-sdk.test.ts +1 -1
- package/src/agents/ai-sdk.ts +2 -2
- package/src/agents/bub.ts +14 -4
- package/src/agents/claude-code.ts +1 -1
- package/src/agents/codex.ts +2 -2
- package/src/agents/streaming.test.ts +1 -1
- package/src/agents/types.ts +4 -4
- package/src/agents/ui-message-stream.test.ts +1 -1
- package/src/cli.ts +104 -14
- package/src/context/context.test.ts +1 -1
- package/src/context/context.ts +3 -3
- package/src/context/session.ts +2 -2
- package/src/context/types.ts +2 -2
- package/src/i18n/en.ts +29 -7
- package/src/i18n/zh-CN.ts +24 -2
- package/src/report/aggregate.ts +79 -50
- package/src/report/components.tsx +190 -0
- package/src/report/compute.ts +149 -84
- package/src/report/default-report.tsx +222 -0
- package/src/report/dual-face.test.tsx +527 -0
- package/src/report/format.ts +29 -0
- package/src/report/index.ts +79 -24
- package/src/report/load.ts +67 -0
- package/src/report/metrics.ts +2 -1
- package/src/report/param.ts +18 -0
- package/src/report/primitives.tsx +117 -0
- package/src/report/react/CaseList.tsx +2 -2
- package/src/report/react/DeltaTable.tsx +2 -2
- package/src/report/react/MetricBars.tsx +109 -0
- package/src/report/react/MetricLine.tsx +200 -0
- package/src/report/react/MetricMatrix.tsx +1 -1
- package/src/report/react/MetricScatter.tsx +1 -1
- package/src/report/react/MetricTable.tsx +1 -1
- package/src/report/react/RunOverview.tsx +4 -4
- package/src/report/react/Scoreboard.tsx +3 -3
- package/src/report/react/cell.tsx +1 -1
- package/src/report/react/fixtures.ts +98 -36
- package/src/report/react/format.ts +3 -28
- package/src/report/react/index.tsx +41 -24
- package/src/report/react/render.test.tsx +8 -8
- package/src/report/react/styles.css +77 -0
- package/src/report/report.test.ts +248 -128
- package/src/report/report.ts +69 -0
- package/src/report/text/faces.ts +356 -0
- package/src/report/text/layout.ts +125 -0
- package/src/report/text/plot.ts +127 -0
- package/src/report/tree.ts +220 -0
- package/src/report/types.ts +75 -27
- package/src/report/web.ts +40 -0
- package/src/results/copy.ts +95 -41
- package/src/results/format.ts +12 -11
- package/src/results/index.ts +34 -17
- package/src/results/open.ts +201 -81
- package/src/results/results.test.ts +595 -191
- package/src/results/select.ts +107 -55
- package/src/results/types.ts +137 -45
- package/src/results/writer.ts +258 -0
- package/src/runner/attempt.ts +3 -3
- package/src/runner/fingerprint.ts +1 -1
- package/src/runner/reporters/artifacts.ts +38 -78
- package/src/runner/reporters/braintrust.test.ts +2 -2
- package/src/runner/reporters/braintrust.ts +3 -3
- package/src/runner/run.ts +1 -1
- package/src/runner/types.ts +18 -12
- package/src/show/compose.ts +215 -0
- package/src/show/index.ts +263 -0
- package/src/show/render.ts +456 -0
- package/src/show/show.test.ts +505 -0
- package/src/view/app/App.tsx +85 -24
- package/src/view/app/components/CostScoreChart.tsx +29 -10
- package/src/view/app/components/ExperimentTable.tsx +27 -9
- package/src/view/app/components/GroupSelector.tsx +1 -1
- package/src/view/app/i18n.ts +18 -3
- package/src/view/app/lib/attempt-route.test.ts +9 -9
- package/src/view/app/lib/attempt-route.ts +5 -5
- package/src/view/app/lib/outcome.ts +1 -3
- package/src/view/app/lib/rows.ts +95 -15
- package/src/view/app/main.tsx +18 -6
- package/src/view/app/pages/RunsPage.tsx +4 -7
- package/src/view/app/pages/TracesPage.tsx +3 -8
- package/src/view/app/types.ts +50 -6
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +20 -20
- package/src/view/data.test.ts +254 -0
- package/src/view/data.ts +336 -0
- package/src/view/index.ts +64 -10
- package/src/view/server.ts +31 -9
- package/src/view/shared/types.ts +47 -41
- package/src/view/styles.css +5 -0
- package/src/view/template.html +1 -0
- package/src/view/view-report.test.ts +308 -0
- package/docs-site/.mintignore +0 -13
- package/docs-site/AGENTS.md +0 -46
- package/docs-site/concepts/adapter.mdx +0 -287
- package/docs-site/concepts/assert.mdx +0 -243
- package/docs-site/concepts/drive.mdx +0 -118
- package/docs-site/concepts/evals.mdx +0 -108
- package/docs-site/concepts/experiment.mdx +0 -37
- package/docs-site/concepts/hitl.mdx +0 -83
- package/docs-site/concepts/judge.mdx +0 -129
- package/docs-site/concepts/overview.mdx +0 -98
- package/docs-site/concepts/tier.mdx +0 -42
- package/docs-site/docs-ref/00-index.md +0 -23
- package/docs-site/docs-ref/01-page-types.md +0 -43
- package/docs-site/docs-ref/03-style-rules.md +0 -45
- package/docs-site/docs-ref/04-code-examples.md +0 -37
- package/docs-site/docs-ref/05-dx-failure-paths.md +0 -45
- package/docs-site/docs-ref/06-checklists.md +0 -53
- package/docs-site/docs.json +0 -256
- package/docs-site/example/ai-agent-application.mdx +0 -148
- package/docs-site/example/claude-code-codex-plugin.mdx +0 -162
- package/docs-site/example/claude-code-codex-skill.mdx +0 -147
- package/docs-site/example/showcase.mdx +0 -39
- package/docs-site/example/tier1-ai-sdk-v7.mdx +0 -136
- package/docs-site/example/tier1-claude-sdk.mdx +0 -119
- package/docs-site/example/tier1-codex-sdk.mdx +0 -111
- package/docs-site/example/tier1-langgraph.mdx +0 -119
- package/docs-site/example/tier1-pi-sdk.mdx +0 -119
- package/docs-site/favicon.svg +0 -5
- package/docs-site/github-diff.css +0 -135
- package/docs-site/github-diff.js +0 -11
- package/docs-site/guides/authoring.mdx +0 -129
- package/docs-site/guides/ci-integration.mdx +0 -97
- package/docs-site/guides/connect-otel.mdx +0 -209
- package/docs-site/guides/connect-your-agent.mdx +0 -221
- package/docs-site/guides/dataset-fanout.mdx +0 -98
- package/docs-site/guides/experiments.mdx +0 -71
- package/docs-site/guides/fixtures.mdx +0 -147
- package/docs-site/guides/reporters.mdx +0 -113
- package/docs-site/guides/runner.mdx +0 -79
- package/docs-site/guides/sandbox-agent.mdx +0 -138
- package/docs-site/guides/sandbox-backends.mdx +0 -64
- package/docs-site/guides/scoring-guide.mdx +0 -92
- package/docs-site/guides/viewing-results.mdx +0 -195
- package/docs-site/guides/write-experiment.mdx +0 -81
- package/docs-site/guides/write-send.mdx +0 -347
- package/docs-site/index.mdx +0 -181
- package/docs-site/introduction.mdx +0 -141
- package/docs-site/quickstart.mdx +0 -136
- package/docs-site/reference/builtin-agents.mdx +0 -161
- package/docs-site/reference/capabilities.mdx +0 -76
- package/docs-site/reference/cli.mdx +0 -120
- package/docs-site/reference/define-agent.mdx +0 -168
- package/docs-site/reference/define-config.mdx +0 -41
- package/docs-site/reference/define-eval.mdx +0 -160
- package/docs-site/reference/events.mdx +0 -131
- package/docs-site/reference/expect.mdx +0 -112
- package/docs-site/tracker.js +0 -8
- package/src/report/react/data.ts +0 -17
- package/src/view/aggregate.ts +0 -103
- package/src/view/loader.test.ts +0 -61
- package/src/view/loader.ts +0 -257
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "NiceEval: an eval tool for agents"
|
|
3
|
-
sidebarTitle: "Introduction"
|
|
4
|
-
description: "NiceEval helps teams measure, evaluate, and improve AI in production: compare models, iterate on agents, catch regressions, and improve with real data."
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
[NiceEval](https://niceeval.com/) is an agent eval tool that helps teams measure, evaluate, and improve AI in production. With NiceEval, teams can compare models, iterate on agents, catch regressions, and keep improving their AI applications using real user data. It aims for low-friction DX: most projects can integrate in about 10 minutes, write the first eval, and start checking agent behavior reliably.
|
|
8
|
-
|
|
9
|
-
NiceEval is local-first at its core: your evals run in your own environment, so you don't need to create an account or connect to a platform before running your first eval. When your team needs to share eval results or track regressions, you can push a Report to platforms like BrainTrust, or export a custom report.
|
|
10
|
-
|
|
11
|
-
It can evaluate coding-agent plugins, hooks, and Skills for Claude Code / Codex, and it can also evaluate your own AI agent application directly. Whether your agent is built on AI SDK, LangGraph, Pi, or a custom loop, you can connect it through an Adapter into the same eval surface.
|
|
12
|
-
|
|
13
|
-
## 30 seconds of DX
|
|
14
|
-
|
|
15
|
-
An eval is just a TypeScript file: send the agent a message, assert on what it did.
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
// evals/eval-tool-call.eval.ts
|
|
19
|
-
import { defineEval } from "niceeval";
|
|
20
|
-
|
|
21
|
-
export default defineEval({
|
|
22
|
-
description: "Verify the agent calls the weather tool and answers from its result",
|
|
23
|
-
|
|
24
|
-
async test(t) {
|
|
25
|
-
const turn = await t.send("What's the weather in Beijing today?");
|
|
26
|
-
t.succeeded();
|
|
27
|
-
|
|
28
|
-
await t.group("calls get_weather with the right city", () => {
|
|
29
|
-
t.calledTool("get_weather", { input: { city: "Beijing" } });
|
|
30
|
-
t.messageIncludes(/°C|sunny|cloudy|rain/);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const second = await t.send("What about Shanghai tomorrow?");
|
|
34
|
-
second.messageIncludes("Shanghai");
|
|
35
|
-
|
|
36
|
-
t.judge.autoevals
|
|
37
|
-
.closedQA("Does the reply use the tool's weather data instead of making up a temperature?")
|
|
38
|
-
.atLeast(0.7);
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
```sh
|
|
44
|
-
pnpm exec niceeval exp local eval-tool-call # run only eval-tool-call under the local experiment
|
|
45
|
-
pnpm exec niceeval view # view eval results
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Why NiceEval when DeepEval, LangFuse, and BrainTrust already exist
|
|
49
|
-
|
|
50
|
-
NiceEval is an Agent-Native eval tool. The Dataset / golden pattern of building an Input and an Expected Output doesn't fit real agent evaluation — agents today need to be checked at a finer grain: multi-turn conversations, multi-agent collaboration, tool calls, skill loading. NiceEval is designed for exactly that: assertions land directly on tool calls, message content, structured output, and usage, instead of requiring you to hand-build a golden dataset first.
|
|
51
|
-
|
|
52
|
-
Tools like Langfuse and Braintrust lean more toward tracing and observability. For the path of "write evals, run evals, inspect results", that is often heavier than necessary. NiceEval is designed around that path from the start, with a simpler developer experience. The two aren't mutually exclusive either: NiceEval coexists with LangFuse and BrainTrust — use them for tracing, or upload eval results to both.
|
|
53
|
-
|
|
54
|
-
## What you can evaluate
|
|
55
|
-
|
|
56
|
-
<CardGroup cols={2}>
|
|
57
|
-
<Card title="Coding agents" icon="code">
|
|
58
|
-
Put Claude Code, Codex, or bub into a sandbox, give them a task, then verify the result with real tests and file assertions.
|
|
59
|
-
</Card>
|
|
60
|
-
<Card title="Your own AI agent application" icon="globe">
|
|
61
|
-
Connect to your app directly and assert on replies, tool calls, and structured outputs.
|
|
62
|
-
</Card>
|
|
63
|
-
</CardGroup>
|
|
64
|
-
|
|
65
|
-
## Two integration modes
|
|
66
|
-
|
|
67
|
-
<Tabs>
|
|
68
|
-
<Tab title="Sandbox mode">
|
|
69
|
-
Fits coding agents such as Codex and Claude Code that need a real file system and command execution.
|
|
70
|
-
|
|
71
|
-
```text
|
|
72
|
-
evals/*.eval.ts
|
|
73
|
-
│
|
|
74
|
-
▼
|
|
75
|
-
┌────────────┐
|
|
76
|
-
│ niceeval │
|
|
77
|
-
└────────────┘
|
|
78
|
-
│
|
|
79
|
-
│ Adapter (official)
|
|
80
|
-
▼
|
|
81
|
-
┌───────────────────────────────┐
|
|
82
|
-
│ Docker Sandbox │
|
|
83
|
-
│ ┌───────────────────────┐ │
|
|
84
|
-
│ │ Codex / Claude Code / │ │
|
|
85
|
-
│ │ apps that need an │ │
|
|
86
|
-
│ │ isolated workspace │ │
|
|
87
|
-
│ └───────────────────────┘ │
|
|
88
|
-
└───────────────────────────────┘
|
|
89
|
-
```
|
|
90
|
-
</Tab>
|
|
91
|
-
<Tab title="Direct mode">
|
|
92
|
-
Fits your own AI agent app and does not require Docker.
|
|
93
|
-
|
|
94
|
-
```text
|
|
95
|
-
evals/*.eval.ts
|
|
96
|
-
│
|
|
97
|
-
▼
|
|
98
|
-
┌────────────┐
|
|
99
|
-
│ niceeval │
|
|
100
|
-
└────────────┘
|
|
101
|
-
│
|
|
102
|
-
│ Adapter (official or custom)
|
|
103
|
-
▼
|
|
104
|
-
┌────────────────────────────┐
|
|
105
|
-
│ Your own AI agent app │
|
|
106
|
-
│ (AI SDK · LangGraph · Pi · │
|
|
107
|
-
│ custom agent loop, etc.) │
|
|
108
|
-
└────────────────────────────┘
|
|
109
|
-
```
|
|
110
|
-
</Tab>
|
|
111
|
-
</Tabs>
|
|
112
|
-
|
|
113
|
-
## Core concepts at a glance
|
|
114
|
-
|
|
115
|
-
| Concept | In one line |
|
|
116
|
-
|---|---|
|
|
117
|
-
| Eval | A test case: written in `evals/*.eval.ts`, describing what to check. |
|
|
118
|
-
| Experiment | A checked-in run configuration: which Adapter, which model, which flags. |
|
|
119
|
-
| Adapter | The layer that connects to the system under test: implement one `send`, get back a standard event stream. |
|
|
120
|
-
| Sandbox | Only needed for coding agents that require an isolated workspace; a direct web agent doesn't need one. |
|
|
121
|
-
| Tier | Three levels of Adapter integration effort: Tier 1 wires up `send` only, Tier 2 adds OTel for a call waterfall, Tier 3 makes invasive changes for feature A/B testing. |
|
|
122
|
-
|
|
123
|
-
See the full glossary in the [architecture overview](/concepts/overview).
|
|
124
|
-
|
|
125
|
-
## Start from your scenario
|
|
126
|
-
|
|
127
|
-
<CardGroup cols={3}>
|
|
128
|
-
<Card title="Evaluate a Claude Code / Codex plugin" icon="puzzle-piece" href="/example/claude-code-codex-plugin">
|
|
129
|
-
Drop the plugin into a real workspace and compare pass rate, cost, latency, and behavior with and without it.
|
|
130
|
-
</Card>
|
|
131
|
-
<Card title="Evaluate a Claude Code / Codex Skill" icon="wand-magic-sparkles" href="/example/claude-code-codex-skill">
|
|
132
|
-
Compare coding-agent tasks with and without the Skill to measure whether it actually improves results.
|
|
133
|
-
</Card>
|
|
134
|
-
<Card title="Evaluate your own AI agent application" icon="robot" href="/example/ai-agent-application">
|
|
135
|
-
Connect directly to your app's API and assert on tool calls, image understanding, and multi-turn conversations.
|
|
136
|
-
</Card>
|
|
137
|
-
</CardGroup>
|
|
138
|
-
|
|
139
|
-
## What to read next
|
|
140
|
-
|
|
141
|
-
[Quickstart](/quickstart) walks through installation, writing the first eval, and getting a report.
|
package/docs-site/quickstart.mdx
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Set up evals for your agent project"
|
|
3
|
-
sidebarTitle: "Quickstart"
|
|
4
|
-
description: "Install NiceEval, write three files, and run the first eval against your own application in about 10 minutes."
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
Connecting your own application takes three files: an **adapter** that calls your app, an **experiment** that chooses what to run, and an **eval** that declares what must be true. Below is the shortest complete path. Scenario-specific guides and the coding-agent handoff path come after it.
|
|
8
|
-
|
|
9
|
-
## Let AI wire it up for you (recommended)
|
|
10
|
-
|
|
11
|
-
<Steps>
|
|
12
|
-
<Step title="Install">
|
|
13
|
-
```text
|
|
14
|
-
READ https://niceeval.com/INIT.md and install niceeval for this repo.
|
|
15
|
-
```
|
|
16
|
-
</Step>
|
|
17
|
-
<Step title="Run evals">
|
|
18
|
-
```bash
|
|
19
|
-
pnpm exec niceeval exp <experiment-name>
|
|
20
|
-
```
|
|
21
|
-
</Step>
|
|
22
|
-
<Step title="View results">
|
|
23
|
-
```bash
|
|
24
|
-
pnpm exec niceeval view
|
|
25
|
-
```
|
|
26
|
-
</Step>
|
|
27
|
-
</Steps>
|
|
28
|
-
|
|
29
|
-
## Manual integration
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
pnpm add -D niceeval
|
|
33
|
-
pnpm exec niceeval init
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Adapter
|
|
37
|
-
|
|
38
|
-
The adapter is the layer between your agent and NiceEval. There are multiple integration tiers, from non-invasive to partially instrumented, which unlock more useful eval features. See [Tier](/concepts/tier).
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
// agents/my-bot.ts
|
|
42
|
-
import { defineAgent } from "niceeval/adapter";
|
|
43
|
-
|
|
44
|
-
export default defineAgent({
|
|
45
|
-
name: "my-bot",
|
|
46
|
-
async send(input, ctx) {
|
|
47
|
-
const r = await fetch("http://localhost:3000/chat", {
|
|
48
|
-
method: "POST",
|
|
49
|
-
headers: { "content-type": "application/json" },
|
|
50
|
-
body: JSON.stringify({ message: input.text, model: ctx.model }),
|
|
51
|
-
signal: ctx.signal,
|
|
52
|
-
});
|
|
53
|
-
const body = await r.json();
|
|
54
|
-
return {
|
|
55
|
-
status: r.ok ? "completed" : "failed",
|
|
56
|
-
events: [{ type: "message", role: "assistant", text: body.reply }],
|
|
57
|
-
};
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### Experiment
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
// experiments/my-bot.ts
|
|
66
|
-
import { defineExperiment } from "niceeval";
|
|
67
|
-
import myBot from "../agents/my-bot.ts";
|
|
68
|
-
|
|
69
|
-
export default defineExperiment({
|
|
70
|
-
description: "my-bot baseline",
|
|
71
|
-
model: "gpt-4o",
|
|
72
|
-
agent: myBot,
|
|
73
|
-
runs: 1,
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Eval
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
// evals/refund-policy.eval.ts
|
|
81
|
-
import { defineEval } from "niceeval";
|
|
82
|
-
import { includes } from "niceeval/expect";
|
|
83
|
-
|
|
84
|
-
export default defineEval({
|
|
85
|
-
description: "Refund policy question",
|
|
86
|
-
async test(t) {
|
|
87
|
-
await t.send("What is your refund policy?");
|
|
88
|
-
t.succeeded();
|
|
89
|
-
t.check(t.reply, includes("30 days"));
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
pnpm exec niceeval exp my-bot
|
|
96
|
-
pnpm exec niceeval view
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
At this point the first eval is running.
|
|
100
|
-
|
|
101
|
-
This minimal adapter is **single-turn**. The second turn will not remember the first until you teach the app how to continue conversation history. Multi-turn sessions, tool-call events that unlock `t.calledTool()`, HITL, and tracing are all optional increments you add later. Follow [Connect Your Agent](/guides/connect-your-agent) step by step. Existing evals do not need to be rewritten.
|
|
102
|
-
|
|
103
|
-
## Start from a scenario
|
|
104
|
-
|
|
105
|
-
If you already know what you want to evaluate, jump straight into the matching path:
|
|
106
|
-
|
|
107
|
-
<CardGroup cols={3}>
|
|
108
|
-
<Card title="Eval your Claude Code / Codex plugin" icon="plug" href="/example/claude-code-codex-plugin">
|
|
109
|
-
For plugins, hooks, MCP servers, and project-level coding-agent extensions.
|
|
110
|
-
</Card>
|
|
111
|
-
<Card title="Eval your Claude Code / Codex Skill" icon="wand-magic-sparkles" href="/example/claude-code-codex-skill">
|
|
112
|
-
For verifying whether a Skill is triggered, follows the intended workflow, and genuinely improves task success.
|
|
113
|
-
</Card>
|
|
114
|
-
<Card title="Eval your AI agent application" icon="globe" href="/example/ai-agent-application">
|
|
115
|
-
For full examples built on AI SDK, LangGraph, Pi, or a custom agent app.
|
|
116
|
-
</Card>
|
|
117
|
-
</CardGroup>
|
|
118
|
-
|
|
119
|
-
## Put it in CI
|
|
120
|
-
|
|
121
|
-
```yaml
|
|
122
|
-
name: evals
|
|
123
|
-
on: [pull_request]
|
|
124
|
-
jobs:
|
|
125
|
-
evals:
|
|
126
|
-
runs-on: ubuntu-latest
|
|
127
|
-
steps:
|
|
128
|
-
- uses: actions/checkout@v4
|
|
129
|
-
- uses: actions/setup-node@v4
|
|
130
|
-
- run: npm ci
|
|
131
|
-
- run: npm exec niceeval exp my-bot
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
<Tip>
|
|
135
|
-
Next, read [Authoring Evals](/guides/authoring) and [Scoring Guide](/guides/scoring-guide), then replace the example with your real behavior.
|
|
136
|
-
</Tip>
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Built-in agent capabilities reference"
|
|
3
|
-
sidebarTitle: "Built-in Agent Capabilities"
|
|
4
|
-
description: "Which capabilities NiceEval's built-in claude-code, codex, and bub adapters implement, which assertions they map to, and known limits."
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
The three built-in sandbox agents (`claude-code`, `codex`, `bub`) do not implement exactly the same capabilities — capabilities have no declaration layer and are proven entirely by construction and actual behavior, see the [capabilities reference](/reference/capabilities). This page inventories what each built-in agent does per capability, plus two built-ins for connecting AI SDK apps: the non-invasive HTTP adapter `uiMessageStreamAgent` (with HITL) and the result converter `fromAiSdk`.
|
|
8
|
-
|
|
9
|
-
## Capability overview
|
|
10
|
-
|
|
11
|
-
| Capability | Corresponding assertions / API |
|
|
12
|
-
|---|---|
|
|
13
|
-
| Send/receive messages (base contract) | `t.send()` (can be called multiple times), `t.reply`, `turn.outputEquals` / `outputMatches`, status-based `t.succeeded()` |
|
|
14
|
-
| Event stream completeness | The whole scoped assertion set: `calledTool` / `toolOrder` / `usedNoTools` / `maxToolCalls` / `messageIncludes` / `noFailedActions` / `event` / `eventOrder`, etc.; **negative assertions (`notCalledTool` and friends) are only trustworthy with a complete event stream** |
|
|
15
|
-
| Session continuation | Cross-turn memory assertions, `t.newSession()` session isolation |
|
|
16
|
-
| HITL (human in the loop) | `t.parked()`, `t.requireInputRequest()`, `t.respond()` / `t.respondAll()` |
|
|
17
|
-
| `tracing` | `EvalResult.trace`, the call waterfall in `niceeval view` |
|
|
18
|
-
|
|
19
|
-
## What each built-in agent implements
|
|
20
|
-
|
|
21
|
-
| Agent | Send/receive | Event stream | Session continuation | HITL | tracing | Notes |
|
|
22
|
-
|---|---|---|---|---|---|---|
|
|
23
|
-
| `claude-code` | ✅ | ✅ | ✅ (`claude --resume <id>`) | ❌ | ❌ | Built-in parser emits `compaction` events automatically; `t.event("compaction")` works |
|
|
24
|
-
| `codex` | ✅ | ✅ | ✅ (`codex exec resume <id>`) | ❌ | ✅ (`http/json` → OTLP) | Built-in parser emits `compaction` events automatically |
|
|
25
|
-
| `bub` | ✅ | ✅ | ✅ (`--session-id` + tape continuation) | ❌ | ✅ (`http/protobuf` → OTLP) | Built-in parser emits `compaction` events automatically |
|
|
26
|
-
|
|
27
|
-
All three are constructed with `defineSandboxAgent` (`Agent.kind` is always `"sandbox"`), so `t.sandbox.fileChanged()` / diff assertions, file IO, and command execution work on all three agents regardless of this table.
|
|
28
|
-
|
|
29
|
-
<Warning>
|
|
30
|
-
None of the three built-in sandbox agents currently support HITL: their `send` only returns `"completed"` / `"failed"`, never `"waiting"`, and never emits `input.requested` events. When you need `t.respond()` / `t.requireInputRequest()`: if the system under test is an AI SDK `useChat` backend, use the built-in `uiMessageStreamAgent` below (v7 tool approval maps natively to HITL); for anything else write your own adapter: implement the `waiting` status + `input.requested` event + resume handoff — [`examples/zh/tier1/pi-sdk`](https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/tier1/pi-sdk) / [`examples/zh/tier1/claude-sdk`](https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/tier1/claude-sdk) both have ready-made hand-written HITL references.
|
|
31
|
-
</Warning>
|
|
32
|
-
|
|
33
|
-
## Per-agent details
|
|
34
|
-
|
|
35
|
-
### claude-code
|
|
36
|
-
|
|
37
|
-
- Connection: spawns `claude --print --dangerously-skip-permissions` in the sandbox and reads back the latest transcript from `~/.claude/projects/**/*.jsonl`.
|
|
38
|
-
- Session continuation: appends `--resume <id>` when `ctx.session.id` has a value; the session id parsed from the transcript is written back via `ctx.session.capture()`.
|
|
39
|
-
- Auth: `ANTHROPIC_API_KEY` (overridable via the `apiKey` option), optional `ANTHROPIC_BASE_URL`.
|
|
40
|
-
- Supports `mcpServers` and `skills` (GitHub `org/repo`) options.
|
|
41
|
-
- Does not declare `tracing`: no OTLP receiver is started and `EvalResult.trace` is empty.
|
|
42
|
-
|
|
43
|
-
### codex
|
|
44
|
-
|
|
45
|
-
- Connection: runs `codex exec --json` in the sandbox (`codex exec resume <id> --json` when continuing), using stdout JSONL as the transcript.
|
|
46
|
-
- Auth: `CODEX_API_KEY` (not `OPENAI_API_KEY`), optional `CODEX_BASE_URL` for OpenAI-compatible proxies.
|
|
47
|
-
- `tracing` is configured through the `[otel.trace_exporter.otlp-http]` section of `~/.codex/config.toml`, protocol `http/json`.
|
|
48
|
-
- Supports `mcpServers` and `skills` options.
|
|
49
|
-
|
|
50
|
-
### bub
|
|
51
|
-
|
|
52
|
-
- Connection: runs `bub run` + `--session-id` in the sandbox and reads the tape from `~/.bub/tapes/<hash>.jsonl` as the transcript.
|
|
53
|
-
- Auth: `BUB_API_KEY` + `BUB_API_BASE` (OpenAI-compatible proxy).
|
|
54
|
-
- `tracing` is injected via environment variables (`OTEL_EXPORTER_OTLP_TRACES_ENDPOINT`, etc.), protocol `http/protobuf`.
|
|
55
|
-
- Supports the `pythonPlugins` option.
|
|
56
|
-
- Installed via `uv tool install` (not an npm package); the first install builds a checkpoint cache to speed up later sandboxes.
|
|
57
|
-
|
|
58
|
-
## `uiMessageStreamAgent`: built-in non-invasive adapter for AI SDK apps (with HITL)
|
|
59
|
-
|
|
60
|
-
`uiMessageStreamAgent` (exported from `niceeval/adapter`) talks non-invasively to an HTTP endpoint speaking the [UI Message Stream protocol](https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol) (the standard SSE of AI SDK `useChat` backends) — it only `fetch`es, never imports app code, so it connects wherever the app is deployed:
|
|
61
|
-
|
|
62
|
-
```ts
|
|
63
|
-
import { uiMessageStreamAgent } from "niceeval/adapter";
|
|
64
|
-
|
|
65
|
-
export default uiMessageStreamAgent({
|
|
66
|
-
name: "my-assistant",
|
|
67
|
-
url: "https://my-app.example.com/api/chat",
|
|
68
|
-
body: (ctx) => ({ model: ctx.model }), // When the app supports request-level model selection, model comparison needs zero changes
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Capabilities it handles for you:
|
|
73
|
-
|
|
74
|
-
- **Send/receive + event stream**: SSE frames are reduced by the `ai` package's official framework-agnostic reducer `readUIMessageStream` (the same one inside `useChat`); tool calls/results/message text are built directly from message parts — **the app is not required to wire up OTel**.
|
|
75
|
-
- **Session continuation**: the protocol is server-stateless, "client carries the full history" — the factory stores the whole `UIMessage[]` with `ctx.session.history()` and replays it verbatim each turn; a new conversation line (after `t.newSession()`) gets an empty history.
|
|
76
|
-
- **HITL**: AI SDK v7 tool approval (tools with `needsApproval: true`) maps natively — when a part stops at `approval-requested`, the whole turn is `status: "waiting"` + `input.requested`; `t.respond("approve" / "deny")` is translated into `approval-responded`, rewriting that part in place and resending `messages` verbatim to trigger the server to continue (identical protocol behavior to a real frontend's `addToolApprovalResponse()` + `sendMessage()`; there is no separate approve endpoint). Denied calls land in the event stream as `rejected`, and by default carry a "do not retry" reason (`denyReason` can override) — without one, models often resend the very same call.
|
|
77
|
-
- **usage / waterfall**: UI Message Stream protocol frames carry no usage, so usage assertions like `t.maxTokens` have no data by default on this built-in (an app putting usage into message metadata is the app's own protocol extension). The waterfall is a separate matter: when the app has OTel instrumentation (e.g. the official `@ai-sdk/otel`), send spans to NiceEval per [OTel integration](/guides/connect-otel) and `niceeval view` gets a full waterfall — spans only feed the waterfall, never assertions.
|
|
78
|
-
|
|
79
|
-
You need `ai` installed in the eval project (optional peer dependency; the protocol reducer comes from it). Full runnable example: [`examples/zh/tier1/ai-sdk-v7`](https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/tier1/ai-sdk-v7).
|
|
80
|
-
|
|
81
|
-
## SDK event stream converters: `fromClaudeSdkMessages` / `fromPiAgentEvents` / `fromCodexThreadEvents`
|
|
82
|
-
|
|
83
|
-
Each agent SDK's streaming protocol is a generic protocol defined by the SDK, not some app's private format — the native-frame-to-standard-event mapping knowledge lives in the official package (exported from `niceeval/adapter`), so when writing your own non-invasive adapter only the **transport glue** remains (which endpoint the app puts the stream on, which endpoint approvals go through). Driving the loop frame by frame is itself an official piece (`driveFrameStream`, see the next section), so no hand-written `for` loop:
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
import { sseJsonFrames, fromClaudeSdkMessages, driveFrameStream } from "niceeval/adapter";
|
|
87
|
-
|
|
88
|
-
const frames = sseJsonFrames<SDKMessage>(res.body); // Standard SSE → per-frame JSON
|
|
89
|
-
const stream = fromClaudeSdkMessages(); // SDKMessage → standard events
|
|
90
|
-
return driveFrameStream(frames, stream, ctx); // Feed stream.add() frame by frame, summarize into a Turn
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
- **`fromClaudeSdkMessages`** (Claude Agent SDK `SDKMessage`): `assistant` text/tool_use blocks, `user` tool_result blocks, `system`/`permission_denied` (→ `rejected`), `result` usage; `markRejected()` registers denied calls.
|
|
94
|
-
- **`fromPiAgentEvents`** (pi-agent-core `AgentEvent`): text/thinking/usage from `message_end`, tool pairs from `tool_execution_start/end`.
|
|
95
|
-
- **`fromCodexThreadEvents`** (Codex SDK `ThreadEvent`): message-class frames from `agent_message`/`reasoning`, tool items (`command_execution` / `mcp_tool_call` / `file_change` / `web_search` → `action.called` + `action.result`), usage from `turn.completed`, error frames. The waterfall needs the codex CLI's native OTLP (the `[otel]` block in config.toml) sending spans to NiceEval, normalized with the official `mapCodexSpans`.
|
|
96
|
-
|
|
97
|
-
Runnable references: the claude-sdk / pi-sdk / codex-sdk adapters under [`examples/zh/tier1`](https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/tier1) are exactly these three converters + `driveFrameStream` + transport glue.
|
|
98
|
-
|
|
99
|
-
## Generic "assembly" pieces: `driveFrameStream` / `deltaStream` + `ctx.session`
|
|
100
|
-
|
|
101
|
-
Inside a hand-written send, only three truly independent parts exist: transport (how to send), reduce (raw data → events, handled by the converters above), and orchestration (session continuation + HITL pause/resume). The third part is protocol-agnostic, pure control-flow pattern — the accessors for session continuation (`id`/`capture`, `history`) and the HITL paused-turn scene (`hold`/`take`) hang directly off `ctx.session` (`AgentSession`, see the [Adapter concept](/concepts/adapter#context-agentcontext)); nothing to import separately and nothing to declare. What actually remains reusable across protocols is just two official pieces:
|
|
102
|
-
|
|
103
|
-
| Piece | What it solves |
|
|
104
|
-
|---|---|
|
|
105
|
-
| `driveFrameStream(cursor, reducer, ctx, onFrame?)` | Feeds the reducer frame by frame, handles transport frames, detects the HITL pause signal — the loop itself collapsed into one function. On pause the `cursor` is not closed: store it with `ctx.session.hold(cursor)` inside `onFrame`, and on the answer turn `ctx.session.take()` retrieves it and keeps reading, without issuing a new request |
|
|
106
|
-
| `deltaStream(spec)` | Generic accumulator for token-by-token / per-argument deltas — for protocols with "no whole-chunk landing frames" like raw OpenAI/Anthropic streaming APIs or your own hand-rolled token-by-token backend; declare "which operation this frame belongs to" (text delta / tool-argument delta / finalize) and it manages assembly timing itself |
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
// First thing in send: check for a held scene (HITL answer turn)
|
|
110
|
-
const pending = ctx.session.take<Pending>();
|
|
111
|
-
if (pending) {
|
|
112
|
-
// ...match verdicts from input.responses, hand the approval back to the app...
|
|
113
|
-
return driveFrameStream(pending.cursor, pending.stream, ctx, onFrame);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Normal turn: session continuation via ctx.session.id / capture; on pause, ctx.session.hold stores the cursor
|
|
117
|
-
return driveFrameStream(cursor, stream, ctx, (frame, derived) => {
|
|
118
|
-
ctx.session.capture(stream.sessionId);
|
|
119
|
-
const gated = derived.find((e) => e.type === "action.called" && e.name === GATED_TOOL_NAME);
|
|
120
|
-
if (gated?.type === "action.called") {
|
|
121
|
-
ctx.session.hold<Pending>({ cursor, stream, toolUseId: gated.callId });
|
|
122
|
-
return { pause: { id: gated.callId, action: GATED_TOOL_NAME, options: [{ id: "approve" }, { id: "deny" }] } };
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
The three Tier 1 examples (claude-sdk and pi-sdk use `driveFrameStream` + `ctx.session.hold`/`take`; codex-sdk uses only `driveFrameStream`) have been rewritten on these pieces — the adapters are down to transport plus the "does this frame need extra handling" decision in `onFrame`, with no hand-written loops or module-level Maps.
|
|
128
|
-
|
|
129
|
-
## `fromAiSdk`: AI SDK result → event stream converter
|
|
130
|
-
|
|
131
|
-
`fromAiSdk` (exported from `niceeval/adapter`) is used inside adapters you write yourself (e.g. server-side direct construction for an HTTP web agent, see `examples/zh/ai-sdk/`). It maps an AI SDK `generateText` / `streamText` result into `{ events, usage, status }` — exact `toolCallId` pairing, order-faithful, `tool-error` mapped to a failed `action.result`, usage aggregation (v4 / v5 / v7 field drift all covered) — laid straight into the `Turn`:
|
|
132
|
-
|
|
133
|
-
```ts
|
|
134
|
-
import { defineAgent, fromAiSdk } from "niceeval/adapter";
|
|
135
|
-
|
|
136
|
-
export default defineAgent({
|
|
137
|
-
name: "my-ai-sdk-agent",
|
|
138
|
-
async send(input) {
|
|
139
|
-
const result = await generateText({ model, tools, prompt: input.text });
|
|
140
|
-
return { ...fromAiSdk(result), data: result.text };
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
The `status` comes from the converter: `"waiting"` when there is a tool approval pending human review (with an `input.requested` event attached), otherwise `"completed"`. Session continuation (multi-turn resume), handing HITL verdicts back, and tracing depend on how you write your own `send`.
|
|
146
|
-
|
|
147
|
-
## How to choose
|
|
148
|
-
|
|
149
|
-
- The system under test is an AI SDK app (`useChat` backend): the built-in `uiMessageStreamAgent` connects non-invasively with zero mapping (including HITL).
|
|
150
|
-
- The system under test is another agent system (HTTP / gRPC service): connect non-invasively and hand-write the event mapping (official converters cover most of it) — all five examples under [`examples/zh/tier1`](https://github.com/CorrectRoadH/niceeval/tree/main/examples/zh/tier1) take this path; if the app already has OTel instrumentation, wire it up for a bonus waterfall.
|
|
151
|
-
- You need the call waterfall (the trace in `niceeval view`): all three of `claude-code` / `codex` / `bub` emit OTLP themselves (claude-code via the CLI's beta telemetry; its spans carry structure and timing only); adapters you write yourself just declare `tracing` (see the OTel guide).
|
|
152
|
-
- You need human approval / multi-step confirmation (HITL): `uiMessageStreamAgent` supports it natively (AI SDK v7 tool approval); none of the three sandbox agents do; for other systems write your own adapter, assembled from `driveFrameStream` + `ctx.session.hold`/`take` (the pi-sdk / claude-sdk Tier 1 examples have ready-made patterns).
|
|
153
|
-
- You want to run a coding agent that edits code, inspect diffs, and judge tool calls: `claude-code` / `codex` / `bub` (send/receive + event stream + session continuation + workspace + sandbox).
|
|
154
|
-
- Your backend protocol is home-grown with no official SDK converter: do not hand-write send from scratch — pick a reduce shape (a small mapping for whole-chunk landing, `deltaStream` for token-by-token deltas), take `ctx.session.id`/`capture` or `ctx.session.history` for session continuation, add `ctx.session.hold`/`take` if you have HITL, and the assembly is your send.
|
|
155
|
-
|
|
156
|
-
## Related reading
|
|
157
|
-
|
|
158
|
-
- [Connect your agent](/guides/connect-your-agent) — how to implement each capability in your own adapter and which assertions it maps to.
|
|
159
|
-
- [Sandbox Agent](/guides/sandbox-agent) — how to run the built-in sandbox agents and write your own.
|
|
160
|
-
- [defineAgent reference](/reference/define-agent) — full parameters of `defineAgent` / `defineSandboxAgent`.
|
|
161
|
-
- [OTel integration](/guides/connect-otel) — send your app's spans to NiceEval in exchange for the call waterfall in `niceeval view`.
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "Capabilities reference"
|
|
3
|
-
sidebarTitle: "Capabilities"
|
|
4
|
-
description: "How NiceEval knows what your adapter has done: there are no declarative capability bits — everything is proven by construction and actual behavior."
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
Capabilities answer one question: **what has your adapter actually done** — which decides which assertions are trustworthy and which services the runner prepares for you.
|
|
8
|
-
|
|
9
|
-
There is no `capabilities` field on `defineAgent` / `defineSandboxAgent` — **nothing needs to be declared**. Every capability is proven by how the agent is constructed or by what it actually returns. The decision table is below.
|
|
10
|
-
|
|
11
|
-
## Where capabilities come from: one decision table
|
|
12
|
-
|
|
13
|
-
| Capability | How it is obtained | What you write |
|
|
14
|
-
|---|---|---|
|
|
15
|
-
| `t.sandbox` / `t.sandbox.fileChanged()` and other filesystem assertions | Constructed with `defineSandboxAgent` (`Agent.kind` is always `"sandbox"`) | Nothing |
|
|
16
|
-
| `tracing` (OTLP receiver → `niceeval view` waterfall) | CLI-style agents write the `tracing` field; long-running apps configure a fixed `telemetry` port in the config | Nothing (the configuration is what you would write anyway) |
|
|
17
|
-
| Tool/action assertions (`t.calledTool()` etc., including negative assertions) | Use an official converter (`fromClaudeSdkMessages`, `fromAiSdk`, etc.) — the `action.*` events in the return value are exactly what you get | Nothing; with manual mapping, event completeness is decided by your mapping code |
|
|
18
|
-
| Cross-turn memory, `t.newSession()` isolation | Used `ctx.session` (`id`/`capture`, `history`) to continue context | Nothing — wire it up and continuation works; skip it and every turn is a fresh conversation, with no error either way |
|
|
19
|
-
| HITL (`t.respond()`) | `send` has returned `status: "waiting"` + `input.requested` | Nothing |
|
|
20
|
-
|
|
21
|
-
A capability you have not implemented = the corresponding events/fields never appear, and the related assertion group is unavailable (not an "error" — the assertions simply have nothing to judge from); basic assertions (text, structured output, judge) are always available.
|
|
22
|
-
|
|
23
|
-
## Why there is no declaration layer
|
|
24
|
-
|
|
25
|
-
An earlier design had boolean declarations like `capabilities: { conversation, toolObservability }` to cover "the parts code cannot prove". That layer has been removed, because what it promised should never have been guaranteed by a declaration in the first place:
|
|
26
|
-
|
|
27
|
-
- **Construction is proof**: using `defineSandboxAgent` means the system under test edits files in a sandbox — there is no need to declare `workspace: true` on top of that.
|
|
28
|
-
- **Fields are proof**: writing `tracing` (how to hand the OTLP endpoint to the CLI) makes tracing real. The field itself is the evidence of use; a boolean sitting next to it would just repeat it.
|
|
29
|
-
- **Source is proof**: `fromAiSdk(result)` consumes `result.steps`, which the AI SDK contract guarantees is the **complete** tool-loop record, so the return value carries its own proof of a complete event stream; built-in CLI parsers work the same way. With manual mapping, whether events are complete depends on whether your mapping code covers everything — that is not something a declaration can guarantee, and declaring it would not make the code any more complete.
|
|
30
|
-
- **Behavior is proof**: HITL has no capability bit — `send` returns `status: "waiting"` + an `input.requested` event, so the behavior is directly observable.
|
|
31
|
-
- **Wire it up and it continues**: session continuation is no longer a "declared to exist" switch. `ctx.session` is always there; use the `id`/`capture` or `history` accessors and context continues, skip them and every turn is a brand-new session — both are normal states, neither errors, and the difference is purely how your adapter is actually written.
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
export default defineAgent({
|
|
35
|
-
name: "my-ai-sdk-agent",
|
|
36
|
-
async send(input, ctx) {
|
|
37
|
-
const result = await generateText({ model, tools, prompt: input.text });
|
|
38
|
-
return { ...fromAiSdk(result), data: result.text };
|
|
39
|
-
// ↑ The return value carries complete tool events; no capability declaration needed
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## What happens when a manual mapping misses events
|
|
45
|
-
|
|
46
|
-
With the declaration layer gone, "false claims" no longer exist either — whatever tool events your manual mapping misses, assertions like `t.calledTool()` see exactly the post-omission picture, and negative assertions (`notCalledTool()`) may "look like they pass" simply because the events were never written. That is not a framework misjudgment; it is a completeness problem in the manual mapping itself: **whether to trust a negative assertion depends on whether you have confirmed your mapping code covers every tool call** — there is no framework-provided middle layer vouching for you.
|
|
47
|
-
|
|
48
|
-
## Item-by-item quick reference
|
|
49
|
-
|
|
50
|
-
### Tool/action event completeness
|
|
51
|
-
|
|
52
|
-
Meaning: whether every tool call in the turn appears in events as `action.called` + `action.result` ([events reference](/reference/events)). There are only two sources: official converters guarantee it per the contract of the protocol under test; manual mapping is decided by your code. OTel spans are not a source — spans only feed the waterfall, never the event stream (see [OTel integration](/guides/connect-otel)).
|
|
53
|
-
|
|
54
|
-
### Session continuation
|
|
55
|
-
|
|
56
|
-
Meaning: using `ctx.session.id` / `capture` or `ctx.session.history` to connect requests to the same backend session / accumulated history. With it, cross-turn memory assertions and `t.newSession()` isolation assertions become meaningful. Both implementation styles are shown in [Write send](/guides/write-send).
|
|
57
|
-
|
|
58
|
-
### `tracing` — emitting OTLP traces
|
|
59
|
-
|
|
60
|
-
What the runner consumes: at runtime it starts a local OTLP receiver and hands this turn's trace context to you via `ctx.telemetry.headers`; normalized spans are drawn as a waterfall in `niceeval view` — drawing only, never feeding assertions. Source: CLI-style agents write the `tracing` field (`env` / `configure` hands the endpoint to the process under test); long-running apps configure a fixed port with `telemetry: { port }` in `niceeval.config.ts` (see [OTel integration](/guides/connect-otel)).
|
|
61
|
-
|
|
62
|
-
### `kind: "sandbox"` — sandbox and file changes
|
|
63
|
-
|
|
64
|
-
What the runner consumes: it prepares a sandbox (Docker / Vercel / E2B) for each attempt and hands it to the adapter via `ctx.sandbox`; this unlocks diff assertions like `t.sandbox.fileChanged()`. Source: constructed with `defineSandboxAgent` — an internal discriminant field you never write.
|
|
65
|
-
|
|
66
|
-
### Compaction visibility
|
|
67
|
-
|
|
68
|
-
Meaning: when the agent under test compacts its context, it emits a `compaction` event, making `t.event("compaction")` meaningful. Source: built-in parsers emit it automatically; a hand-written parser has it if you covered that logic.
|
|
69
|
-
|
|
70
|
-
## Related reading
|
|
71
|
-
|
|
72
|
-
- [Events reference](/reference/events) — exactly what the event stream promises.
|
|
73
|
-
- [Adapter concept](/concepts/adapter) — the full `ctx`/`Turn` contract and where capabilities come from.
|
|
74
|
-
- [Connect your agent](/guides/connect-your-agent) — how to follow each integration path.
|
|
75
|
-
- [OTel integration](/guides/connect-otel) — the full walkthrough of spans feeding only the waterfall, never assertions.
|
|
76
|
-
- [Built-in agent capabilities](/reference/builtin-agents) — what each built-in adapter has done.
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: "NiceEval CLI: commands, flags, and exit codes reference"
|
|
3
|
-
sidebarTitle: "CLI"
|
|
4
|
-
description: "NiceEval CLI reference for exp, init, list, clean, and view, plus experiment selection, eval filtering, sandbox, concurrency, budget, and JUnit CI output."
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
The NiceEval CLI is the entry point for discovery, execution, and result viewing. Actual eval execution follows an experiment-first model: **`exp` selects a commit-ready run configuration**, and only the positional arguments after the experiment filter eval IDs by prefix. Agent, model, and feature flags live in `experiments/`, not in temporary CLI flags.
|
|
8
|
-
|
|
9
|
-
## Commands
|
|
10
|
-
|
|
11
|
-
<CardGroup cols={2}>
|
|
12
|
-
<Card title="npx niceeval exp <group> [id-prefix]" icon="flask">
|
|
13
|
-
Run a named experiment group or config. Optional trailing positional args filter by eval ID prefix.
|
|
14
|
-
</Card>
|
|
15
|
-
<Card title="npx niceeval init" icon="wand-magic-sparkles">
|
|
16
|
-
Generate `evals/`, `niceeval.config.ts`, and sample files.
|
|
17
|
-
</Card>
|
|
18
|
-
<Card title="npx niceeval list" icon="list">
|
|
19
|
-
Discover and print all evals without running them.
|
|
20
|
-
</Card>
|
|
21
|
-
<Card title="npx niceeval clean" icon="trash">
|
|
22
|
-
Remove historical `.niceeval/` artifacts.
|
|
23
|
-
</Card>
|
|
24
|
-
<Card title="npx niceeval view" icon="eye">
|
|
25
|
-
Open the result viewer for the most recent run.
|
|
26
|
-
</Card>
|
|
27
|
-
</CardGroup>
|
|
28
|
-
|
|
29
|
-
## Output language
|
|
30
|
-
|
|
31
|
-
NiceEval supports localized CLI and runtime copy without configuration in `niceeval.config.ts`. When you need fixed output language, use environment variables:
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
NICEEVAL_LANG=en npx niceeval list
|
|
35
|
-
NICEEVAL_LANG=zh-CN npx niceeval list
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Detection order is `NICEEVAL_LANG`, `NICEEVAL_LOCALE`, `LC_ALL`, `LC_MESSAGES`, then `LANG`. Values starting with `zh` use `zh-CN`; everything else uses `en`. If nothing is set, the default is `zh-CN`. This affects terminal/runtime copy only. It does not change machine fields in result JSON or translate judge prompts.
|
|
39
|
-
|
|
40
|
-
## `npx niceeval exp [group|config] [id-prefix...]`
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
# Run every experiment under experiments/
|
|
44
|
-
npx niceeval exp
|
|
45
|
-
|
|
46
|
-
# Run one experiment group
|
|
47
|
-
npx niceeval exp compare-models
|
|
48
|
-
|
|
49
|
-
# Inside that group, only run evals whose ID starts with weather
|
|
50
|
-
npx niceeval exp compare-models weather
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
<Note>
|
|
54
|
-
Eval filters only appear after the experiment selector. Bare `npx niceeval weather` does not run. Use `npx niceeval exp local weather` or `npx niceeval exp compare weather`.
|
|
55
|
-
</Note>
|
|
56
|
-
|
|
57
|
-
## Common flags
|
|
58
|
-
|
|
59
|
-
<ParamField query="--agent" type="string">
|
|
60
|
-
Experiment runs do not support this flag. To switch agents, add or duplicate a config file in `experiments/`.
|
|
61
|
-
</ParamField>
|
|
62
|
-
|
|
63
|
-
> There is no CLI flag for selecting a sandbox backend. Backend choice only lives in code: set `sandbox` on the experiment (`defineExperiment`) or in `niceeval.config.ts` (`defineConfig`) as a project-level fallback, using `dockerSandbox()` / `vercelSandbox()` / `e2bSandbox()` from `niceeval/sandbox`.
|
|
64
|
-
|
|
65
|
-
<ParamField query="--model" type="string">
|
|
66
|
-
Experiment runs do not support this flag. To switch models, add or duplicate an experiment file and change `model`.
|
|
67
|
-
</ParamField>
|
|
68
|
-
|
|
69
|
-
<ParamField query="--max-concurrency" type="number">
|
|
70
|
-
Set how many evals may run at the same time.
|
|
71
|
-
</ParamField>
|
|
72
|
-
|
|
73
|
-
<ParamField query="--runs" type="number">
|
|
74
|
-
Set how many times each eval runs, commonly for pass@N.
|
|
75
|
-
</ParamField>
|
|
76
|
-
|
|
77
|
-
<ParamField query="--early-exit" type="boolean">
|
|
78
|
-
Stop the remaining attempts for an eval after one attempt passes.
|
|
79
|
-
</ParamField>
|
|
80
|
-
|
|
81
|
-
<ParamField query="--timeout-ms" type="number">
|
|
82
|
-
Timeout for one eval.
|
|
83
|
-
</ParamField>
|
|
84
|
-
|
|
85
|
-
<ParamField query="--budget" type="number">
|
|
86
|
-
Budget limit for the whole run.
|
|
87
|
-
</ParamField>
|
|
88
|
-
|
|
89
|
-
<ParamField query="--strict" type="boolean">
|
|
90
|
-
Recommended in CI. Make failures show up more explicitly in the exit code.
|
|
91
|
-
</ParamField>
|
|
92
|
-
|
|
93
|
-
## `list`
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
npx niceeval list
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Use this to confirm eval discovery, IDs, and config loading.
|
|
100
|
-
|
|
101
|
-
## `exp`
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
npx niceeval exp compare-models
|
|
105
|
-
npx niceeval exp compare-models weather-tool
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Run a named experiment and compare agents, models, or flags. Positional args after the experiment filter eval IDs by prefix.
|
|
109
|
-
|
|
110
|
-
## `view`
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
npx niceeval view
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
Open the local result viewer. By default it shows the latest run under `.niceeval/`.
|
|
117
|
-
|
|
118
|
-
## Exit codes
|
|
119
|
-
|
|
120
|
-
The CI rule is simple: when a gate fails or the run itself fails, the command should exit non-zero. When everything passes, it should exit 0. Use `--strict` when you want the stricter CI behavior explicitly.
|