{"version":3,"file":"dataset.mjs","names":[],"sources":["../../../../../../../ai/src/eval/dataset.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type { DatasetContract, DatasetEntry, DatasetOptions } from \"./dataset.type\";\nimport { InvalidRequestError } from \"../errors\";\n\n/**\n * Parse a JSONL file's contents into {@link DatasetEntry} rows. Blank\n * lines (and trailing whitespace-only lines) are skipped; every other\n * line must be a JSON object. A malformed line throws an\n * `InvalidRequestError` naming the 1-based line number — failing loud at\n * construction, like `SystemPrompt.fromFile`.\n */\nfunction parseJsonl<TOutput>(path: string, contents: string): DatasetEntry<TOutput>[] {\n  const entries: DatasetEntry<TOutput>[] = [];\n  const lines = contents.split(/\\r?\\n/);\n\n  for (let index = 0; index < lines.length; index++) {\n    const line = lines[index].trim();\n\n    if (line === \"\") {\n      continue;\n    }\n\n    let parsed: unknown;\n\n    try {\n      parsed = JSON.parse(line);\n    } catch (error) {\n      throw new InvalidRequestError(\n        `Failed to parse dataset file \"${path}\" — line ${index + 1} is not valid JSON: ${\n          error instanceof Error ? error.message : String(error)\n        }`,\n        { context: { path, line: index + 1 }, cause: error },\n      );\n    }\n\n    if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n      throw new InvalidRequestError(\n        `Failed to parse dataset file \"${path}\" — line ${index + 1} is not a JSON object`,\n        { context: { path, line: index + 1 } },\n      );\n    }\n\n    entries.push(parsed as DatasetEntry<TOutput>);\n  }\n\n  return entries;\n}\n\n/**\n * Read a JSONL dataset file once, synchronously, at construction.\n * Mirrors `SystemPrompt.fromFile`: a read failure (missing path,\n * permission denied) throws an `InvalidRequestError` surfacing the\n * underlying cause.\n */\nfunction readDatasetFile<TOutput>(path: string): DatasetEntry<TOutput>[] {\n  let contents: string;\n\n  try {\n    contents = readFileSync(path, \"utf8\");\n  } catch (error) {\n    throw new InvalidRequestError(\n      `Failed to read dataset file \"${path}\" — ${\n        error instanceof Error ? error.message : String(error)\n      }`,\n      { context: { path }, cause: error },\n    );\n  }\n\n  return parseJsonl<TOutput>(path, contents);\n}\n\n/**\n * Build an immutable {@link DatasetContract} over the given entries.\n * Shared by the {@link dataset} factory and by `filter` / `shard`, which\n * each return a fresh dataset built from a derived case list.\n */\nfunction makeDataset<TOutput>(\n  name: string,\n  cases: DatasetEntry<TOutput>[],\n): DatasetContract<TOutput> {\n  return {\n    name,\n    cases,\n    filter(predicate) {\n      return makeDataset(name, cases.filter(predicate));\n    },\n    shard(index, total) {\n      if (!Number.isInteger(total) || total <= 0) {\n        throw new InvalidRequestError(\n          `dataset.shard: \"total\" must be a positive integer, received ${total}`,\n          { context: { name, total } },\n        );\n      }\n\n      if (!Number.isInteger(index) || index < 0 || index >= total) {\n        throw new InvalidRequestError(\n          `dataset.shard: \"index\" must be an integer in [0, ${total}), received ${index}`,\n          { context: { name, index, total } },\n        );\n      }\n\n      return makeDataset(\n        name,\n        cases.filter((_, position) => position % total === index),\n      );\n    },\n  };\n}\n\n/**\n * Create an immutable evaluation dataset that feeds `agent.eval({ cases })`\n * directly.\n *\n * **Role.** A taggable, filterable, shardable wrapper around a list of\n * {@link DatasetEntry} rows. `agent.eval` accepts a `DatasetContract` in\n * place of a raw `EvalCase[]`, reading `.cases` off it.\n *\n * Sources (combinable — file entries append after inline `cases`):\n * - `cases` → inline entries.\n * - `fromFile` → a JSONL file read once, synchronously, at construction\n *   (one JSON object per line). A malformed line throws an\n *   `InvalidRequestError` naming the 1-based line number.\n *\n * @example\n * const ds = dataset({ name: \"support\", fromFile: \"./eval/support.jsonl\" });\n * const smoke = ds.filter((entry) => entry.tags?.includes(\"smoke\"));\n * const shard = ds.shard(0, 4); // first of four parallel CI shards\n *\n * const report = await agent.eval({ cases: ds, scorers: [contains()] });\n */\nexport function dataset<TOutput = unknown>(\n  options: DatasetOptions<TOutput>,\n): DatasetContract<TOutput> {\n  const cases: DatasetEntry<TOutput>[] = [...(options.cases ?? [])];\n\n  if (options.fromFile !== undefined) {\n    cases.push(...readDatasetFile<TOutput>(options.fromFile));\n  }\n\n  return makeDataset(options.name, cases);\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAS,WAAoB,MAAc,UAA2C;CACpF,MAAM,UAAmC,CAAC;CAC1C,MAAM,QAAQ,SAAS,MAAM,OAAO;CAEpC,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS;EACjD,MAAM,OAAO,MAAM,MAAM,CAAC,KAAK;EAE/B,IAAI,SAAS,IACX;EAGF,IAAI;EAEJ,IAAI;GACF,SAAS,KAAK,MAAM,IAAI;EAC1B,SAAS,OAAO;GACd,MAAM,IAAI,oBACR,iCAAiC,KAAK,WAAW,QAAQ,EAAE,sBACzD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAEvD;IAAE,SAAS;KAAE;KAAM,MAAM,QAAQ;IAAE;IAAG,OAAO;GAAM,CACrD;EACF;EAEA,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GACvE,MAAM,IAAI,oBACR,iCAAiC,KAAK,WAAW,QAAQ,EAAE,wBAC3D,EAAE,SAAS;GAAE;GAAM,MAAM,QAAQ;EAAE,EAAE,CACvC;EAGF,QAAQ,KAAK,MAA+B;CAC9C;CAEA,OAAO;AACT;;;;;;;AAQA,SAAS,gBAAyB,MAAuC;CACvE,IAAI;CAEJ,IAAI;EACF,WAAW,aAAa,MAAM,MAAM;CACtC,SAAS,OAAO;EACd,MAAM,IAAI,oBACR,gCAAgC,KAAK,MACnC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,KAEvD;GAAE,SAAS,EAAE,KAAK;GAAG,OAAO;EAAM,CACpC;CACF;CAEA,OAAO,WAAoB,MAAM,QAAQ;AAC3C;;;;;;AAOA,SAAS,YACP,MACA,OAC0B;CAC1B,OAAO;EACL;EACA;EACA,OAAO,WAAW;GAChB,OAAO,YAAY,MAAM,MAAM,OAAO,SAAS,CAAC;EAClD;EACA,MAAM,OAAO,OAAO;GAClB,IAAI,CAAC,OAAO,UAAU,KAAK,KAAK,SAAS,GACvC,MAAM,IAAI,oBACR,+DAA+D,SAC/D,EAAE,SAAS;IAAE;IAAM;GAAM,EAAE,CAC7B;GAGF,IAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,KAAK,SAAS,OACpD,MAAM,IAAI,oBACR,oDAAoD,MAAM,cAAc,SACxE,EAAE,SAAS;IAAE;IAAM;IAAO;GAAM,EAAE,CACpC;GAGF,OAAO,YACL,MACA,MAAM,QAAQ,GAAG,aAAa,WAAW,UAAU,KAAK,CAC1D;EACF;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,QACd,SAC0B;CAC1B,MAAM,QAAiC,CAAC,GAAI,QAAQ,SAAS,CAAC,CAAE;CAEhE,IAAI,QAAQ,aAAa,QACvB,MAAM,KAAK,GAAG,gBAAyB,QAAQ,QAAQ,CAAC;CAG1D,OAAO,YAAY,QAAQ,MAAM,KAAK;AACxC"}