@hazeljs/eval 2.0.13 → 2.0.14

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 CHANGED
@@ -23,9 +23,10 @@ npm install @hazeljs/core
23
23
  - **Retrieval metrics** — `precisionAtK`, `recallAtK`, `meanReciprocalRank`, `ndcgAtK`
24
24
  - **RAG helpers** — `evaluateRetrieval`, `answerContextOverlap` (lightweight faithfulness proxy)
25
25
  - **Agent trajectories** — `trajectoryScore`, `toolCallAccuracy`
26
- - **LLM judge** — `parseJudgeScore`, `buildRelevanceJudgePrompt`, `buildFaithfulnessJudgePrompt` (wire to your model)
26
+ - **LLM judge** — `parseJudgeScore`, `buildRelevanceJudgePrompt`, `buildFaithfulnessJudgePrompt`; pass `judge` on `EvalRunOptions` to blend into `runGoldenDataset`
27
27
  - **Runner** — `runGoldenDataset` with configurable concurrency and thresholds
28
28
  - **CI** — `reportEvalForCi` with optional non-zero exit via `process.exitCode`
29
+ - **AI call goldens** — put `metadata.expectedCalls` on cases and use `@hazeljs/ai` `runAiGolden` / `FakeModel` for provider-request regression
29
30
 
30
31
  ## Quick start
31
32
 
@@ -46,7 +47,13 @@ const result = await runGoldenDataset(
46
47
  retrievedIds: ['doc-1', 'doc-2'],
47
48
  };
48
49
  },
49
- { concurrency: 2, minAverageScore: 0.75 }
50
+ {
51
+ concurrency: 2,
52
+ minAverageScore: 0.75,
53
+ // optional: blend LLM-as-judge (skip per-case with metadata.skipJudge)
54
+ // judge: async (prompt) => ai.chat(prompt).text(),
55
+ // judgePrompt: 'relevance',
56
+ }
50
57
  );
51
58
 
52
59
  reportEvalForCi(result, { exitOnFail: true });
