@m4trix/evals 0.30.0 → 0.32.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 CHANGED
@@ -141,7 +141,7 @@ Group several dataset/evaluator runs under one named config. Each row is either
141
141
  `evaluators: [...]` (same module instances discovery loads) or `evaluatorPattern: "..."`
142
142
  (wildcard / regex rules from `RunnerApi.resolveEvaluatorsByNamePattern`). Multiple jobs share one `--concurrency` cap.
143
143
 
144
- Optional **`repetitions`** on a row (default `1`) runs each matching test case that many times. Every execution in that group shares the same **`repetitionId`** in the evaluator callback **`meta`**, with **`repetitionIndex`** / **`repetitionCount`**. Evaluator **`meta`** includes **`triggerId`**, **`triggerTimestamp`** (ms since epoch when the run was triggered; the simple CLI sets this once at process start), **`datasetName`** (`Dataset.getDisplayLabel()` → `displayName ?? name`), **`testCaseId`** (discovery id, same as runner events), **`testCaseName`** (`TestCase.getDisplayLabel()` → `displayName ?? name`), **`runConfigName`** (the **`RunConfig`** id or **`programmatic`** from **`PROGRAMMATIC_RUN_CONFIG`** for API/TUI-only **`runDatasetWith`**), optional **`experimentName`**, and declared tag lists **`testCaseTags`**, **`runConfigTags`**, and **`evaluatorTags`** (empty arrays when unset). **`Dataset`** **`includedTags` / `excludedTags`** only filter which test cases belong to a dataset; they are not the same as **`TestCase.describe({ tags })`** or **`Evaluator.define({ tags })`**, which label the case/evaluator and show up in **`meta`**. **`Dataset`** and **`TestCase`** follow the same naming convention as **`RunConfig`**: **`name`** is the stable id; optional **`displayName`** is unrestricted for UI. Names may use **kebab-case**, **snake_case**, **camelCase**, etc. (letters, digits, `_`, `-` only, no spaces); resolution is **case-insensitive**.
144
+ Optional **`repetitions`** on a row (default `1`) runs each matching test case that many times. Every execution in that group shares the same **`repetitionId`** in the evaluator callback **`meta`**, with **`repetitionIndex`** / **`repetitionCount`**. Evaluator **`meta`** includes **`triggerId`**, **`triggerTimestamp`** (ms since epoch when the run was triggered; the simple CLI sets this once at process start), **`triggeredAt`** (same instant as ISO 8601), **`datasetName`** (`Dataset.getDisplayLabel()` → `displayName ?? name`), **`testCaseId`** (discovery id, same as runner events), **`testCaseName`** (`TestCase.getDisplayLabel()` → `displayName ?? name`), **`runConfigName`** (the **`RunConfig`** id or **`programmatic`** from **`PROGRAMMATIC_RUN_CONFIG`** for API/TUI-only **`runDatasetWith`**), optional **`experimentName`**, and declared tag lists **`testCaseTags`**, **`runConfigTags`**, and **`evaluatorTags`** (empty arrays when unset). **`Dataset`** **`includedTags` / `excludedTags`** only filter which test cases belong to a dataset; they are not the same as **`TestCase.describe({ tags })`** or **`Evaluator.define({ tags })`**, which label the case/evaluator and show up in **`meta`**. **`Dataset`** and **`TestCase`** follow the same naming convention as **`RunConfig`**: **`name`** is the stable id; optional **`displayName`** is unrestricted for UI. Names may use **kebab-case**, **snake_case**, **camelCase**, etc. (letters, digits, `_`, `-` only, no spaces); resolution is **case-insensitive**.
145
145
 
146
146
  ```ts
147
147
  import { RunConfig } from '@m4trix/evals';
@@ -346,6 +346,23 @@ function isRunConfigLike(value) {
346
346
  function isTestCaseLike(value) {
347
347
  return hasMethod(value, "getName") && hasMethod(value, "getTags") && hasMethod(value, "getInput");
348
348
  }
349
+ function collectTestCasesFromExportValues(exports) {
350
+ const out = [];
351
+ for (const value of exports) {
352
+ if (isTestCaseLike(value)) {
353
+ out.push(value);
354
+ continue;
355
+ }
356
+ if (Array.isArray(value)) {
357
+ for (const item of value) {
358
+ if (isTestCaseLike(item)) {
359
+ out.push(item);
360
+ }
361
+ }
362
+ }
363
+ }
364
+ return out;
365
+ }
349
366
  async function walkDirectory(rootDir, excludeDirectories) {
350
367
  const out = [];
351
368
  async function walk(currentDir) {
@@ -454,7 +471,7 @@ async function collectTestCasesFromFiles(config) {
454
471
  const found = await Promise.all(
455
472
  matched.map(async (absolutePath) => {
456
473
  const exports = await loadModuleExports(absolutePath);
457
- const testCases = exports.filter(isTestCaseLike);
474
+ const testCases = collectTestCasesFromExportValues(exports);
458
475
  const relPath = path.relative(config.rootDir, absolutePath);
459
476
  return testCases.map((testCase) => ({
460
477
  id: toId("test-case", relPath, testCase.getName()),
@@ -1013,6 +1030,7 @@ function processOneEvaluation(task, unit, totalEvaluations, publishEvent, persis
1013
1030
  meta: {
1014
1031
  triggerId: task.triggerId,
1015
1032
  triggerTimestamp: task.triggerTimestamp,
1033
+ triggeredAt: new Date(task.triggerTimestamp).toISOString(),
1016
1034
  runId: evaluatorRunId,
1017
1035
  datasetName: task.dataset.getDisplayLabel(),
1018
1036
  testCaseId: testCaseItem.id,