@dudousxd/nestjs-agent-rag 0.12.0 → 0.13.1

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/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Passage, Retriever, EmbeddingProvider, RetrieveOptions, Reranker, ToolSpec, ToolHandler, RerankOptions } from '@dudousxd/nestjs-agent-core';
1
+ import { Passage, Retriever, EmbeddingProvider, RetrieveOptions, Reranker, ToolSpec, ToolHandler, RerankOptions, ModelProvider, AiToolCtx } from '@dudousxd/nestjs-agent-core';
2
2
 
3
3
  interface ChunkOptions {
4
4
  /** Target max characters per chunk. Default 800. */
@@ -327,7 +327,7 @@ declare function documentIdOf(chunkId: string): string;
327
327
  * both halves in one module would make every retriever ↔ telemetry edge a cycle.
328
328
  */
329
329
  /** Which retrieval strategy answered. `unknown` is a retriever this package did not ship. */
330
- type RetrieverKind = 'embedding' | 'keyword' | 'lexical' | 'hybrid' | 'filtered' | 'reranking' | 'unknown';
330
+ type RetrieverKind = 'embedding' | 'keyword' | 'lexical' | 'hybrid' | 'filtered' | 'reranking' | 'tree-navigation' | 'two-stage' | 'unknown';
331
331
  /** Which backend held the chunks. Omitted entirely for a retriever that holds its own (keyword). */
332
332
  type VectorStoreKind = 'memory' | 'pg' | 'redis';
333
333
  /**
@@ -1448,7 +1448,12 @@ interface OpenAiEmbeddingsOptions {
1448
1448
  apiKey?: string;
1449
1449
  /** Output width for models that support shortening (`text-embedding-3-*`), sent as `dimensions`. */
1450
1450
  dimensions?: number;
1451
- /** Inputs per request. Default 64. */
1451
+ /**
1452
+ * Inputs per request. Default 64. When the server refuses a request for its size (see
1453
+ * {@link isBatchTooLarge}) the batch is split in halves and retried, and later requests use the
1454
+ * size that worked — so a server whose limit is below this (TEI's `--max-client-batch-size`
1455
+ * defaults to 32) costs one refused request, not a failed ingestion.
1456
+ */
1452
1457
  batchSize?: number;
1453
1458
  /**
1454
1459
  * Inputs longer than this many characters are truncated before sending, so one oversized chunk
@@ -1462,15 +1467,28 @@ interface OpenAiEmbeddingsOptions {
1462
1467
  timeoutMs?: number;
1463
1468
  /** Called with the provider-reported token count of every request — for cost accounting. */
1464
1469
  onUsage?: (tokens: number) => void;
1470
+ /**
1471
+ * Called when the server refused a batch for its size and it was split — a misconfiguration worth
1472
+ * surfacing: raise the server's limit (TEI: `--max-client-batch-size`) or lower `batchSize`.
1473
+ */
1474
+ onWarn?: (message: string) => void;
1465
1475
  /** `fetch` to use. Default the global one. */
1466
1476
  fetch?: typeof fetch;
1467
1477
  }