@@ -1 +1 @@
1
- {"version":3,"file":"golden-runner.d.ts","sourceRoot":"","sources":["../src/golden-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAc,aAAa,EAAE,MAAM,SAAS,CAAC;AAIxF,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAEjF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,aAAa,CAAC,CAyFxB"}
1
+ {"version":3,"file":"golden-runner.d.ts","sourceRoot":"","sources":["../src/golden-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAc,aAAa,EAAE,MAAM,SAAS,CAAC;AASxF,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAAC;AAEjF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,aAAa,CAAC,CA6GxB"}
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runGoldenDataset = runGoldenDataset;
4
4
  const agent_trajectory_1 = require("./agent-trajectory");
5
5
  const retrieval_metrics_1 = require("./retrieval-metrics");
6
+ const llm_judge_1 = require("./llm-judge");
6
7
  /**
7
8
  * Run a golden dataset against an async runner (your app, agent, or RAG pipeline).
8
9
  */
@@ -22,30 +23,48 @@ async function runGoldenDataset(dataset, runner, options) {
22
23
  metadata: c.metadata,
23
24
  });
24
25
  let score = 0;
26
+ let scoredParts = 0;
25
27
  const details = {};
26
28
  if (c.expectedOutput !== undefined) {
27
29
  const out = result.output ?? '';
28
30
  const exp = c.expectedOutput;
29
31
  const passed = out.includes(exp) || out.trim().toLowerCase() === exp.trim().toLowerCase();
30
- score = passed ? 1 : 0;
32
+ score += passed ? 1 : 0;
33
+ scoredParts += 1;
31
34
  details.outputMatch = passed;
32
35
  }
33
36
  if (c.expectedToolCalls && c.expectedToolCalls.length > 0) {
34
37
  const traj = (0, agent_trajectory_1.trajectoryScore)(c.expectedToolCalls, result.toolCalls ?? []);
35
- score = score === 0 ? traj : (score + traj) / 2;
38
+ score += traj;
39
+ scoredParts += 1;
36
40
  details.trajectoryScore = traj;
37
41
  }
38
42
  if (c.expectedRetrievedIds && c.expectedRetrievedIds.length > 0) {
39
43
  const rel = new Set(c.expectedRetrievedIds);
40
44
  const p = (0, retrieval_metrics_1.precisionAtK)(result.retrievedIds ?? [], rel, 5);
41
- score = score === 0 ? p : (score + p) / 2;
45
+ score += p;
46
+ scoredParts += 1;
42
47
  details.precisionAt5 = p;
43
48
  }
44
- if (c.expectedOutput === undefined &&
45
- (!c.expectedToolCalls || c.expectedToolCalls.length === 0) &&
46
- (!c.expectedRetrievedIds || c.expectedRetrievedIds.length === 0)) {
49
+ const skipJudge = Boolean(c.metadata?.skipJudge);
50
+ if (options?.judge && !skipJudge) {
51
+ const promptKind = options.judgePrompt ?? 'relevance';
52
+ const judgePrompt = promptKind === 'faithfulness'
53
+ ? (0, llm_judge_1.buildFaithfulnessJudgePrompt)(String(c.metadata?.judgeContext ?? ''), result.output ?? '')
54
+ : (0, llm_judge_1.buildRelevanceJudgePrompt)(c.input, result.output ?? '');
55
+ const raw = await options.judge(judgePrompt);
56
+ const judged = await (0, llm_judge_1.parseJudgeScore)(raw);
57
+ score += judged.score;
58
+ scoredParts += 1;
59
+ details.judgeScore = judged.score;
60
+ details.judgeReasoning = judged.reasoning;
61
+ }
62
+ if (scoredParts === 0) {
47
63
  score = 1;
48
64
  }
65
+ else {
66
+ score = score / scoredParts;
67
+ }
49
68
  const threshold = options?.minAverageScore ?? 0.7;
50
69
  return {
51
70
  caseId: c.id,
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @hazeljs/eval — Evaluation toolkit for AI-native HazelJS apps
3
3
  */
4
- export type { GoldenCase, GoldenDataset, EvalRunOptions, CaseResult, EvalRunResult } from './types';
4
+ export type { GoldenCase, GoldenDataset, EvalRunOptions, CaseResult, EvalRunResult, ExpectedCallSlice, } from './types';
5
5
  export { precisionAtK, recallAtK, meanReciprocalRank, ndcgAtK } from './retrieval-metrics';
6
6
  export { evaluateRetrieval, answerContextOverlap, type RagRetrievalEvalInput, type RagRetrievalEvalResult, } from './rag-metrics';
7
7
  export { trajectoryScore, toolCallAccuracy, type AgentTrajectory } from './agent-trajectory';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEpG,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE3F,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE7F,OAAO,EACL,eAAe,EACf,yBAAyB,EACzB,4BAA4B,EAC5B,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EACV,UAAU,EACV,aAAa,EACb,cAAc,EACd,UAAU,EACV,aAAa,EACb,iBAAiB,GAClB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE3F,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE7F,OAAO,EACL,eAAe,EACf,yBAAyB,EACzB,4BAA4B,EAC5B,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAExE,OAAO,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,6 +1,21 @@
1
1
  /**
2
2
  * Core types for @hazeljs/eval
3
3
  */
4
+ import type { LLMJudgeFn } from './llm-judge';
5
+ /**
6
+ * Optional provider-call golden shape (used by `@hazeljs/ai` FakeModel matchers).
7
+ * Store under `GoldenCase.metadata.expectedCalls`.
8
+ */
9
+ export interface ExpectedCallSlice {
10
+ /** Exact model id if asserted */
11
+ model?: string;
12
+ /** Subset of message roles in order (e.g. ['system','user']) */
13
+ messageRoles?: string[];
14
+ /** Tool / function names expected to be offered on the request */
15
+ toolNames?: string[];
16
+ /** Substring that must appear in some message content */
17
+ messageIncludes?: string;
18
+ }
4
19
  export interface GoldenCase {
5
20
  id: string;
6
21
  input: string;
@@ -10,6 +25,12 @@ export interface GoldenCase {
10
25
  expectedToolCalls?: string[];
11
26
  /** Relevant document IDs or content snippets retrieval should surface */
12
27
  expectedRetrievedIds?: string[];
28
+ /**
29
+ * Free-form case metadata. Recognized keys:
30
+ * - `expectedCalls`: ExpectedCallSlice[] for FakeModel request goldens
31
+ * - `judgeContext`: string used when `EvalRunOptions.judgePrompt === 'faithfulness'`
32
+ * - `skipJudge`: boolean to skip LLM judge for this case
33
+ */
13
34
  metadata?: Record<string, unknown>;
14
35
  }
15
36
  export interface GoldenDataset {
@@ -22,6 +43,13 @@ export interface EvalRunOptions {
22
43
  concurrency?: number;
23
44
  /** Fail CI when average score below threshold */
24
45
  minAverageScore?: number;
46
+ /**
47
+ * Optional LLM-as-judge. When set, blends judge score with existing matchers
48
+ * (unless `metadata.skipJudge` is true).
49
+ */
50
+ judge?: LLMJudgeFn;
51
+ /** Which rubric prompt to build (default relevance). */
52
+ judgePrompt?: 'relevance' | 'faithfulness';
25
53
  }
26
54
  export interface CaseResult {
27
55
  caseId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yEAAyE;IACzE,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yEAAyE;IACzE,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,wDAAwD;IACxD,WAAW,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC;CAC5C;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazeljs/eval",
3
- "version": "2.0.13",
3
+ "version": "2.0.14",
4
4
  "description": "Evaluation toolkit for HazelJS AI apps — golden datasets, RAG metrics, agent trajectories, LLM-as-judge, CI reports",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,10 +19,10 @@
19
19
  "zod": "^3.25.76"
20
20
  },
21
21
  "peerDependencies": {
22
- "@hazeljs/core": "^2.0.13"
22
+ "@hazeljs/core": "^2.0.14"
23
23
  },
24
24
  "devDependencies": {
25
- "@hazeljs/core": "^2.0.13",
25
+ "@hazeljs/core": "^2.0.14",
26
26
  "@types/jest": "^29.5.14",
27
27
  "@types/node": "^20.19.39",
28
28
  "@typescript-eslint/eslint-plugin": "^8.58.0",