### Development Setup and Testing Source: https://github.com/nanonets/nanoindex/blob/main/README.md Commands to clone the repository, install development dependencies, and run tests. ```bash git clone https://github.com/nanonets/nanoindex.git && cd nanoindex uv sync --extra dev && uv run pytest # or: pip install -e ".[dev]" && pytest ``` -------------------------------- ### Clone and Install NanoIndex Source: https://github.com/nanonets/nanoindex/blob/main/CONTRIBUTING.md Initializes the development environment by cloning the repository and installing dependencies. ```bash git clone https://github.com/NanoNets/nanoindex.git cd nanoindex pip install -e ".[dev]" python -m spacy download en_core_web_sm ``` -------------------------------- ### Install NanoIndex Source: https://github.com/nanonets/nanoindex/blob/main/docs/launch-narrative.md Install the nanoindex library using pip. ```bash pip install nanoindex ``` -------------------------------- ### Install nanoindex Library Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Install the nanoindex library using pip. This is a prerequisite for using NanoIndex. ```python !pip install nanoindex -q ``` -------------------------------- ### Install Entity Extraction Dependencies Source: https://github.com/nanonets/nanoindex/blob/main/README.md Install optional dependencies for entity extraction, specifying CPU or GPU support. ```bash pip install nanoindex[gliner] # (CPU) or: pip install nanoindex[gliner-gpu] # (GPU) ``` -------------------------------- ### Ask Questions and Get Answers with Citations Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Ask a list of questions about the indexed document. NanoIndex uses an LLM to find relevant sections and generate answers with page citations. ```python questions = [ "What was the revenue?", "Which segment grew the fastest?", "What is the full year guidance?", ] for q in questions: answer = ni.ask(q, tree) pages = sorted({p for c in answer.citations for p in c.pages}) print(f"Q: {q}") print(f"A: {answer.content[:200]}") print(f"Pages cited: {pages}") print(f"Sections: {[c.title for c in answer.citations]}") print("-" * 60) ``` -------------------------------- ### Configuration loading Source: https://context7.com/nanonets/nanoindex/llms.txt Shows how to initialize NanoIndex using a YAML configuration file. ```python from nanoindex import NanoIndex, NanoIndexConfig from nanoindex.config import load_config # Load from config file config = load_config(config_path="custom_config.yaml") ni = NanoIndex(config=config) ``` -------------------------------- ### Initialize NanoIndex and Index Document Source: https://github.com/nanonets/nanoindex/blob/main/docs/launch-narrative.md Instantiate the NanoIndex with a specified LLM and then index a PDF document. The indexing process generates a structured tree and a knowledge graph from the document. The resulting tree object contains metadata about the indexed document, such as node count, entity count, relationship count, and page coverage. ```python from nanoindex import NanoIndex ni = NanoIndex(llm="anthropic:claude-sonnet-4-6") # Index a 200-page SEC filing -> structured tree + knowledge graph tree = ni.index("3M_2018_10K.pdf") # tree.domain = "sec_10k" # 153 nodes, 921 entities, 103 relationships # Self-validation: 100% page coverage, depth 4 ``` -------------------------------- ### GET /get_graph Source: https://context7.com/nanonets/nanoindex/llms.txt Retrieves the entity-relationship graph extracted from a document. ```APIDOC ## GET /get_graph ### Description Retrieves the entity-relationship graph containing entities (people, companies, amounts, dates) and their relationships from an indexed document tree. ### Parameters #### Request Body - **tree** (object) - Required - The indexed document tree object. ``` -------------------------------- ### Query Across Multiple Documents with KnowledgeBase Source: https://github.com/nanonets/nanoindex/blob/main/README.md Create a KnowledgeBase to compile multiple documents into a persistent wiki. Add documents using `kb.add()` and query synthesized information. Use `kb.lint()` to find contradictions. ```python from nanoindex.kb import KnowledgeBase kb = KnowledgeBase("./sec-filings") kb.add("3M_2018_10K.pdf") # extracts entities, builds concept pages kb.add("3M_2019_10K.pdf") # updates existing concepts, flags changes kb.add("3M_2020_10K.pdf") # cross-references across all three years answer = kb.ask("How has 3M's revenue changed from 2018 to 2020?") kb.lint() # find contradictions, stale claims, orphan pages ``` -------------------------------- ### Initialize NanoIndex with LLM Providers Source: https://context7.com/nanonets/nanoindex/llms.txt Initialize the NanoIndex class, setting API keys for desired LLM providers. Supports multiple providers like Anthropic, OpenAI, Google/Gemini, Groq, Ollama, Together, and DeepSeek. ```python import os from nanoindex import NanoIndex # Set API keys os.environ["NANONETS_API_KEY"] = "your_nanonets_key" # Get free at docstrange.nanonets.com os.environ["ANTHROPIC_API_KEY"] = "your_anthropic_key" # Initialize with provider shorthand ni = NanoIndex(llm="anthropic:claude-sonnet-4-6") # Alternative providers # ni = NanoIndex(llm="openai:gpt-4o") # ni = NanoIndex(llm="gemini:gemini-2.5-flash") # ni = NanoIndex(llm="ollama:llama3") # fully local # With entity graph building enabled ni_with_graph = NanoIndex( llm="anthropic:claude-sonnet-4-6", build_graph=True ) ``` -------------------------------- ### Basic NanoIndex Usage Source: https://github.com/nanonets/nanoindex/blob/main/docs/launch-narrative.md Initialize NanoIndex, index a document, and ask a question. The answer includes content and citations. ```python from nanoindex import NanoIndex ni = NanoIndex() tree = ni.index("document.pdf") answer = ni.ask("Your question", tree) print(answer.content, answer.citations) ``` -------------------------------- ### Initialize NanoIndex and Index PDF Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Initialize the NanoIndex object, which automatically detects API keys from environment variables. Then, index the uploaded PDF to create a searchable tree structure. ```python from nanoindex import NanoIndex # Auto-detects API keys from env vars ni = NanoIndex() # Or pick your LLM explicitly: # ni = NanoIndex(llm="anthropic:claude-sonnet-4-6") # ni = NanoIndex(llm="openai:gpt-5.4") # ni = NanoIndex(groq:llama-3.3-70b-versatile") tree = ni.index(pdf_path) print(f"Indexed: {tree.doc_name}") ``` -------------------------------- ### Initialize Query Pipeline Animation (Fast Mode) Source: https://github.com/nanonets/nanoindex/blob/main/assets/generate-diagrams.html This snippet animates the fast query pipeline using the nanoindex.ask() method. ```javascript (function() { const c = document.getElementById('query_fast'); const ctx = c.getContext('2d'); const rc = rough.canvas(c); let frame = 0; const ANIM_F = 180, HOLD_F = 300, TOT_F = ANIM_F + HOLD_F; function draw() { const t = Math.min(1, (frame % TOT_F) / ANIM_F); ctx.fillStyle = BG; ctx.fillRect(0, 0, c.width, c.height); titleBlock(ctx, c.width, 32, 'Query Pipeline — Fast Mode (default)', 'nanoindex.ask("What was the revenue?", tree)'); const f = (s, d) => Math.min(1, Math.max(0, (t - s) / d)); ctx.globalAlpha = f(0, 0.1); stepN(ctx, 58, 90, '1'); box(rc, ctx, 72, 72, 155, 48, { label: 'Your Question' }); ctx.globalAlpha = f(0.1, 0.1); arr(rc, ctx, 227, 96, 280, 96); stepN(ctx, 293, 90, '2'); box(rc, ctx, 310, 72, 195, 48, { color: AMBER, label: 'Embed Query', sub: 'Local model · instant', badge: '0 LLM calls' }); ctx.globalAlpha = f(0.22, 0.1); arr(rc, ctx, 407, 120, 407, 155); stepN(ctx, 293, 176, '3'); box(rc, ctx, 310, 158, 195, 48, { color: AMBER, label: 'Cosine Search', sub: 'Top 20 candidates', badge: '0 LLM calls' }); ctx.globalAlpha = f(0.32, 0.1); arr(rc, ctx, 505, 182, 558, 182); box(rc, ctx, 560, 158, 195, 48, { color: VIOLET, label: 'Graph Expand', sub: 'Find related sections' }); ctx.globalAlpha = f(0.44, 0.1); arr(rc, ctx, 407, 206, 407, 248); stepN(ctx, 293, 269, '4'); box(rc, ctx, 310, 252, 195, 48, { color: RED, label: 'LLM Selection', sub: 'Picks ~10 from ~25', badge: '1 LLM call' }); ctx.globalAlpha = f(0.58, 0.1); arr(rc, ctx, 407, 300, 407, 338); stepN(ctx, 293, 359, '5'); box(rc, ctx, 310, 342, 195, 48, { color: GREEN, label: 'Answer + Citations', sub: '1 LLM call' }); ctx.globalAlpha = f(0.72, 0.12); arr(rc, ctx, 505, 366, 558, 366); box(rc, ctx, 560, 342, 260, 48, { label: 'Cited answer', sub: 'Page numbers + bounding boxes' }); ctx.globalAlpha = f(0.86, 0.12); footer(ctx, c.width, 435, 'Total: 2 LLM calls · 3x cheaper than agentic mode', BLUE); footer(ctx, c.width, 455, '~20s per question', T3); ctx.globalAlpha = 1; frame++; requestAnimationFrame(draw); } draw(); })(); ``` -------------------------------- ### FinanceBench Evaluation Architecture Source: https://github.com/nanonets/nanoindex/blob/main/assets/generate-diagrams.html Illustrates the two-phase architecture for evaluating Nanonets on the FinanceBench dataset. Phase 1 covers the one-time indexing of SEC filings, while Phase 2 details the per-question evaluation process. ```javascript // ============================================= // FINANCEBENCH (static, no animation) // ============================================= (function() { const c = document.getElementById('financebench'); const ctx = c.getContext('2d'); ctx.fillStyle = BG; ctx.fillRect(0, 0, c.width, c.height); const rc = rough.canvas(c); titleBlock(ctx, c.width, 32, 'FinanceBench Evaluation Architecture', '150 questions across 84 SEC filings · Avg 143 pages per document'); ctx.fillStyle = BLUE; ctx.font = 'bold 12px system-ui'; ctx.textAlign = 'left'; ctx.fillText('PHASE 1: INDEX (one-time)', 50, 75); box(rc, ctx, 50, 85, 140, 48, { label: '84 SEC PDFs', sub: '10-K, 10-Q, 8-K' }); arr(rc, ctx, 190, 109, 240, 109); box(rc, ctx, 240, 82, 180, 55, { color: BLUE, label: 'Nanonets OCR-3', sub: 'Extract all 84 docs' }); arr(rc, ctx, 420, 109, 470, 109); box(rc, ctx, 470, 82, 170, 55, { color: GREEN, label: 'Build Trees', sub: '86 tree indexes' }); arr(rc, ctx, 640, 109, 690, 109); box(rc, ctx, 690, 85, 140, 48, { color: VIOLET, label: 'Entity Graphs', sub: '+ Embeddings' }); ctx.fillStyle = RED; ctx.font = 'bold 12px system-ui'; ctx.textAlign = 'left'; ctx.fillText('PHASE 2: EVALUATE (per question)', 50, 175); box(rc, ctx, 50, 188, 160, 55, { label: '150 Questions', sub: 'Gold answers + evidence' }); arr(rc, ctx, 210, 215, 265, 215); box(rc, ctx, 265, 188, 190, 55, { color: RED, label: 'Agentic Retrieval', sub: 'Claude Sonnet 4.6', highlight: true }); // Agent icon next to retrieval drawAgent(rc, ctx, 478, 208, 14); arr(rc, ctx, 455, 215, 510, 215); box(rc, ctx, 510, 188, 170, 55, { color: GREEN, label: 'Answer Generation', sub: 'Vision + text + KB' }); arr(rc, ctx, 680 ``` -------------------------------- ### CLI command reference Source: https://context7.com/nanonets/nanoindex/llms.txt Commands for indexing, searching, asking questions, and managing knowledge bases via the command line. ```bash # Index a document nanoindex index report.pdf -o tree.json nanoindex index report.pdf --add-summaries --add-description # Search an indexed tree nanoindex search tree.json "What was the revenue?" # Ask a question (indexes if needed) nanoindex ask report.pdf "What was the free cash flow?" nanoindex ask report.pdf "Describe the chart on page 5" --mode vision nanoindex ask report.pdf "Calculate the margin" --tree-path tree.json --metadata # Launch interactive visualization dashboard nanoindex viz nanoindex viz tree.json --port 8080 # Knowledge base commands nanoindex kb create ./my-wiki nanoindex kb add document.pdf --wiki ./my-wiki nanoindex kb ask "What are the trends?" --wiki ./my-wiki nanoindex kb status --wiki ./my-wiki nanoindex kb lint --wiki ./my-wiki ``` -------------------------------- ### Add Pre-built Trees and Graphs to KnowledgeBase Source: https://github.com/nanonets/nanoindex/blob/main/README.md Add existing indexed trees and graphs to a KnowledgeBase. This is useful if you have already indexed documents and want to incorporate them into the wiki structure. ```python from nanoindex.utils.tree_ops import load_tree, load_graph tree = load_tree("3M_2018_10K.json") graph = load_graph("3M_2018_10K_graph.json") kb.add_tree(tree, graph) ``` -------------------------------- ### Build and Manage a Knowledge Base Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Aggregate multiple documents into a single knowledge base and inspect the generated wiki-style file structure. ```python from nanoindex import KnowledgeBase kb = KnowledgeBase( "./my_kb", llm="anthropic:claude-sonnet-4-6", ) # Add your document kb.add(pdf_path) # Check status status = kb.status() print(f"Documents: {status['documents']}") print(f"Concepts: {status['concepts']}") # See the generated wiki files import os for root, dirs, files_list in os.walk("./my_kb"): dirs[:] = [d for d in dirs if d != ".nanoindex"] for f in sorted(files_list): print(f" {os.path.relpath(os.path.join(root, f), './my_kb')}") ``` -------------------------------- ### Initialize Ingestion Pipeline Animation Source: https://github.com/nanonets/nanoindex/blob/main/assets/generate-diagrams.html This snippet sets up the canvas animation for the document ingestion process using the nanoindex.index() method. ```javascript width, 32, 'Ingestion Pipeline', 'nanoindex.index("report.pdf")'); const fadeIn = (start, dur) => Math.min(1, Math.max(0, (t - start) / dur)); const f1 = fadeIn(0, 0.1); const f2 = fadeIn(0.12, 0.1); const f3 = fadeIn(0.28, 0.08); const f4 = fadeIn(0.42, 0.1); const f5 = fadeIn(0.58, 0.12); const f6 = fadeIn(0.78, 0.1); ctx.globalAlpha = f1; stepN(ctx, 55, 92, '1'); box(rc, ctx, 80, 72, 160, 48, { label: 'Your PDF' }); ctx.globalAlpha = f2; arr(rc, ctx, 240, 96, 290, 96); stepN(ctx, 305, 92, '2'); box(rc, ctx, 320, 68, 240, 56, { color: BLUE, label: 'Nanonets OCR-3', sub: '1 API call', badge: '10K pages free' }); ctx.globalAlpha = f3; arr(rc, ctx, cx, 124, cx, 158); const py = 174; pill(rc, ctx, 120, py, 'Markdown', BLUE); pill(rc, ctx, 232, py, 'Hierarchy', BLUE); pill(rc, ctx, 332, py, 'Tables', BLUE); pill(rc, ctx, 448, py, 'Bounding Boxes', BLUE); pill(rc, ctx, 578, py, 'Page Layouts', BLUE); pill(rc, ctx, 700, py, 'TOC', BLUE); pill(rc, ctx, 790, py, 'Images', BLUE); ctx.globalAlpha = f4; arr(rc, ctx, cx, 190, cx, 220); stepN(ctx, 305, 240, '3'); box(rc, ctx, 320, 224, 240, 56, { color: GREEN, label: 'Tree Builder', sub: 'Deterministic · 0 LLM calls', badge: 'Free' }); ctx.globalAlpha = f5; arr(rc, ctx, 380, 280, 170, 332); arr(rc, ctx, cx, 280, cx, 332); arr(rc, ctx, 500, 280, 710, 332); ctx.font = '9px system-ui'; ctx.textAlign = 'center'; ctx.fillStyle = '#7B8FFF'; ctx.fillText('+ LLM summaries', 170, 328); ctx.fillStyle = VIOLET; ctx.fillText('+ LLM entity extraction', cx, 328); ctx.fillStyle = AMBER; ctx.fillText('Local model · no API key', 710, 328); stepN(ctx, 60, 358, '4'); box(rc, ctx, 65, 342, 210, 55, { color: '#7B8FFF', label: 'Document Tree', sub: 'Sections, headings, pages' }); box(rc, ctx, 335, 342, 210, 55, { color: VIOLET, label: 'Entity Graph', sub: 'Companies, metrics, relationships' }); box(rc, ctx, 605, 342, 210, 55, { color: AMBER, label: 'Embeddings', sub: 'Node summary vectors' }); ctx.globalAlpha = f6; arr(rc, ctx, cx, 397, cx, 432); rc.rectangle(320, 436, 240, 38, { ...ROUGH, stroke: GREEN, strokeWidth: 1.2, fill: GREEN+'08', fillStyle: 'solid' }); ctx.fillStyle = GREEN; ctx.font = 'bold 12px system-ui'; ctx.textAlign = 'center'; ctx.fillText('Ready for queries', cx, 460); footer(ctx, c.width, 500, 'All built automatically by nanoindex.index()', T3); ctx.globalAlpha = 1; frame++; requestAnimationFrame(draw); } draw(); })(); ``` -------------------------------- ### Configure NanoIndex Directly Source: https://context7.com/nanonets/nanoindex/llms.txt Initialize NanoIndex with specific API keys and configuration parameters for building document graphs and embeddings, adding summaries, and controlling node splitting strategies. ```python ni = NanoIndex( nanonets_api_key="your_key", llm="anthropic:claude-sonnet-4-6", build_graph=True, build_embeddings=True, add_summaries=True, add_doc_description=False, confidence_threshold=0.7, min_node_tokens=50, max_node_tokens=20000, split_strategy="hybrid" # "heuristic", "llm", or "hybrid" ) ``` -------------------------------- ### Multi-document search and query strategies Source: https://context7.com/nanonets/nanoindex/llms.txt Demonstrates listing documents and performing multi-document queries using direct, metadata-filtered, or description-based strategies. ```python # List all documents for entry in store.list_documents(): print(f" [{entry.doc_id}] {entry.doc_name}") print(f" Metadata: {entry.metadata}") # Strategy 1: Direct search across all documents answer = ni.multi_ask("What are the main risks?", store, strategy="direct") # Strategy 2: Filter by metadata answer = ni.multi_ask( "What was the revenue?", store, strategy="metadata", filters={"company": "3M", "year__gte": 2020} ) # Strategy 3: LLM selects relevant docs by description answer = ni.multi_ask( "Compare Apple and 3M's R&D spending", store, strategy="description", max_docs=5 ) # Multi-document search (returns nodes) results = ni.multi_search("debt obligations", store, strategy="direct") for node in results: print(f" [{node.doc_name}] {node.node.title}") ``` -------------------------------- ### Access DocumentTree Properties Source: https://context7.com/nanonets/nanoindex/llms.txt Demonstrates how to access key properties of the DocumentTree model, such as document name, domain, and financial status. ```python # DocumentTree structure tree: DocumentTree print(tree.doc_name) print(tree.domain) # sec_10k, legal, medical, generic print(tree.is_financial) # True for financial documents ``` -------------------------------- ### Initialize NanoIndex with an LLM Source: https://github.com/nanonets/nanoindex/blob/main/README.md Initialize the NanoIndex object, specifying the LLM to be used. You can choose from various providers like Anthropic, OpenAI, Gemini, or local models via Ollama. ```python from nanoindex import NanoIndex # Pick your LLM ni = NanoIndex(llm="anthropic:claude-sonnet-4-6") # ni = NanoIndex(llm="openai:gpt-5.4") # ni = NanoIndex(llm="gemini:gemini-2.5-flash") # ni = NanoIndex(llm="ollama:llama3") # fully local ``` -------------------------------- ### Ask Questions with NanoIndex Source: https://context7.com/nanonets/nanoindex/llms.txt Employ the ask() method to combine search and answer generation for querying documents. Supports various retrieval modes, including 'agentic_vision' for highest accuracy, and provides answers with reasoning and pixel-level citations. ```python from nanoindex import NanoIndex ni = NanoIndex(llm="anthropic:claude-sonnet-4-6", build_graph=True) tree = ni.index("10k_filing.pdf") # Default mode: agentic_vision (highest accuracy) answer = ni.ask("What was the free cash flow?", tree) print(f"Answer: {answer.content}") print(f"Reasoning: {answer.reasoning}") # Access citations with pixel-level coordinates for citation in answer.citations: print(f" Source: {citation.title} (pp. {citation.pages})") for bbox in citation.bounding_boxes: print(f" Page {bbox.page}: ({bbox.x}, {bbox.y}) {bbox.width}x{bbox.height}") # Different query modes # agentic_vision (default): 5-8 LLM calls, highest accuracy answer = ni.ask("Calculate the debt-to-equity ratio", tree, mode="agentic_vision") ``` -------------------------------- ### Set API Keys for Nanonets and LLM Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Set your Nanonets API key and an LLM API key (Anthropic, OpenAI, or Google) as environment variables. Ensure these keys are valid before proceeding. ```python import os # Paste your keys here os.environ["NANONETS_API_KEY"] = "" # Get from https://docstrange.nanonets.com/app os.environ["ANTHROPIC_API_KEY"] = "" # Or use OPENAI_API_KEY / GOOGLE_API_KEY assert os.environ["NANONETS_API_KEY"], "Please set your Nanonets API key above" assert os.environ["ANTHROPIC_API_KEY"], "Please set your LLM API key above" print("Keys set.") ``` -------------------------------- ### Ask Question via CLI Source: https://github.com/nanonets/nanoindex/blob/main/README.md Command-line interface command to ask a question about a specific document using its indexed tree. ```bash nanoindex ask report.pdf "What was the revenue?" ``` -------------------------------- ### Ask a question with agent reasoning Source: https://github.com/nanonets/nanoindex/blob/main/docs/launch-narrative.md Use the ask function to query indexed documents. The agent can reason in multiple rounds to decompose complex questions. ```python answer = ni.ask("What was 3M's free cash flow conversion ratio in FY2018?", tree) ``` -------------------------------- ### Visualize Tree via CLI Source: https://github.com/nanonets/nanoindex/blob/main/README.md Command-line interface command to visualize the indexed tree structure. ```bash nanoindex viz tree.json ``` -------------------------------- ### Index Document via CLI Source: https://github.com/nanonets/nanoindex/blob/main/README.md Command-line interface command to index a PDF document and save the resulting tree to a JSON file. ```bash nanoindex index report.pdf -o tree.json ``` -------------------------------- ### Set API Keys for NanoIndex Source: https://github.com/nanonets/nanoindex/blob/main/README.md Set the NANONETS_API_KEY for NanoIndex and an API key for your chosen LLM provider (Anthropic, OpenAI, or Google). Free keys are available at docstrange.nanonets.com. ```bash export NANONETS_API_KEY=your_key # free at docstrange.nanonets.com (10K pages) export ANTHROPIC_API_KEY=your_key # or OPENAI_API_KEY, GOOGLE_API_KEY ``` -------------------------------- ### POST /ask Source: https://context7.com/nanonets/nanoindex/llms.txt Queries an indexed document tree using various vision-based LLM strategies. ```APIDOC ## POST /ask ### Description Queries an indexed document tree using specific modes like agentic_graph_vision, fast_vision, or global map-reduce. ### Parameters #### Request Body - **query** (string) - Required - The question to ask the document. - **tree** (object) - Required - The indexed document tree object. - **mode** (string) - Required - The strategy to use (agentic_graph_vision, fast_vision, global, agentic_vision). - **pdf_path** (string) - Optional - Path to the source PDF. - **include_metadata** (boolean) - Optional - Whether to include bounding box metadata. ``` -------------------------------- ### KnowledgeBase for Multi-Document Wiki Source: https://context7.com/nanonets/nanoindex/llms.txt Manages a persistent, multi-document knowledge store that compiles documents into interlinked markdown pages with concept tracking and backlinks. Supports adding documents by path or pre-built trees/graphs. ```python from nanoindex.kb import KnowledgeBase from nanoindex.utils.tree_ops import load_tree, load_graph # Create or open a knowledge base kb = KnowledgeBase("./sec-filings", llm="anthropic:claude-sonnet-4-6") # Add documents by PDF path kb.add("3M_2018_10K.pdf") kb.add("3M_2019_10K.pdf") kb.add("3M_2020_10K.pdf") # Or add pre-built trees and graphs tree = load_tree("cached_trees/AAPL_2023_10K.json") graph = load_graph("cached_graphs/AAPL_2023_10K_graph.json") kb.add_tree(tree, graph) # Ask questions across all documents answer = kb.ask("How has 3M's revenue changed from 2018 to 2020?") print(answer.content) for citation in answer.citations: print(f" [{citation.doc_name}] {citation.title} (pp. {citation.pages})") # Search across documents results = kb.search("operating margin trends") # Check knowledge base health stats = kb.status() print(f"Documents: {stats['documents']}") print(f"Concepts: {stats['concepts']}") print(f"Entities: {stats['entities']}") print(f"Relationships: {stats['relationships']}") # Find inconsistencies warnings = kb.lint() for warning in warnings: print(f" Warning: {warning}") ``` -------------------------------- ### NanoIndex Class Initialization Source: https://context7.com/nanonets/nanoindex/llms.txt Initializes the main orchestrator class for document processing with support for various LLM providers. ```APIDOC ## NanoIndex Initialization ### Description Initializes the NanoIndex orchestrator to manage document extraction, tree building, and retrieval. ### Parameters #### Request Body - **llm** (string) - Required - Provider shorthand (e.g., "anthropic:claude-sonnet-4-6", "openai:gpt-4o") - **build_graph** (boolean) - Optional - Whether to enable entity graph building ``` -------------------------------- ### Index a Document and Ask a Question Source: https://github.com/nanonets/nanoindex/blob/main/README.md Basic usage for indexing a PDF document and asking a question. The answer object contains the content, citations, and bounding boxes. ```python tree = ni.index("10k_filing.pdf") answer = ni.ask("What was the free cash flow?", tree) print(answer.content) # computed answer with reasoning print(answer.citations[0].pages) # [52] print(answer.citations[0].bounding_boxes) # exact coordinates on the page ``` -------------------------------- ### DocumentStore for Multi-Document Management Source: https://context7.com/nanonets/nanoindex/llms.txt Manages multiple indexed documents with strategies for querying based on metadata filtering, LLM-based description matching, or direct document selection. Supports adding documents with metadata and saving/loading the store. ```python from nanoindex import NanoIndex, DocumentStore from nanoindex.utils.tree_ops import save_tree, load_tree ni = NanoIndex(llm="anthropic:claude-sonnet-4-6") store = DocumentStore() # Add documents with metadata tree1 = ni.index("3M_2022_10K.pdf") store.add(tree1, metadata={"company": "3M", "year": 2022, "doc_type": "10-K"}) tree2 = ni.index("AAPL_2023_10K.pdf") store.add(tree2, metadata={"company": "Apple", "year": 2023, "doc_type": "10-K"}) # Save store for later use store.save("./document_store") # Load existing store store = DocumentStore.load("./document_store") print(f"Store contains {store.count} documents") ``` -------------------------------- ### Upload PDF for Indexing Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Upload a PDF file from your local system using Google Colab's file upload utility. This PDF will be indexed by NanoIndex. ```python from google.colab import files uploaded = files.upload() pdf_path = list(uploaded.keys())[0] print(f"Uploaded: {pdf_path}") ``` -------------------------------- ### NanoIndex Query Modes Source: https://context7.com/nanonets/nanoindex/llms.txt Demonstrates different modes for querying documents with NanoIndex, including agentic_graph_vision, fast_vision, and global. ```python answer = ni.ask("Who are the key executives?", tree, mode="agentic_graph_vision") ``` ```python answer = ni.ask("What is the company name?", tree, mode="fast_vision") ``` ```python answer = ni.ask("What are the main business risks?", tree, mode="global") ``` ```python answer = ni.ask("What were the quarterly revenues?", tree, mode="agentic_vision", pdf_path="10k_filing.pdf", include_metadata=True) ``` -------------------------------- ### Compare Agentic and Fast Retrieval Modes Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Evaluate performance differences between the default agentic vision mode and the graph-based fast retrieval mode. ```python # Compare: agentic (default) vs fast mode import time # Default: agentic_vision (highest accuracy, more LLM calls) t0 = time.time() answer_agentic = ni.ask("What was the revenue?", tree, pdf_path=pdf_path) agentic_time = time.time() - t0 # Fast: graph-based retrieval (cheaper, fewer LLM calls) t0 = time.time() answer_fast = ni.ask("What was the revenue?", tree, mode="fast") fast_time = time.time() - t0 print(f"Agentic ({agentic_time:.1f}s): {answer_agentic.content[:100]}") print(f"Fast ({fast_time:.1f}s): {answer_fast.content[:100]}") print(f"\nSpeedup: {agentic_time/max(fast_time, 0.1):.1f}x faster with fast mode") ``` -------------------------------- ### Tree operations and utilities Source: https://context7.com/nanonets/nanoindex/llms.txt Provides utilities for saving, loading, and traversing document trees, as well as extracting text and generating outlines. ```python from nanoindex.utils.tree_ops import ( save_tree, load_tree, load_graph, iter_nodes, find_node, collect_text, tree_to_outline, tree_to_json_outline, find_siblings ) from nanoindex import NanoIndex ni = NanoIndex(llm="anthropic:claude-sonnet-4-6") tree = ni.index("report.pdf") # Save and load trees save_tree(tree, "report_tree.json") loaded_tree = load_tree("report_tree.json") # Load entity graph graph = load_graph("report_graph.json") # Iterate all nodes depth-first for node in iter_nodes(tree.structure): print(f"[{node.node_id}] {node.title}") # Find a specific node by ID node = find_node(tree.structure, "0001.0002") if node: # Collect all text from node and descendants full_text = collect_text(node) print(f"Text length: {len(full_text)}") # Get human-readable outline for LLM prompts outline = tree_to_outline(tree.structure) print(outline) # - [0001] Part I: (pp. 1-50) # - [0001.0001] Item 1: Business (pp. 1-20) # - [0001.0002] Item 1A: Risk Factors (pp. 21-35) # Get JSON outline (compact, no text) json_outline = tree_to_json_outline(tree.structure) # Find sibling nodes siblings = find_siblings(tree.structure, "0001.0002", max_each_side=2) for sibling in siblings: print(f" Sibling: {sibling.title}") ``` -------------------------------- ### Lint and Format Code Source: https://github.com/nanonets/nanoindex/blob/main/CONTRIBUTING.md Applies Ruff linting and formatting to the source code directory. ```bash ruff check nanoindex/ ruff format nanoindex/ ``` -------------------------------- ### Asynchronous API operations Source: https://context7.com/nanonets/nanoindex/llms.txt Demonstrates non-blocking operations for indexing, searching, and graph building using the async API. ```python import asyncio from nanoindex import NanoIndex async def process_documents(): ni = NanoIndex(llm="anthropic:claude-sonnet-4-6", build_graph=True) try: # Async indexing tree = await ni.async_index("10k_filing.pdf", add_summaries=True) # Async search results = await ni.async_search("revenue breakdown", tree) # Async question answering answer = await ni.async_ask( "What was the operating income?", tree, mode="agentic_vision" ) print(answer.content) # Async graph building graph = await ni.async_build_graph(tree) print(f"Entities: {len(graph.entities)}") # Async multi-document operations from nanoindex import DocumentStore store = DocumentStore() store.add(tree) results = await ni.async_multi_search("risk factors", store) answer = await ni.async_multi_ask("Compare risks", store) finally: await ni.async_close() asyncio.run(process_documents()) ``` -------------------------------- ### Process Answer with Citations Source: https://context7.com/nanonets/nanoindex/llms.txt Shows how to extract information from an Answer object, including its content, reasoning, mode, and associated citations with bounding box details. ```python # Answer with citations answer: Answer print(answer.content) # The answer text print(answer.reasoning) # LLM reasoning print(answer.mode) # "agentic_vision", "fast", etc. for citation in answer.citations: print(citation.node_id) print(citation.title) print(citation.pages) # [52, 53] for bbox in citation.bounding_boxes: print(f" Page {bbox.page}: ({bbox.x}, {bbox.y})") ``` -------------------------------- ### Retrieve Detailed Answer with Citations and Bounding Boxes Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Ask a specific question and retrieve a detailed answer including content, citations, node IDs, and bounding box information if available. This allows for precise location of information within the document. ```python answer = ni.ask("What was the revenue and gross margin?", tree) print("Answer:") print(answer.content) print(f"\nCitations ({len(answer.citations)}):") for c in answer.citations: print(f" Section: {c.title}") print(f" Pages: {c.pages}") print(f" Node ID: {c.node_id}") if c.bounding_boxes: print(f" Bounding boxes ({len(c.bounding_boxes)}):") for bb in c.bounding_boxes[:3]: print(f" Page {bb.page}: ({bb.x:.2f}, {bb.y:.2f}) {bb.width:.2f}x{bb.height:.2f}") if bb.text: print(f" Text: {bb.text[:60]}") print() ``` -------------------------------- ### Set API Keys Source: https://github.com/nanonets/nanoindex/blob/main/docs/launch-narrative.md Set the NANONETS_API_KEY for document parsing and an LLM provider's API key (Anthropic, OpenAI, or Google). ```bash export NANONETS_API_KEY=your_key # Document parsing (free 10K pages) export ANTHROPIC_API_KEY=your_key # Or OPENAI_API_KEY, GOOGLE_API_KEY ``` -------------------------------- ### Diagram Rendering Utilities Source: https://github.com/nanonets/nanoindex/blob/main/assets/generate-diagrams.html Helper functions for drawing boxes, arrows, pills, and text labels on a 2D canvas context using Rough.js. ```javascript const BLUE = '#546FFF'; const GREEN = '#20C997'; const AMBER = '#E8A800'; const RED = '#FF6B6B'; const VIOLET = '#7950F2'; const BG = '#ffffff'; const BORDER = '#e0e0e0'; const T1 = '#1a1a1a'; const T2 = '#555'; const T3 = '#999'; const T4 = '#ccc'; const ROUGH = { roughness: 0, bowing: 0 }; function box(rc, ctx, x, y, w, h, opts = {}) { const { color = BORDER, label = '', sub = '', badge = '', highlight = false } = opts; rc.rectangle(x, y, w, h, { ...ROUGH, stroke: color, strokeWidth: highlight ? 2 : 1.5, fill: color + (highlight ? '14' : '08'), fillStyle: 'solid', }); if (label) { ctx.fillStyle = color === BORDER ? T2 : color; ctx.font = 'bold 13px system-ui'; ctx.textAlign = 'center'; ctx.fillText(label, x + w/2, y + (sub ? h/2 - 2 : h/2 + 5)); } if (sub) { ctx.fillStyle = T3; ctx.font = '10px system-ui'; ctx.textAlign = 'center'; ctx.fillText(sub, x + w/2, y + h/2 + 14); } if (badge) { const bw = ctx.measureText(badge).width + 14; const bx = x + w + 8, by = y + h/2 - 9; rc.rectangle(bx, by, bw, 18, { roughness: 0, bowing: 0, stroke: GREEN+'50', fill: GREEN+'0C', fillStyle: 'solid', strokeWidth: 1 }); ctx.fillStyle = GREEN; ctx.font = '9px system-ui'; ctx.textAlign = 'left'; ctx.fillText(badge, bx + 7, by + 13); } } function arr(rc, ctx, x1, y1, x2, y2, label) { rc.line(x1, y1, x2, y2, { ...ROUGH, stroke: T4, strokeWidth: 1.3 }); const a = Math.atan2(y2-y1, x2-x1); ctx.beginPath(); ctx.moveTo(x2, y2); ctx.lineTo(x2 - 7*Math.cos(a-0.35), y2 - 7*Math.sin(a-0.35)); ctx.moveTo(x2, y2); ctx.lineTo(x2 - 7*Math.cos(a+0.35), y2 - 7*Math.sin(a+0.35)); ctx.strokeStyle = T4; ctx.lineWidth = 1.4; ctx.stroke(); if (label) { ctx.fillStyle = T3; ctx.font = '9px system-ui'; ctx.textAlign = 'center'; ctx.fillText(label, (x1+x2)/2 + 14, (y1+y2)/2 - 5); } } function pill(rc, ctx, x, y, text, color) { const pw = ctx.measureText(text).width + 18; rc.rectangle(x-pw/2, y-11, pw, 22, { roughness: 0, bowing: 0, stroke: color+'50', fill: color+'0C', fillStyle: 'solid', strokeWidth: 1 }); ctx.fillStyle = color; ctx.font = '10px system-ui'; ctx.textAlign = 'center'; ctx.fillText(text, x, y + 4); } function stepN(ctx, x, y, n) { ctx.fillStyle = T4; ctx.font = 'bold 20px system-ui'; ctx.textAlign = 'center'; ctx.fillText(n, x, y); } function titleBlock(ctx, w, y1, main, sub) { ctx.fillStyle = T1; ctx.font = 'bold 16px system-ui'; ctx.textAlign = 'center'; ctx.fillText(main, w/2, y1); ctx.fillStyle = T3; ctx.font = '11px system-ui'; ctx.fillText(sub, w/2, y1+18); } function footer(ctx, w, y, text, color) { ctx.fillStyle = color || BLUE; ctx.font = 'bold 11px system-ui'; ctx.textAlign = 'center'; ctx.fillText(text, w/2, y); } ``` -------------------------------- ### Import NanoIndex Data Models Source: https://context7.com/nanonets/nanoindex/llms.txt Import necessary Pydantic models from the nanoindex.models module for working with structured document data. ```python from nanoindex.models import ( DocumentTree, TreeNode, DocumentGraph, Entity, Relationship, Answer, Citation, BoundingBox, RetrievedNode, ExtractionResult2 ) ``` -------------------------------- ### ask() - Question Answering Source: https://context7.com/nanonets/nanoindex/llms.txt Performs question answering over the document tree with citation support. ```APIDOC ## ask() ### Description Combines search and answer generation, supporting multiple retrieval modes. ### Parameters #### Request Body - **query** (string) - Required - The question to ask - **tree** (object) - Required - The indexed document tree - **mode** (string) - Optional - Retrieval mode (e.g., "agentic_vision") ``` -------------------------------- ### Execute Tests Source: https://github.com/nanonets/nanoindex/blob/main/CONTRIBUTING.md Runs the test suite using pytest. Tests requiring a NANONETS_API_KEY are skipped if the variable is not set. ```bash pytest tests/ -x -q ``` -------------------------------- ### Project Directory Structure Source: https://github.com/nanonets/nanoindex/blob/main/CONTRIBUTING.md Overview of the NanoIndex source code organization. ```text nanoindex/ __init__.py # NanoIndex class, main API config.py # Configuration models.py # Pydantic data models core/ extractor.py # Nanonets API extraction strategies tree_builder.py # Tree construction from extraction results enricher.py # LLM-based summary generation agentic.py # Multi-round agentic retrieval client.py # Nanonets API client parsers/ # Document parsers (nanonets, pymupdf) utils/ # PDF, markdown, token utilities ``` -------------------------------- ### Persist and Load Document Trees Source: https://github.com/nanonets/nanoindex/blob/main/examples/nanoindex_quickstart.ipynb Save processed document trees to disk to enable instant loading without re-running extraction. ```python from nanoindex.utils.tree_ops import save_tree, load_tree # Save save_tree(tree, "my_tree.json") print("Saved tree to my_tree.json") # Load (instant, no API call) tree2 = load_tree("my_tree.json") print(f"Loaded: {tree2.doc_name} with {len(list(iter_nodes(tree2.structure)))} nodes") # You can keep asking questions on the loaded tree answer = ni.ask("Summarize this document in 2 sentences", tree2) print(f"\n{answer.content}") ``` -------------------------------- ### KnowledgeBase API Source: https://context7.com/nanonets/nanoindex/llms.txt Manages a persistent, multi-document knowledge store. ```APIDOC ## KnowledgeBase API ### Description Provides methods to add documents, trees, or graphs to a knowledge base and perform cross-document queries and health checks. ### Methods - **add(pdf_path)**: Adds a document to the KB. - **add_tree(tree, graph)**: Adds pre-built trees and graphs. - **ask(query)**: Performs a cross-document query. - **search(query)**: Searches across documents. - **status()**: Returns KB statistics. - **lint()**: Finds inconsistencies in the KB. ``` -------------------------------- ### Build Entity Graph During Indexing Source: https://github.com/nanonets/nanoindex/blob/main/README.md Index a document while also building an entity graph by setting `build_graph=True`. The entity graph can be retrieved using `ni.get_graph(tree)`. ```python ni = NanoIndex(llm="anthropic:claude-sonnet-4-6", build_graph=True) tree = ni.index("10k_filing.pdf") # tree + entity graph graph = ni.get_graph(tree) # 921 entities, 103 relationships ```