| layout | default |
|---|---|
| title | API Reference |
| nav_order | 4 |
The main class for building and querying document indices.
# Python
TreeDex(tree: list[dict], pages: list[dict], llm=None)// TypeScript
new TreeDex(tree: TreeNode[], pages: Page[], llm: BaseLLM | null = null)You typically don't call the constructor directly — use the factory methods below.
Build an index from a document file. This is the primary entry point.
# Python
index = TreeDex.from_file(
path: str,
llm: BaseLLM,
loader=None, # Custom loader (auto-detect if None)
max_tokens: int = 20000, # Token budget per page group
overlap: int = 1, # Page overlap between groups
verbose: bool = True, # Print progress
extract_images: bool = False # Extract images for vision LLM
)// TypeScript
const index = await TreeDex.fromFile(path, llm, {
loader?, // Custom loader
maxTokens?: 20000,
overlap?: 1,
verbose?: true,
extractImages?: false,
});Pipeline:
- Check for PDF ToC → if found, build tree directly (0 LLM calls)
- Load pages with heading detection (PDFs without ToC)
- Group pages by token budget
- LLM extracts structure per group
- Repair orphans → build tree
Build from pre-extracted pages (skip document loading).
index = TreeDex.from_pages(pages, llm, max_tokens=20000, overlap=1, verbose=True)const index = await TreeDex.fromPages(pages, llm, { maxTokens, overlap, verbose });Create from an existing tree and pages (no processing).
index = TreeDex.from_tree(tree, pages, llm)const index = TreeDex.fromTree(tree, pages, llm);Load a previously saved index from JSON.
index = TreeDex.load("index.json", llm=llm)const index = await TreeDex.load("index.json", llm);Retrieve relevant sections for a question.
result = index.query(
question: str,
llm=None, # Override LLM (uses constructor LLM if None)
agentic: bool = False # Generate an answer from context
) -> QueryResultconst result = await index.query(question, {
llm?, // Override LLM
agentic?, // Generate answer
});
// Or shorthand: await index.query(question, llm)Query multiple indexes simultaneously and merge results into a single
MultiQueryResult. All indexes are queried in parallel (Node.js) or
sequentially (Python). Results are combined with clear [Document N]
separators so downstream LLMs or users can distinguish sources.
multi = TreeDex.query_all(
indexes: list[TreeDex],
question: str,
llm=None, # Shared LLM override (falls back to each index's LLM)
agentic: bool = False, # Generate one answer over the combined context
labels: list[str] = None # Human-readable names (default: "Document 1", "Document 2", …)
) -> MultiQueryResultconst multi = await TreeDex.queryAll(indexes, question, {
llm?, // Shared LLM override
agentic?, // Generate one answer over combined context
labels?, // Human-readable names per index
});Example:
multi = TreeDex.query_all(
[index_a, index_b, index_c],
"What are the safety guidelines?",
llm=llm,
labels=["Manual A", "Manual B", "Manual C"],
agentic=True,
)
print(multi.combined_context) # merged text with [Manual A] / [Manual B] headers
print(multi.answer) # single LLM-generated answer over all sources
print(multi.results[0].pages_str) # pages matched in Manual Aconst multi = await TreeDex.queryAll(
[indexA, indexB, indexC],
"What are the safety guidelines?",
{ llm, labels: ["Manual A", "Manual B", "Manual C"], agentic: true },
);
console.log(multi.combinedContext);
console.log(multi.answer);
console.log(multi.results[0].pagesStr);Export the index to a JSON file.
path = index.save("index.json") # Returns the pathconst path = await index.save("index.json");The saved JSON contains the tree structure (without embedded text) and all pages. Text is re-embedded on load().
Pretty-print the tree structure.
index.show_tree()Output:
[0001] 1: Introduction (pages 1-4)
[0002] 1.1: Background (pages 1-2)
[0003] 1.2: Motivation (pages 3-4)
[0004] 2: Methods (pages 5-12)
...
Return index statistics.
stats = index.stats()
# {
# "total_pages": 21,
# "total_tokens": 11710,
# "total_nodes": 41,
# "leaf_nodes": 32,
# "root_sections": 10
# }Find sections that exceed size thresholds.
large = index.find_large_sections(max_pages=10, max_tokens=20000)const large = index.findLargeSections({ maxPages: 10, maxTokens: 20000 });Returned by index.query().
| Property | Python | Node.js | Type | Description |
|---|---|---|---|---|
| Context | .context |
.context |
str |
Concatenated text from selected nodes |
| Node IDs | .node_ids |
.nodeIds |
list[str] |
IDs of selected tree nodes |
| Page ranges | .page_ranges |
.pageRanges |
list[tuple] |
[(start, end), ...] (0-indexed) |
| Pages string | .pages_str |
.pagesStr |
str |
Human-readable: "pages 5-8, 12-15" |
| Reasoning | .reasoning |
.reasoning |
str |
LLM's explanation |
| Answer | .answer |
.answer |
str |
Generated answer (agentic mode only) |
Returned by TreeDex.query_all() / TreeDex.queryAll().
| Property | Python | Node.js | Type | Description |
|---|---|---|---|---|
| Per-index results | .results |
.results |
list[QueryResult] |
One QueryResult per index, in input order |
| Labels | .labels |
.labels |
list[str] |
Human-readable name for each index |
| Combined context | .combined_context |
.combinedContext |
str |
All contexts merged with [Label] headers and --- separators |
| Answer | .answer |
.answer |
str |
Single LLM-generated answer over all sources (agentic mode only) |
Combined context format:
[Manual A]
[Section: Safety Guidelines]
Content from Manual A...
---
[Manual B]
[Section: Hazard Procedures]
Content from Manual B...
Extract table of contents from PDF bookmarks.
toc = extract_toc("doc.pdf")
# Returns: [{"level": 1, "title": "Intro", "physical_index": 0}, ...] or Noneconst toc = await extractToc("doc.pdf");
// Returns: TocEntry[] | nullReturns None/null if the PDF has fewer than 3 ToC entries.
Extract text from each page of a PDF.
pages = extract_pages(
"doc.pdf",
extract_images=False, # Extract images as base64
detect_headings=False # Inject [H1]/[H2]/[H3] markers
)const pages = await extractPages("doc.pdf", {
extractImages: false,
detectHeadings: false,
});Split pages into token-budget groups.
groups = group_pages(pages, max_tokens=20000, overlap=1)
# Returns: list of tagged text stringsCount tokens using cl100k_base encoding.
n = count_tokens("Hello world") # → 2Convert ToC entries to numbered sections.
sections = toc_to_sections([
{"level": 1, "title": "Intro", "physical_index": 0},
{"level": 2, "title": "Background", "physical_index": 2},
])
# → [{"structure": "1", "title": "Intro", ...},
# {"structure": "1.1", "title": "Background", ...}]Insert synthetic parent nodes for orphaned subsections.
repaired = repair_orphans([
{"structure": "1", "title": "Intro", "physical_index": 0},
{"structure": "2.3.1", "title": "Deep", "physical_index": 5},
])
# Inserts "2" and "2.3" as synthetic parentsConvert flat sections to a hierarchical tree.
tree = list_to_tree(sections)Set start_index and end_index on each node.
assign_page_ranges(tree, total_pages=21)DFS traversal, assigns sequential IDs ("0001", "0002", ...).
assign_node_ids(tree)Populate each node's text field by concatenating pages in its range.
embed_text_in_tree(tree, pages)Return nodes exceeding page or token thresholds.
large = find_large_nodes(tree, max_pages=10, max_tokens=20000, pages=pages)Build a flat {node_id: node} map for O(1) lookups.
node_map = create_node_mapping(tree)
node = node_map["0005"]Deep copy the tree with all text fields removed (for LLM prompts).
stripped = strip_text_from_tree(tree)Concatenate text from specific nodes.
text = collect_node_texts(["0005", "0008"], node_map)Robust JSON extraction from LLM output. Handles:
- Raw JSON
- Markdown code blocks
- Trailing commas
- Text before/after JSON
data = extract_json('Some text ```json\n[{"a": 1}]\n``` more text')
# → [{"a": 1}]n = count_nodes(tree) # Total nodes including childrenleaves = get_leaf_nodes(tree) # Nodes with no childrenprint_tree(tree)
# [0001] 1: Introduction (pages 1-4)
# [0002] 1.1: Background (pages 1-2)pages = auto_loader("doc.pdf", extract_images=False, detect_headings=False)const pages = await autoLoader("doc.pdf", { extractImages, detectHeadings });Auto-detects format by extension: .pdf, .txt, .md, .html, .htm, .docx.
loader = PDFLoader(extract_images=False, detect_headings=False)
pages = loader.load("doc.pdf")loader = TextLoader(chars_per_page=3000)
pages = loader.load("doc.txt")loader = HTMLLoader(chars_per_page=3000)
pages = loader.load("doc.html")loader = DOCXLoader(chars_per_page=3000)
pages = loader.load("doc.docx")Next: LLM Backends →