@graphorin/evals 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/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +157 -0
- package/dist/cli/index.d.ts +43 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +102 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/loaders/csv.d.ts +23 -0
- package/dist/loaders/csv.d.ts.map +1 -0
- package/dist/loaders/csv.js +116 -0
- package/dist/loaders/csv.js.map +1 -0
- package/dist/loaders/dmr.d.ts +31 -0
- package/dist/loaders/dmr.d.ts.map +1 -0
- package/dist/loaders/dmr.js +121 -0
- package/dist/loaders/dmr.js.map +1 -0
- package/dist/loaders/from-traces.d.ts +32 -0
- package/dist/loaders/from-traces.d.ts.map +1 -0
- package/dist/loaders/from-traces.js +66 -0
- package/dist/loaders/from-traces.js.map +1 -0
- package/dist/loaders/index.d.ts +9 -0
- package/dist/loaders/index.js +9 -0
- package/dist/loaders/iterable.d.ts +12 -0
- package/dist/loaders/iterable.d.ts.map +1 -0
- package/dist/loaders/iterable.js +16 -0
- package/dist/loaders/iterable.js.map +1 -0
- package/dist/loaders/jsonl.d.ts +34 -0
- package/dist/loaders/jsonl.d.ts.map +1 -0
- package/dist/loaders/jsonl.js +64 -0
- package/dist/loaders/jsonl.js.map +1 -0
- package/dist/loaders/locomo.d.ts +32 -0
- package/dist/loaders/locomo.d.ts.map +1 -0
- package/dist/loaders/locomo.js +155 -0
- package/dist/loaders/locomo.js.map +1 -0
- package/dist/loaders/longmemeval.d.ts +36 -0
- package/dist/loaders/longmemeval.d.ts.map +1 -0
- package/dist/loaders/longmemeval.js +135 -0
- package/dist/loaders/longmemeval.js.map +1 -0
- package/dist/loaders/memory-eval.d.ts +67 -0
- package/dist/loaders/memory-eval.d.ts.map +1 -0
- package/dist/regression.d.ts +13 -0
- package/dist/regression.d.ts.map +1 -0
- package/dist/regression.js +63 -0
- package/dist/regression.js.map +1 -0
- package/dist/reporters/html.d.ts +11 -0
- package/dist/reporters/html.d.ts.map +1 -0
- package/dist/reporters/html.js +60 -0
- package/dist/reporters/html.js.map +1 -0
- package/dist/reporters/index.d.ts +6 -0
- package/dist/reporters/index.js +7 -0
- package/dist/reporters/json.d.ts +11 -0
- package/dist/reporters/json.d.ts.map +1 -0
- package/dist/reporters/json.js +10 -0
- package/dist/reporters/json.js.map +1 -0
- package/dist/reporters/junit.d.ts +11 -0
- package/dist/reporters/junit.d.ts.map +1 -0
- package/dist/reporters/junit.js +29 -0
- package/dist/reporters/junit.js.map +1 -0
- package/dist/reporters/markdown.d.ts +9 -0
- package/dist/reporters/markdown.d.ts.map +1 -0
- package/dist/reporters/markdown.js +59 -0
- package/dist/reporters/markdown.js.map +1 -0
- package/dist/reporters/terminal.d.ts +9 -0
- package/dist/reporters/terminal.d.ts.map +1 -0
- package/dist/reporters/terminal.js +38 -0
- package/dist/reporters/terminal.js.map +1 -0
- package/dist/runner.d.ts +11 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +181 -0
- package/dist/runner.js.map +1 -0
- package/dist/scorers/code/exact-match.d.ts +22 -0
- package/dist/scorers/code/exact-match.d.ts.map +1 -0
- package/dist/scorers/code/exact-match.js +67 -0
- package/dist/scorers/code/exact-match.js.map +1 -0
- package/dist/scorers/code/json-path.d.ts +18 -0
- package/dist/scorers/code/json-path.d.ts.map +1 -0
- package/dist/scorers/code/json-path.js +71 -0
- package/dist/scorers/code/json-path.js.map +1 -0
- package/dist/scorers/code/predicate.d.ts +18 -0
- package/dist/scorers/code/predicate.d.ts.map +1 -0
- package/dist/scorers/code/predicate.js +19 -0
- package/dist/scorers/code/predicate.js.map +1 -0
- package/dist/scorers/code/regex.d.ts +15 -0
- package/dist/scorers/code/regex.d.ts.map +1 -0
- package/dist/scorers/code/regex.js +37 -0
- package/dist/scorers/code/regex.js.map +1 -0
- package/dist/scorers/index.d.ts +13 -0
- package/dist/scorers/index.js +13 -0
- package/dist/scorers/llm/judge.d.ts +57 -0
- package/dist/scorers/llm/judge.d.ts.map +1 -0
- package/dist/scorers/llm/judge.js +108 -0
- package/dist/scorers/llm/judge.js.map +1 -0
- package/dist/scorers/prebuilt/index.d.ts +22 -0
- package/dist/scorers/prebuilt/index.d.ts.map +1 -0
- package/dist/scorers/prebuilt/index.js +49 -0
- package/dist/scorers/prebuilt/index.js.map +1 -0
- package/dist/scorers/trajectory/argument-validity.d.ts +25 -0
- package/dist/scorers/trajectory/argument-validity.d.ts.map +1 -0
- package/dist/scorers/trajectory/argument-validity.js +46 -0
- package/dist/scorers/trajectory/argument-validity.js.map +1 -0
- package/dist/scorers/trajectory/correct-tool-selected.d.ts +19 -0
- package/dist/scorers/trajectory/correct-tool-selected.d.ts.map +1 -0
- package/dist/scorers/trajectory/correct-tool-selected.js +45 -0
- package/dist/scorers/trajectory/correct-tool-selected.js.map +1 -0
- package/dist/scorers/trajectory/final-state-correct.d.ts +21 -0
- package/dist/scorers/trajectory/final-state-correct.d.ts.map +1 -0
- package/dist/scorers/trajectory/final-state-correct.js +42 -0
- package/dist/scorers/trajectory/final-state-correct.js.map +1 -0
- package/dist/scorers/trajectory/recovery-after-error.d.ts +15 -0
- package/dist/scorers/trajectory/recovery-after-error.d.ts.map +1 -0
- package/dist/scorers/trajectory/recovery-after-error.js +37 -0
- package/dist/scorers/trajectory/recovery-after-error.js.map +1 -0
- package/dist/scorers/trajectory/redundant-call-detection.d.ts +19 -0
- package/dist/scorers/trajectory/redundant-call-detection.d.ts.map +1 -0
- package/dist/scorers/trajectory/redundant-call-detection.js +39 -0
- package/dist/scorers/trajectory/redundant-call-detection.js.map +1 -0
- package/dist/scorers/trajectory/types.d.ts +49 -0
- package/dist/scorers/trajectory/types.d.ts.map +1 -0
- package/dist/scorers/trajectory/util.js +71 -0
- package/dist/scorers/trajectory/util.js.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +81 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# @graphorin/evals
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Initial release of the eval framework for the Graphorin framework.
|
|
8
|
+
Ships scorer libraries, dataset loaders (JSONL/CSV/from-traces),
|
|
9
|
+
reporters (terminal/markdown/JSON/JUnit), parallel runner with
|
|
10
|
+
regression detection, and CLI integration helpers.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Oleksiy Stepurenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# @graphorin/evals
|
|
2
|
+
|
|
3
|
+
> Eval framework for the [Graphorin](https://github.com/o-stepper/graphorin)
|
|
4
|
+
> framework. Ships scorer libraries (code, LLM-judge, prebuilt rubrics),
|
|
5
|
+
> dataset loaders (JSONL / CSV / from-traces / iterable), reporters
|
|
6
|
+
> (terminal / markdown / JSON / JUnit / HTML), a parallel runner with
|
|
7
|
+
> bounded concurrency, and regression detection that compares the
|
|
8
|
+
> current run against a stored baseline.
|
|
9
|
+
>
|
|
10
|
+
> Project Graphorin · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko ·
|
|
11
|
+
> <https://github.com/o-stepper/graphorin>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Status
|
|
16
|
+
|
|
17
|
+
- **Published:** v0.5.0 (optional sub-pack; the full orchestrator is
|
|
18
|
+
decoupled from `@graphorin/observability` per RB-17 / DEC-152).
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add @graphorin/evals
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The package depends only on `@graphorin/core` and
|
|
29
|
+
`@graphorin/observability`; reporters / loaders are part of the same
|
|
30
|
+
bundle so consumers do not need additional installs.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Quickstart
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import {
|
|
38
|
+
runEvals,
|
|
39
|
+
loadJsonlDataset,
|
|
40
|
+
exactMatch,
|
|
41
|
+
renderTerminalReport,
|
|
42
|
+
exitOnFailures,
|
|
43
|
+
} from '@graphorin/evals';
|
|
44
|
+
|
|
45
|
+
const dataset = await loadJsonlDataset('./fixtures/golden.jsonl');
|
|
46
|
+
const report = await runEvals({
|
|
47
|
+
agent, // anything with `run(input)`
|
|
48
|
+
dataset,
|
|
49
|
+
scorers: [exactMatch({ caseInsensitive: true })],
|
|
50
|
+
concurrency: 4,
|
|
51
|
+
});
|
|
52
|
+
console.log(renderTerminalReport(report));
|
|
53
|
+
exitOnFailures(report);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Scorers
|
|
59
|
+
|
|
60
|
+
| Scorer family | Identifiers | Notes |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| `code/` | `exactMatch`, `regexMatch`, `jsonPath`, `predicate` | Pure-code grading. No provider call.|
|
|
63
|
+
| `llm/` | `llmJudge` | LLM-as-judge. Default `gpt-4o-mini`-class judge with `temperature: 0`. |
|
|
64
|
+
| `prebuilt/` | `toxicityScorer`, `factualityScorer`, `helpfulnessScorer` | Wrap `llmJudge` with a project-tested rubric. |
|
|
65
|
+
| `trajectory/` | `correctToolSelected`, `argumentValidity`, `redundantCallDetection`, `recoveryAfterError`, `finalStateCorrect` | Pure-code, offline scorers over a `Trajectory` (the tool calls a harness made). Measure harness reliability — tool selection, argument validity, redundant work, error recovery, goal state. |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Dataset loaders
|
|
70
|
+
|
|
71
|
+
| Loader | Use |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `loadJsonlDataset(path)` | Read a JSONL file. Each line is a JSON object with `input` + optional `expected` / `id` / `metadata`.|
|
|
74
|
+
| `loadCsvDataset(path)` | Read a CSV file (RFC 4180 strict subset). Columns map by name.|
|
|
75
|
+
| `loadDatasetFromTraces(path, { extract })` | Distil a dataset from the framework's replay log.|
|
|
76
|
+
| `fromIterable(cases)` | Wrap an in-memory array as a dataset (tests / ad-hoc data).|
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Reporters
|
|
81
|
+
|
|
82
|
+
| Reporter | Output | Best for |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `renderTerminalReport(report)` | Plain text (no ANSI). | CI logs, local dev. |
|
|
85
|
+
| `renderMarkdownReport(report)` | Markdown. | PR descriptions, doc sites. |
|
|
86
|
+
| `renderJsonReport(report)` | Canonical JSON. | Dashboards, regression checkers. |
|
|
87
|
+
| `renderJunitReport(report)` | JUnit XML. | GitHub Actions / GitLab / CircleCI. |
|
|
88
|
+
| `renderHtmlReport(report)` | Self-contained HTML. | Artifact viewers. |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Parallel runner
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const report = await runEvals({
|
|
96
|
+
agent,
|
|
97
|
+
dataset,
|
|
98
|
+
scorers,
|
|
99
|
+
iterations: 3, // each case run 3 times for variance estimation
|
|
100
|
+
concurrency: 8, // up to 8 parallel agent.run() calls
|
|
101
|
+
signal: controller.signal,
|
|
102
|
+
onProgress: (e) => console.log(`${e.index}/${e.total} ${e.caseId}`),
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Regression detection
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { detectRegressions, exitOnFailures } from '@graphorin/evals';
|
|
112
|
+
|
|
113
|
+
const baseline = JSON.parse(await readFile('./baselines/golden.json', 'utf8'));
|
|
114
|
+
const report = await runEvals(...);
|
|
115
|
+
const regression = detectRegressions(report, baseline, {
|
|
116
|
+
maxPassRateDropPct: 5,
|
|
117
|
+
maxAvgScoreDrop: 0.05,
|
|
118
|
+
maxAvgDurationIncreaseMs: 250,
|
|
119
|
+
});
|
|
120
|
+
if (regression.hasRegressions) {
|
|
121
|
+
for (const f of regression.findings) {
|
|
122
|
+
console.error(`regression — ${f.kind}: ${f.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exitOnFailures(report, regression);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Multi-format report writing
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { writeReports } from '@graphorin/evals';
|
|
134
|
+
|
|
135
|
+
await writeReports({
|
|
136
|
+
report,
|
|
137
|
+
outDir: './eval-out',
|
|
138
|
+
formats: ['terminal', 'markdown', 'json', 'junit', 'html'],
|
|
139
|
+
basename: 'golden',
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Related decisions
|
|
146
|
+
|
|
147
|
+
- DEC-152 — Eval split: keep evaluation interfaces in `@graphorin/observability`, ship the full eval framework as `@graphorin/evals`.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## License
|
|
152
|
+
|
|
153
|
+
MIT © 2026 Oleksiy Stepurenko
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
**Project Graphorin** · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { RegressionOptions, RegressionReport } from "../types.js";
|
|
2
|
+
import { detectRegressions } from "../regression.js";
|
|
3
|
+
import { EvalReport } from "@graphorin/observability/eval";
|
|
4
|
+
|
|
5
|
+
//#region src/cli/index.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Set `process.exitCode` to `1` when at least one case failed, or
|
|
9
|
+
* when a regression report contains findings. Uses `exitCode` rather
|
|
10
|
+
* than `process.exit` so other async tasks finish cleanly.
|
|
11
|
+
*
|
|
12
|
+
* @stable
|
|
13
|
+
*/
|
|
14
|
+
declare function exitOnFailures<I, O>(report: EvalReport<I, O>, regression?: RegressionReport<I, O>): void;
|
|
15
|
+
/**
|
|
16
|
+
* Reporter ids accepted by {@link writeReports}.
|
|
17
|
+
*
|
|
18
|
+
* @stable
|
|
19
|
+
*/
|
|
20
|
+
type ReporterFormat = 'terminal' | 'markdown' | 'json' | 'junit' | 'html';
|
|
21
|
+
/** @stable */
|
|
22
|
+
interface WriteReportsOptions<I, O> {
|
|
23
|
+
readonly report: EvalReport<I, O>;
|
|
24
|
+
readonly outDir?: string;
|
|
25
|
+
readonly formats: ReadonlyArray<ReporterFormat>;
|
|
26
|
+
readonly basename?: string;
|
|
27
|
+
}
|
|
28
|
+
/** @stable */
|
|
29
|
+
interface WrittenReport {
|
|
30
|
+
readonly format: ReporterFormat;
|
|
31
|
+
readonly path: string;
|
|
32
|
+
readonly bytes: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Render the report in every requested format and write each one to a
|
|
36
|
+
* file. Returns the manifest of written files.
|
|
37
|
+
*
|
|
38
|
+
* @stable
|
|
39
|
+
*/
|
|
40
|
+
declare function writeReports<I, O>(options: WriteReportsOptions<I, O>): Promise<ReadonlyArray<WrittenReport>>;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { type RegressionOptions, ReporterFormat, WriteReportsOptions, WrittenReport, detectRegressions, exitOnFailures, writeReports };
|
|
43
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;iBAkDgB,6BACN,WAAW,GAAG,iBACT,iBAAiB,GAAG;;;;;;KAWvB,cAAA;;UAGK;mBACE,WAAW,GAAG;;oBAEb,cAAc;;;;UAKjB,aAAA;mBACE;;;;;;;;;;iBAWG,4BACX,oBAAoB,GAAG,KAC/B,QAAQ,cAAc"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { detectRegressions } from "../regression.js";
|
|
2
|
+
import { renderHtmlReport } from "../reporters/html.js";
|
|
3
|
+
import { renderJsonReport } from "../reporters/json.js";
|
|
4
|
+
import { renderJunitReport } from "../reporters/junit.js";
|
|
5
|
+
import { renderMarkdownReport } from "../reporters/markdown.js";
|
|
6
|
+
import { renderTerminalReport } from "../reporters/terminal.js";
|
|
7
|
+
import { writeFile } from "node:fs/promises";
|
|
8
|
+
import process from "node:process";
|
|
9
|
+
|
|
10
|
+
//#region src/cli/index.ts
|
|
11
|
+
/**
|
|
12
|
+
* CLI integration helpers. Convenience wrappers that combine the
|
|
13
|
+
* runner + a reporter + an exit-code mapping so consumer scripts can
|
|
14
|
+
* stay short.
|
|
15
|
+
*
|
|
16
|
+
* Typical use from a `package.json` script:
|
|
17
|
+
*
|
|
18
|
+
* ```jsonc
|
|
19
|
+
* {
|
|
20
|
+
* "scripts": {
|
|
21
|
+
* "eval": "node ./scripts/run-evals.mjs"
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Where `run-evals.mjs` looks like:
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { runEvals, exitOnFailures, renderTerminalReport } from '@graphorin/evals';
|
|
30
|
+
*
|
|
31
|
+
* const report = await runEvals({...});
|
|
32
|
+
* console.log(renderTerminalReport(report));
|
|
33
|
+
* exitOnFailures(report);
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @packageDocumentation
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Set `process.exitCode` to `1` when at least one case failed, or
|
|
40
|
+
* when a regression report contains findings. Uses `exitCode` rather
|
|
41
|
+
* than `process.exit` so other async tasks finish cleanly.
|
|
42
|
+
*
|
|
43
|
+
* @stable
|
|
44
|
+
*/
|
|
45
|
+
function exitOnFailures(report, regression) {
|
|
46
|
+
if (report.summary.failed > 0) process.exitCode = 1;
|
|
47
|
+
if (regression?.hasRegressions) process.exitCode = 1;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Render the report in every requested format and write each one to a
|
|
51
|
+
* file. Returns the manifest of written files.
|
|
52
|
+
*
|
|
53
|
+
* @stable
|
|
54
|
+
*/
|
|
55
|
+
async function writeReports(options) {
|
|
56
|
+
const out = [];
|
|
57
|
+
const dir = options.outDir ?? ".";
|
|
58
|
+
const basename = options.basename ?? "eval-report";
|
|
59
|
+
for (const format of options.formats) {
|
|
60
|
+
const { extension, body } = renderForFormat(format, options.report);
|
|
61
|
+
const path = `${dir}/${basename}.${extension}`;
|
|
62
|
+
await writeFile(path, body, "utf8");
|
|
63
|
+
out.push({
|
|
64
|
+
format,
|
|
65
|
+
path,
|
|
66
|
+
bytes: Buffer.byteLength(body, "utf8")
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
function renderForFormat(format, report) {
|
|
72
|
+
switch (format) {
|
|
73
|
+
case "terminal": return {
|
|
74
|
+
extension: "txt",
|
|
75
|
+
body: renderTerminalReport(report)
|
|
76
|
+
};
|
|
77
|
+
case "markdown": return {
|
|
78
|
+
extension: "md",
|
|
79
|
+
body: renderMarkdownReport(report)
|
|
80
|
+
};
|
|
81
|
+
case "json": return {
|
|
82
|
+
extension: "json",
|
|
83
|
+
body: renderJsonReport(report, { pretty: true })
|
|
84
|
+
};
|
|
85
|
+
case "junit": return {
|
|
86
|
+
extension: "xml",
|
|
87
|
+
body: renderJunitReport(report)
|
|
88
|
+
};
|
|
89
|
+
case "html": return {
|
|
90
|
+
extension: "html",
|
|
91
|
+
body: renderHtmlReport(report)
|
|
92
|
+
};
|
|
93
|
+
default: {
|
|
94
|
+
const exhaustive = format;
|
|
95
|
+
throw new Error(`unknown reporter format '${exhaustive}'`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
export { detectRegressions, exitOnFailures, writeReports };
|
|
102
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["out: WrittenReport[]","exhaustive: never"],"sources":["../../src/cli/index.ts"],"sourcesContent":["/**\n * CLI integration helpers. Convenience wrappers that combine the\n * runner + a reporter + an exit-code mapping so consumer scripts can\n * stay short.\n *\n * Typical use from a `package.json` script:\n *\n * ```jsonc\n * {\n * \"scripts\": {\n * \"eval\": \"node ./scripts/run-evals.mjs\"\n * }\n * }\n * ```\n *\n * Where `run-evals.mjs` looks like:\n *\n * ```ts\n * import { runEvals, exitOnFailures, renderTerminalReport } from '@graphorin/evals';\n *\n * const report = await runEvals({...});\n * console.log(renderTerminalReport(report));\n * exitOnFailures(report);\n * ```\n *\n * @packageDocumentation\n */\n\nimport { writeFile } from 'node:fs/promises';\nimport process from 'node:process';\n\nimport type { EvalReport } from '@graphorin/observability/eval';\n\nimport { detectRegressions } from '../regression.js';\nimport {\n renderHtmlReport,\n renderJsonReport,\n renderJunitReport,\n renderMarkdownReport,\n renderTerminalReport,\n} from '../reporters/index.js';\nimport type { RegressionOptions, RegressionReport } from '../types.js';\n\n/**\n * Set `process.exitCode` to `1` when at least one case failed, or\n * when a regression report contains findings. Uses `exitCode` rather\n * than `process.exit` so other async tasks finish cleanly.\n *\n * @stable\n */\nexport function exitOnFailures<I, O>(\n report: EvalReport<I, O>,\n regression?: RegressionReport<I, O>,\n): void {\n if (report.summary.failed > 0) process.exitCode = 1;\n if (regression?.hasRegressions) process.exitCode = 1;\n}\n\n/**\n * Reporter ids accepted by {@link writeReports}.\n *\n * @stable\n */\nexport type ReporterFormat = 'terminal' | 'markdown' | 'json' | 'junit' | 'html';\n\n/** @stable */\nexport interface WriteReportsOptions<I, O> {\n readonly report: EvalReport<I, O>;\n readonly outDir?: string;\n readonly formats: ReadonlyArray<ReporterFormat>;\n readonly basename?: string;\n}\n\n/** @stable */\nexport interface WrittenReport {\n readonly format: ReporterFormat;\n readonly path: string;\n readonly bytes: number;\n}\n\n/**\n * Render the report in every requested format and write each one to a\n * file. Returns the manifest of written files.\n *\n * @stable\n */\nexport async function writeReports<I, O>(\n options: WriteReportsOptions<I, O>,\n): Promise<ReadonlyArray<WrittenReport>> {\n const out: WrittenReport[] = [];\n const dir = options.outDir ?? '.';\n const basename = options.basename ?? 'eval-report';\n for (const format of options.formats) {\n const { extension, body } = renderForFormat(format, options.report);\n const path = `${dir}/${basename}.${extension}`;\n await writeFile(path, body, 'utf8');\n out.push({ format, path, bytes: Buffer.byteLength(body, 'utf8') });\n }\n return out;\n}\n\nfunction renderForFormat<I, O>(\n format: ReporterFormat,\n report: EvalReport<I, O>,\n): { extension: string; body: string } {\n switch (format) {\n case 'terminal':\n return { extension: 'txt', body: renderTerminalReport(report) };\n case 'markdown':\n return { extension: 'md', body: renderMarkdownReport(report) };\n case 'json':\n return { extension: 'json', body: renderJsonReport(report, { pretty: true }) };\n case 'junit':\n return { extension: 'xml', body: renderJunitReport(report) };\n case 'html':\n return { extension: 'html', body: renderHtmlReport(report) };\n default: {\n const exhaustive: never = format;\n throw new Error(`unknown reporter format '${exhaustive as string}'`);\n }\n }\n}\n\nexport { detectRegressions, type RegressionOptions };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,SAAgB,eACd,QACA,YACM;AACN,KAAI,OAAO,QAAQ,SAAS,EAAG,SAAQ,WAAW;AAClD,KAAI,YAAY,eAAgB,SAAQ,WAAW;;;;;;;;AA+BrD,eAAsB,aACpB,SACuC;CACvC,MAAMA,MAAuB,EAAE;CAC/B,MAAM,MAAM,QAAQ,UAAU;CAC9B,MAAM,WAAW,QAAQ,YAAY;AACrC,MAAK,MAAM,UAAU,QAAQ,SAAS;EACpC,MAAM,EAAE,WAAW,SAAS,gBAAgB,QAAQ,QAAQ,OAAO;EACnE,MAAM,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG;AACnC,QAAM,UAAU,MAAM,MAAM,OAAO;AACnC,MAAI,KAAK;GAAE;GAAQ;GAAM,OAAO,OAAO,WAAW,MAAM,OAAO;GAAE,CAAC;;AAEpE,QAAO;;AAGT,SAAS,gBACP,QACA,QACqC;AACrC,SAAQ,QAAR;EACE,KAAK,WACH,QAAO;GAAE,WAAW;GAAO,MAAM,qBAAqB,OAAO;GAAE;EACjE,KAAK,WACH,QAAO;GAAE,WAAW;GAAM,MAAM,qBAAqB,OAAO;GAAE;EAChE,KAAK,OACH,QAAO;GAAE,WAAW;GAAQ,MAAM,iBAAiB,QAAQ,EAAE,QAAQ,MAAM,CAAC;GAAE;EAChF,KAAK,QACH,QAAO;GAAE,WAAW;GAAO,MAAM,kBAAkB,OAAO;GAAE;EAC9D,KAAK,OACH,QAAO;GAAE,WAAW;GAAQ,MAAM,iBAAiB,OAAO;GAAE;EAC9D,SAAS;GACP,MAAMC,aAAoB;AAC1B,SAAM,IAAI,MAAM,4BAA4B,WAAqB,GAAG"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { AgentLike, Case, Dataset, EvalCaseResult, EvalReport, ProgressEvent, RegressionFinding, RegressionOptions, RegressionReport, RunEvalOptions, RunOptions, ScoreResult, Scorer } from "./types.js";
|
|
2
|
+
import { detectRegressions } from "./regression.js";
|
|
3
|
+
import { ReporterFormat, WriteReportsOptions, WrittenReport, exitOnFailures, writeReports } from "./cli/index.js";
|
|
4
|
+
import { LoadCsvOptions, loadCsvDataset, parseCsv } from "./loaders/csv.js";
|
|
5
|
+
import { MemoryEvalAbility, MemoryEvalInput, MemoryEvalSession, MemoryEvalTurn } from "./loaders/memory-eval.js";
|
|
6
|
+
import { LoadDmrOptions, loadDmrDataset, parseDmr } from "./loaders/dmr.js";
|
|
7
|
+
import { FromTracesOptions, TraceEvent, loadDatasetFromTraces } from "./loaders/from-traces.js";
|
|
8
|
+
import { fromIterable } from "./loaders/iterable.js";
|
|
9
|
+
import { LoadJsonlOptions, loadJsonlDataset, parseJsonl } from "./loaders/jsonl.js";
|
|
10
|
+
import { LoadLocomoOptions, loadLocomoDataset, parseLocomo } from "./loaders/locomo.js";
|
|
11
|
+
import { LoadLongMemEvalOptions, loadLongMemEvalDataset, parseLongMemEval } from "./loaders/longmemeval.js";
|
|
12
|
+
import { renderHtmlReport } from "./reporters/html.js";
|
|
13
|
+
import { renderJsonReport } from "./reporters/json.js";
|
|
14
|
+
import { renderJunitReport } from "./reporters/junit.js";
|
|
15
|
+
import { renderMarkdownReport } from "./reporters/markdown.js";
|
|
16
|
+
import { renderTerminalReport } from "./reporters/terminal.js";
|
|
17
|
+
import { runEvals } from "./runner.js";
|
|
18
|
+
import { ExactMatchOptions, exactMatch } from "./scorers/code/exact-match.js";
|
|
19
|
+
import { JsonPathOptions, jsonPath } from "./scorers/code/json-path.js";
|
|
20
|
+
import { PredicateOptions, predicate } from "./scorers/code/predicate.js";
|
|
21
|
+
import { RegexMatchOptions, regexMatch } from "./scorers/code/regex.js";
|
|
22
|
+
import { LlmJudgeOptions, fenceForJudge, llmJudge, parseScore, scoreContract } from "./scorers/llm/judge.js";
|
|
23
|
+
import { PrebuiltScorerOptions, factualityScorer, helpfulnessScorer, toxicityScorer } from "./scorers/prebuilt/index.js";
|
|
24
|
+
import { Trajectory, TrajectoryToolCall } from "./scorers/trajectory/types.js";
|
|
25
|
+
import { ArgumentValidityOptions, argumentValidity } from "./scorers/trajectory/argument-validity.js";
|
|
26
|
+
import { CorrectToolSelectedOptions, correctToolSelected } from "./scorers/trajectory/correct-tool-selected.js";
|
|
27
|
+
import { FinalStateCorrectOptions, finalStateCorrect } from "./scorers/trajectory/final-state-correct.js";
|
|
28
|
+
import { RecoveryAfterErrorOptions, recoveryAfterError } from "./scorers/trajectory/recovery-after-error.js";
|
|
29
|
+
import { RedundantCallDetectionOptions, redundantCallDetection } from "./scorers/trajectory/redundant-call-detection.js";
|
|
30
|
+
|
|
31
|
+
//#region src/index.d.ts
|
|
32
|
+
/**
|
|
33
|
+
* @graphorin/evals — eval framework for the Graphorin framework.
|
|
34
|
+
*
|
|
35
|
+
* Builds on the eval primitives shipped from `@graphorin/observability`
|
|
36
|
+
* (RB-17 / DEC-152) — full orchestrator lives here, post-MVP, decoupled
|
|
37
|
+
* from the core observability bundle so consumers do not pay the
|
|
38
|
+
* dataset / reporter cost when only the inline runner is needed.
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* import {
|
|
42
|
+
* runEvals,
|
|
43
|
+
* loadJsonlDataset,
|
|
44
|
+
* exactMatch,
|
|
45
|
+
* renderTerminalReport,
|
|
46
|
+
* exitOnFailures,
|
|
47
|
+
* } from '@graphorin/evals';
|
|
48
|
+
*
|
|
49
|
+
* const dataset = await loadJsonlDataset('./fixtures/golden.jsonl');
|
|
50
|
+
* const report = await runEvals({
|
|
51
|
+
* agent,
|
|
52
|
+
* dataset,
|
|
53
|
+
* scorers: [exactMatch()],
|
|
54
|
+
* concurrency: 4,
|
|
55
|
+
* });
|
|
56
|
+
* console.log(renderTerminalReport(report));
|
|
57
|
+
* exitOnFailures(report);
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @packageDocumentation
|
|
61
|
+
*/
|
|
62
|
+
/** Canonical version constant. Mirrors the `package.json` version. */
|
|
63
|
+
declare const VERSION = "0.5.0";
|
|
64
|
+
//#endregion
|
|
65
|
+
export { type AgentLike, type ArgumentValidityOptions, type Case, type CorrectToolSelectedOptions, type Dataset, type EvalCaseResult, type EvalReport, type ExactMatchOptions, type FinalStateCorrectOptions, type FromTracesOptions, type JsonPathOptions, type LlmJudgeOptions, type LoadCsvOptions, type LoadDmrOptions, type LoadJsonlOptions, type LoadLocomoOptions, type LoadLongMemEvalOptions, type MemoryEvalAbility, type MemoryEvalInput, type MemoryEvalSession, type MemoryEvalTurn, type PrebuiltScorerOptions, type PredicateOptions, type ProgressEvent, type RecoveryAfterErrorOptions, type RedundantCallDetectionOptions, type RegexMatchOptions, type RegressionFinding, type RegressionOptions, type RegressionReport, type ReporterFormat, type RunEvalOptions, type RunOptions, type ScoreResult, type Scorer, type TraceEvent, type Trajectory, type TrajectoryToolCall, VERSION, type WriteReportsOptions, type WrittenReport, argumentValidity, correctToolSelected, detectRegressions, detectRegressions as detectRegressionsFromReports, exactMatch, exitOnFailures, factualityScorer, fenceForJudge, finalStateCorrect, fromIterable, helpfulnessScorer, jsonPath, llmJudge, loadCsvDataset, loadDatasetFromTraces, loadDmrDataset, loadJsonlDataset, loadLocomoDataset, loadLongMemEvalDataset, parseCsv, parseDmr, parseJsonl, parseLocomo, parseLongMemEval, parseScore, predicate, recoveryAfterError, redundantCallDetection, regexMatch, renderHtmlReport, renderJsonReport, renderJunitReport, renderMarkdownReport, renderTerminalReport, runEvals, scoreContract, toxicityScorer, writeReports };
|
|
66
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCa,cAAA,OAAA,GAAO,OAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { detectRegressions } from "./regression.js";
|
|
2
|
+
import { renderHtmlReport } from "./reporters/html.js";
|
|
3
|
+
import { renderJsonReport } from "./reporters/json.js";
|
|
4
|
+
import { renderJunitReport } from "./reporters/junit.js";
|
|
5
|
+
import { renderMarkdownReport } from "./reporters/markdown.js";
|
|
6
|
+
import { renderTerminalReport } from "./reporters/terminal.js";
|
|
7
|
+
import { exitOnFailures, writeReports } from "./cli/index.js";
|
|
8
|
+
import { loadCsvDataset, parseCsv } from "./loaders/csv.js";
|
|
9
|
+
import { loadDmrDataset, parseDmr } from "./loaders/dmr.js";
|
|
10
|
+
import { loadDatasetFromTraces } from "./loaders/from-traces.js";
|
|
11
|
+
import { fromIterable } from "./loaders/iterable.js";
|
|
12
|
+
import { loadJsonlDataset, parseJsonl } from "./loaders/jsonl.js";
|
|
13
|
+
import { loadLocomoDataset, parseLocomo } from "./loaders/locomo.js";
|
|
14
|
+
import { loadLongMemEvalDataset, parseLongMemEval } from "./loaders/longmemeval.js";
|
|
15
|
+
import { runEvals } from "./runner.js";
|
|
16
|
+
import { exactMatch } from "./scorers/code/exact-match.js";
|
|
17
|
+
import { jsonPath } from "./scorers/code/json-path.js";
|
|
18
|
+
import { predicate } from "./scorers/code/predicate.js";
|
|
19
|
+
import { regexMatch } from "./scorers/code/regex.js";
|
|
20
|
+
import { fenceForJudge, llmJudge, parseScore, scoreContract } from "./scorers/llm/judge.js";
|
|
21
|
+
import { factualityScorer, helpfulnessScorer, toxicityScorer } from "./scorers/prebuilt/index.js";
|
|
22
|
+
import { argumentValidity } from "./scorers/trajectory/argument-validity.js";
|
|
23
|
+
import { correctToolSelected } from "./scorers/trajectory/correct-tool-selected.js";
|
|
24
|
+
import { finalStateCorrect } from "./scorers/trajectory/final-state-correct.js";
|
|
25
|
+
import { recoveryAfterError } from "./scorers/trajectory/recovery-after-error.js";
|
|
26
|
+
import { redundantCallDetection } from "./scorers/trajectory/redundant-call-detection.js";
|
|
27
|
+
|
|
28
|
+
//#region src/index.ts
|
|
29
|
+
/**
|
|
30
|
+
* @graphorin/evals — eval framework for the Graphorin framework.
|
|
31
|
+
*
|
|
32
|
+
* Builds on the eval primitives shipped from `@graphorin/observability`
|
|
33
|
+
* (RB-17 / DEC-152) — full orchestrator lives here, post-MVP, decoupled
|
|
34
|
+
* from the core observability bundle so consumers do not pay the
|
|
35
|
+
* dataset / reporter cost when only the inline runner is needed.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import {
|
|
39
|
+
* runEvals,
|
|
40
|
+
* loadJsonlDataset,
|
|
41
|
+
* exactMatch,
|
|
42
|
+
* renderTerminalReport,
|
|
43
|
+
* exitOnFailures,
|
|
44
|
+
* } from '@graphorin/evals';
|
|
45
|
+
*
|
|
46
|
+
* const dataset = await loadJsonlDataset('./fixtures/golden.jsonl');
|
|
47
|
+
* const report = await runEvals({
|
|
48
|
+
* agent,
|
|
49
|
+
* dataset,
|
|
50
|
+
* scorers: [exactMatch()],
|
|
51
|
+
* concurrency: 4,
|
|
52
|
+
* });
|
|
53
|
+
* console.log(renderTerminalReport(report));
|
|
54
|
+
* exitOnFailures(report);
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @packageDocumentation
|
|
58
|
+
*/
|
|
59
|
+
/** Canonical version constant. Mirrors the `package.json` version. */
|
|
60
|
+
const VERSION = "0.5.0";
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
export { VERSION, argumentValidity, correctToolSelected, detectRegressions, detectRegressions as detectRegressionsFromReports, exactMatch, exitOnFailures, factualityScorer, fenceForJudge, finalStateCorrect, fromIterable, helpfulnessScorer, jsonPath, llmJudge, loadCsvDataset, loadDatasetFromTraces, loadDmrDataset, loadJsonlDataset, loadLocomoDataset, loadLongMemEvalDataset, parseCsv, parseDmr, parseJsonl, parseLocomo, parseLongMemEval, parseScore, predicate, recoveryAfterError, redundantCallDetection, regexMatch, renderHtmlReport, renderJsonReport, renderJunitReport, renderMarkdownReport, renderTerminalReport, runEvals, scoreContract, toxicityScorer, writeReports };
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/evals — eval framework for the Graphorin framework.\n *\n * Builds on the eval primitives shipped from `@graphorin/observability`\n * (RB-17 / DEC-152) — full orchestrator lives here, post-MVP, decoupled\n * from the core observability bundle so consumers do not pay the\n * dataset / reporter cost when only the inline runner is needed.\n *\n * ```ts\n * import {\n * runEvals,\n * loadJsonlDataset,\n * exactMatch,\n * renderTerminalReport,\n * exitOnFailures,\n * } from '@graphorin/evals';\n *\n * const dataset = await loadJsonlDataset('./fixtures/golden.jsonl');\n * const report = await runEvals({\n * agent,\n * dataset,\n * scorers: [exactMatch()],\n * concurrency: 4,\n * });\n * console.log(renderTerminalReport(report));\n * exitOnFailures(report);\n * ```\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant. Mirrors the `package.json` version. */\nexport const VERSION = '0.5.0';\n\nexport {\n detectRegressions,\n exitOnFailures,\n type ReporterFormat,\n type WriteReportsOptions,\n type WrittenReport,\n writeReports,\n} from './cli/index.js';\nexport type {\n FromTracesOptions,\n LoadDmrOptions,\n LoadLocomoOptions,\n LoadLongMemEvalOptions,\n MemoryEvalAbility,\n MemoryEvalInput,\n MemoryEvalSession,\n MemoryEvalTurn,\n TraceEvent,\n} from './loaders/index.js';\nexport {\n fromIterable,\n type LoadCsvOptions,\n type LoadJsonlOptions,\n loadCsvDataset,\n loadDatasetFromTraces,\n loadDmrDataset,\n loadJsonlDataset,\n loadLocomoDataset,\n loadLongMemEvalDataset,\n parseCsv,\n parseDmr,\n parseJsonl,\n parseLocomo,\n parseLongMemEval,\n} from './loaders/index.js';\nexport { detectRegressions as detectRegressionsFromReports } from './regression.js';\nexport {\n renderHtmlReport,\n renderJsonReport,\n renderJunitReport,\n renderMarkdownReport,\n renderTerminalReport,\n} from './reporters/index.js';\nexport { runEvals } from './runner.js';\nexport {\n type ArgumentValidityOptions,\n argumentValidity,\n type CorrectToolSelectedOptions,\n correctToolSelected,\n type ExactMatchOptions,\n exactMatch,\n type FinalStateCorrectOptions,\n factualityScorer,\n fenceForJudge,\n finalStateCorrect,\n helpfulnessScorer,\n type JsonPathOptions,\n jsonPath,\n type LlmJudgeOptions,\n llmJudge,\n type PrebuiltScorerOptions,\n type PredicateOptions,\n parseScore,\n predicate,\n type RecoveryAfterErrorOptions,\n type RedundantCallDetectionOptions,\n type RegexMatchOptions,\n recoveryAfterError,\n redundantCallDetection,\n regexMatch,\n scoreContract,\n type Trajectory,\n type TrajectoryToolCall,\n toxicityScorer,\n} from './scorers/index.js';\nexport type {\n AgentLike,\n Case,\n Dataset,\n EvalCaseResult,\n EvalReport,\n ProgressEvent,\n RegressionFinding,\n RegressionOptions,\n RegressionReport,\n RunEvalOptions,\n RunOptions,\n ScoreResult,\n Scorer,\n} from './types.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,MAAa,UAAU"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Case, Dataset } from "@graphorin/observability/eval";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/csv.d.ts
|
|
4
|
+
|
|
5
|
+
/** @stable */
|
|
6
|
+
interface LoadCsvOptions {
|
|
7
|
+
readonly delimiter?: string;
|
|
8
|
+
readonly name?: string;
|
|
9
|
+
readonly description?: string;
|
|
10
|
+
readonly mapper?: (row: Record<string, string>, index: number) => Case<unknown, unknown>;
|
|
11
|
+
}
|
|
12
|
+
/** @stable */
|
|
13
|
+
declare function loadCsvDataset(path: string, options?: LoadCsvOptions): Promise<Dataset<unknown, unknown>>;
|
|
14
|
+
/**
|
|
15
|
+
* Pure parser. Exported separately so tests can exercise the
|
|
16
|
+
* column-mapping behaviour without touching the filesystem.
|
|
17
|
+
*
|
|
18
|
+
* @stable
|
|
19
|
+
*/
|
|
20
|
+
declare function parseCsv(text: string, options?: LoadCsvOptions): ReadonlyArray<Case<unknown, unknown>>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { LoadCsvOptions, loadCsvDataset, parseCsv };
|
|
23
|
+
//# sourceMappingURL=csv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.d.ts","names":[],"sources":["../../src/loaders/csv.ts"],"sourcesContent":[],"mappings":";;;;;UAmBiB,cAAA;;;;0BAIS,0CAA0C;;;iBAI9C,cAAA,yBAEX,iBACR,QAAQ;;;;;;;iBAiBK,QAAA,yBAEL,iBACR,cAAc"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/csv.ts
|
|
4
|
+
/**
|
|
5
|
+
* CSV dataset loader. Implements RFC 4180 strict subset:
|
|
6
|
+
*
|
|
7
|
+
* - Comma separator (configurable).
|
|
8
|
+
* - Optional `"`-quoted cells; doubled `""` to embed a literal `"`.
|
|
9
|
+
* - Header row required by default.
|
|
10
|
+
*
|
|
11
|
+
* Columns map to `Case` fields by name: `input`, `expected`, `id`,
|
|
12
|
+
* `metadata` (parsed as JSON when present). Unknown columns are
|
|
13
|
+
* ignored unless a `mapper` is supplied.
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
/** @stable */
|
|
18
|
+
async function loadCsvDataset(path, options = {}) {
|
|
19
|
+
return {
|
|
20
|
+
cases: parseCsv(await readFile(path, "utf8"), options),
|
|
21
|
+
metadata: {
|
|
22
|
+
...options.name !== void 0 ? { name: options.name } : {},
|
|
23
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
24
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Pure parser. Exported separately so tests can exercise the
|
|
30
|
+
* column-mapping behaviour without touching the filesystem.
|
|
31
|
+
*
|
|
32
|
+
* @stable
|
|
33
|
+
*/
|
|
34
|
+
function parseCsv(text, options = {}) {
|
|
35
|
+
const rows = parseCsvRows(text, options.delimiter ?? ",");
|
|
36
|
+
if (rows.length === 0) return [];
|
|
37
|
+
const header = rows[0];
|
|
38
|
+
if (header === void 0) return [];
|
|
39
|
+
const dataRows = rows.slice(1);
|
|
40
|
+
const out = [];
|
|
41
|
+
for (let i = 0; i < dataRows.length; i++) {
|
|
42
|
+
const row = dataRows[i] ?? [];
|
|
43
|
+
if (row.every((cell) => cell.length === 0)) continue;
|
|
44
|
+
const record = {};
|
|
45
|
+
for (let c = 0; c < header.length; c++) {
|
|
46
|
+
const key = header[c] ?? "";
|
|
47
|
+
record[key] = row[c] ?? "";
|
|
48
|
+
}
|
|
49
|
+
if (options.mapper !== void 0) {
|
|
50
|
+
out.push(options.mapper(record, i));
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (record.input === void 0) throw new Error(`[graphorin/evals] CSV row ${i + 1} missing required 'input' column.`);
|
|
54
|
+
const sampleCase = {
|
|
55
|
+
...record.id !== void 0 && record.id.length > 0 ? { id: record.id } : {},
|
|
56
|
+
input: record.input,
|
|
57
|
+
..."expected" in record && record.expected !== void 0 && record.expected.length > 0 ? { expected: record.expected } : {},
|
|
58
|
+
...record.metadata !== void 0 && record.metadata.length > 0 ? { metadata: parseMetadata(record.metadata) } : {}
|
|
59
|
+
};
|
|
60
|
+
out.push(sampleCase);
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function parseMetadata(raw) {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(raw);
|
|
67
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
68
|
+
return { value: parsed };
|
|
69
|
+
} catch {
|
|
70
|
+
return { raw };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function parseCsvRows(text, delimiter) {
|
|
74
|
+
const rows = [];
|
|
75
|
+
let current = [];
|
|
76
|
+
let cell = "";
|
|
77
|
+
let inQuotes = false;
|
|
78
|
+
for (let i = 0; i < text.length; i++) {
|
|
79
|
+
const ch = text[i];
|
|
80
|
+
if (inQuotes) {
|
|
81
|
+
if (ch === "\"") if (text[i + 1] === "\"") {
|
|
82
|
+
cell += "\"";
|
|
83
|
+
i += 1;
|
|
84
|
+
} else inQuotes = false;
|
|
85
|
+
else cell += ch;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (ch === "\"") {
|
|
89
|
+
inQuotes = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (ch === delimiter) {
|
|
93
|
+
current.push(cell);
|
|
94
|
+
cell = "";
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (ch === "\n" || ch === "\r") {
|
|
98
|
+
if (ch === "\r" && text[i + 1] === "\n") i += 1;
|
|
99
|
+
current.push(cell);
|
|
100
|
+
rows.push(current);
|
|
101
|
+
current = [];
|
|
102
|
+
cell = "";
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
cell += ch;
|
|
106
|
+
}
|
|
107
|
+
if (cell.length > 0 || current.length > 0) {
|
|
108
|
+
current.push(cell);
|
|
109
|
+
rows.push(current);
|
|
110
|
+
}
|
|
111
|
+
return rows;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
//#endregion
|
|
115
|
+
export { loadCsvDataset, parseCsv };
|
|
116
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","names":["out: Case<unknown, unknown>[]","record: Record<string, string>","sampleCase: Case<unknown, unknown>","rows: string[][]","current: string[]"],"sources":["../../src/loaders/csv.ts"],"sourcesContent":["/**\n * CSV dataset loader. Implements RFC 4180 strict subset:\n *\n * - Comma separator (configurable).\n * - Optional `\"`-quoted cells; doubled `\"\"` to embed a literal `\"`.\n * - Header row required by default.\n *\n * Columns map to `Case` fields by name: `input`, `expected`, `id`,\n * `metadata` (parsed as JSON when present). Unknown columns are\n * ignored unless a `mapper` is supplied.\n *\n * @packageDocumentation\n */\n\nimport { readFile } from 'node:fs/promises';\n\nimport type { Case, Dataset } from '@graphorin/observability/eval';\n\n/** @stable */\nexport interface LoadCsvOptions {\n readonly delimiter?: string;\n readonly name?: string;\n readonly description?: string;\n readonly mapper?: (row: Record<string, string>, index: number) => Case<unknown, unknown>;\n}\n\n/** @stable */\nexport async function loadCsvDataset(\n path: string,\n options: LoadCsvOptions = {},\n): Promise<Dataset<unknown, unknown>> {\n const text = await readFile(path, 'utf8');\n const cases = parseCsv(text, options);\n const meta: Dataset<unknown, unknown>['metadata'] = {\n ...(options.name !== undefined ? { name: options.name } : {}),\n ...(options.description !== undefined ? { description: options.description } : {}),\n createdAt: new Date(),\n };\n return { cases, metadata: meta };\n}\n\n/**\n * Pure parser. Exported separately so tests can exercise the\n * column-mapping behaviour without touching the filesystem.\n *\n * @stable\n */\nexport function parseCsv(\n text: string,\n options: LoadCsvOptions = {},\n): ReadonlyArray<Case<unknown, unknown>> {\n const delimiter = options.delimiter ?? ',';\n const rows = parseCsvRows(text, delimiter);\n if (rows.length === 0) return [];\n const header = rows[0];\n if (header === undefined) return [];\n const dataRows = rows.slice(1);\n const out: Case<unknown, unknown>[] = [];\n for (let i = 0; i < dataRows.length; i++) {\n const row = dataRows[i] ?? [];\n if (row.every((cell) => cell.length === 0)) continue;\n const record: Record<string, string> = {};\n for (let c = 0; c < header.length; c++) {\n const key = header[c] ?? '';\n const value = row[c] ?? '';\n record[key] = value;\n }\n if (options.mapper !== undefined) {\n out.push(options.mapper(record, i));\n continue;\n }\n if (record.input === undefined) {\n throw new Error(`[graphorin/evals] CSV row ${i + 1} missing required 'input' column.`);\n }\n const sampleCase: Case<unknown, unknown> = {\n ...(record.id !== undefined && record.id.length > 0 ? { id: record.id } : {}),\n input: record.input,\n ...('expected' in record && record.expected !== undefined && record.expected.length > 0\n ? { expected: record.expected }\n : {}),\n ...(record.metadata !== undefined && record.metadata.length > 0\n ? { metadata: parseMetadata(record.metadata) }\n : {}),\n };\n out.push(sampleCase);\n }\n return out;\n}\n\nfunction parseMetadata(raw: string): Readonly<Record<string, unknown>> {\n try {\n const parsed = JSON.parse(raw);\n if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Readonly<Record<string, unknown>>;\n }\n return { value: parsed } as Readonly<Record<string, unknown>>;\n } catch {\n return { raw } as Readonly<Record<string, unknown>>;\n }\n}\n\nfunction parseCsvRows(text: string, delimiter: string): string[][] {\n const rows: string[][] = [];\n let current: string[] = [];\n let cell = '';\n let inQuotes = false;\n for (let i = 0; i < text.length; i++) {\n const ch = text[i];\n if (inQuotes) {\n if (ch === '\"') {\n const next = text[i + 1];\n if (next === '\"') {\n cell += '\"';\n i += 1;\n } else {\n inQuotes = false;\n }\n } else {\n cell += ch;\n }\n continue;\n }\n if (ch === '\"') {\n inQuotes = true;\n continue;\n }\n if (ch === delimiter) {\n current.push(cell);\n cell = '';\n continue;\n }\n if (ch === '\\n' || ch === '\\r') {\n if (ch === '\\r' && text[i + 1] === '\\n') i += 1;\n current.push(cell);\n rows.push(current);\n current = [];\n cell = '';\n continue;\n }\n cell += ch;\n }\n if (cell.length > 0 || current.length > 0) {\n current.push(cell);\n rows.push(current);\n }\n return rows;\n}\n"],"mappings":"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"}
|