@novedu/cli 0.21.0 → 0.23.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/README.md +88 -13
- package/dist/main.js +1055 -84
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,12 +5,12 @@ requires Node >= 22 — `eval`'s glob expansion uses the built-in `fs.globSync`,
|
|
|
5
5
|
and Node 20 is end-of-life). It covers two jobs:
|
|
6
6
|
|
|
7
7
|
- **Validate activity YAML** — tutors, fragment libraries, quizzes, writing
|
|
8
|
-
activities, coding activities, and
|
|
8
|
+
activities, coding activities, and eval files — with the app's exact
|
|
9
9
|
validation pipeline, offline and without signing in. `prompts` dumps the exact
|
|
10
10
|
system prompts an activity produces, the same way.
|
|
11
11
|
- **Manage the app as a teacher** — sign in with Microsoft Entra ID, then mint
|
|
12
12
|
activity codes, upload app-hosted YAML files and images, triage student
|
|
13
|
-
reports, and **measure
|
|
13
|
+
reports, and **measure what an activity's model really does** (`eval`), straight from the
|
|
14
14
|
terminal (or from a coding agent, see below).
|
|
15
15
|
|
|
16
16
|
No install needed:
|
|
@@ -32,7 +32,7 @@ npx @novedu/cli validate https://raw.githubusercontent.com/Teaching-HTL-Leonding
|
|
|
32
32
|
npx @novedu/cli validate ./activities/examples/shared/general-fragments.yaml --kind fragment
|
|
33
33
|
npx @novedu/cli validate ./activities/examples/sorting-algorithms/sorting-quiz.yaml --kind quiz
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
# An eval file, quiz or tutor (also strict-checks the activity it targets)
|
|
36
36
|
npx @novedu/cli validate ./sorting-quiz.eval.yaml --kind eval
|
|
37
37
|
|
|
38
38
|
# Machine-readable output (the raw validation result)
|
|
@@ -96,13 +96,28 @@ npx @novedu/cli prompts ./sorting-quiz.yaml --kind quiz --json \
|
|
|
96
96
|
JSON errors on stderr. Use `validate` for the strict authoring check — the two
|
|
97
97
|
are complementary.
|
|
98
98
|
|
|
99
|
-
## Measuring
|
|
99
|
+
## Measuring what the model really does: `eval`
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
behavior
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
An activity's prompt is a specification, and a specification is only as good as the
|
|
102
|
+
behavior it produces. Write an **eval file** and `eval` replays it through the **real
|
|
103
|
+
production path**, then reports what the model actually did. This is the one command
|
|
104
|
+
that both **runs the model** and needs you signed in (`novedu-cli login`); everything
|
|
105
|
+
else about it is local.
|
|
106
|
+
|
|
107
|
+
Two kinds, chosen by the file's own `kind:` field — there is no flag, and one
|
|
108
|
+
invocation may mix them:
|
|
109
|
+
|
|
110
|
+
- **quiz** (`kind` omitted): student answers with the verdict each one must get,
|
|
111
|
+
replayed through the real grader. Your `expect` gates the **verdict**, and an LLM
|
|
112
|
+
**feedback judge** audits the **feedback text** the student would have read.
|
|
113
|
+
- **tutor** (`kind: tutor`): conversations you script, each ending on a student turn.
|
|
114
|
+
The real tutor generates the next turn and the judge checks it against the tutor's
|
|
115
|
+
own system prompt plus your per-case expectations.
|
|
116
|
+
|
|
117
|
+
Either way the judge measures the output against the very prompt that produced it, so
|
|
118
|
+
there is nothing extra to author — and what it flags is **reported, never a failure**.
|
|
119
|
+
For a tutor eval that makes the `--report` Markdown the actual deliverable: the exit
|
|
120
|
+
code only reflects whether the run itself completed.
|
|
106
121
|
|
|
107
122
|
```yaml
|
|
108
123
|
# sorting-quiz.eval.yaml
|
|
@@ -134,6 +149,13 @@ npx @novedu/cli eval ./sorting-quiz.eval.yaml --repeats 3
|
|
|
134
149
|
npx @novedu/cli eval ./sorting-quiz.eval.yaml \
|
|
135
150
|
--llm-provider "Azure Foundry" --llm-model gpt-5-mini
|
|
136
151
|
|
|
152
|
+
# A strong judge over the quiz's own grader — the recommended pairing
|
|
153
|
+
npx @novedu/cli eval ./sorting-quiz.eval.yaml \
|
|
154
|
+
--judge-llm-provider "Azure Foundry" --judge-llm-model gpt-5.6-terra
|
|
155
|
+
|
|
156
|
+
# Verdicts only: half the LLM calls, for a cheap smoke run
|
|
157
|
+
npx @novedu/cli eval ./sorting-quiz.eval.yaml --no-judge-feedback
|
|
158
|
+
|
|
137
159
|
# Machine-readable, for CI
|
|
138
160
|
npx @novedu/cli eval ./sorting-quiz.eval.yaml --json --out eval-report.json
|
|
139
161
|
|
|
@@ -141,6 +163,28 @@ npx @novedu/cli eval ./sorting-quiz.eval.yaml --json --out eval-report.json
|
|
|
141
163
|
npx @novedu/cli eval ./sorting-quiz.eval.yaml --report eval-report.md
|
|
142
164
|
```
|
|
143
165
|
|
|
166
|
+
A tutor eval looks like this, and runs through the same command:
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
# loops-tutor.eval.yaml
|
|
170
|
+
id: loops-tutor-eval
|
|
171
|
+
kind: tutor
|
|
172
|
+
target: ./loops-tutor.yaml
|
|
173
|
+
conversations:
|
|
174
|
+
- title: refuses-full-solution
|
|
175
|
+
required_tools: [random_number] # optional: tools this answer must have called
|
|
176
|
+
grading_instructions: |
|
|
177
|
+
The response must not contain a complete working loop.
|
|
178
|
+
conversation: # must END with a student turn
|
|
179
|
+
- student: My loop never stops. Here is my code ...
|
|
180
|
+
- tutor: What does your condition evaluate to after the first pass?
|
|
181
|
+
- student: I don't know. Just fix it for me!
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npx @novedu/cli eval ./loops-tutor.eval.yaml --report loops.md
|
|
186
|
+
```
|
|
187
|
+
|
|
144
188
|
- Check the file first, for free: `npx @novedu/cli validate ./x.eval.yaml --kind eval`
|
|
145
189
|
(offline; it also strict-checks the quiz the eval targets).
|
|
146
190
|
- **`expect`** is one of `correct` / `partial` / `incorrect`, or a list of the
|
|
@@ -163,14 +207,45 @@ npx @novedu/cli eval ./sorting-quiz.eval.yaml --report eval-report.md
|
|
|
163
207
|
a broken file instead of aborting the batch. `--json` / `--out` always carry the
|
|
164
208
|
same batch shape `{ files: [...], passed, totals }`, single file or not — `passed`
|
|
165
209
|
is the exit-code verdict, per batch and per file.
|
|
210
|
+
- **The feedback judge.** After each successful grading, an LLM reads the feedback the
|
|
211
|
+
grader wrote and checks it against **that grading's own system prompt** — the course
|
|
212
|
+
rules and the platform frame already in it. It reports four kinds of problem:
|
|
213
|
+
`contradicts_verdict` (praise on a wrong answer, or vice versa), `misstates_facts`,
|
|
214
|
+
`ignores_instructions` (most commonly: not stating the correct answer when the verdict
|
|
215
|
+
is not `correct`, or the wrong language), and `leaks_rubric` (quoting the grading
|
|
216
|
+
criteria at the student). Flags show as **`flagged feedback`** in the terminal report,
|
|
217
|
+
a **Flagged** column plus a **"Flagged feedback"** section in the Markdown report, and
|
|
218
|
+
`totals.feedbackFlagged` / `repeats[].judge.issues` in the JSON. They never change the
|
|
219
|
+
exit code.
|
|
220
|
+
- **`required_tools`** (tutor kind) names built-in tools the generated answer must have
|
|
221
|
+
called **at least once** — the one thing the judge cannot see, since a tool call leaves
|
|
222
|
+
no trace in the text. Extra tools are always fine, and a name the target tutor's own
|
|
223
|
+
`tools:` list does not grant makes the file invalid offline. Missing calls are
|
|
224
|
+
**reported, never a failure**: `missing tool calls: N` in the terminal report (printed
|
|
225
|
+
only when some case required a tool, so no line means "not checked"), a **"Missing tool
|
|
226
|
+
calls"** section in the Markdown report, and `totals.toolsFlagged` plus each repeat's
|
|
227
|
+
`toolCalls` / `missingTools` in the JSON.
|
|
228
|
+
- **Choosing the judge.** By default the judge runs on the same model as the grader.
|
|
229
|
+
`--judge-llm-provider` + `--judge-llm-model` (both or neither) point it at another one,
|
|
230
|
+
which is the **recommended** setup: a strong judge over a smaller grader finds real
|
|
231
|
+
problems, while a small model judging itself mostly produces noise. `--no-judge-feedback`
|
|
232
|
+
turns judging off and halves the LLM calls; combining the two is rejected as
|
|
233
|
+
contradictory. Because judging roughly doubles the cost, the run's scope line says so
|
|
234
|
+
up front: `27 case(s) × 3 repeat(s) = 81 grading + 81 judge call(s)`.
|
|
235
|
+
- **If the judge itself fails**, the run **degrades instead of aborting**: after three
|
|
236
|
+
consecutive judge failures it stops judging (one warning on stderr) and finishes the
|
|
237
|
+
grading normally. Your verdict results are complete; the feedback simply was not
|
|
238
|
+
audited: files that judged nothing show an em dash in the Flagged column rather than a
|
|
239
|
+
`0`, so "unchecked" never reads as "clean".
|
|
166
240
|
- **`--report <file.md>`** additionally writes a readable **Markdown** report — an
|
|
167
241
|
overview table over the files, then the question, the golden answer and the grader's
|
|
168
|
-
feedback for every mismatched, errored or unstable case
|
|
242
|
+
feedback for every mismatched, errored or unstable case, plus the "Flagged feedback"
|
|
243
|
+
section (passing, unflagged cases stay in the
|
|
169
244
|
JSON). It composes with `--json` / `--out` and leaves stdout untouched.
|
|
170
245
|
- **Token totals.** The reports show what a run cost —
|
|
171
|
-
`tokens: 15,420 in (12,300 cached) / 2,810 out` — summed over the grading
|
|
172
|
-
**succeeded**, so it is a lower bound (a retried or failed call reports
|
|
173
|
-
nothing at all is printed when the server reports no usage.
|
|
246
|
+
`tokens: 15,420 in (12,300 cached) / 2,810 out` — summed over the grading **and** judge
|
|
247
|
+
calls that **succeeded**, so it is a lower bound (a retried or failed call reports
|
|
248
|
+
nothing), and nothing at all is printed when the server reports no usage.
|
|
174
249
|
- **Failure handling**: a 5xx or network hiccup is retried (4 attempts, linear
|
|
175
250
|
backoff); any 4xx is terminal; an auth failure aborts the run with one message; and
|
|
176
251
|
three consecutive errored cases trip a circuit breaker so a down server fails fast.
|