@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
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { MemoryEvalInput } from "./memory-eval.js";
|
|
2
|
+
import { Case, Dataset } from "@graphorin/observability/eval";
|
|
3
|
+
|
|
4
|
+
//#region src/loaders/dmr.d.ts
|
|
5
|
+
|
|
6
|
+
/** @stable */
|
|
7
|
+
interface LoadDmrOptions {
|
|
8
|
+
/** Local path to the DMR JSON (under `benchmarks/.datasets/`). */
|
|
9
|
+
readonly path: string;
|
|
10
|
+
/** Optional dataset name surfaced in `Dataset.metadata.name`. */
|
|
11
|
+
readonly name?: string;
|
|
12
|
+
/** Optional description surfaced in `Dataset.metadata.description`. */
|
|
13
|
+
readonly description?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Read a DMR JSON file and return a fully-materialised {@link Dataset}
|
|
17
|
+
* of multi-session retrieval cases scored against the reference answer.
|
|
18
|
+
*
|
|
19
|
+
* @stable
|
|
20
|
+
*/
|
|
21
|
+
declare function loadDmrDataset(options: LoadDmrOptions): Promise<Dataset<MemoryEvalInput, string>>;
|
|
22
|
+
/**
|
|
23
|
+
* Pure parser. Exported so tests can exercise the mapping without
|
|
24
|
+
* touching the filesystem.
|
|
25
|
+
*
|
|
26
|
+
* @stable
|
|
27
|
+
*/
|
|
28
|
+
declare function parseDmr(text: string): ReadonlyArray<Case<MemoryEvalInput, string>>;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { LoadDmrOptions, loadDmrDataset, parseDmr };
|
|
31
|
+
//# sourceMappingURL=dmr.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dmr.d.ts","names":[],"sources":["../../src/loaders/dmr.ts"],"sourcesContent":[],"mappings":";;;;;;UAqCiB,cAAA;;;;;;;;;;;;;;iBAeK,cAAA,UACX,iBACR,QAAQ,QAAQ;;;;;;;iBAmBH,QAAA,gBAAwB,cAAc,KAAK"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/dmr.ts
|
|
4
|
+
/**
|
|
5
|
+
* DMR (Deep Memory Retrieval) loader — the MemGPT/Letta continuity set
|
|
6
|
+
* derived from Multi-Session Chat (arXiv:2310.08560). DMR's published
|
|
7
|
+
* shape is less standardised than LongMemEval/LOCOMO, so this loader is
|
|
8
|
+
* deliberately tolerant: it accepts a JSON **array** of items, each
|
|
9
|
+
* exposing a `question`, an `answer`, and the prior conversation under
|
|
10
|
+
* either `sessions` or `haystack_sessions`. Sessions may be a bare
|
|
11
|
+
* array of turns or an object wrapping the turns under
|
|
12
|
+
* `turns` / `messages` / `dialog`; a turn may use `role` or `speaker`
|
|
13
|
+
* and `content` or `text`.
|
|
14
|
+
*
|
|
15
|
+
* ```jsonc
|
|
16
|
+
* {
|
|
17
|
+
* "id": "dmr-1",
|
|
18
|
+
* "question": "What is the user's sister's name?",
|
|
19
|
+
* "answer": "Anna",
|
|
20
|
+
* "sessions": [
|
|
21
|
+
* { "id": "s1", "turns": [ { "role": "user", "content": "..." } ] },
|
|
22
|
+
* [ { "speaker": "assistant", "text": "..." } ]
|
|
23
|
+
* ]
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Every DMR case is treated as `multi-session` (its defining ability).
|
|
28
|
+
* The exact field names should be reconciled against the downloaded
|
|
29
|
+
* file the first time `scripts/fetch-eval-datasets.mjs` is run.
|
|
30
|
+
*
|
|
31
|
+
* @packageDocumentation
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Read a DMR JSON file and return a fully-materialised {@link Dataset}
|
|
35
|
+
* of multi-session retrieval cases scored against the reference answer.
|
|
36
|
+
*
|
|
37
|
+
* @stable
|
|
38
|
+
*/
|
|
39
|
+
async function loadDmrDataset(options) {
|
|
40
|
+
return {
|
|
41
|
+
cases: parseDmr(await readFile(options.path, "utf8")),
|
|
42
|
+
metadata: {
|
|
43
|
+
name: options.name ?? "dmr",
|
|
44
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
45
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Pure parser. Exported so tests can exercise the mapping without
|
|
51
|
+
* touching the filesystem.
|
|
52
|
+
*
|
|
53
|
+
* @stable
|
|
54
|
+
*/
|
|
55
|
+
function parseDmr(text) {
|
|
56
|
+
const parsed = JSON.parse(text);
|
|
57
|
+
if (!Array.isArray(parsed)) throw new Error("[graphorin/evals] DMR dataset must be a JSON array of items.");
|
|
58
|
+
const items = parsed;
|
|
59
|
+
const out = [];
|
|
60
|
+
for (let i = 0; i < items.length; i++) {
|
|
61
|
+
const item = items[i];
|
|
62
|
+
if (!isRecord(item)) throw new Error(`[graphorin/evals] DMR item ${i} is not an object.`);
|
|
63
|
+
const question = asString(item.question);
|
|
64
|
+
if (question === void 0) throw new Error(`[graphorin/evals] DMR item ${i} is missing a 'question'.`);
|
|
65
|
+
const sessions = coerceSessions(item.sessions ?? item.haystack_sessions);
|
|
66
|
+
out.push({
|
|
67
|
+
id: asString(item.id) ?? `dmr-${i}`,
|
|
68
|
+
input: {
|
|
69
|
+
haystackSessions: sessions,
|
|
70
|
+
question,
|
|
71
|
+
ability: "multi-session"
|
|
72
|
+
},
|
|
73
|
+
expected: asString(item.answer) ?? "",
|
|
74
|
+
metadata: {
|
|
75
|
+
datasetName: "dmr",
|
|
76
|
+
ability: "multi-session"
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
function coerceSessions(value) {
|
|
83
|
+
const rawSessions = asArray(value);
|
|
84
|
+
const sessions = [];
|
|
85
|
+
for (let s = 0; s < rawSessions.length; s++) {
|
|
86
|
+
const raw = rawSessions[s];
|
|
87
|
+
const rawTurns = Array.isArray(raw) ? raw : isRecord(raw) ? asArray(raw.turns ?? raw.messages ?? raw.dialog) : [];
|
|
88
|
+
const id = isRecord(raw) ? asString(raw.id) ?? `session-${s}` : `session-${s}`;
|
|
89
|
+
const turns = [];
|
|
90
|
+
for (const t of rawTurns) {
|
|
91
|
+
if (!isRecord(t)) continue;
|
|
92
|
+
const content = asString(t.content) ?? asString(t.text);
|
|
93
|
+
if (content === void 0) continue;
|
|
94
|
+
const role = (asString(t.role) ?? asString(t.speaker)) === "assistant" ? "assistant" : "user";
|
|
95
|
+
const timestamp = asString(t.timestamp);
|
|
96
|
+
turns.push({
|
|
97
|
+
role,
|
|
98
|
+
content,
|
|
99
|
+
...timestamp !== void 0 ? { timestamp } : {}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
sessions.push({
|
|
103
|
+
id,
|
|
104
|
+
turns
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
return sessions;
|
|
108
|
+
}
|
|
109
|
+
function asString(value) {
|
|
110
|
+
return typeof value === "string" ? value : void 0;
|
|
111
|
+
}
|
|
112
|
+
function asArray(value) {
|
|
113
|
+
return Array.isArray(value) ? value : [];
|
|
114
|
+
}
|
|
115
|
+
function isRecord(value) {
|
|
116
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
export { loadDmrDataset, parseDmr };
|
|
121
|
+
//# sourceMappingURL=dmr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dmr.js","names":["parsed: unknown","out: Case<MemoryEvalInput, string>[]","sessions: MemoryEvalSession[]","turns: MemoryEvalTurn[]"],"sources":["../../src/loaders/dmr.ts"],"sourcesContent":["/**\n * DMR (Deep Memory Retrieval) loader — the MemGPT/Letta continuity set\n * derived from Multi-Session Chat (arXiv:2310.08560). DMR's published\n * shape is less standardised than LongMemEval/LOCOMO, so this loader is\n * deliberately tolerant: it accepts a JSON **array** of items, each\n * exposing a `question`, an `answer`, and the prior conversation under\n * either `sessions` or `haystack_sessions`. Sessions may be a bare\n * array of turns or an object wrapping the turns under\n * `turns` / `messages` / `dialog`; a turn may use `role` or `speaker`\n * and `content` or `text`.\n *\n * ```jsonc\n * {\n * \"id\": \"dmr-1\",\n * \"question\": \"What is the user's sister's name?\",\n * \"answer\": \"Anna\",\n * \"sessions\": [\n * { \"id\": \"s1\", \"turns\": [ { \"role\": \"user\", \"content\": \"...\" } ] },\n * [ { \"speaker\": \"assistant\", \"text\": \"...\" } ]\n * ]\n * }\n * ```\n *\n * Every DMR case is treated as `multi-session` (its defining ability).\n * The exact field names should be reconciled against the downloaded\n * file the first time `scripts/fetch-eval-datasets.mjs` is run.\n *\n * @packageDocumentation\n */\n\nimport { readFile } from 'node:fs/promises';\n\nimport type { Case, Dataset } from '@graphorin/observability/eval';\n\nimport type { MemoryEvalInput, MemoryEvalSession, MemoryEvalTurn } from './memory-eval.js';\n\n/** @stable */\nexport interface LoadDmrOptions {\n /** Local path to the DMR JSON (under `benchmarks/.datasets/`). */\n readonly path: string;\n /** Optional dataset name surfaced in `Dataset.metadata.name`. */\n readonly name?: string;\n /** Optional description surfaced in `Dataset.metadata.description`. */\n readonly description?: string;\n}\n\n/**\n * Read a DMR JSON file and return a fully-materialised {@link Dataset}\n * of multi-session retrieval cases scored against the reference answer.\n *\n * @stable\n */\nexport async function loadDmrDataset(\n options: LoadDmrOptions,\n): Promise<Dataset<MemoryEvalInput, string>> {\n const text = await readFile(options.path, 'utf8');\n const cases = parseDmr(text);\n return {\n cases,\n metadata: {\n name: options.name ?? 'dmr',\n ...(options.description !== undefined ? { description: options.description } : {}),\n createdAt: new Date(),\n },\n };\n}\n\n/**\n * Pure parser. Exported so tests can exercise the mapping without\n * touching the filesystem.\n *\n * @stable\n */\nexport function parseDmr(text: string): ReadonlyArray<Case<MemoryEvalInput, string>> {\n const parsed: unknown = JSON.parse(text);\n if (!Array.isArray(parsed)) {\n throw new Error('[graphorin/evals] DMR dataset must be a JSON array of items.');\n }\n const items = parsed as ReadonlyArray<unknown>;\n const out: Case<MemoryEvalInput, string>[] = [];\n for (let i = 0; i < items.length; i++) {\n const item = items[i];\n if (!isRecord(item)) {\n throw new Error(`[graphorin/evals] DMR item ${i} is not an object.`);\n }\n const question = asString(item.question);\n if (question === undefined) {\n throw new Error(`[graphorin/evals] DMR item ${i} is missing a 'question'.`);\n }\n const sessions = coerceSessions(item.sessions ?? item.haystack_sessions);\n out.push({\n id: asString(item.id) ?? `dmr-${i}`,\n input: { haystackSessions: sessions, question, ability: 'multi-session' },\n expected: asString(item.answer) ?? '',\n metadata: { datasetName: 'dmr', ability: 'multi-session' },\n });\n }\n return out;\n}\n\nfunction coerceSessions(value: unknown): MemoryEvalSession[] {\n const rawSessions = asArray(value);\n const sessions: MemoryEvalSession[] = [];\n for (let s = 0; s < rawSessions.length; s++) {\n const raw = rawSessions[s];\n const rawTurns = Array.isArray(raw)\n ? (raw as ReadonlyArray<unknown>)\n : isRecord(raw)\n ? asArray(raw.turns ?? raw.messages ?? raw.dialog)\n : [];\n const id = isRecord(raw) ? (asString(raw.id) ?? `session-${s}`) : `session-${s}`;\n const turns: MemoryEvalTurn[] = [];\n for (const t of rawTurns) {\n if (!isRecord(t)) continue;\n const content = asString(t.content) ?? asString(t.text);\n if (content === undefined) continue;\n const speaker = asString(t.role) ?? asString(t.speaker);\n const role = speaker === 'assistant' ? 'assistant' : 'user';\n const timestamp = asString(t.timestamp);\n turns.push({ role, content, ...(timestamp !== undefined ? { timestamp } : {}) });\n }\n sessions.push({ id, turns });\n }\n return sessions;\n}\n\nfunction asString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction asArray(value: unknown): ReadonlyArray<unknown> {\n return Array.isArray(value) ? (value as ReadonlyArray<unknown>) : [];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,eAAsB,eACpB,SAC2C;AAG3C,QAAO;EACL,OAFY,SADD,MAAM,SAAS,QAAQ,MAAM,OAAO,CACrB;EAG1B,UAAU;GACR,MAAM,QAAQ,QAAQ;GACtB,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GACjF,2BAAW,IAAI,MAAM;GACtB;EACF;;;;;;;;AASH,SAAgB,SAAS,MAA4D;CACnF,MAAMA,SAAkB,KAAK,MAAM,KAAK;AACxC,KAAI,CAAC,MAAM,QAAQ,OAAO,CACxB,OAAM,IAAI,MAAM,+DAA+D;CAEjF,MAAM,QAAQ;CACd,MAAMC,MAAuC,EAAE;AAC/C,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,OAAO,MAAM;AACnB,MAAI,CAAC,SAAS,KAAK,CACjB,OAAM,IAAI,MAAM,8BAA8B,EAAE,oBAAoB;EAEtE,MAAM,WAAW,SAAS,KAAK,SAAS;AACxC,MAAI,aAAa,OACf,OAAM,IAAI,MAAM,8BAA8B,EAAE,2BAA2B;EAE7E,MAAM,WAAW,eAAe,KAAK,YAAY,KAAK,kBAAkB;AACxE,MAAI,KAAK;GACP,IAAI,SAAS,KAAK,GAAG,IAAI,OAAO;GAChC,OAAO;IAAE,kBAAkB;IAAU;IAAU,SAAS;IAAiB;GACzE,UAAU,SAAS,KAAK,OAAO,IAAI;GACnC,UAAU;IAAE,aAAa;IAAO,SAAS;IAAiB;GAC3D,CAAC;;AAEJ,QAAO;;AAGT,SAAS,eAAe,OAAqC;CAC3D,MAAM,cAAc,QAAQ,MAAM;CAClC,MAAMC,WAAgC,EAAE;AACxC,MAAK,IAAI,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;EAC3C,MAAM,MAAM,YAAY;EACxB,MAAM,WAAW,MAAM,QAAQ,IAAI,GAC9B,MACD,SAAS,IAAI,GACX,QAAQ,IAAI,SAAS,IAAI,YAAY,IAAI,OAAO,GAChD,EAAE;EACR,MAAM,KAAK,SAAS,IAAI,GAAI,SAAS,IAAI,GAAG,IAAI,WAAW,MAAO,WAAW;EAC7E,MAAMC,QAA0B,EAAE;AAClC,OAAK,MAAM,KAAK,UAAU;AACxB,OAAI,CAAC,SAAS,EAAE,CAAE;GAClB,MAAM,UAAU,SAAS,EAAE,QAAQ,IAAI,SAAS,EAAE,KAAK;AACvD,OAAI,YAAY,OAAW;GAE3B,MAAM,QADU,SAAS,EAAE,KAAK,IAAI,SAAS,EAAE,QAAQ,MAC9B,cAAc,cAAc;GACrD,MAAM,YAAY,SAAS,EAAE,UAAU;AACvC,SAAM,KAAK;IAAE;IAAM;IAAS,GAAI,cAAc,SAAY,EAAE,WAAW,GAAG,EAAE;IAAG,CAAC;;AAElF,WAAS,KAAK;GAAE;GAAI;GAAO,CAAC;;AAE9B,QAAO;;AAGT,SAAS,SAAS,OAAoC;AACpD,QAAO,OAAO,UAAU,WAAW,QAAQ;;AAG7C,SAAS,QAAQ,OAAwC;AACvD,QAAO,MAAM,QAAQ,MAAM,GAAI,QAAmC,EAAE;;AAGtE,SAAS,SAAS,OAAkD;AAClE,QAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Case, Dataset } from "@graphorin/observability/eval";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/from-traces.d.ts
|
|
4
|
+
|
|
5
|
+
/** @stable */
|
|
6
|
+
interface TraceEvent {
|
|
7
|
+
readonly runId: string;
|
|
8
|
+
readonly type: string;
|
|
9
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
10
|
+
readonly timestamp?: string;
|
|
11
|
+
}
|
|
12
|
+
/** @stable */
|
|
13
|
+
interface FromTracesOptions<I, O> {
|
|
14
|
+
/** Distil one `Case<I, O>` from every group of events sharing a `runId`. */
|
|
15
|
+
readonly extract: (events: ReadonlyArray<TraceEvent>) => Case<I, O> | null;
|
|
16
|
+
/** Optional name surfaced in `Dataset.metadata.name`. */
|
|
17
|
+
readonly name?: string;
|
|
18
|
+
/** Optional description surfaced in `Dataset.metadata.description`. */
|
|
19
|
+
readonly description?: string;
|
|
20
|
+
}
|
|
21
|
+
/** @stable */
|
|
22
|
+
declare function loadDatasetFromTraces<I, O>(path: string, options: FromTracesOptions<I, O>): Promise<Dataset<I, O>>;
|
|
23
|
+
/**
|
|
24
|
+
* Pure parser for the trace JSONL format. Exported so tests can
|
|
25
|
+
* exercise the extraction without touching the filesystem.
|
|
26
|
+
*
|
|
27
|
+
* @stable
|
|
28
|
+
*/
|
|
29
|
+
declare function groupAndExtract<I, O>(text: string, options: FromTracesOptions<I, O>): Dataset<I, O>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { FromTracesOptions, TraceEvent, groupAndExtract, loadDatasetFromTraces };
|
|
32
|
+
//# sourceMappingURL=from-traces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"from-traces.d.ts","names":[],"sources":["../../src/loaders/from-traces.ts"],"sourcesContent":[],"mappings":";;;;;AAiCsB,UAlBL,UAAA,CAkB0B;EAEd,SAAA,KAAA,EAAA,MAAA;EAAG,SAAA,IAAA,EAAA,MAAA;EAArB,SAAA,OAAA,EAjBS,QAiBT,CAjBkB,MAiBlB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;EACQ,SAAA,SAAA,CAAA,EAAA,MAAA;;;AAAhB,UAbc,iBAad,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA;EAAO;EAWM,SAAA,OAAA,EAAe,CAAA,MAAA,EAtBF,aAsBE,CAtBY,UAsBZ,CAAA,EAAA,GAtB4B,IAsB5B,CAtBiC,CAsBjC,EAtBoC,CAsBpC,CAAA,GAAA,IAAA;EAEF;EAAG,SAAA,IAAA,CAAA,EAAA,MAAA;EAArB;EACA,SAAA,WAAA,CAAA,EAAA,MAAA;;;AAAD,iBAjBY,qBAiBZ,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA,IAAA,EAAA,MAAA,EAAA,OAAA,EAfC,iBAeD,CAfmB,CAenB,EAfsB,CAetB,CAAA,CAAA,EAdP,OAcO,CAdC,OAcD,CAdS,CAcT,EAdY,CAcZ,CAAA,CAAA;;;;;;;iBAHM,6CAEL,kBAAkB,GAAG,KAC7B,QAAQ,GAAG"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/from-traces.ts
|
|
4
|
+
/**
|
|
5
|
+
* `loadDatasetFromTraces` — distil a dataset from the framework's
|
|
6
|
+
* replay log. The caller supplies the JSONL replay file and a small
|
|
7
|
+
* extraction function that pulls the `(input, output)` pair out of
|
|
8
|
+
* each event group; the loader handles the JSONL parsing + grouping
|
|
9
|
+
* by `runId`.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
/** @stable */
|
|
14
|
+
async function loadDatasetFromTraces(path, options) {
|
|
15
|
+
return groupAndExtract(await readFile(path, "utf8"), options);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Pure parser for the trace JSONL format. Exported so tests can
|
|
19
|
+
* exercise the extraction without touching the filesystem.
|
|
20
|
+
*
|
|
21
|
+
* @stable
|
|
22
|
+
*/
|
|
23
|
+
function groupAndExtract(text, options) {
|
|
24
|
+
const lines = text.split(/\r?\n/);
|
|
25
|
+
const groups = /* @__PURE__ */ new Map();
|
|
26
|
+
for (const raw of lines) {
|
|
27
|
+
if (raw.trim().length === 0) continue;
|
|
28
|
+
let parsed;
|
|
29
|
+
try {
|
|
30
|
+
parsed = JSON.parse(raw);
|
|
31
|
+
} catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) continue;
|
|
35
|
+
const obj = parsed;
|
|
36
|
+
const runId = typeof obj.runId === "string" ? obj.runId : null;
|
|
37
|
+
const type = typeof obj.type === "string" ? obj.type : null;
|
|
38
|
+
if (runId === null || type === null) continue;
|
|
39
|
+
const event = {
|
|
40
|
+
runId,
|
|
41
|
+
type,
|
|
42
|
+
payload: obj.payload !== null && typeof obj.payload === "object" && !Array.isArray(obj.payload) ? obj.payload : {},
|
|
43
|
+
...typeof obj.timestamp === "string" ? { timestamp: obj.timestamp } : {}
|
|
44
|
+
};
|
|
45
|
+
const existing = groups.get(runId);
|
|
46
|
+
if (existing === void 0) groups.set(runId, [event]);
|
|
47
|
+
else existing.push(event);
|
|
48
|
+
}
|
|
49
|
+
const cases = [];
|
|
50
|
+
for (const events of groups.values()) {
|
|
51
|
+
const sample = options.extract(events);
|
|
52
|
+
if (sample !== null) cases.push(sample);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
cases,
|
|
56
|
+
metadata: {
|
|
57
|
+
...options.name !== void 0 ? { name: options.name } : {},
|
|
58
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
59
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
export { groupAndExtract, loadDatasetFromTraces };
|
|
66
|
+
//# sourceMappingURL=from-traces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"from-traces.js","names":["parsed: unknown","event: TraceEvent","cases: Case<I, O>[]"],"sources":["../../src/loaders/from-traces.ts"],"sourcesContent":["/**\n * `loadDatasetFromTraces` — distil a dataset from the framework's\n * replay log. The caller supplies the JSONL replay file and a small\n * extraction function that pulls the `(input, output)` pair out of\n * each event group; the loader handles the JSONL parsing + grouping\n * by `runId`.\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 TraceEvent {\n readonly runId: string;\n readonly type: string;\n readonly payload: Readonly<Record<string, unknown>>;\n readonly timestamp?: string;\n}\n\n/** @stable */\nexport interface FromTracesOptions<I, O> {\n /** Distil one `Case<I, O>` from every group of events sharing a `runId`. */\n readonly extract: (events: ReadonlyArray<TraceEvent>) => Case<I, O> | null;\n /** Optional name surfaced in `Dataset.metadata.name`. */\n readonly name?: string;\n /** Optional description surfaced in `Dataset.metadata.description`. */\n readonly description?: string;\n}\n\n/** @stable */\nexport async function loadDatasetFromTraces<I, O>(\n path: string,\n options: FromTracesOptions<I, O>,\n): Promise<Dataset<I, O>> {\n const text = await readFile(path, 'utf8');\n return groupAndExtract<I, O>(text, options);\n}\n\n/**\n * Pure parser for the trace JSONL format. Exported so tests can\n * exercise the extraction without touching the filesystem.\n *\n * @stable\n */\nexport function groupAndExtract<I, O>(\n text: string,\n options: FromTracesOptions<I, O>,\n): Dataset<I, O> {\n const lines = text.split(/\\r?\\n/);\n const groups = new Map<string, TraceEvent[]>();\n for (const raw of lines) {\n if (raw.trim().length === 0) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n continue;\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) continue;\n const obj = parsed as Record<string, unknown>;\n const runId = typeof obj.runId === 'string' ? obj.runId : null;\n const type = typeof obj.type === 'string' ? obj.type : null;\n if (runId === null || type === null) continue;\n const payload =\n obj.payload !== null && typeof obj.payload === 'object' && !Array.isArray(obj.payload)\n ? (obj.payload as Readonly<Record<string, unknown>>)\n : ({} as Readonly<Record<string, unknown>>);\n const event: TraceEvent = {\n runId,\n type,\n payload,\n ...(typeof obj.timestamp === 'string' ? { timestamp: obj.timestamp } : {}),\n };\n const existing = groups.get(runId);\n if (existing === undefined) groups.set(runId, [event]);\n else existing.push(event);\n }\n const cases: Case<I, O>[] = [];\n for (const events of groups.values()) {\n const sample = options.extract(events);\n if (sample !== null) cases.push(sample);\n }\n const meta: Dataset<I, O>['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"],"mappings":";;;;;;;;;;;;;AAiCA,eAAsB,sBACpB,MACA,SACwB;AAExB,QAAO,gBADM,MAAM,SAAS,MAAM,OAAO,EACN,QAAQ;;;;;;;;AAS7C,SAAgB,gBACd,MACA,SACe;CACf,MAAM,QAAQ,KAAK,MAAM,QAAQ;CACjC,MAAM,yBAAS,IAAI,KAA2B;AAC9C,MAAK,MAAM,OAAO,OAAO;AACvB,MAAI,IAAI,MAAM,CAAC,WAAW,EAAG;EAC7B,IAAIA;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,IAAI;UAClB;AACN;;AAEF,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CAAE;EAC5E,MAAM,MAAM;EACZ,MAAM,QAAQ,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ;EAC1D,MAAM,OAAO,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AACvD,MAAI,UAAU,QAAQ,SAAS,KAAM;EAKrC,MAAMC,QAAoB;GACxB;GACA;GACA,SANA,IAAI,YAAY,QAAQ,OAAO,IAAI,YAAY,YAAY,CAAC,MAAM,QAAQ,IAAI,QAAQ,GACjF,IAAI,UACJ,EAAE;GAKP,GAAI,OAAO,IAAI,cAAc,WAAW,EAAE,WAAW,IAAI,WAAW,GAAG,EAAE;GAC1E;EACD,MAAM,WAAW,OAAO,IAAI,MAAM;AAClC,MAAI,aAAa,OAAW,QAAO,IAAI,OAAO,CAAC,MAAM,CAAC;MACjD,UAAS,KAAK,MAAM;;CAE3B,MAAMC,QAAsB,EAAE;AAC9B,MAAK,MAAM,UAAU,OAAO,QAAQ,EAAE;EACpC,MAAM,SAAS,QAAQ,QAAQ,OAAO;AACtC,MAAI,WAAW,KAAM,OAAM,KAAK,OAAO;;AAOzC,QAAO;EAAE;EAAO,UALwB;GACtC,GAAI,QAAQ,SAAS,SAAY,EAAE,MAAM,QAAQ,MAAM,GAAG,EAAE;GAC5D,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GACjF,2BAAW,IAAI,MAAM;GACtB;EAC+B"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { LoadCsvOptions, loadCsvDataset, parseCsv } from "./csv.js";
|
|
2
|
+
import { MemoryEvalAbility, MemoryEvalInput, MemoryEvalSession, MemoryEvalTurn } from "./memory-eval.js";
|
|
3
|
+
import { LoadDmrOptions, loadDmrDataset, parseDmr } from "./dmr.js";
|
|
4
|
+
import { FromTracesOptions, TraceEvent, groupAndExtract, loadDatasetFromTraces } from "./from-traces.js";
|
|
5
|
+
import { fromIterable } from "./iterable.js";
|
|
6
|
+
import { LoadJsonlOptions, loadJsonlDataset, parseJsonl } from "./jsonl.js";
|
|
7
|
+
import { LoadLocomoOptions, loadLocomoDataset, parseLocomo } from "./locomo.js";
|
|
8
|
+
import { LoadLongMemEvalOptions, loadLongMemEvalDataset, parseLongMemEval } from "./longmemeval.js";
|
|
9
|
+
export { type FromTracesOptions, type LoadCsvOptions, type LoadDmrOptions, type LoadJsonlOptions, type LoadLocomoOptions, type LoadLongMemEvalOptions, type MemoryEvalAbility, type MemoryEvalInput, type MemoryEvalSession, type MemoryEvalTurn, type TraceEvent, fromIterable, groupAndExtract, loadCsvDataset, loadDatasetFromTraces, loadDmrDataset, loadJsonlDataset, loadLocomoDataset, loadLongMemEvalDataset, parseCsv, parseDmr, parseJsonl, parseLocomo, parseLongMemEval };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { loadCsvDataset, parseCsv } from "./csv.js";
|
|
2
|
+
import { loadDmrDataset, parseDmr } from "./dmr.js";
|
|
3
|
+
import { groupAndExtract, loadDatasetFromTraces } from "./from-traces.js";
|
|
4
|
+
import { fromIterable } from "./iterable.js";
|
|
5
|
+
import { loadJsonlDataset, parseJsonl } from "./jsonl.js";
|
|
6
|
+
import { loadLocomoDataset, parseLocomo } from "./locomo.js";
|
|
7
|
+
import { loadLongMemEvalDataset, parseLongMemEval } from "./longmemeval.js";
|
|
8
|
+
|
|
9
|
+
export { fromIterable, groupAndExtract, loadCsvDataset, loadDatasetFromTraces, loadDmrDataset, loadJsonlDataset, loadLocomoDataset, loadLongMemEvalDataset, parseCsv, parseDmr, parseJsonl, parseLocomo, parseLongMemEval };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Case, Dataset } from "@graphorin/observability/eval";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/iterable.d.ts
|
|
4
|
+
|
|
5
|
+
/** @stable */
|
|
6
|
+
declare function fromIterable<I, O = unknown>(cases: Iterable<Case<I, O>> | ReadonlyArray<Case<I, O>>, options?: {
|
|
7
|
+
readonly name?: string;
|
|
8
|
+
readonly description?: string;
|
|
9
|
+
}): Dataset<I, O>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { fromIterable };
|
|
12
|
+
//# sourceMappingURL=iterable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iterable.d.ts","names":[],"sources":["../../src/loaders/iterable.ts"],"sourcesContent":[],"mappings":";;;;;AAWmD,iBADnC,YACmC,CAAA,CAAA,EAAA,IAAA,OAAA,CAAA,CAAA,KAAA,EAA1C,QAA0C,CAAjC,IAAiC,CAA5B,CAA4B,EAAzB,CAAyB,CAAA,CAAA,GAAnB,aAAmB,CAAL,IAAK,CAAA,CAAA,EAAG,CAAH,CAAA,CAAA,EAAA,OAAnB,CAAmB,EAAA;EAAG,SAAA,IAAA,CAAA,EAAA,MAAA;EAAR,SAAA,WAAA,CAAA,EAAA,MAAA;CAAd,CAAA,EAE7B,OAF6B,CAErB,CAFqB,EAElB,CAFkB,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/loaders/iterable.ts
|
|
2
|
+
/** @stable */
|
|
3
|
+
function fromIterable(cases, options = {}) {
|
|
4
|
+
return {
|
|
5
|
+
cases: Array.isArray(cases) ? cases : Array.from(cases),
|
|
6
|
+
metadata: {
|
|
7
|
+
...options.name !== void 0 ? { name: options.name } : {},
|
|
8
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
9
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { fromIterable };
|
|
16
|
+
//# sourceMappingURL=iterable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"iterable.js","names":[],"sources":["../../src/loaders/iterable.ts"],"sourcesContent":["/**\n * `fromIterable` — wrap an in-memory array / iterable as a\n * {@link Dataset}. Useful for tests + ad-hoc data.\n *\n * @packageDocumentation\n */\n\nimport type { Case, Dataset } from '@graphorin/observability/eval';\n\n/** @stable */\nexport function fromIterable<I, O = unknown>(\n cases: Iterable<Case<I, O>> | ReadonlyArray<Case<I, O>>,\n options: { readonly name?: string; readonly description?: string } = {},\n): Dataset<I, O> {\n const arr = Array.isArray(cases) ? (cases as ReadonlyArray<Case<I, O>>) : Array.from(cases);\n const meta: Dataset<I, O>['metadata'] = {\n ...(options.name !== undefined ? { name: options.name } : {}),\n ...(options.description !== undefined ? { description: options.description } : {}),\n createdAt: new Date(),\n };\n return { cases: arr, metadata: meta };\n}\n"],"mappings":";;AAUA,SAAgB,aACd,OACA,UAAqE,EAAE,EACxD;AAOf,QAAO;EAAE,OANG,MAAM,QAAQ,MAAM,GAAI,QAAsC,MAAM,KAAK,MAAM;EAMtE,UALmB;GACtC,GAAI,QAAQ,SAAS,SAAY,EAAE,MAAM,QAAQ,MAAM,GAAG,EAAE;GAC5D,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GACjF,2BAAW,IAAI,MAAM;GACtB;EACoC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Case, Dataset } from "@graphorin/observability/eval";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/jsonl.d.ts
|
|
4
|
+
|
|
5
|
+
/** @stable */
|
|
6
|
+
interface LoadJsonlOptions {
|
|
7
|
+
/** Optional dataset name surfaced in `Dataset.metadata.name`. */
|
|
8
|
+
readonly name?: string;
|
|
9
|
+
/** Optional description surfaced in `Dataset.metadata.description`. */
|
|
10
|
+
readonly description?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Map a parsed line into a `Case`. Default forwards the line
|
|
13
|
+
* verbatim. Override to translate column names or coerce types.
|
|
14
|
+
*/
|
|
15
|
+
readonly mapper?: (line: Record<string, unknown>, index: number) => Case<unknown, unknown>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Read a JSONL file and return a fully-materialised {@link Dataset}.
|
|
19
|
+
* Empty lines are skipped; malformed lines throw with the line
|
|
20
|
+
* number.
|
|
21
|
+
*
|
|
22
|
+
* @stable
|
|
23
|
+
*/
|
|
24
|
+
declare function loadJsonlDataset(path: string, options?: LoadJsonlOptions): Promise<Dataset<unknown, unknown>>;
|
|
25
|
+
/**
|
|
26
|
+
* Pure parser. Exported separately so tests can exercise the line-by-
|
|
27
|
+
* line behaviour without touching the filesystem.
|
|
28
|
+
*
|
|
29
|
+
* @stable
|
|
30
|
+
*/
|
|
31
|
+
declare function parseJsonl(text: string, mapper?: LoadJsonlOptions['mapper']): ReadonlyArray<Case<unknown, unknown>>;
|
|
32
|
+
//#endregion
|
|
33
|
+
export { LoadJsonlOptions, loadJsonlDataset, parseJsonl };
|
|
34
|
+
//# sourceMappingURL=jsonl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonl.d.ts","names":[],"sources":["../../src/loaders/jsonl.ts"],"sourcesContent":[],"mappings":";;;;;AAkCU,UAtBO,gBAAA,CAsBP;EAiBM;EAEL,SAAA,IAAA,CAAA,EAAA,MAAA;EACM;EAAd,SAAA,WAAA,CAAA,EAAA,MAAA;EAAa;;;;2BAjCW,2CAA2C;;;;;;;;;iBAUhD,gBAAA,yBAEX,mBACR,QAAQ;;;;;;;iBAiBK,UAAA,wBAEL,6BACR,cAAc"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/jsonl.ts
|
|
4
|
+
/**
|
|
5
|
+
* JSONL dataset loader. Each line is a JSON object with `input` and
|
|
6
|
+
* optional `expected` / `metadata` / `id` fields.
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Read a JSONL file and return a fully-materialised {@link Dataset}.
|
|
12
|
+
* Empty lines are skipped; malformed lines throw with the line
|
|
13
|
+
* number.
|
|
14
|
+
*
|
|
15
|
+
* @stable
|
|
16
|
+
*/
|
|
17
|
+
async function loadJsonlDataset(path, options = {}) {
|
|
18
|
+
return {
|
|
19
|
+
cases: parseJsonl(await readFile(path, "utf8"), options.mapper),
|
|
20
|
+
metadata: {
|
|
21
|
+
...options.name !== void 0 ? { name: options.name } : {},
|
|
22
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
23
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Pure parser. Exported separately so tests can exercise the line-by-
|
|
29
|
+
* line behaviour without touching the filesystem.
|
|
30
|
+
*
|
|
31
|
+
* @stable
|
|
32
|
+
*/
|
|
33
|
+
function parseJsonl(text, mapper) {
|
|
34
|
+
const out = [];
|
|
35
|
+
const lines = text.split(/\r?\n/);
|
|
36
|
+
for (let i = 0; i < lines.length; i++) {
|
|
37
|
+
const raw = lines[i] ?? "";
|
|
38
|
+
if (raw.trim().length === 0) continue;
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = JSON.parse(raw);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
throw new Error(`[graphorin/evals] failed to parse JSONL at line ${i + 1}: ${err.message}`, { cause: err });
|
|
44
|
+
}
|
|
45
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`[graphorin/evals] JSONL line ${i + 1} must be a JSON object, got ${typeof parsed}.`);
|
|
46
|
+
const obj = parsed;
|
|
47
|
+
const mapped = mapper !== void 0 ? mapper(obj, i) : defaultMapper(obj, i);
|
|
48
|
+
out.push(mapped);
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
function defaultMapper(line, _index) {
|
|
53
|
+
if (!("input" in line)) throw new Error(`[graphorin/evals] JSONL row missing required 'input' field.`);
|
|
54
|
+
return {
|
|
55
|
+
...typeof line.id === "string" ? { id: line.id } : {},
|
|
56
|
+
input: line.input,
|
|
57
|
+
..."expected" in line ? { expected: line.expected } : {},
|
|
58
|
+
...line.metadata !== void 0 && typeof line.metadata === "object" && line.metadata !== null ? { metadata: line.metadata } : {}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//#endregion
|
|
63
|
+
export { loadJsonlDataset, parseJsonl };
|
|
64
|
+
//# sourceMappingURL=jsonl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsonl.js","names":["out: Case<unknown, unknown>[]","parsed: unknown"],"sources":["../../src/loaders/jsonl.ts"],"sourcesContent":["/**\n * JSONL dataset loader. Each line is a JSON object with `input` and\n * optional `expected` / `metadata` / `id` fields.\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 LoadJsonlOptions {\n /** Optional dataset name surfaced in `Dataset.metadata.name`. */\n readonly name?: string;\n /** Optional description surfaced in `Dataset.metadata.description`. */\n readonly description?: string;\n /**\n * Map a parsed line into a `Case`. Default forwards the line\n * verbatim. Override to translate column names or coerce types.\n */\n readonly mapper?: (line: Record<string, unknown>, index: number) => Case<unknown, unknown>;\n}\n\n/**\n * Read a JSONL file and return a fully-materialised {@link Dataset}.\n * Empty lines are skipped; malformed lines throw with the line\n * number.\n *\n * @stable\n */\nexport async function loadJsonlDataset(\n path: string,\n options: LoadJsonlOptions = {},\n): Promise<Dataset<unknown, unknown>> {\n const text = await readFile(path, 'utf8');\n const cases = parseJsonl(text, options.mapper);\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 line-by-\n * line behaviour without touching the filesystem.\n *\n * @stable\n */\nexport function parseJsonl(\n text: string,\n mapper?: LoadJsonlOptions['mapper'],\n): ReadonlyArray<Case<unknown, unknown>> {\n const out: Case<unknown, unknown>[] = [];\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i] ?? '';\n if (raw.trim().length === 0) continue;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `[graphorin/evals] failed to parse JSONL at line ${i + 1}: ${(err as Error).message}`,\n { cause: err },\n );\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(\n `[graphorin/evals] JSONL line ${i + 1} must be a JSON object, got ${typeof parsed}.`,\n );\n }\n const obj = parsed as Record<string, unknown>;\n const mapped = mapper !== undefined ? mapper(obj, i) : defaultMapper(obj, i);\n out.push(mapped);\n }\n return out;\n}\n\nfunction defaultMapper(line: Record<string, unknown>, _index: number): Case<unknown, unknown> {\n if (!('input' in line)) {\n throw new Error(`[graphorin/evals] JSONL row missing required 'input' field.`);\n }\n return {\n ...(typeof line.id === 'string' ? { id: line.id } : {}),\n input: line.input,\n ...('expected' in line ? { expected: line.expected } : {}),\n ...(line.metadata !== undefined && typeof line.metadata === 'object' && line.metadata !== null\n ? { metadata: line.metadata as Readonly<Record<string, unknown>> }\n : {}),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA+BA,eAAsB,iBACpB,MACA,UAA4B,EAAE,EACM;AAQpC,QAAO;EAAE,OANK,WADD,MAAM,SAAS,MAAM,OAAO,EACV,QAAQ,OAAO;EAM9B,UALoC;GAClD,GAAI,QAAQ,SAAS,SAAY,EAAE,MAAM,QAAQ,MAAM,GAAG,EAAE;GAC5D,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GACjF,2BAAW,IAAI,MAAM;GACtB;EAC+B;;;;;;;;AASlC,SAAgB,WACd,MACA,QACuC;CACvC,MAAMA,MAAgC,EAAE;CACxC,MAAM,QAAQ,KAAK,MAAM,QAAQ;AACjC,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;EACrC,MAAM,MAAM,MAAM,MAAM;AACxB,MAAI,IAAI,MAAM,CAAC,WAAW,EAAG;EAC7B,IAAIC;AACJ,MAAI;AACF,YAAS,KAAK,MAAM,IAAI;WACjB,KAAK;AACZ,SAAM,IAAI,MACR,mDAAmD,IAAI,EAAE,IAAK,IAAc,WAC5E,EAAE,OAAO,KAAK,CACf;;AAEH,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,OAAO,CACxE,OAAM,IAAI,MACR,gCAAgC,IAAI,EAAE,8BAA8B,OAAO,OAAO,GACnF;EAEH,MAAM,MAAM;EACZ,MAAM,SAAS,WAAW,SAAY,OAAO,KAAK,EAAE,GAAG,cAAc,KAAK,EAAE;AAC5E,MAAI,KAAK,OAAO;;AAElB,QAAO;;AAGT,SAAS,cAAc,MAA+B,QAAwC;AAC5F,KAAI,EAAE,WAAW,MACf,OAAM,IAAI,MAAM,8DAA8D;AAEhF,QAAO;EACL,GAAI,OAAO,KAAK,OAAO,WAAW,EAAE,IAAI,KAAK,IAAI,GAAG,EAAE;EACtD,OAAO,KAAK;EACZ,GAAI,cAAc,OAAO,EAAE,UAAU,KAAK,UAAU,GAAG,EAAE;EACzD,GAAI,KAAK,aAAa,UAAa,OAAO,KAAK,aAAa,YAAY,KAAK,aAAa,OACtF,EAAE,UAAU,KAAK,UAA+C,GAChE,EAAE;EACP"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MemoryEvalInput } from "./memory-eval.js";
|
|
2
|
+
import { Case, Dataset } from "@graphorin/observability/eval";
|
|
3
|
+
|
|
4
|
+
//#region src/loaders/locomo.d.ts
|
|
5
|
+
|
|
6
|
+
/** @stable */
|
|
7
|
+
interface LoadLocomoOptions {
|
|
8
|
+
/** Local path to the LOCOMO JSON (under `benchmarks/.datasets/`). */
|
|
9
|
+
readonly path: string;
|
|
10
|
+
/** Optional dataset name surfaced in `Dataset.metadata.name`. */
|
|
11
|
+
readonly name?: string;
|
|
12
|
+
/** Optional description surfaced in `Dataset.metadata.description`. */
|
|
13
|
+
readonly description?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Read a LOCOMO JSON file and return a fully-materialised
|
|
17
|
+
* {@link Dataset} — one case per QA pair, scored against the reference
|
|
18
|
+
* answer string (LLM-judge "J").
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
declare function loadLocomoDataset(options: LoadLocomoOptions): Promise<Dataset<MemoryEvalInput, string>>;
|
|
23
|
+
/**
|
|
24
|
+
* Pure parser. Exported so tests can exercise the mapping without
|
|
25
|
+
* touching the filesystem.
|
|
26
|
+
*
|
|
27
|
+
* @stable
|
|
28
|
+
*/
|
|
29
|
+
declare function parseLocomo(text: string): ReadonlyArray<Case<MemoryEvalInput, string>>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { LoadLocomoOptions, loadLocomoDataset, parseLocomo };
|
|
32
|
+
//# sourceMappingURL=locomo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locomo.d.ts","names":[],"sources":["../../src/loaders/locomo.ts"],"sourcesContent":[],"mappings":";;;;;;UA6CiB,iBAAA;;;;;;;;;;;;;;;iBAgBK,iBAAA,UACX,oBACR,QAAQ,QAAQ;;;;;;;iBAmBH,WAAA,gBAA2B,cAAc,KAAK"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
//#region src/loaders/locomo.ts
|
|
4
|
+
/**
|
|
5
|
+
* Real LOCOMO dataset loader (arXiv:2402.17753 —
|
|
6
|
+
* https://snap-research.github.io/locomo/). LOCOMO ships as a JSON
|
|
7
|
+
* **array** of samples; each sample bundles one multi-session
|
|
8
|
+
* conversation with many QA pairs over it:
|
|
9
|
+
*
|
|
10
|
+
* ```jsonc
|
|
11
|
+
* {
|
|
12
|
+
* "sample_id": "conv-26",
|
|
13
|
+
* "conversation": {
|
|
14
|
+
* "speaker_a": "Caroline",
|
|
15
|
+
* "speaker_b": "Melanie",
|
|
16
|
+
* "session_1_date_time": "1:56 pm on 8 May, 2023",
|
|
17
|
+
* "session_1": [ { "speaker": "Caroline", "dia_id": "D1:1", "text": "..." }, ... ],
|
|
18
|
+
* "session_2_date_time": "...",
|
|
19
|
+
* "session_2": [ ... ]
|
|
20
|
+
* },
|
|
21
|
+
* "qa": [
|
|
22
|
+
* { "question": "...", "answer": "...", "evidence": ["D1:1"], "category": 1 },
|
|
23
|
+
* { "question": "...", "category": 5, "adversarial_answer": "Not mentioned ..." }
|
|
24
|
+
* ]
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Each QA pair becomes one {@link MemoryEvalInput} case that shares the
|
|
29
|
+
* sample's sessions. LOCOMO categories map to abilities as:
|
|
30
|
+
* `1`→multi-session (multi-hop), `2`→temporal, `5`→abstention
|
|
31
|
+
* (adversarial), everything else (`3` open-domain, `4` single-hop)
|
|
32
|
+
* →info-extraction.
|
|
33
|
+
*
|
|
34
|
+
* @packageDocumentation
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Read a LOCOMO JSON file and return a fully-materialised
|
|
38
|
+
* {@link Dataset} — one case per QA pair, scored against the reference
|
|
39
|
+
* answer string (LLM-judge "J").
|
|
40
|
+
*
|
|
41
|
+
* @stable
|
|
42
|
+
*/
|
|
43
|
+
async function loadLocomoDataset(options) {
|
|
44
|
+
return {
|
|
45
|
+
cases: parseLocomo(await readFile(options.path, "utf8")),
|
|
46
|
+
metadata: {
|
|
47
|
+
name: options.name ?? "locomo",
|
|
48
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
49
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pure parser. Exported so tests can exercise the mapping without
|
|
55
|
+
* touching the filesystem.
|
|
56
|
+
*
|
|
57
|
+
* @stable
|
|
58
|
+
*/
|
|
59
|
+
function parseLocomo(text) {
|
|
60
|
+
const parsed = JSON.parse(text);
|
|
61
|
+
if (!Array.isArray(parsed)) throw new Error("[graphorin/evals] LOCOMO dataset must be a JSON array of samples.");
|
|
62
|
+
const samples = parsed;
|
|
63
|
+
const out = [];
|
|
64
|
+
for (let i = 0; i < samples.length; i++) {
|
|
65
|
+
const sample = samples[i];
|
|
66
|
+
if (sample === null || typeof sample !== "object") throw new Error(`[graphorin/evals] LOCOMO sample ${i} is not an object.`);
|
|
67
|
+
appendSampleCases(sample, i, out);
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
function appendSampleCases(sample, index, out) {
|
|
72
|
+
const sampleId = asString(sample.sample_id) ?? `locomo-${index}`;
|
|
73
|
+
const sessions = extractSessions(isRecord(sample.conversation) ? sample.conversation : {});
|
|
74
|
+
const qa = asArray(sample.qa);
|
|
75
|
+
for (let q = 0; q < qa.length; q++) {
|
|
76
|
+
const item = qa[q];
|
|
77
|
+
if (!isRecord(item)) continue;
|
|
78
|
+
const question = asString(item.question);
|
|
79
|
+
if (question === void 0) continue;
|
|
80
|
+
const category = typeof item.category === "number" ? item.category : void 0;
|
|
81
|
+
const ability = mapCategory(category);
|
|
82
|
+
const answer = asString(item.answer) ?? asString(item.adversarial_answer) ?? "";
|
|
83
|
+
const evidence = asArray(item.evidence).filter((e) => typeof e === "string");
|
|
84
|
+
out.push({
|
|
85
|
+
id: `${sampleId}-q${q}`,
|
|
86
|
+
input: {
|
|
87
|
+
haystackSessions: sessions,
|
|
88
|
+
question,
|
|
89
|
+
ability
|
|
90
|
+
},
|
|
91
|
+
expected: answer,
|
|
92
|
+
metadata: {
|
|
93
|
+
datasetName: "locomo",
|
|
94
|
+
sampleId,
|
|
95
|
+
ability,
|
|
96
|
+
...category !== void 0 ? { category } : {},
|
|
97
|
+
evidence
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function extractSessions(conversation) {
|
|
103
|
+
const speakerA = asString(conversation.speaker_a);
|
|
104
|
+
const indices = [];
|
|
105
|
+
for (const key of Object.keys(conversation)) {
|
|
106
|
+
const m = /^session_(\d+)$/.exec(key);
|
|
107
|
+
if (m?.[1] !== void 0) indices.push(Number.parseInt(m[1], 10));
|
|
108
|
+
}
|
|
109
|
+
indices.sort((a, b) => a - b);
|
|
110
|
+
const sessions = [];
|
|
111
|
+
for (const n of indices) {
|
|
112
|
+
const raw = asArray(conversation[`session_${n}`]);
|
|
113
|
+
const date = asString(conversation[`session_${n}_date_time`]);
|
|
114
|
+
const turns = [];
|
|
115
|
+
for (const t of raw) {
|
|
116
|
+
if (!isRecord(t)) continue;
|
|
117
|
+
const content = asString(t.text) ?? asString(t.content);
|
|
118
|
+
if (content === void 0) continue;
|
|
119
|
+
const speaker = asString(t.speaker);
|
|
120
|
+
const role = speakerA !== void 0 && speaker !== void 0 && speaker !== speakerA ? "assistant" : "user";
|
|
121
|
+
turns.push({
|
|
122
|
+
role,
|
|
123
|
+
content,
|
|
124
|
+
...date !== void 0 ? { timestamp: date } : {}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
sessions.push({
|
|
128
|
+
id: `session_${n}`,
|
|
129
|
+
turns
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return sessions;
|
|
133
|
+
}
|
|
134
|
+
/** Maps a LOCOMO numeric `category` onto a {@link MemoryEvalAbility}. */
|
|
135
|
+
function mapCategory(category) {
|
|
136
|
+
switch (category) {
|
|
137
|
+
case 1: return "multi-session";
|
|
138
|
+
case 2: return "temporal";
|
|
139
|
+
case 5: return "abstention";
|
|
140
|
+
default: return "info-extraction";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function asString(value) {
|
|
144
|
+
return typeof value === "string" ? value : void 0;
|
|
145
|
+
}
|
|
146
|
+
function asArray(value) {
|
|
147
|
+
return Array.isArray(value) ? value : [];
|
|
148
|
+
}
|
|
149
|
+
function isRecord(value) {
|
|
150
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
//#endregion
|
|
154
|
+
export { loadLocomoDataset, parseLocomo };
|
|
155
|
+
//# sourceMappingURL=locomo.js.map
|