1478
+ /**
1479
+ * Did the server refuse this request for its **size** — too many inputs (TEI's
1480
+ * `--max-client-batch-size`: `batch size 64 > maximum allowed batch size 32`) or too large a
1481
+ * payload? Those are fixed by sending fewer inputs per request; anything else (auth, a bad model,
1482
+ * one input over the context window) is not, and is rethrown as is.
1483
+ */
1484
+ declare function isBatchTooLarge(error: unknown): boolean;
1468
1485
  /**
1469
1486
  * An {@link EmbeddingProvider} over any OpenAI-compatible `POST /v1/embeddings` endpoint, with no SDK
1470
1487
  * dependency: OpenAI itself, or a gateway/local server speaking the same protocol (TEI, Ollama,
1471
1488
  * vLLM, LocalAI…). For a Vercel AI SDK model use `aiSdkEmbedding` from `-ai-sdk` instead.
1472
1489
  *
1473
- * Batches by {@link OpenAiEmbeddingsOptions.batchSize}, restores input order from each item's
1490
+ * Batches by {@link OpenAiEmbeddingsOptions.batchSize} — splitting and retrying a batch the server
1491
+ * refuses as too large, and remembering the size that worked — restores input order from each item's
1474
1492
  * `index`, and fails loudly (an {@link HttpModelError}) on a non-2xx answer or a vector count that
1475
1493
  * doesn't match the inputs. Empty strings are sent as a single space, since OpenAI rejects them.
1476
1494
  *
@@ -1525,4 +1543,788 @@ declare class HttpReranker implements Reranker {
1525
1543
  rerank(query: string, passages: Passage[], options?: RerankOptions): Promise<Passage[]>;
1526
1544
  }
1527
1545
 
1528
- export { type ChunkOptions, type ChunkRecord, DEFAULT_STOP_WORDS, type DescribesRetrieval, type DrizzleLike, EmbeddingRetriever, type EmbeddingRetrieverOptions, FilteredRetriever, HttpModelError, HttpReranker, type HttpRerankerOptions, HybridRetriever, type HybridRetrieverOptions, type IndexedDocument, type IngestChunksOptions, type IngestDocument, type IngestOptions, type InstrumentRetrieverOptions, KeywordRetriever, type KeywordRetrieverOptions, LexicalRetriever, type LexicalVectorStore, type ListChunksOptions, MemoryVectorStore, type MetadataPatch, type NodePgQueryable, type OpenAiEmbeddingsOptions, type PgClient, type PgClientSource, type PgFullTextOptions, PgLexicalVectorStore, PgVectorStore, type PgVectorStoreOptions, type PostgresJsSql, RAG_DIAGNOSTIC_LIB, RAG_RETRIEVAL_CHANNEL, RAG_RETRIEVAL_EVENT, type RagRetrievalEvent, type RedisSearchClient, RedisVectorSchemaMismatchError, RedisVectorStore, type RedisVectorStoreOptions, RerankingRetriever, type RerankingRetrieverOptions, type RetrievalDescriptor, type RetrievalTool, type RetrievalToolOptions, type RetrieverKind, type StoredChunk, UnsafeRemovalError, type VectorRecord, type VectorSearchOptions, type VectorStore, type VectorStoreKind, anyTermTsquery, applyMetadataPatch, chunkDocuments, chunkIndexOf, chunkText, createRetrievalTool, describeRetrieval, describeSharedSource, describeSource, describesRetrieval, documentIdOf, emitRagRetrieval, hasSearchSyntax, ingestChunks, ingestDocuments, instrumentRetriever, isLexicalVectorStore, keywordTerms, openAiEmbeddings, toPgClient };
1546
+ /**
1547
+ * The data model of tree navigation retrieval: a hierarchical table of contents per long document
1548
+ * (PageIndex-style: sections with page ranges and summaries), built once at ingestion and read by an
1549
+ * LLM at query time to walk to the relevant sections instead of ranking hundreds of look-alike chunks.
1550
+ */
1551
+ /**
1552
+ * One addressable piece of a document's text, in reading order — a PDF page, or a section of a
1553
+ * markdown/HTML document. Tree nodes point at ranges of units, and navigation reads the text of the
1554
+ * units a chosen node spans. `index` is the unit's zero-based position; `page` its 1-based page
1555
+ * number when the document has pages.
1556
+ */
1557
+ interface TreeUnit {
1558
+ index: number;
1559
+ text: string;
1560
+ /** 1-based page number (PDF). Defaults to `index + 1` for page input; absent for section input. */
1561
+ page?: number;
1562
+ }
1563
+ /**
1564
+ * A heading the document declares about itself: a PDF outline (bookmark) entry, a markdown header,
1565
+ * a section boundary your extractor already knows. `unit` is the zero-based unit it starts in;
1566
+ * `page` (1-based) is accepted instead for page input. `level` 1 is top level.
1567
+ */
1568
+ interface TreeHeading {
1569
+ title: string;
1570
+ level: number;
1571
+ unit?: number;
1572
+ page?: number;
1573
+ }
1574
+ /** A section as produced by an extractor that already splits on headings. */
1575
+ interface TreeSection {
1576
+ text: string;
1577
+ title?: string;
1578
+ /** Heading depth, 1 = top level. Sections without a level are treated as level 1. */
1579
+ level?: number;
1580
+ /** 1-based page the section starts on, when known. */
1581
+ page?: number;
1582
+ }
1583
+ /**
1584
+ * What {@link import('./build.js').buildDocumentTree} builds from. Give exactly one of `pages`
1585
+ * (one string per page, in order) or `sections` (heading-split text). `outline`, when present, is the
1586
+ * cheapest and most reliable structure there is (a PDF's bookmarks) and is used before any heuristic.
1587
+ */
1588
+ interface DocumentTreeInput {
1589
+ pages?: string[];
1590
+ sections?: TreeSection[];
1591
+ /** Headings the document declares (PDF outline/bookmarks), in reading order. */
1592
+ outline?: TreeHeading[];
1593
+ /** Document title, shown to the navigating LLM and used as the citation source. */
1594
+ title?: string;
1595
+ }
1596
+ /** How the structure of a tree was obtained, cheapest first. */
1597
+ type TreeStructureSource =
1598
+ /** From {@link DocumentTreeInput.outline}. */
1599
+ 'outline'
1600
+ /** From section titles/levels or headings detected in the text — no LLM. */
1601
+ | 'headings'
1602
+ /** An LLM proposed the sections (the document had no usable outline). */
1603
+ | 'llm'
1604
+ /** Fallback: fixed-size page groups. Always succeeds; used when everything above failed or was skipped. */
1605
+ | 'flat';
1606
+ /** Where a node's summary came from. */
1607
+ type TreeSummarySource = 'llm' | 'text' | 'reused';
1608
+ interface DocumentTreeNode {
1609
+ /** Stable within the tree: `0001`, `0002`… in depth-first order (PageIndex's scheme). */
1610
+ id: string;
1611
+ title: string;
1612
+ /** What the section covers — what the navigating LLM reads to decide whether to go in. */
1613
+ summary?: string;
1614
+ summarySource?: TreeSummarySource;
1615
+ /** First and last unit (inclusive, zero-based) the node spans, children included. */
1616
+ unitStart: number;
1617
+ unitEnd: number;
1618
+ /** First and last page (1-based, inclusive) when the document has pages. */
1619
+ pageStart?: number;
1620
+ pageEnd?: number;
1621
+ /**
1622
+ * Titles of descendants removed to respect `maxDepth` — kept because they are routing information:
1623
+ * the pages are still reachable by reading this node, the titles tell the navigator they are here.
1624
+ */
1625
+ keyItems?: string[];
1626
+ /** Hash of the node's title and the text it spans; an incremental rebuild reuses a summary when it matches. */
1627
+ contentHash: string;
1628
+ children: DocumentTreeNode[];
1629
+ }
1630
+ /** What a build cost and how it ended — persisted with the tree, so an operator can see it. */
1631
+ interface TreeBuildStats {
1632
+ llmCalls: number;
1633
+ inputTokens: number;
1634
+ outputTokens: number;
1635
+ durationMs: number;
1636
+ /** Set when the structure step fell back (to `flat`) or summaries were cut short, and why. */
1637
+ fallbackReason?: string;
1638
+ /** True when the per-document budget or timeout ran out at some point during the build. */
1639
+ budgetExhausted: boolean;
1640
+ /** Summaries carried over unchanged from the previous tree (incremental rebuild). */
1641
+ reusedSummaries: number;
1642
+ }
1643
+ interface DocumentTree {
1644
+ documentId: string;
1645
+ /** Format version of this structure. */
1646
+ version: 1;
1647
+ title?: string;
1648
+ /** One-paragraph description of the whole document (LLM, when budget allows). */
1649
+ description?: string;
1650
+ structure: TreeStructureSource;
1651
+ /**
1652
+ * Hash of the input text and every build option that shapes the tree. Equal fingerprint ⇒ the
1653
+ * build would produce the same tree, so it is skipped (see `previous` in the build options).
1654
+ */
1655
+ fingerprint: string;
1656
+ unitCount: number;
1657
+ /** Top-level nodes. */
1658
+ nodes: DocumentTreeNode[];
1659
+ stats: TreeBuildStats;
1660
+ /**
1661
+ * Document metadata used for access filtering, exactly like chunk metadata in a vector store
1662
+ * (tenant, audience, collection…). Stores apply `filter` to it with the vector stores' semantics.
1663
+ */
1664
+ metadata?: Record<string, unknown>;
1665
+ /** Citation-facing origin (title, URL), copied onto returned passages. */
1666
+ source?: string;
1667
+ /** ISO timestamp of the build. */
1668
+ builtAt: string;
1669
+ }
1670
+ /** Walk a tree depth-first (pre-order). */
1671
+ declare function walkTree(nodes: readonly DocumentTreeNode[], path?: readonly DocumentTreeNode[]): Generator<{
1672
+ node: DocumentTreeNode;
1673
+ path: readonly DocumentTreeNode[];
1674
+ }>;
1675
+ /** Index a tree's nodes by id, each with its ancestors. */
1676
+ declare function indexTree(tree: Pick<DocumentTree, 'nodes'>): Map<string, {
1677
+ node: DocumentTreeNode;
1678
+ path: readonly DocumentTreeNode[];
1679
+ }>;
1680
+
1681
+ /**
1682
+ * Deterministic structure detection — the cheap path that runs before any LLM. Finds headings in
1683
+ * page text: markdown headers, and the numbered/keyword headings long structured documents use
1684
+ * (`PART 52`, `Subpart 9.1`, `Item 7.`, `52.236-5 Material and Workmanship.`, `3.2 Scope`).
1685
+ */
1686
+ /** A heading found in (or declared for) the document, before nesting. */
1687
+ interface DetectedHeading {
1688
+ title: string;
1689
+ level: number;
1690
+ unit: number;
1691
+ /** The heading is the first text of its unit, so the previous section ends in the unit before. */
1692
+ atUnitStart: boolean;
1693
+ }
1694
+ /**
1695
+ * Detect headings in each unit's text. `markdownOnly` restricts detection to markdown headers (for
1696
+ * text you know is markdown, where a numbered line is more likely a list item).
1697
+ *
1698
+ * Besides running headers (below), it drops **table-of-contents listings**: five or more
1699
+ * consecutive keyword/numbered heading lines with no body text between them are a contents page
1700
+ * (a regulation part opens with one), not sections — only a leading top-level heading of the run
1701
+ * (`PART 2—…`) and its last line (the first real heading, which body text follows) are kept. Markdown headers are explicit and never dropped this way.
1702
+ */
1703
+ declare function detectHeadings(texts: readonly string[], markdownOnly?: boolean): DetectedHeading[];
1704
+
1705
+ /**
1706
+ * The structured side of every tree-LLM request: what is being asked, as data. The prompt says the
1707
+ * same thing in prose; `task` exists so a deterministic fake (tests, offline dev — see
1708
+ * {@link import('./keyword-tree-llm.js').keywordTreeLlm}) can answer without parsing prose, and so a
1709
+ * host can log or route requests by kind.
1710
+ */
1711
+ type TreeLlmTask = {
1712
+ kind: 'structure';
1713
+ /** The window of pages the LLM is asked to find section starts in. */
1714
+ units: {
1715
+ unit: number;
1716
+ page?: number;
1717
+ text: string;
1718
+ }[];
1719
+ } | {
1720
+ kind: 'summarize';
1721
+ title: string;
1722
+ text: string;
1723
+ } | {
1724
+ kind: 'describe';
1725
+ title?: string;
1726
+ outline: {
1727
+ title: string;
1728
+ summary?: string;
1729
+ }[];
1730
+ } | {
1731
+ kind: 'navigate';
1732
+ query: string;
1733
+ /** `single-pass`: the whole tree is shown; `beam`: one level of candidates at a time. */
1734
+ mode: 'single-pass' | 'beam';
1735
+ candidates: TreeLlmCandidate[];
1736
+ /** Max nodes the answer may name. */
1737
+ maxNodes: number;
1738
+ } | {
1739
+ kind: 'select';
1740
+ query: string;
1741
+ documents: {
1742
+ id: string;
1743
+ title?: string;
1744
+ description?: string;
1745
+ }[];
1746
+ maxDocuments: number;
1747
+ };
1748
+ /** A node as shown to the navigating LLM. */
1749
+ interface TreeLlmCandidate {
1750
+ id: string;
1751
+ title: string;
1752
+ summary?: string;
1753
+ pages?: string;
1754
+ /** Present on beam candidates: whether the node can be expanded. */
1755
+ hasChildren?: boolean;
1756
+ /** Present in single-pass mode: depth (0 = top level). */
1757
+ depth?: number;
1758
+ }
1759
+ interface TreeLlmRequest {
1760
+ task: TreeLlmTask;
1761
+ system: string;
1762
+ prompt: string;
1763
+ /** A cap for the reply. Adapters pass it as `max_tokens` when they can. */
1764
+ maxOutputTokens: number;
1765
+ /** Aborted when the call's timeout or the build/navigation deadline passes. */
1766
+ signal: AbortSignal;
1767
+ }
1768
+ interface TreeLlmResponse {
1769
+ text: string;
1770
+ /** Provider-reported usage. When absent, the budget estimates ~4 characters per token. */
1771
+ usage?: {
1772
+ inputTokens?: number;
1773
+ outputTokens?: number;
1774
+ };
1775
+ }
1776
+ /**
1777
+ * The only LLM surface tree building and navigation need: one prompt in, one text reply out. Replies
1778
+ * are expected to be JSON (for structure / navigate / select) or plain text (summaries); parsing is
1779
+ * lenient (code fences, prose around the object). Bring any model:
1780
+ * {@link treeLlmFromModelProvider} (the agent's own `ModelProvider`), {@link openAiChatTreeLlm}
1781
+ * (any OpenAI-compatible `/chat/completions`), or a function of your own. Use a deterministic
1782
+ * setting (temperature 0) — trees are cached by content, so a flaky model makes rebuilds noisy.
1783
+ */
1784
+ type TreeLlm = (request: TreeLlmRequest) => Promise<TreeLlmResponse>;
1785
+ /**
1786
+ * Hard limits for one document build or one navigation. Every LLM call is checked against them
1787
+ * **before** it is made (with the prompt's estimated size), so a budget is never overshot by more
1788
+ * than one reply, and nothing retries past it: the failure mode this exists for is an indexer that
1789
+ * burned ~1,240 calls on one PDF it could not structure.
1790
+ */
1791
+ interface TreeBudget {
1792
+ /** Max LLM calls. */
1793
+ maxCalls?: number;
1794
+ /** Max input (prompt) tokens across all calls, estimated at ~4 chars/token before each call. */
1795
+ maxInputTokens?: number;
1796
+ /** Max output tokens across all calls. */
1797
+ maxOutputTokens?: number;
1798
+ /** Wall-clock deadline for the whole operation, ms. */
1799
+ timeoutMs?: number;
1800
+ /** Per-call timeout, ms. Default: whatever is left of `timeoutMs`. */
1801
+ callTimeoutMs?: number;
1802
+ }
1803
+ /** Thrown internally when a budget or deadline would be exceeded; callers turn it into a fallback. */
1804
+ declare class TreeBudgetExceededError extends Error {
1805
+ readonly limit: 'calls' | 'inputTokens' | 'outputTokens' | 'timeout';
1806
+ constructor(limit: 'calls' | 'inputTokens' | 'outputTokens' | 'timeout');
1807
+ }
1808
+ /** ~4 characters per token: the budget's estimate when a provider reports no usage. */
1809
+ declare function estimateTokens(text: string): number;
1810
+ /**
1811
+ * Pull the JSON object out of a model reply: bare, fenced in ```json, or surrounded by prose.
1812
+ * Returns `undefined` when there is none (never throws).
1813
+ */
1814
+ declare function parseJsonReply(text: string): Record<string, unknown> | undefined;
1815
+ /**
1816
+ * A {@link TreeLlm} over the agent's own {@link ModelProvider}: one `runTurn` with no tools and a
1817
+ * discarding sink. Usage comes from the provider's report.
1818
+ */
1819
+ declare function treeLlmFromModelProvider(provider: ModelProvider): TreeLlm;
1820
+ interface OpenAiChatTreeLlmOptions {
1821
+ model: string;
1822
+ /** Base URL up to and including `/v1`. Default `https://api.openai.com/v1`. */
1823
+ baseUrl?: string;
1824
+ apiKey?: string;
1825
+ headers?: Record<string, string>;
1826
+ /** Default 0 — trees are cached by content, determinism keeps rebuilds quiet. */
1827
+ temperature?: number;
1828
+ /**
1829
+ * Send `response_format: { type: 'json_object' }` on JSON tasks. Default `false`: not every
1830
+ * OpenAI-compatible server accepts it, and replies are parsed leniently anyway.
1831
+ */
1832
+ jsonMode?: boolean;
1833
+ /** Extra body fields (a reasoning switch, provider routing). */
1834
+ body?: Record<string, unknown>;
1835
+ fetch?: typeof fetch;
1836
+ }
1837
+ /**
1838
+ * A {@link TreeLlm} over any OpenAI-compatible `POST /v1/chat/completions` — OpenAI, a gateway,
1839
+ * vLLM, Ollama, DeepSeek, Qwen… — with no SDK dependency. Throws `HttpModelError` on a non-2xx.
1840
+ */
1841
+ declare function openAiChatTreeLlm(options: OpenAiChatTreeLlmOptions): TreeLlm;
1842
+ /** A get/set cache — a `Map`, an LRU, a Redis wrapper. Values are whole replies. */
1843
+ interface TreeLlmCache {
1844
+ get(key: string): TreeLlmResponse | undefined | Promise<TreeLlmResponse | undefined>;
1845
+ set(key: string, value: TreeLlmResponse): unknown;
1846
+ }
1847
+ /**
1848
+ * Memoize a {@link TreeLlm} by the exact request (task kind, system prompt, prompt, output cap).
1849
+ * With a deterministic model that makes a rebuild of an unchanged section, or a repeated question,
1850
+ * free. Cached replies report zero usage, so they cost nothing against a budget but a call slot.
1851
+ */
1852
+ declare function cachedTreeLlm(llm: TreeLlm, cache?: TreeLlmCache): TreeLlm;
1853
+ /**
1854
+ * A fast, deterministic, non-cryptographic 64-bit hash (two independent 32-bit FNV-1a/murmur-mixed
1855
+ * lanes) as 16 hex chars — content fingerprints and cache keys, not security.
1856
+ */
1857
+ declare function stableHash(text: string): string;
1858
+
1859
+ interface BuildDocumentTreeOptions {
1860
+ documentId: string;
1861
+ /**
1862
+ * The model for summaries, the document description, and structuring documents with no usable
1863
+ * outline. Omit it and the build is LLM-free: outline/headings or page groups, with the start of
1864
+ * each section's text as its summary.
1865
+ */
1866
+ llm?: TreeLlm;
1867
+ /** Deepest level kept (1 = top-level sections only). Deeper headings become `keyItems`. Default 4. */
1868
+ maxDepth?: number;
1869
+ /**
1870
+ * Hard per-document limits for every LLM call of the build. Defaults: 60 calls, 400k input
1871
+ * tokens, 40k output tokens, 5 minutes overall, 60 s per call. Running out is not an error: the
1872
+ * structure step falls back to page groups, and remaining summaries use the section's own text.
1873
+ */
1874
+ budget?: TreeBudget;
1875
+ /** Summarize nodes with the LLM. Default `true` when `llm` is set. */
1876
+ summaries?: boolean;
1877
+ /** A node whose text is shorter than this is its own summary (no LLM call). Default 600 chars. */
1878
+ summaryMinChars?: number;
1879
+ /** Text of a node sent to the summarizer, at most. Default 8 000 chars. */
1880
+ summaryInputChars?: number;
1881
+ /** Concurrent summary calls. Default 4. */
1882
+ concurrency?: number;
1883
+ /** Write a one-paragraph description of the document. Default `true` when `llm` is set. */
1884
+ describe?: boolean;
1885
+ /**
1886
+ * Find headings in the text (markdown headers, `PART 52`, `Item 7.`, `3.2 Scope`…) when there is
1887
+ * no outline and sections carry no titles. `'markdown'` only trusts markdown headers. Default `true`.
1888
+ */
1889
+ detectHeadings?: boolean | 'markdown';
1890
+ /** At least this many headings for a detected structure to be used. Default 3. */
1891
+ minHeadings?: number;
1892
+ /** Ask the LLM for the structure when nothing deterministic worked. Default `true` when `llm` is set. */
1893
+ llmStructure?: boolean;
1894
+ /** Characters of text per structuring call. Default 24 000. */
1895
+ structureWindowChars?: number;
1896
+ /**
1897
+ * Structuring calls allowed. A document needing more windows than this is not sent at all —
1898
+ * it goes straight to page groups instead of spending a budget it cannot finish. Default 12.
1899
+ */
1900
+ maxStructureCalls?: number;
1901
+ /** Units per page-group node (flat trees, and splitting long leaves). Default 5. */
1902
+ groupSize?: number;
1903
+ /** A leaf spanning more units than this is split into page groups. Default 10. */
1904
+ maxLeafUnits?: number;
1905
+ /** Children per level before page groups are grouped again (flat trees). Default 12. */
1906
+ maxChildren?: number;
1907
+ /** The previous tree of this document: an unchanged input returns it as is; changed nodes are re-summarized, the rest reuse their summaries. */
1908
+ previous?: DocumentTree;
1909
+ metadata?: Record<string, unknown>;
1910
+ source?: string;
1911
+ signal?: AbortSignal;
1912
+ }
1913
+ interface BuiltDocumentTree {
1914
+ tree: DocumentTree;
1915
+ /** The units the node ranges index — persist them with the tree ({@link import('./store.js').DocumentTreeStore.put}). */
1916
+ units: TreeUnit[];
1917
+ /** `true` when `previous` had the same fingerprint and was returned without building. */
1918
+ unchanged: boolean;
1919
+ }
1920
+ /** The text a node spans (children included), joined in reading order. */
1921
+ declare function spanText(units: readonly TreeUnit[], start: number, end: number): string;
1922
+ /**
1923
+ * Build a document's navigation tree: a hierarchical table of contents whose nodes carry a title,
1924
+ * the page (or section) range they span, and a summary.
1925
+ *
1926
+ * Structure, cheapest first, never looping:
1927
+ *
1928
+ * 1. the `outline` the input declares (PDF bookmarks);
1929
+ * 2. section titles/levels (`sections` input), or headings detected in the text;
1930
+ * 3. the LLM, window by window (only when a document has no usable outline, and only if every
1931
+ * window fits `maxStructureCalls` and the budget up front);
1932
+ * 4. page groups — always works; the fallback for any failure above.
1933
+ *
1934
+ * Then summaries: top levels first (they matter most for navigation), each node's text sent once,
1935
+ * within the budget; what the budget leaves unsummarized gets the start of its own text. With a
1936
+ * `previous` tree, an unchanged input is returned as is (zero calls), and nodes whose title and text
1937
+ * are unchanged keep their summary.
1938
+ *
1939
+ * The result is deterministic for a deterministic model: node ids are depth-first positions, and
1940
+ * nothing depends on timing except which summaries a timed-out build got to.
1941
+ */
1942
+ declare function buildDocumentTree(input: DocumentTreeInput, options: BuildDocumentTreeOptions): Promise<BuiltDocumentTree>;
1943
+
1944
+ /**
1945
+ * A deterministic stand-in for the tree LLM — no model, no network — for tests and offline
1946
+ * development. It answers from the structured {@link TreeLlmRequest.task}, never the prose:
1947
+ *
1948
+ * - `navigate` / `select`: ranks candidates by how many of the question's terms their title and
1949
+ * summary share, keeps those sharing any (deepest first on ties in single-pass mode); in a beam,
1950
+ * candidates with subsections are expanded, the rest read;
1951
+ * - `summarize`: the section's first sentence; `describe`: the top-level titles;
1952
+ * - `structure`: lines in ALL CAPS (or markdown headers) become level-1 sections.
1953
+ *
1954
+ * Wrap it to count calls, inject failures or slow replies when testing budgets.
1955
+ */
1956
+ declare function keywordTreeLlm(): TreeLlm;
1957
+
1958
+ /** A stored tree without its nodes — what listing and document selection need. */
1959
+ interface DocumentTreeHeader {
1960
+ documentId: string;
1961
+ title?: string;
1962
+ description?: string;
1963
+ structure: DocumentTree['structure'];
1964
+ fingerprint: string;
1965
+ unitCount: number;
1966
+ metadata?: Record<string, unknown>;
1967
+ source?: string;
1968
+ builtAt: string;
1969
+ }
1970
+ /**
1971
+ * Where navigation trees live: one tree per document plus the units (pages/sections) its node
1972
+ * ranges point at, so navigation can read a node's text without going back to the source file.
1973
+ *
1974
+ * `filter` has **exactly** the vector stores' semantics (see `VectorStore`): every key must match
1975
+ * the tree's `metadata`, an array value is match-any, and an empty array denies. It is an access
1976
+ * boundary — a navigator given a tenant filter must not read another tenant's tree — so every read
1977
+ * that takes one applies it. `readUnits` takes none: call it only for a tree a filtered `get` just
1978
+ * returned (which is what the navigator does).
1979
+ */
1980
+ interface DocumentTreeStore {
1981
+ /** Store (replace) a document's tree and units. */
1982
+ put(tree: DocumentTree, units: TreeUnit[]): Promise<void>;
1983
+ /** A document's tree, when it exists **and** passes `filter`. */
1984
+ get(documentId: string, filter?: Record<string, unknown>): Promise<DocumentTree | undefined>;
1985
+ /** The trees of those of `documentIds` that exist and pass `filter` (any order). */
1986
+ getMany(documentIds: readonly string[], filter?: Record<string, unknown>): Promise<DocumentTree[]>;
1987
+ /** Headers (no nodes) of every tree passing `filter`, or of the listed `documentIds` only. */
1988
+ list(options?: {
1989
+ filter?: Record<string, unknown>;
1990
+ documentIds?: readonly string[];
1991
+ limit?: number;
1992
+ }): Promise<DocumentTreeHeader[]>;
1993
+ /** Units `start..end` (inclusive) of a document, in order. */
1994
+ readUnits(documentId: string, start: number, end: number): Promise<TreeUnit[]>;
1995
+ /** Drop a document's tree and units. Unknown ids are a no-op. */
1996
+ remove(documentId: string): Promise<void>;
1997
+ }
1998
+ /** An in-process {@link DocumentTreeStore} — tests, and single-process apps with small corpora. */
1999
+ declare class MemoryDocumentTreeStore implements DocumentTreeStore {
2000
+ private readonly trees;
2001
+ put(tree: DocumentTree, units: TreeUnit[]): Promise<void>;
2002
+ get(documentId: string, filter?: Record<string, unknown>): Promise<DocumentTree | undefined>;
2003
+ getMany(documentIds: readonly string[], filter?: Record<string, unknown>): Promise<DocumentTree[]>;
2004
+ list(options?: {
2005
+ filter?: Record<string, unknown>;
2006
+ documentIds?: readonly string[];
2007
+ limit?: number;
2008
+ }): Promise<DocumentTreeHeader[]>;
2009
+ readUnits(documentId: string, start: number, end: number): Promise<TreeUnit[]>;
2010
+ remove(documentId: string): Promise<void>;
2011
+ }
2012
+
2013
+ interface PgDocumentTreeStoreOptions {
2014
+ /** Trees table. Default `agent_rag_trees`. */
2015
+ table?: string;
2016
+ /** Units (page/section text) table. Default `${table}_units`, i.e. `agent_rag_trees_units`. */
2017
+ unitsTable?: string;
2018
+ /** Unit rows per multi-row `INSERT`. Default 200. */
2019
+ insertBatchSize?: number;
2020
+ }
2021
+ /**
2022
+ * A Postgres {@link DocumentTreeStore}: one row per tree (the header columns, `metadata` as `jsonb`
2023
+ * for filtering, the nodes as `jsonb`) and one row per unit of text. Filters compile to the same SQL
2024
+ * as `PgVectorStore`'s — scalar `@>`, array match-any, empty array denies — so the filter you pass
2025
+ * the vector store works here unchanged.
2026
+ *
2027
+ * Takes the same clients as `PgVectorStore`: a `pg` `Pool`, a Drizzle database, a `postgres.js`
2028
+ * `sql`, or a {@link PgClient}. Call {@link PgDocumentTreeStore.ensureSchema} at boot, or copy
2029
+ * {@link PgDocumentTreeStore.schemaStatements} into a migration. Needs no extension.
2030
+ *
2031
+ * Subclassing: every filtered statement goes through {@link PgDocumentTreeStore.whereConditions}, like
2032
+ * `PgVectorStore.whereConditions`, so a subclass can map a filter onto its own columns.
2033
+ */
2034
+ declare class PgDocumentTreeStore implements DocumentTreeStore {
2035
+ protected readonly client: PgClient;
2036
+ protected readonly table: string;
2037
+ protected readonly unitsTable: string;
2038
+ private readonly insertBatchSize;
2039
+ constructor(client: PgClientSource, options?: PgDocumentTreeStoreOptions);
2040
+ /** The idempotent DDL {@link PgDocumentTreeStore.ensureSchema} runs, in order — for a migration. */
2041
+ schemaStatements(): string[];
2042
+ ensureSchema(): Promise<void>;
2043
+ /**
2044
+ * The SQL conditions a metadata `filter` compiles to (ANDed), appending bindings to `params`.
2045
+ * Override to map a filter onto your own columns. Return `['false']` to deny.
2046
+ */
2047
+ protected whereConditions(filter: Record<string, unknown> | undefined, params: unknown[]): string[];
2048
+ /** Replace the tree and its units in one transaction (when the client has one). */
2049
+ put(tree: DocumentTree, units: TreeUnit[]): Promise<void>;
2050
+ get(documentId: string, filter?: Record<string, unknown>): Promise<DocumentTree | undefined>;
2051
+ getMany(documentIds: readonly string[], filter?: Record<string, unknown>): Promise<DocumentTree[]>;
2052
+ list(options?: {
2053
+ filter?: Record<string, unknown>;
2054
+ documentIds?: readonly string[];
2055
+ limit?: number;
2056
+ }): Promise<DocumentTreeHeader[]>;
2057
+ readUnits(documentId: string, start: number, end: number): Promise<TreeUnit[]>;
2058
+ remove(documentId: string): Promise<void>;
2059
+ }
2060
+
2061
+ interface IndexDocumentTreeOptions extends Omit<BuildDocumentTreeOptions, 'previous'> {
2062
+ store: DocumentTreeStore;
2063
+ /**
2064
+ * Only documents with at least this many units (pages, or sections) get a tree; a shorter one is
2065
+ * skipped and any tree it had is removed. Default 20 — tree navigation pays off on long
2066
+ * structured documents and costs latency and LLM spend for nothing on short notes.
2067
+ */
2068
+ minUnits?: number;
2069
+ /** …or at least this many characters, whichever is met first. Default 60 000 (~15k tokens). */
2070
+ minChars?: number;
2071
+ }
2072
+ type IndexDocumentTreeResult = {
2073
+ status: 'skipped';
2074
+ reason: string;
2075
+ } | {
2076
+ status: 'unchanged';
2077
+ tree: DocumentTree;
2078
+ } | {
2079
+ status: 'built';
2080
+ tree: DocumentTree;
2081
+ };
2082
+ /**
2083
+ * Build (or rebuild) a document's tree and store it — the call to make at upload/ingestion time,
2084
+ * next to chunking and embedding. Short documents are skipped (see `minUnits`/`minChars`); an
2085
+ * unchanged document costs one store read and no LLM call; a changed one reuses the summaries of
2086
+ * its unchanged sections. Never throws for a document the LLM cannot structure: the tree falls back
2087
+ * to page groups and `tree.stats.fallbackReason` says why. Store errors do propagate.
2088
+ */
2089
+ declare function indexDocumentTree(input: DocumentTreeInput, options: IndexDocumentTreeOptions): Promise<IndexDocumentTreeResult>;
2090
+
2091
+ interface TreeNavigationRetrieverOptions {
2092
+ store: DocumentTreeStore;
2093
+ llm: TreeLlm;
2094
+ /**
2095
+ * A tree whose outline (titles, pages, summaries) fits in this many characters is navigated in
2096
+ * **one** call that sees the whole outline — PageIndex's tree search, the variant measured in the
2097
+ * benchmark. Bigger trees are walked level by level (a beam). Default 40 000 (~10k tokens).
2098
+ */
2099
+ singlePassMaxChars?: number;
2100
+ /** Nodes the navigator may expand or read per beam step. Default 4. */
2101
+ beamWidth?: number;
2102
+ /** Beam levels, at most. Nodes still marked "expand" at the last level are read whole. Default 4. */
2103
+ maxSteps?: number;
2104
+ /** Nodes returned per document, at most. Default 5. */
2105
+ maxNodes?: number;
2106
+ /**
2107
+ * Hard per-document limits for one navigation. Defaults: 6 calls, 60k input tokens, 4k output
2108
+ * tokens, 60 s. Running out mid-walk returns what was chosen so far (the nodes last marked for
2109
+ * expansion are read whole) and records `stoppedBy: 'budget'`.
2110
+ */
2111
+ budget?: TreeBudget;
2112
+ /** Characters of text per returned node. Default 12 000. */
2113
+ maxPassageChars?: number;
2114
+ /** Units (pages/sections) read per returned node, at most. Default 25. */
2115
+ maxUnitsPerNode?: number;
2116
+ /** Characters across all passages of one navigation (every document). Default 48 000 (~12k tokens). */
2117
+ maxContextChars?: number;
2118
+ /**
2119
+ * {@link TreeNavigationRetriever.retrieve} with no first stage: documents navigated per query. When
2120
+ * more trees pass the filter, the LLM picks this many from their titles and descriptions
2121
+ * (PageIndex's document selection). Default 3.
2122
+ */
2123
+ maxDocuments?: number;
2124
+ /** Trees shown to document selection, at most. Default 200. */
2125
+ maxSelectionCandidates?: number;
2126
+ /** Documents navigated in parallel. Default 3. */
2127
+ concurrency?: number;
2128
+ }
2129
+ /** One step of a navigation, for the audit trail. */
2130
+ interface NavigationStep {
2131
+ step: number;
2132
+ /** Node ids shown to the LLM this step. */
2133
+ shown: string[];
2134
+ /** Node ids it chose to open further. */
2135
+ expanded: string[];
2136
+ /** Node ids it chose to read. */
2137
+ read: string[];
2138
+ /** The LLM's stated reasoning, verbatim (capped). */
2139
+ reasoning?: string;
2140
+ }
2141
+ /** A node navigation chose, with the path that led to it. */
2142
+ interface NavigatedNode {
2143
+ nodeId: string;
2144
+ title: string;
2145
+ /** Ancestors from the top level down, then the node itself. */
2146
+ path: {
2147
+ id: string;
2148
+ title: string;
2149
+ }[];
2150
+ pageStart?: number;
2151
+ pageEnd?: number;
2152
+ unitStart: number;
2153
+ unitEnd: number;
2154
+ }
2155
+ /** The auditable record of navigating one document. */
2156
+ interface DocumentNavigation {
2157
+ documentId: string;
2158
+ title?: string;
2159
+ mode: 'single-pass' | 'beam';
2160
+ steps: NavigationStep[];
2161
+ nodes: NavigatedNode[];
2162
+ llmCalls: number;
2163
+ inputTokens: number;
2164
+ outputTokens: number;
2165
+ /** Why the walk stopped early, when it did. */
2166
+ stoppedBy?: 'budget' | 'timeout' | 'maxSteps' | 'error' | 'not-found';
2167
+ error?: string;
2168
+ }
2169
+ interface NavigationResult {
2170
+ passages: Passage[];
2171
+ documents: DocumentNavigation[];
2172
+ /** Set when {@link TreeNavigationRetriever.retrieve} had the LLM pick the documents. */
2173
+ selection?: {
2174
+ candidates: number;
2175
+ selected: string[];
2176
+ reasoning?: string;
2177
+ error?: string;
2178
+ };
2179
+ }
2180
+ interface NavigateOptions {
2181
+ /** Access filter applied to every tree read (vector-store semantics). */
2182
+ filter?: Record<string, unknown>;
2183
+ /** Overrides the retriever's per-document budget. */
2184
+ budget?: TreeBudget;
2185
+ signal?: AbortSignal;
2186
+ }
2187
+ /**
2188
+ * Tree navigation retrieval ("vectorless", reasoning-based — PageIndex's method): an LLM reads a
2189
+ * document's table of contents (titles, page ranges, summaries — no body text) and picks the
2190
+ * sections that answer the question; their text comes back as passages, with the path taken and the
2191
+ * model's reasoning recorded so the choice is auditable.
2192
+ *
2193
+ * A small tree is navigated in one call over the whole outline. A large one is walked level by
2194
+ * level: the model sees one level of candidates, opens (`expand`) the promising ones and takes
2195
+ * (`read`) the ones that already answer, until it reaches leaves, runs out of `maxSteps`, or runs
2196
+ * out of its hard per-document {@link TreeBudget} — in which case it returns what it had.
2197
+ *
2198
+ * It pays off on **long structured documents** (regulations, contracts, filings, manuals) where
2199
+ * chunk ranking struggles among hundreds of look-alike passages; on short notes it costs seconds and
2200
+ * LLM spend for nothing. Compose it behind a first stage with {@link TwoStageRetriever}, or give it
2201
+ * to the agent as a tool with {@link createNavigateDocumentTool}.
2202
+ *
2203
+ * As a plain `Retriever`, `retrieve(query, { filter })` navigates the trees that pass `filter`:
2204
+ * all of them when there are at most `maxDocuments`, otherwise the ones the LLM picks from their
2205
+ * descriptions.
2206
+ */
2207
+ declare class TreeNavigationRetriever implements Retriever {
2208
+ private readonly options;
2209
+ readonly store: DocumentTreeStore;
2210
+ private readonly llm;
2211
+ constructor(options: TreeNavigationRetrieverOptions);
2212
+ describeRetrieval(): RetrievalDescriptor;
2213
+ retrieve(query: string, options?: RetrieveOptions): Promise<Passage[]>;
2214
+ /** {@link retrieve} with the audit trail: which documents, which nodes, why. */
2215
+ search(query: string, options?: NavigateOptions): Promise<NavigationResult>;
2216
+ /**
2217
+ * Navigate the given documents (in parallel, up to `concurrency`) and return their passages in
2218
+ * the order given, within `maxContextChars`. Documents without a tree, or whose tree fails
2219
+ * `filter`, yield no passages and `stoppedBy: 'not-found'`.
2220
+ */
2221
+ navigate(query: string, documentIds: readonly string[], options?: NavigateOptions): Promise<NavigationResult>;
2222
+ private walk;
2223
+ private passagesOf;
2224
+ }
2225
+
2226
+ interface TwoStageRetrieverOptions {
2227
+ /** Passages pulled from the first stage. Default 20. */
2228
+ fetchTopK?: number;
2229
+ /**
2230
+ * Only the first this-many distinct documents of the first stage are considered for navigation.
2231
+ * Default 3 — past the top few, a document is more likely a large irrelevant tree (the costliest
2232
+ * failure measured: navigating FAR Part 52 because it ranked fourth) than the answer.
2233
+ */
2234
+ considerDocuments?: number;
2235
+ /** Of those, navigate at most this many (the ones that have a tree). Default 2. */
2236
+ navigateDocuments?: number;
2237
+ /** Per-document navigation budget (overrides the navigator's). */
2238
+ budget?: TreeBudget;
2239
+ /** Keep the first stage's chunks of a navigated document after its tree passages. Default `false`. */
2240
+ keepFirstStageChunks?: boolean;
2241
+ /**
2242
+ * The document a first-stage passage belongs to. Default: `metadata.documentId` when it is a
2243
+ * string, else the chunk id without its `#<n>` suffix (the id scheme `chunkDocuments` produces).
2244
+ */
2245
+ documentIdOf?: (passage: Passage) => string;
2246
+ /** Called with each navigation's audit trail (log it, trace it, show it). */
2247
+ onNavigation?: (navigation: DocumentNavigation) => void;
2248
+ }
2249
+ /**
2250
+ * Hybrid first stage, tree navigation second — the design the benchmark recommends: keep fast
2251
+ * chunk search (hybrid/lexical/dense, 0.3 s, no LLM) for picking **documents**, and navigate the
2252
+ * table of contents only inside the top **long** documents, where chunk ranking is what fails.
2253
+ *
2254
+ * 1. The first stage retrieves `fetchTopK` passages.
2255
+ * 2. Among its first `considerDocuments` distinct documents, those with a tree in the navigator's
2256
+ * store (build trees only for long documents, see `indexDocumentTree`'s `minUnits`) are long;
2257
+ * the first `navigateDocuments` of them are navigated, in parallel, each on its own budget.
2258
+ * 3. The output keeps the first stage's order: a navigated document's section passages take the
2259
+ * place of its chunks (at its best chunk's rank); short documents keep their chunks. A navigation
2260
+ * that fails or finds nothing leaves that document's chunks in place — never worse than stage one.
2261
+ *
2262
+ * `filter` is passed to both stages, so the access boundary holds on either path.
2263
+ */
2264
+ declare class TwoStageRetriever implements Retriever {
2265
+ private readonly firstStage;
2266
+ private readonly navigator;
2267
+ private readonly options;
2268
+ constructor(firstStage: Retriever, navigator: TreeNavigationRetriever, options?: TwoStageRetrieverOptions);
2269
+ describeRetrieval(): RetrievalDescriptor;
2270
+ retrieve(query: string, options?: RetrieveOptions): Promise<Passage[]>;
2271
+ /** {@link retrieve} with the navigations that were run. */
2272
+ search(query: string, options?: RetrieveOptions): Promise<{
2273
+ passages: Passage[];
2274
+ navigations: DocumentNavigation[];
2275
+ }>;
2276
+ }
2277
+
2278
+ interface NavigateDocumentToolOptions {
2279
+ /** Tool name the model sees. Default `navigate_document`. */
2280
+ name?: string;
2281
+ description?: string;
2282
+ /**
2283
+ * The access filter for this call, from the tool context — e.g. `(ctx) => ({ tenant: ctx.actor.tenantRef })`.
2284
+ * Applied to the tree read, with the vector stores' semantics. Omit only for single-tenant corpora.
2285
+ */
2286
+ filter?: (ctx: AiToolCtx) => Record<string, unknown> | undefined;
2287
+ /** Per-call navigation budget (overrides the navigator's). */
2288
+ budget?: TreeBudget;
2289
+ }
2290
+ interface NavigateDocumentInput {
2291
+ documentId: string;
2292
+ question: string;
2293
+ }
2294
+ interface NavigateDocumentOutput {
2295
+ documentId: string;
2296
+ /** `false` when the document has no tree (short documents don't get one) or the caller may not read it. */
2297
+ found: boolean;
2298
+ passages: Passage[];
2299
+ /** The sections chosen and the path to each. */
2300
+ nodes: NavigatedNode[];
2301
+ /** The walk, step by step, with the model's reasoning. */
2302
+ steps: NavigationStep[];
2303
+ stoppedBy?: string;
2304
+ note?: string;
2305
+ }
2306
+ /** A functional tool (`{ spec, handler }`) — pass it to nestjs `provideAgentTool`. */
2307
+ interface NavigateDocumentTool {
2308
+ spec: ToolSpec;
2309
+ handler: ToolHandler;
2310
+ }
2311
+ /**
2312
+ * The agentic half of two-stage retrieval: `navigate_document(documentId, question)`, a `read`-kind
2313
+ * tool the model calls **after** `search_knowledge` put a long document among its hits — it walks
2314
+ * that document's table of contents to the sections that answer and returns their text, with the
2315
+ * path and reasoning (the citation surface: section titles and page ranges).
2316
+ *
2317
+ * `documentId` may also be a chunk id from a search result (`doc#12`); the chunk suffix is dropped.
2318
+ * A document with no tree answers `found: false` with a note, so the model falls back to the
2319
+ * passages it already has instead of retrying.
2320
+ *
2321
+ * ```ts
2322
+ * providers: [
2323
+ * provideAgentTool(createRetrievalTool(hybrid)),
2324
+ * provideAgentTool(createNavigateDocumentTool(navigator, { filter: (ctx) => ({ tenant: ctx.actor.tenantRef }) })),
2325
+ * ]
2326
+ * ```
2327
+ */
2328
+ declare function createNavigateDocumentTool(navigator: TreeNavigationRetriever, options?: NavigateDocumentToolOptions): NavigateDocumentTool;
2329
+
2330
+ export { type BuildDocumentTreeOptions, type BuiltDocumentTree, type ChunkOptions, type ChunkRecord, DEFAULT_STOP_WORDS, type DescribesRetrieval, type DetectedHeading, type DocumentNavigation, type DocumentTree, type DocumentTreeHeader, type DocumentTreeInput, type DocumentTreeNode, type DocumentTreeStore, type DrizzleLike, EmbeddingRetriever, type EmbeddingRetrieverOptions, FilteredRetriever, HttpModelError, HttpReranker, type HttpRerankerOptions, HybridRetriever, type HybridRetrieverOptions, type IndexDocumentTreeOptions, type IndexDocumentTreeResult, type IndexedDocument, type IngestChunksOptions, type IngestDocument, type IngestOptions, type InstrumentRetrieverOptions, KeywordRetriever, type KeywordRetrieverOptions, LexicalRetriever, type LexicalVectorStore, type ListChunksOptions, MemoryDocumentTreeStore, MemoryVectorStore, type MetadataPatch, type NavigateDocumentInput, type NavigateDocumentOutput, type NavigateDocumentTool, type NavigateDocumentToolOptions, type NavigateOptions, type NavigatedNode, type NavigationResult, type NavigationStep, type NodePgQueryable, type OpenAiChatTreeLlmOptions, type OpenAiEmbeddingsOptions, type PgClient, type PgClientSource, PgDocumentTreeStore, type PgDocumentTreeStoreOptions, type PgFullTextOptions, PgLexicalVectorStore, PgVectorStore, type PgVectorStoreOptions, type PostgresJsSql, RAG_DIAGNOSTIC_LIB, RAG_RETRIEVAL_CHANNEL, RAG_RETRIEVAL_EVENT, type RagRetrievalEvent, type RedisSearchClient, RedisVectorSchemaMismatchError, RedisVectorStore, type RedisVectorStoreOptions, RerankingRetriever, type RerankingRetrieverOptions, type RetrievalDescriptor, type RetrievalTool, type RetrievalToolOptions, type RetrieverKind, type StoredChunk, type TreeBudget, TreeBudgetExceededError, type TreeBuildStats, type TreeHeading, type TreeLlm, type TreeLlmCache, type TreeLlmCandidate, type TreeLlmRequest, type TreeLlmResponse, type TreeLlmTask, TreeNavigationRetriever, type TreeNavigationRetrieverOptions, type TreeSection, type TreeStructureSource, type TreeSummarySource, type TreeUnit, TwoStageRetriever, type TwoStageRetrieverOptions, UnsafeRemovalError, type VectorRecord, type VectorSearchOptions, type VectorStore, type VectorStoreKind, anyTermTsquery, applyMetadataPatch, buildDocumentTree, cachedTreeLlm, chunkDocuments, chunkIndexOf, chunkText, createNavigateDocumentTool, createRetrievalTool, describeRetrieval, describeSharedSource, describeSource, describesRetrieval, detectHeadings, documentIdOf, emitRagRetrieval, estimateTokens, hasSearchSyntax, indexDocumentTree, indexTree, ingestChunks, ingestDocuments, instrumentRetriever, isBatchTooLarge, isLexicalVectorStore, keywordTerms, keywordTreeLlm, openAiChatTreeLlm, openAiEmbeddings, parseJsonReply, spanText, stableHash, toPgClient, treeLlmFromModelProvider, walkTree };