@adonis-agora/agent 0.36.0 → 0.37.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/NOTICE +31 -0
  3. package/README.md +5 -5
  4. package/dist/configure.d.ts.map +1 -1
  5. package/dist/configure.js +1 -0
  6. package/dist/configure.js.map +1 -1
  7. package/dist/src/index.d.ts +1 -1
  8. package/dist/src/index.js +1 -1
  9. package/dist/src/rag/index.d.ts +2 -1
  10. package/dist/src/rag/index.d.ts.map +1 -1
  11. package/dist/src/rag/index.js +2 -1
  12. package/dist/src/rag/index.js.map +1 -1
  13. package/dist/src/rag/openai-embeddings.d.ts +20 -2
  14. package/dist/src/rag/openai-embeddings.d.ts.map +1 -1
  15. package/dist/src/rag/openai-embeddings.js +107 -42
  16. package/dist/src/rag/openai-embeddings.js.map +1 -1
  17. package/dist/src/rag/pg-vector-store.d.ts +27 -0
  18. package/dist/src/rag/pg-vector-store.d.ts.map +1 -1
  19. package/dist/src/rag/pg-vector-store.js +2 -2
  20. package/dist/src/rag/pg-vector-store.js.map +1 -1
  21. package/dist/src/rag/tree/build.d.ts +96 -0
  22. package/dist/src/rag/tree/build.d.ts.map +1 -0
  23. package/dist/src/rag/tree/build.js +659 -0
  24. package/dist/src/rag/tree/build.js.map +1 -0
  25. package/dist/src/rag/tree/headings.d.ts +29 -0
  26. package/dist/src/rag/tree/headings.d.ts.map +1 -0
  27. package/dist/src/rag/tree/headings.js +227 -0
  28. package/dist/src/rag/tree/headings.js.map +1 -0
  29. package/dist/src/rag/tree/index-document.d.ts +33 -0
  30. package/dist/src/rag/tree/index-document.d.ts.map +1 -0
  31. package/dist/src/rag/tree/index-document.js +36 -0
  32. package/dist/src/rag/tree/index-document.js.map +1 -0
  33. package/dist/src/rag/tree/index.d.ts +13 -0
  34. package/dist/src/rag/tree/index.d.ts.map +1 -0
  35. package/dist/src/rag/tree/index.js +12 -0
  36. package/dist/src/rag/tree/index.js.map +1 -0
  37. package/dist/src/rag/tree/keyword-tree-llm.d.ts +15 -0
  38. package/dist/src/rag/tree/keyword-tree-llm.d.ts.map +1 -0
  39. package/dist/src/rag/tree/keyword-tree-llm.js +105 -0
  40. package/dist/src/rag/tree/keyword-tree-llm.js.map +1 -0
  41. package/dist/src/rag/tree/llm.d.ts +179 -0
  42. package/dist/src/rag/tree/llm.d.ts.map +1 -0
  43. package/dist/src/rag/tree/llm.js +264 -0
  44. package/dist/src/rag/tree/llm.js.map +1 -0
  45. package/dist/src/rag/tree/navigate-tool.d.ts +58 -0
  46. package/dist/src/rag/tree/navigate-tool.d.ts.map +1 -0
  47. package/dist/src/rag/tree/navigate-tool.js +95 -0
  48. package/dist/src/rag/tree/navigate-tool.js.map +1 -0
  49. package/dist/src/rag/tree/navigate.d.ts +137 -0
  50. package/dist/src/rag/tree/navigate.d.ts.map +1 -0
  51. package/dist/src/rag/tree/navigate.js +404 -0
  52. package/dist/src/rag/tree/navigate.js.map +1 -0
  53. package/dist/src/rag/tree/pg-tree-store.d.ts +45 -0
  54. package/dist/src/rag/tree/pg-tree-store.d.ts.map +1 -0
  55. package/dist/src/rag/tree/pg-tree-store.js +205 -0
  56. package/dist/src/rag/tree/pg-tree-store.js.map +1 -0
  57. package/dist/src/rag/tree/store.d.ts +57 -0
  58. package/dist/src/rag/tree/store.d.ts.map +1 -0
  59. package/dist/src/rag/tree/store.js +66 -0
  60. package/dist/src/rag/tree/store.js.map +1 -0
  61. package/dist/src/rag/tree/two-stage.d.ts +54 -0
  62. package/dist/src/rag/tree/two-stage.d.ts.map +1 -0
  63. package/dist/src/rag/tree/two-stage.js +92 -0
  64. package/dist/src/rag/tree/two-stage.js.map +1 -0
  65. package/dist/src/rag/tree/types.d.ts +135 -0
  66. package/dist/src/rag/tree/types.d.ts.map +1 -0
  67. package/dist/src/rag/tree/types.js +21 -0
  68. package/dist/src/rag/tree/types.js.map +1 -0
  69. package/dist/stubs/database/migrations/create_agent_rag_trees.stub +40 -0
  70. package/package.json +2 -1
