@novedu/cli 0.24.1 → 0.25.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 +2 -1
- package/dist/main.js +26 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -331,7 +331,8 @@ images list [--search <q>] [--all]
|
|
|
331
331
|
shareable `url`. `--start`/`--end` must be ISO 8601 **with an explicit
|
|
332
332
|
offset or `Z`** (e.g. `2026-07-07T08:00:00Z`); the
|
|
333
333
|
`--llm-provider`/`--llm-model` override pair is both-or-nothing, and
|
|
334
|
-
`--llm-reasoning <level>` (`minimal`, `low`, `medium
|
|
334
|
+
`--llm-reasoning <level>` (`none`, `minimal`, `low`, `medium`, `high` or
|
|
335
|
+
`xhigh`) rides on top of
|
|
335
336
|
the pair — it is rejected without it. The override replaces the activity's whole
|
|
336
337
|
`llm:` block, so leaving the level out also drops the file's.
|
|
337
338
|
- `codes sync <registry-file>` mints codes for a whole **course** at once — see
|
package/dist/main.js
CHANGED
|
@@ -2,15 +2,35 @@
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { readFile, writeFile } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, join, resolve } from "node:path";
|
|
5
|
+
import { z } from "zod";
|
|
5
6
|
import { spawn } from "node:child_process";
|
|
6
7
|
import { globSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
7
8
|
import { createServer } from "node:http";
|
|
8
9
|
import { homedir } from "node:os";
|
|
9
10
|
import { CryptoProvider, PublicClientApplication } from "@azure/msal-node";
|
|
10
11
|
import { parse, stringify } from "yaml";
|
|
11
|
-
import { z } from "zod";
|
|
12
12
|
import Handlebars from "handlebars";
|
|
13
13
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
14
|
+
//#region ../lib/llm/provider.ts
|
|
15
|
+
const LLM_PROVIDERS = ["SCCH", "Azure Foundry"];
|
|
16
|
+
const DEFAULT_PROVIDER = "SCCH";
|
|
17
|
+
const providerSchema = z.enum(LLM_PROVIDERS).default(DEFAULT_PROVIDER).meta({ description: "The LLM provider serving the model. For Azure Foundry, model is the deployment name." });
|
|
18
|
+
const REASONING_LEVELS = [
|
|
19
|
+
"none",
|
|
20
|
+
"minimal",
|
|
21
|
+
"low",
|
|
22
|
+
"medium",
|
|
23
|
+
"high",
|
|
24
|
+
"xhigh"
|
|
25
|
+
];
|
|
26
|
+
const reasoningLevelSchema = z.enum(REASONING_LEVELS).optional().meta({ description: "Optional reasoning effort for reasoning models. Not every model accepts every level. Omit to let the model decide (the parameter is then not sent); \"none\" instead turns a reasoning model off." });
|
|
27
|
+
function parseLenientProvider(value) {
|
|
28
|
+
return value === "SCCH" || value === "Azure Foundry" ? value : void 0;
|
|
29
|
+
}
|
|
30
|
+
function parseLenientReasoningLevel(value) {
|
|
31
|
+
return typeof value === "string" && REASONING_LEVELS.includes(value) ? value : void 0;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
14
34
|
//#region src/auth.ts
|
|
15
35
|
const DEFAULT_TENANT_ID = "91fc072c-edef-4f97-bdc5-cfb67718ae3a";
|
|
16
36
|
const DEFAULT_CLIENT_ID = "4d44fc4b-0434-4981-9765-62e2074ceecb";
|
|
@@ -309,24 +329,6 @@ async function runApiRequest(options) {
|
|
|
309
329
|
if (result.ok) printJson(result.payload ?? null);
|
|
310
330
|
}
|
|
311
331
|
//#endregion
|
|
312
|
-
//#region ../lib/llm/provider.ts
|
|
313
|
-
const LLM_PROVIDERS = ["SCCH", "Azure Foundry"];
|
|
314
|
-
const DEFAULT_PROVIDER = "SCCH";
|
|
315
|
-
const providerSchema = z.enum(LLM_PROVIDERS).default(DEFAULT_PROVIDER).meta({ description: "The LLM provider serving the model. For Azure Foundry, model is the deployment name." });
|
|
316
|
-
const REASONING_LEVELS = [
|
|
317
|
-
"minimal",
|
|
318
|
-
"low",
|
|
319
|
-
"medium",
|
|
320
|
-
"high"
|
|
321
|
-
];
|
|
322
|
-
const reasoningLevelSchema = z.enum(REASONING_LEVELS).optional().meta({ description: "Optional reasoning effort for reasoning models. Omit to let the model decide (the parameter is then not sent)." });
|
|
323
|
-
function parseLenientProvider(value) {
|
|
324
|
-
return value === "SCCH" || value === "Azure Foundry" ? value : void 0;
|
|
325
|
-
}
|
|
326
|
-
function parseLenientReasoningLevel(value) {
|
|
327
|
-
return typeof value === "string" && REASONING_LEVELS.includes(value) ? value : void 0;
|
|
328
|
-
}
|
|
329
|
-
//#endregion
|
|
330
332
|
//#region ../lib/registry-schema.ts
|
|
331
333
|
/** The fixed group names and the code module each one mints for. */
|
|
332
334
|
const GROUP_MODULES = {
|
|
@@ -896,7 +898,7 @@ async function readLock(lockPath) {
|
|
|
896
898
|
}
|
|
897
899
|
function registerCodes(program) {
|
|
898
900
|
const codes = program.command("codes").description("Manage activity codes on the Novedu server");
|
|
899
|
-
codes.command("create").description("Create a code for an activity YAML (validated server-side before storing)").requiredOption("--module <module>", "activity module: tutor, quiz, writing or coding").requiredOption("--file <url>", "public http(s) URL of the activity YAML").option("--start <iso>", "window start, ISO 8601 with explicit offset (e.g. 2026-07-07T08:00:00Z)").option("--end <iso>", "window end, ISO 8601 with explicit offset").option("--note <text>", "note shown in the codes list").option("--llm-provider <provider>", "LLM override provider (\"SCCH\" or \"Azure Foundry\"; needs --llm-model)").option("--llm-model <model>", "LLM override model id (needs --llm-provider)").option("--llm-reasoning <level>",
|
|
901
|
+
codes.command("create").description("Create a code for an activity YAML (validated server-side before storing)").requiredOption("--module <module>", "activity module: tutor, quiz, writing or coding").requiredOption("--file <url>", "public http(s) URL of the activity YAML").option("--start <iso>", "window start, ISO 8601 with explicit offset (e.g. 2026-07-07T08:00:00Z)").option("--end <iso>", "window end, ISO 8601 with explicit offset").option("--note <text>", "note shown in the codes list").option("--llm-provider <provider>", "LLM override provider (\"SCCH\" or \"Azure Foundry\"; needs --llm-model)").option("--llm-model <model>", "LLM override model id (needs --llm-provider)").option("--llm-reasoning <level>", `LLM override reasoning effort (${REASONING_LEVELS.join(", ")}; needs the provider/model pair)`).option(...SERVER_OPTION$3).action(async (options) => {
|
|
900
902
|
const llmGiven = options.llmProvider !== void 0 || options.llmModel !== void 0 || options.llmReasoning !== void 0;
|
|
901
903
|
await runApiRequest({
|
|
902
904
|
server: options.server,
|
|
@@ -1168,7 +1170,7 @@ function parseCoding(content) {
|
|
|
1168
1170
|
const reasoning = llm?.reasoning === void 0 ? void 0 : parseLenientReasoningLevel(llm.reasoning);
|
|
1169
1171
|
if (llm?.reasoning !== void 0 && !reasoning) return {
|
|
1170
1172
|
ok: false,
|
|
1171
|
-
message:
|
|
1173
|
+
message: `This coding activity uses an unsupported llm.reasoning (one of ${REASONING_LEVELS.join(", ")}).`
|
|
1172
1174
|
};
|
|
1173
1175
|
const instructions = asString$2(root.instructions);
|
|
1174
1176
|
if (!instructions) return {
|
|
@@ -2477,7 +2479,7 @@ function parseQuiz(content) {
|
|
|
2477
2479
|
const reasoning = llm?.reasoning === void 0 ? void 0 : parseLenientReasoningLevel(llm.reasoning);
|
|
2478
2480
|
if (llm?.reasoning !== void 0 && !reasoning) return {
|
|
2479
2481
|
ok: false,
|
|
2480
|
-
message:
|
|
2482
|
+
message: `This quiz uses an unsupported llm.reasoning (one of ${REASONING_LEVELS.join(", ")}).`
|
|
2481
2483
|
};
|
|
2482
2484
|
const quizFiles = Array.isArray(root.quiz_files) ? root.quiz_files : [];
|
|
2483
2485
|
const rawQuestions = Array.isArray(root.questions) ? root.questions : [];
|
|
@@ -2858,7 +2860,7 @@ function parseWriting(content) {
|
|
|
2858
2860
|
const reasoning = llm?.reasoning === void 0 ? void 0 : parseLenientReasoningLevel(llm.reasoning);
|
|
2859
2861
|
if (llm?.reasoning !== void 0 && !reasoning) return {
|
|
2860
2862
|
ok: false,
|
|
2861
|
-
message:
|
|
2863
|
+
message: `This writing activity uses an unsupported llm.reasoning (one of ${REASONING_LEVELS.join(", ")}).`
|
|
2862
2864
|
};
|
|
2863
2865
|
const instructions = asString(root.instructions);
|
|
2864
2866
|
if (!instructions) return {
|
|
@@ -5810,7 +5812,7 @@ async function runEvalCommand(pathsOrUrls, options, seams = {}) {
|
|
|
5810
5812
|
process.exitCode = batchPassed(batch) ? 0 : 1;
|
|
5811
5813
|
}
|
|
5812
5814
|
function registerEval(program) {
|
|
5813
|
-
program.command("eval").description("Run an eval file (quiz golden answers, or tutor conversations) against the real activity path and report the result").argument("<evalPathOrUrl...>", "one or more eval YAML files (paths, http(s)/file URLs, or a quoted glob pattern)").option("--server <url>", "Novedu server base URL (defaults to the NOVEDU_SERVER env var, then production)").option("--concurrency <n>", "cases in flight per file", String(CONCURRENCY_DEFAULT)).option("--repeats <n>", "run every case N times (quiz: take the majority verdict)", "1").option("--llm-provider <provider>", "run with this provider instead of the activity's (\"SCCH\" or \"Azure Foundry\"; needs --llm-model)").option("--llm-model <model>", "run with this model instead of the activity's (needs --llm-provider)").option("--llm-reasoning <level>",
|
|
5815
|
+
program.command("eval").description("Run an eval file (quiz golden answers, or tutor conversations) against the real activity path and report the result").argument("<evalPathOrUrl...>", "one or more eval YAML files (paths, http(s)/file URLs, or a quoted glob pattern)").option("--server <url>", "Novedu server base URL (defaults to the NOVEDU_SERVER env var, then production)").option("--concurrency <n>", "cases in flight per file", String(CONCURRENCY_DEFAULT)).option("--repeats <n>", "run every case N times (quiz: take the majority verdict)", "1").option("--llm-provider <provider>", "run with this provider instead of the activity's (\"SCCH\" or \"Azure Foundry\"; needs --llm-model)").option("--llm-model <model>", "run with this model instead of the activity's (needs --llm-provider)").option("--llm-reasoning <level>", `run at this reasoning effort (${REASONING_LEVELS.join(", ")}); on its own it keeps the activity's model`).option("--no-judge-feedback", "skip the LLM audit of what the model wrote (halves the LLM calls)").option("--judge-llm-provider <provider>", "judge with this provider (\"SCCH\" or \"Azure Foundry\"; needs --judge-llm-model)").option("--judge-llm-model <model>", "judge with this model instead of the one under test (needs --judge-llm-provider)").option("--judge-llm-reasoning <level>", `judge at this reasoning effort (${REASONING_LEVELS.join(", ")}); on its own it keeps the judge's model`).option("--json", "print the machine-readable batch report on stdout").option("--out <file>", "additionally write the machine-readable batch report to a file").option("--report <file>", "additionally write a readable Markdown report to a file").addHelpText("after", `
|
|
5814
5816
|
Examples:
|
|
5815
5817
|
# Evaluate one quiz's golden answers
|
|
5816
5818
|
$ novedu-cli eval ./0010-welcome-quiz.eval.yaml
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novedu/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Command-line companion for the Novedu chat app. Validates tutor, fragment, quiz, writing, coding and eval YAML definitions, dumps the exact LLM prompts an activity produces, evaluates a quiz's grading rubric against golden answers and replays scripted conversations against a tutor; signs in with Entra ID and manages codes, app-hosted files and images over the app's API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|