@@ -0,0 +1,205 @@
1
+ import { buildMetadataWhere, normalizeRows, stripNulBytes } from '../pg-vector-store.js';
2
+ import { headerOf } from './store.js';
3
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
4
+ function assertIdentifier(name) {
5
+ if (!IDENTIFIER.test(name)) {
6
+ throw new Error(`PgDocumentTreeStore: invalid table name ${JSON.stringify(name)}`);
7
+ }
8
+ return name;
9
+ }
10
+ /** A `jsonb` column comes back parsed on `pg`, as a string on some drivers — accept both. */
11
+ function json(value) {
12
+ if (value === null || value === undefined) {
13
+ return undefined;
14
+ }
15
+ return (typeof value === 'string' ? JSON.parse(value) : value);
16
+ }
17
+ /** `WHERE a AND b` → `['a', 'b']`-style: the conditions of {@link buildMetadataWhere}, without the keyword. */
18
+ function metadataConditions(filter) {
19
+ const where = buildMetadataWhere(filter, 'metadata');
20
+ return {
21
+ sql: where.sql === '' ? undefined : where.sql.replace(/^WHERE\s+/, ''),
22
+ bindings: where.bindings,
23
+ };
24
+ }
25
+ /**
26
+ * A Postgres {@link DocumentTreeStore} over Lucid's raw runner (`db` or `db.connection(name)` — the
27
+ * same structural {@link LucidRawRunner} `PgVectorStore` takes, so `@adonisjs/lucid` stays an
28
+ * optional peer): one row per tree (header columns, `metadata` as `jsonb` for filtering, the nodes as
29
+ * `jsonb`) and one row per unit of text. Filters compile through the same builder as `PgVectorStore`'s
30
+ * — scalar `@>` containment, array match-any, empty array denies — so the filter you pass the vector
31
+ * store works here unchanged.
32
+ *
33
+ * Provision it with the published `create_agent_rag_trees` migration, or {@link ensureSchema} /
34
+ * {@link schemaStatements}. Needs no extension. Writes run in one transaction when the runner has one.
35
+ */
36
+ export class PgDocumentTreeStore {
37
+ db;
38
+ table;
39
+ unitsTable;
40
+ insertBatchSize;
41
+ constructor(db, options = {}) {
42
+ this.db = db;
43
+ this.table = assertIdentifier(options.table ?? 'agent_rag_trees');
44
+ this.unitsTable = assertIdentifier(options.unitsTable ?? `${this.table}_units`);
45
+ this.insertBatchSize = Math.max(1, Math.floor(options.insertBatchSize ?? 200));
46
+ }
47
+ /** The idempotent DDL {@link ensureSchema} runs, in order. */
48
+ schemaStatements() {
49
+ return [
50
+ `CREATE TABLE IF NOT EXISTS ${this.table} (
51
+ document_id TEXT PRIMARY KEY,
52
+ title TEXT,
53
+ description TEXT,
54
+ structure TEXT NOT NULL,
55
+ fingerprint TEXT NOT NULL,
56
+ unit_count INTEGER NOT NULL,
57
+ source TEXT,
58
+ metadata JSONB,
59
+ tree JSONB NOT NULL,
60
+ built_at TIMESTAMPTZ NOT NULL
61
+ )`,
62
+ `CREATE INDEX IF NOT EXISTS ${this.table}_metadata_idx ON ${this.table} USING gin (metadata)`,
63
+ `CREATE TABLE IF NOT EXISTS ${this.unitsTable} (
64
+ document_id TEXT NOT NULL,
65
+ idx INTEGER NOT NULL,
66
+ page INTEGER,
67
+ text TEXT NOT NULL,
68
+ PRIMARY KEY (document_id, idx)
69
+ )`,
70
+ ];
71
+ }
72
+ async ensureSchema() {
73
+ for (const statement of this.schemaStatements()) {
74
+ await this.db.rawQuery(statement);
75
+ }
76
+ }
77
+ async write(work) {
78
+ const transaction = this.db.transaction;
79
+ if (typeof transaction !== 'function') {
80
+ await work(this.db);
81
+ return;
82
+ }
83
+ await transaction.call(this.db, async (trx) => {
84
+ const runner = trx;
85
+ await work(typeof runner.rawQuery === 'function' ? runner : this.db);
86
+ });
87
+ }
88
+ /** Replace the tree and its units (one transaction when the runner has one). */
89
+ async put(tree, units) {
90
+ const clean = stripNulBytes(tree);
91
+ const { metadata, ...rest } = clean;
92
+ await this.write(async (runner) => {
93
+ await runner.rawQuery(`DELETE FROM ${this.unitsTable} WHERE document_id = ?`, [
94
+ clean.documentId,
95
+ ]);
96
+ for (let start = 0; start < units.length; start += this.insertBatchSize) {
97
+ const batch = units.slice(start, start + this.insertBatchSize);
98
+ const bindings = [];
99
+ for (const unit of batch) {
100
+ bindings.push(clean.documentId, unit.index, unit.page ?? null, stripNulBytes(unit.text));
101
+ }
102
+ await runner.rawQuery(`INSERT INTO ${this.unitsTable} (document_id, idx, page, text) VALUES ${batch
103
+ .map(() => '(?, ?, ?, ?)')
104
+ .join(', ')}`, bindings);
105
+ }
106
+ await runner.rawQuery(`INSERT INTO ${this.table}
107
+ (document_id, title, description, structure, fingerprint, unit_count, source, metadata, tree, built_at)
108
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?::jsonb, ?::jsonb, ?)
109
+ ON CONFLICT (document_id) DO UPDATE SET
110
+ title = EXCLUDED.title, description = EXCLUDED.description, structure = EXCLUDED.structure,
111
+ fingerprint = EXCLUDED.fingerprint, unit_count = EXCLUDED.unit_count, source = EXCLUDED.source,
112
+ metadata = EXCLUDED.metadata, tree = EXCLUDED.tree, built_at = EXCLUDED.built_at`, [
113
+ clean.documentId,
114
+ clean.title ?? null,
115
+ clean.description ?? null,
116
+ clean.structure,
117
+ clean.fingerprint,
118
+ clean.unitCount,
119
+ clean.source ?? null,
120
+ metadata === undefined ? null : JSON.stringify(metadata),
121
+ JSON.stringify(rest),
122
+ clean.builtAt,
123
+ ]);
124
+ });
125
+ }
126
+ async get(documentId, filter) {
127
+ return (await this.getMany([documentId], filter))[0];
128
+ }
129
+ async getMany(documentIds, filter) {
130
+ if (documentIds.length === 0) {
131
+ return [];
132
+ }
133
+ const where = metadataConditions(filter);
134
+ const rows = normalizeRows(await this.db.rawQuery(`SELECT document_id, metadata, tree FROM ${this.table}
135
+ WHERE document_id = ANY(?::text[])${where.sql !== undefined ? ` AND ${where.sql}` : ''}`, [[...new Set(documentIds)], ...where.bindings]));
136
+ return rows.map((row) => {
137
+ const tree = json(row.tree);
138
+ const metadata = json(row.metadata);
139
+ return { ...tree, ...(metadata !== undefined ? { metadata } : {}) };
140
+ });
141
+ }
142
+ async list(options = {}) {
143
+ const where = metadataConditions(options.filter);
144
+ const conditions = [];
145
+ const bindings = [];
146
+ if (where.sql !== undefined) {
147
+ conditions.push(where.sql);
148
+ bindings.push(...where.bindings);
149
+ }
150
+ if (options.documentIds !== undefined) {
151
+ conditions.push('document_id = ANY(?::text[])');
152
+ bindings.push([...options.documentIds]);
153
+ }
154
+ let limit = '';
155
+ if (options.limit !== undefined) {
156
+ limit = 'LIMIT ?';
157
+ bindings.push(Math.max(0, Math.floor(options.limit)));
158
+ }
159
+ const rows = normalizeRows(await this.db.rawQuery(`SELECT document_id, title, description, structure, fingerprint, unit_count, source, metadata, built_at
160
+ FROM ${this.table}
161
+ ${conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : ''}
162
+ ORDER BY document_id ${limit}`, bindings));
163
+ return rows.map((row) => {
164
+ const metadata = json(row.metadata);
165
+ const builtAt = row.built_at;
166
+ return headerOf({
167
+ documentId: String(row.document_id),
168
+ version: 1,
169
+ ...(typeof row.title === 'string' ? { title: row.title } : {}),
170
+ ...(typeof row.description === 'string' ? { description: row.description } : {}),
171
+ structure: row.structure,
172
+ fingerprint: String(row.fingerprint),
173
+ unitCount: Number(row.unit_count),
174
+ nodes: [],
175
+ stats: {
176
+ llmCalls: 0,
177
+ inputTokens: 0,
178
+ outputTokens: 0,
179
+ durationMs: 0,
180
+ budgetExhausted: false,
181
+ reusedSummaries: 0,
182
+ },
183
+ ...(metadata !== undefined ? { metadata } : {}),
184
+ ...(typeof row.source === 'string' ? { source: row.source } : {}),
185
+ builtAt: builtAt instanceof Date ? builtAt.toISOString() : new Date(String(builtAt)).toISOString(),
186
+ });
187
+ });
188
+ }
189
+ async readUnits(documentId, start, end) {
190
+ const rows = normalizeRows(await this.db.rawQuery(`SELECT idx, page, text FROM ${this.unitsTable}
191
+ WHERE document_id = ? AND idx BETWEEN ? AND ? ORDER BY idx`, [documentId, Math.max(0, start), end]));
192
+ return rows.map((row) => ({
193
+ index: Number(row.idx),
194
+ text: String(row.text),
195
+ ...(row.page !== null && row.page !== undefined ? { page: Number(row.page) } : {}),
196
+ }));
197
+ }
198
+ async remove(documentId) {
199
+ await this.write(async (runner) => {
200
+ await runner.rawQuery(`DELETE FROM ${this.unitsTable} WHERE document_id = ?`, [documentId]);
201
+ await runner.rawQuery(`DELETE FROM ${this.table} WHERE document_id = ?`, [documentId]);
202
+ });
203
+ }
204
+ }
205
+ //# sourceMappingURL=pg-tree-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg-tree-store.js","sourceRoot":"","sources":["../../../../src/rag/tree/pg-tree-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { DocumentTree, TreeUnit } from './types.js';
2
+ /** A stored tree without its nodes — what listing and document selection need. */
3
+ export interface DocumentTreeHeader {
4
+ documentId: string;
5
+ title?: string;
6
+ description?: string;
7
+ structure: DocumentTree['structure'];
8
+ fingerprint: string;
9
+ unitCount: number;
10
+ metadata?: Record<string, unknown>;
11
+ source?: string;
12
+ builtAt: string;
13
+ }
14
+ /**
15
+ * Where navigation trees live: one tree per document plus the units (pages/sections) its node
16
+ * ranges point at, so navigation can read a node's text without going back to the source file.
17
+ *
18
+ * `filter` has **exactly** the vector stores' semantics (see `VectorStore`): every key must match
19
+ * the tree's `metadata`, an array value is match-any, and an empty array denies. It is an access
20
+ * boundary — a navigator given a tenant filter must not read another tenant's tree — so every read
21
+ * that takes one applies it. `readUnits` takes none: call it only for a tree a filtered `get` just
22
+ * returned (which is what the navigator does).
23
+ */
24
+ export interface DocumentTreeStore {
25
+ /** Store (replace) a document's tree and units. */
26
+ put(tree: DocumentTree, units: TreeUnit[]): Promise<void>;
27
+ /** A document's tree, when it exists **and** passes `filter`. */
28
+ get(documentId: string, filter?: Record<string, unknown>): Promise<DocumentTree | undefined>;
29
+ /** The trees of those of `documentIds` that exist and pass `filter` (any order). */
30
+ getMany(documentIds: readonly string[], filter?: Record<string, unknown>): Promise<DocumentTree[]>;
31
+ /** Headers (no nodes) of every tree passing `filter`, or of the listed `documentIds` only. */
32
+ list(options?: {
33
+ filter?: Record<string, unknown>;
34
+ documentIds?: readonly string[];
35
+ limit?: number;
36
+ }): Promise<DocumentTreeHeader[]>;
37
+ /** Units `start..end` (inclusive) of a document, in order. */
38
+ readUnits(documentId: string, start: number, end: number): Promise<TreeUnit[]>;
39
+ /** Drop a document's tree and units. Unknown ids are a no-op. */
40
+ remove(documentId: string): Promise<void>;
41
+ }
42
+ export declare function headerOf(tree: DocumentTree): DocumentTreeHeader;
43
+ /** An in-process {@link DocumentTreeStore} — tests, and single-process apps with small corpora. */
44
+ export declare class MemoryDocumentTreeStore implements DocumentTreeStore {
45
+ private readonly trees;
46
+ put(tree: DocumentTree, units: TreeUnit[]): Promise<void>;
47
+ get(documentId: string, filter?: Record<string, unknown>): Promise<DocumentTree | undefined>;
48
+ getMany(documentIds: readonly string[], filter?: Record<string, unknown>): Promise<DocumentTree[]>;
49
+ list(options?: {
50
+ filter?: Record<string, unknown>;
51
+ documentIds?: readonly string[];
52
+ limit?: number;
53
+ }): Promise<DocumentTreeHeader[]>;
54
+ readUnits(documentId: string, start: number, end: number): Promise<TreeUnit[]>;
55
+ remove(documentId: string): Promise<void>;
56
+ }
57
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../../src/rag/tree/store.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEzD,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,iEAAiE;IACjE,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IAC7F,oFAAoF;IACpF,OAAO,CACL,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC3B,8FAA8F;IAC9F,IAAI,CAAC,OAAO,CAAC,EAAE;QACb,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAChC,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAClC,8DAA8D;IAC9D,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/E,iEAAiE;IACjE,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,kBAAkB,CAY/D;AAYD,mGAAmG;AACnG,qBAAa,uBAAwB,YAAW,iBAAiB;IAC/D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgE;IAEhF,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9D;IAEK,GAAG,CACP,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAKnC;IAEK,OAAO,CACX,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,YAAY,EAAE,CAAC,CASzB;IAEK,IAAI,CACR,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAChC,KAAK,CAAC,EAAE,MAAM,CAAC;KACX,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAU/B;IAEK,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAGnF;IAEK,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9C;CACF"}
@@ -0,0 +1,66 @@
1
+ import { matchesFilter } from '../filter.js';
2
+ import { filterDeniesAll } from '../vector-store.js';
3
+ export function headerOf(tree) {
4
+ return {
5
+ documentId: tree.documentId,
6
+ ...(tree.title !== undefined ? { title: tree.title } : {}),
7
+ ...(tree.description !== undefined ? { description: tree.description } : {}),
8
+ structure: tree.structure,
9
+ fingerprint: tree.fingerprint,
10
+ unitCount: tree.unitCount,
11
+ ...(tree.metadata !== undefined ? { metadata: tree.metadata } : {}),
12
+ ...(tree.source !== undefined ? { source: tree.source } : {}),
13
+ builtAt: tree.builtAt,
14
+ };
15
+ }
16
+ function passes(tree, filter) {
17
+ if (filter === undefined || Object.keys(filter).length === 0) {
18
+ return true;
19
+ }
20
+ if (filterDeniesAll(filter)) {
21
+ return false;
22
+ }
23
+ return matchesFilter(tree.metadata, filter);
24
+ }
25
+ /** An in-process {@link DocumentTreeStore} — tests, and single-process apps with small corpora. */
26
+ export class MemoryDocumentTreeStore {
27
+ trees = new Map();
28
+ async put(tree, units) {
29
+ this.trees.set(tree.documentId, {
30
+ tree: structuredClone(tree),
31
+ units: units.map((unit) => ({ ...unit })),
32
+ });
33
+ }
34
+ async get(documentId, filter) {
35
+ const entry = this.trees.get(documentId);
36
+ return entry !== undefined && passes(entry.tree, filter)
37
+ ? structuredClone(entry.tree)
38
+ : undefined;
39
+ }
40
+ async getMany(documentIds, filter) {
41
+ const trees = [];
42
+ for (const id of new Set(documentIds)) {
43
+ const tree = await this.get(id, filter);
44
+ if (tree !== undefined) {
45
+ trees.push(tree);
46
+ }
47
+ }
48
+ return trees;
49
+ }
50
+ async list(options = {}) {
51
+ const ids = options.documentIds !== undefined ? new Set(options.documentIds) : undefined;
52
+ const headers = [...this.trees.values()]
53
+ .filter(({ tree }) => (ids === undefined || ids.has(tree.documentId)) && passes(tree, options.filter))
54
+ .map(({ tree }) => headerOf(tree))
55
+ .sort((a, b) => a.documentId.localeCompare(b.documentId));
56
+ return options.limit !== undefined ? headers.slice(0, options.limit) : headers;
57
+ }
58
+ async readUnits(documentId, start, end) {
59
+ const units = this.trees.get(documentId)?.units ?? [];
60
+ return units.slice(Math.max(0, start), Math.max(0, end + 1)).map((unit) => ({ ...unit }));
61
+ }
62
+ async remove(documentId) {
63
+ this.trees.delete(documentId);
64
+ }
65
+ }
66
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../src/rag/tree/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { Passage, RetrieveOptions, Retriever } from '../../spi/retriever.js';
2
+ import type { TreeBudget } from './llm.js';
3
+ import type { DocumentNavigation, TreeNavigationRetriever } from './navigate.js';
4
+ export interface TwoStageRetrieverOptions {
5
+ /** Passages pulled from the first stage. Default 20. */
6
+ fetchTopK?: number;
7
+ /**
8
+ * Only the first this-many distinct documents of the first stage are considered for navigation.
9
+ * Default 3 — past the top few, a document is more likely a large irrelevant tree (the costliest
10
+ * failure measured: navigating FAR Part 52 because it ranked fourth) than the answer.
11
+ */
12
+ considerDocuments?: number;
13
+ /** Of those, navigate at most this many (the ones that have a tree). Default 2. */
14
+ navigateDocuments?: number;
15
+ /** Per-document navigation budget (overrides the navigator's). */
16
+ budget?: TreeBudget;
17
+ /** Keep the first stage's chunks of a navigated document after its tree passages. Default `false`. */
18
+ keepFirstStageChunks?: boolean;
19
+ /**
20
+ * The document a first-stage passage belongs to. Default: `metadata.documentId` when it is a
21
+ * string, else the chunk id without its `#<n>` suffix (the id scheme `chunkDocuments` produces).
22
+ */
23
+ documentIdOf?: (passage: Passage) => string;
24
+ /** Called with each navigation's audit trail (log it, trace it, show it). */
25
+ onNavigation?: (navigation: DocumentNavigation) => void;
26
+ }
27
+ /**
28
+ * Hybrid first stage, tree navigation second — the design the benchmark recommends: keep fast
29
+ * chunk search (hybrid/lexical/dense, 0.3 s, no LLM) for picking **documents**, and navigate the
30
+ * table of contents only inside the top **long** documents, where chunk ranking is what fails.
31
+ *
32
+ * 1. The first stage retrieves `fetchTopK` passages.
33
+ * 2. Among its first `considerDocuments` distinct documents, those with a tree in the navigator's
34
+ * store (build trees only for long documents, see `indexDocumentTree`'s `minUnits`) are long;
35
+ * the first `navigateDocuments` of them are navigated, in parallel, each on its own budget.
36
+ * 3. The output keeps the first stage's order: a navigated document's section passages take the
37
+ * place of its chunks (at its best chunk's rank); short documents keep their chunks. A navigation
38
+ * that fails or finds nothing leaves that document's chunks in place — never worse than stage one.
39
+ *
40
+ * `filter` is passed to both stages, so the access boundary holds on either path.
41
+ */
42
+ export declare class TwoStageRetriever implements Retriever {
43
+ private readonly firstStage;
44
+ private readonly navigator;
45
+ private readonly options;
46
+ constructor(firstStage: Retriever, navigator: TreeNavigationRetriever, options?: TwoStageRetrieverOptions);
47
+ retrieve(query: string, options?: RetrieveOptions): Promise<Passage[]>;
48
+ /** {@link retrieve} with the navigations that were run. */
49
+ search(query: string, options?: RetrieveOptions): Promise<{
50
+ passages: Passage[];
51
+ navigations: DocumentNavigation[];
52
+ }>;
53
+ }
54
+ //# sourceMappingURL=two-stage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"two-stage.d.ts","sourceRoot":"","sources":["../../../../src/rag/tree/two-stage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAEjF,MAAM,WAAW,wBAAwB;IACvC,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,sGAAsG;IACtG,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC;IAC5C,6EAA6E;IAC7E,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACzD;AAOD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAkB,YAAW,SAAS;IAE/C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAH1B,YACmB,UAAU,EAAE,SAAS,EACrB,SAAS,EAAE,uBAAuB,EAClC,OAAO,GAAE,wBAA6B,EACrD;IAEE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAE/E;IAED,2DAA2D;IACrD,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;QAAC,WAAW,EAAE,kBAAkB,EAAE,CAAA;KAAE,CAAC,CA8DrE;CACF"}
@@ -0,0 +1,92 @@
1
+ import { documentIdOf } from '../vector-store.js';
2
+ function defaultDocumentIdOf(passage) {
3
+ const fromMetadata = passage.metadata?.documentId;
4
+ return typeof fromMetadata === 'string' ? fromMetadata : documentIdOf(passage.id);
5
+ }
6
+ /**
7
+ * Hybrid first stage, tree navigation second — the design the benchmark recommends: keep fast
8
+ * chunk search (hybrid/lexical/dense, 0.3 s, no LLM) for picking **documents**, and navigate the
9
+ * table of contents only inside the top **long** documents, where chunk ranking is what fails.
10
+ *
11
+ * 1. The first stage retrieves `fetchTopK` passages.
12
+ * 2. Among its first `considerDocuments` distinct documents, those with a tree in the navigator's
13
+ * store (build trees only for long documents, see `indexDocumentTree`'s `minUnits`) are long;
14
+ * the first `navigateDocuments` of them are navigated, in parallel, each on its own budget.
15
+ * 3. The output keeps the first stage's order: a navigated document's section passages take the
16
+ * place of its chunks (at its best chunk's rank); short documents keep their chunks. A navigation
17
+ * that fails or finds nothing leaves that document's chunks in place — never worse than stage one.
18
+ *
19
+ * `filter` is passed to both stages, so the access boundary holds on either path.
20
+ */
21
+ export class TwoStageRetriever {
22
+ firstStage;
23
+ navigator;
24
+ options;
25
+ constructor(firstStage, navigator, options = {}) {
26
+ this.firstStage = firstStage;
27
+ this.navigator = navigator;
28
+ this.options = options;
29
+ }
30
+ async retrieve(query, options = {}) {
31
+ return (await this.search(query, options)).passages;
32
+ }
33
+ /** {@link retrieve} with the navigations that were run. */
34
+ async search(query, options = {}) {
35
+ const topK = options.topK ?? 5;
36
+ const docOf = this.options.documentIdOf ?? defaultDocumentIdOf;
37
+ const first = await this.firstStage.retrieve(query, {
38
+ topK: Math.max(topK, this.options.fetchTopK ?? 20),
39
+ ...(options.filter !== undefined ? { filter: options.filter } : {}),
40
+ });
41
+ const ranked = [];
42
+ for (const passage of first) {
43
+ const id = docOf(passage);
44
+ if (!ranked.includes(id)) {
45
+ ranked.push(id);
46
+ }
47
+ }
48
+ const considered = ranked.slice(0, Math.max(1, this.options.considerDocuments ?? 3));
49
+ const withTrees = new Set((await this.navigator.store.list({
50
+ documentIds: considered,
51
+ ...(options.filter !== undefined ? { filter: options.filter } : {}),
52
+ })).map((header) => header.documentId));
53
+ const toNavigate = considered
54
+ .filter((id) => withTrees.has(id))
55
+ .slice(0, Math.max(0, this.options.navigateDocuments ?? 2));
56
+ const navigated = new Map();
57
+ let navigations = [];
58
+ if (toNavigate.length > 0) {
59
+ const result = await this.navigator.navigate(query, toNavigate, {
60
+ ...(options.filter !== undefined ? { filter: options.filter } : {}),
61
+ ...(this.options.budget !== undefined ? { budget: this.options.budget } : {}),
62
+ });
63
+ navigations = result.documents;
64
+ for (const passage of result.passages) {
65
+ const id = String(passage.metadata?.documentId ?? '');
66
+ navigated.set(id, [...(navigated.get(id) ?? []), passage]);
67
+ }
68
+ for (const navigation of navigations) {
69
+ this.options.onNavigation?.(navigation);
70
+ }
71
+ }
72
+ const merged = [];
73
+ const emitted = new Set();
74
+ for (const passage of first) {
75
+ const id = docOf(passage);
76
+ const replacement = navigated.get(id);
77
+ if (replacement === undefined || replacement.length === 0) {
78
+ merged.push(passage);
79
+ continue;
80
+ }
81
+ if (!emitted.has(id)) {
82
+ emitted.add(id);
83
+ merged.push(...replacement);
84
+ }
85
+ if (this.options.keepFirstStageChunks) {
86
+ merged.push(passage);
87
+ }
88
+ }
89
+ return { passages: merged.slice(0, topK), navigations };
90
+ }
91
+ }
92
+ //# sourceMappingURL=two-stage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"two-stage.js","sourceRoot":"","sources":["../../../../src/rag/tree/two-stage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AA4BlD,SAAS,mBAAmB,CAAC,OAAgB;IAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC;IAClD,OAAO,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AACpF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,iBAAiB;IAET,UAAU;IACV,SAAS;IACT,OAAO;IAH1B,YACmB,UAAqB,EACrB,SAAkC,EAClC,OAAO,GAA6B,EAAE;0BAFtC,UAAU;yBACV,SAAS;uBACT,OAAO;IACvB,CAAC;IAEJ,KAAK,CAAC,QAAQ,CAAC,KAAa,EAAE,OAAO,GAAoB,EAAE;QACzD,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;IACtD,CAAC;IAED,2DAA2D;IAC3D,KAAK,CAAC,MAAM,CACV,KAAa,EACb,OAAO,GAAoB,EAAE;QAE7B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,IAAI,mBAAmB,CAAC;QAC/D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,EAAE;YAClD,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC;YAClD,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpE,CAAC,CAAC;QACH,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC5B,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;gBACzB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC;QACrF,MAAM,SAAS,GAAG,IAAI,GAAG,CACvB,CACE,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC;YAC9B,WAAW,EAAE,UAAU;YACvB,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpE,CAAC,CACH,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CACrC,CAAC;QACF,MAAM,UAAU,GAAG,UAAU;aAC1B,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;aACjC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC;QAE9D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAqB,CAAC;QAC/C,IAAI,WAAW,GAAyB,EAAE,CAAC;QAC3C,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE;gBAC9D,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACnE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9E,CAAC,CAAC;YACH,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC;YAC/B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACtC,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC;gBACtD,SAAS,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YAC7D,CAAC;YACD,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;gBACrC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC5B,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;YAC1B,MAAM,WAAW,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtC,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1D,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACrB,SAAS;YACX,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACrB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChB,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;YAC9B,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;gBACtC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1D,CAAC;CACF"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * The data model of tree navigation retrieval: a hierarchical table of contents per long document
3
+ * (PageIndex-style: sections with page ranges and summaries), built once at ingestion and read by an
4
+ * LLM at query time to walk to the relevant sections instead of ranking hundreds of look-alike chunks.
5
+ */
6
+ /**
7
+ * One addressable piece of a document's text, in reading order — a PDF page, or a section of a
8
+ * markdown/HTML document. Tree nodes point at ranges of units, and navigation reads the text of the
9
+ * units a chosen node spans. `index` is the unit's zero-based position; `page` its 1-based page
10
+ * number when the document has pages.
11
+ */
12
+ export interface TreeUnit {
13
+ index: number;
14
+ text: string;
15
+ /** 1-based page number (PDF). Defaults to `index + 1` for page input; absent for section input. */
16
+ page?: number;
17
+ }
18
+ /**
19
+ * A heading the document declares about itself: a PDF outline (bookmark) entry, a markdown header,
20
+ * a section boundary your extractor already knows. `unit` is the zero-based unit it starts in;
21
+ * `page` (1-based) is accepted instead for page input. `level` 1 is top level.
22
+ */
23
+ export interface TreeHeading {
24
+ title: string;
25
+ level: number;
26
+ unit?: number;
27
+ page?: number;
28
+ }
29
+ /** A section as produced by an extractor that already splits on headings. */
30
+ export interface TreeSection {
31
+ text: string;
32
+ title?: string;
33
+ /** Heading depth, 1 = top level. Sections without a level are treated as level 1. */
34
+ level?: number;
35
+ /** 1-based page the section starts on, when known. */
36
+ page?: number;
37
+ }
38
+ /**
39
+ * What {@link import('./build.js').buildDocumentTree} builds from. Give exactly one of `pages`
40
+ * (one string per page, in order) or `sections` (heading-split text). `outline`, when present, is the
41
+ * cheapest and most reliable structure there is (a PDF's bookmarks) and is used before any heuristic.
42
+ */
43
+ export interface DocumentTreeInput {
44
+ pages?: string[];
45
+ sections?: TreeSection[];
46
+ /** Headings the document declares (PDF outline/bookmarks), in reading order. */
47
+ outline?: TreeHeading[];
48
+ /** Document title, shown to the navigating LLM and used as the citation source. */
49
+ title?: string;
50
+ }
51
+ /** How the structure of a tree was obtained, cheapest first. */
52
+ export type TreeStructureSource =
53
+ /** From {@link DocumentTreeInput.outline}. */
54
+ 'outline'
55
+ /** From section titles/levels or headings detected in the text — no LLM. */
56
+ | 'headings'
57
+ /** An LLM proposed the sections (the document had no usable outline). */
58
+ | 'llm'
59
+ /** Fallback: fixed-size page groups. Always succeeds; used when everything above failed or was skipped. */
60
+ | 'flat';
61
+ /** Where a node's summary came from. */
62
+ export type TreeSummarySource = 'llm' | 'text' | 'reused';
63
+ export interface DocumentTreeNode {
64
+ /** Stable within the tree: `0001`, `0002`… in depth-first order (PageIndex's scheme). */
65
+ id: string;
66
+ title: string;
67
+ /** What the section covers — what the navigating LLM reads to decide whether to go in. */
68
+ summary?: string;
69
+ summarySource?: TreeSummarySource;
70
+ /** First and last unit (inclusive, zero-based) the node spans, children included. */
71
+ unitStart: number;
72
+ unitEnd: number;
73
+ /** First and last page (1-based, inclusive) when the document has pages. */
74
+ pageStart?: number;
75
+ pageEnd?: number;
76
+ /**
77
+ * Titles of descendants removed to respect `maxDepth` — kept because they are routing information:
78
+ * the pages are still reachable by reading this node, the titles tell the navigator they are here.
79
+ */
80
+ keyItems?: string[];
81
+ /** Hash of the node's title and the text it spans; an incremental rebuild reuses a summary when it matches. */
82
+ contentHash: string;
83
+ children: DocumentTreeNode[];
84
+ }
85
+ /** What a build cost and how it ended — persisted with the tree, so an operator can see it. */
86
+ export interface TreeBuildStats {
87
+ llmCalls: number;
88
+ inputTokens: number;
89
+ outputTokens: number;
90
+ durationMs: number;
91
+ /** Set when the structure step fell back (to `flat`) or summaries were cut short, and why. */
92
+ fallbackReason?: string;
93
+ /** True when the per-document budget or timeout ran out at some point during the build. */
94
+ budgetExhausted: boolean;
95
+ /** Summaries carried over unchanged from the previous tree (incremental rebuild). */
96
+ reusedSummaries: number;
97
+ }
98
+ export interface DocumentTree {
99
+ documentId: string;
100
+ /** Format version of this structure. */
101
+ version: 1;
102
+ title?: string;
103
+ /** One-paragraph description of the whole document (LLM, when budget allows). */
104
+ description?: string;
105
+ structure: TreeStructureSource;
106
+ /**
107
+ * Hash of the input text and every build option that shapes the tree. Equal fingerprint ⇒ the
108
+ * build would produce the same tree, so it is skipped (see `previous` in the build options).
109
+ */
110
+ fingerprint: string;
111
+ unitCount: number;
112
+ /** Top-level nodes. */
113
+ nodes: DocumentTreeNode[];
114
+ stats: TreeBuildStats;
115
+ /**
116
+ * Document metadata used for access filtering, exactly like chunk metadata in a vector store
117
+ * (tenant, audience, collection…). Stores apply `filter` to it with the vector stores' semantics.
118
+ */
119
+ metadata?: Record<string, unknown>;
120
+ /** Citation-facing origin (title, URL), copied onto returned passages. */
121
+ source?: string;
122
+ /** ISO timestamp of the build. */
123
+ builtAt: string;
124
+ }
125
+ /** Walk a tree depth-first (pre-order). */
126
+ export declare function walkTree(nodes: readonly DocumentTreeNode[], path?: readonly DocumentTreeNode[]): Generator<{
127
+ node: DocumentTreeNode;
128
+ path: readonly DocumentTreeNode[];
129
+ }>;
130
+ /** Index a tree's nodes by id, each with its ancestors. */
131
+ export declare function indexTree(tree: Pick<DocumentTree, 'nodes'>): Map<string, {
132
+ node: DocumentTreeNode;
133
+ path: readonly DocumentTreeNode[];
134
+ }>;
135
+ //# sourceMappingURL=types.d.ts.map