How to Use the Chandra Streamlit App for Interactive Single-Page Document Processing

The Chandra Streamlit app provides a lightweight web interface that lets you run layout-aware OCR on single PDF pages or images using either local Hugging Face models or remote vLLM servers, with instant visualization of text, layout, and markdown output.

The datalab-to/chandra repository ships with a built-in Streamlit application designed for rapid, interactive testing of layout-aware OCR capabilities. Whether you need to process a single page from a multi-page PDF or analyze a standalone image, the Chandra Streamlit app offers a modular architecture that separates UI concerns from model inference and document parsing. This guide walks through launching the interface, understanding its underlying components, and extending its functionality for custom workflows.

Launching the Chandra Streamlit Interface

Before starting the app, ensure all dependencies are installed from the repository root. The requirements include Streamlit, pypdfium2, Pillow, and the core Chandra package.

pip install -r requirements.txt

You can launch the application using two methods. The recommended approach uses the convenience wrapper in chandra/scripts/run_app.py, which configures headless server settings automatically:

python -m chandra.scripts.run_app

Alternatively, invoke Streamlit directly for custom flag configurations:

streamlit run chandra/scripts/app.py --server.headless true --server.fileWatcherType none

The wrapper script sets critical deployment flags including --server.headless and disables the file watcher to prevent reload loops in containerized environments.

The front-end logic resides in chandra/scripts/app.py, which implements a clean sidebar-and-tabs layout typical of data-science tools.

File Upload and Page Selection
The sidebar accepts both PDF documents and image files. For PDFs, the app uses page_counter and load_pdf_images from chandra/input.py to render specific pages as PIL Image objects using pypdfium2. A numeric input lets you jump to any page index, and the selected page renders instantly in the main view.

Model Backend Selection
Users choose between two inference modes via a dropdown:

  • hf: Loads a Hugging Face model locally using the InferenceManager
  • vllm: Connects to a remote vLLM server endpoint

The app lazily instantiates the model through a cached load_model function decorated with @st.cache_resource, ensuring the heavy model weights stay in memory across interactions.

Result Tabs
After processing, the interface presents three tabs:

  1. Rendered HTML: Visual preview of the extracted content
  2. Raw Markdown: The parsed text with embedded base-64 images
  3. Layout Visualization: The original image overlaid with bounding boxes generated by draw_layout in chandra/util.py

Understanding the Architecture

The Chandra Streamlit app delegates all heavy lifting to specialized modules, making the codebase maintainable and testable outside the UI context.

The InferenceManager Abstraction

Located in chandra/model/__init__.py, the InferenceManager class provides a unified interface over heterogeneous backends. When you select a mode in the UI, the app invokes:

model = InferenceManager(method="hf")  # or method="vllm"

This abstraction handles tokenizer loading, batching, and generation parameters internally, exposing a single generate() method that accepts a list of BatchInputItem objects.

Document Input Handling

The input pipeline in chandra/input.py distinguishes between PDF and image sources:

  • PDF pages: load_pdf_images(pdf_path, page_indices) renders specified pages as high-resolution PIL images
  • Images: Loaded directly via Pillow for immediate processing

The selected page passes to the OCR engine as a BatchInputItem instance, defined in chandra/model/schema.py, which encapsulates the image tensor and metadata like prompt_type="ocr_layout".

OCR Pipeline and Output Generation

The core processing loop follows this sequence:

  1. Layout Detection: The model generates structured layout data parsed by parse_layout
  2. Visualization: draw_layout overlays bounding boxes on the source image
  3. Content Extraction: parse_markdown, parse_html, and extract_images in chandra/output.py convert raw tokens into usable formats
  4. Image Embedding: embed_images_in_markdown transforms extracted figures into base-64 data-URLs, ensuring self-contained markdown files that render correctly in Streamlit

Results remain cached via @st.cache_data to prevent redundant reprocessing when users switch between visualization tabs.

Processing Documents Programmatically

While the Streamlit UI excels for quick inspections, you can replicate its functionality in scripts for batch automation. The same components power both interfaces:

from chandra.model import InferenceManager
from chandra.input import load_pdf_images
from chandra.model.schema import BatchInputItem
from PIL import Image

# Initialize the backend (local Hugging Face)

model = InferenceManager(method="hf")

# Load a specific PDF page as a PIL image

pdf_path = "document.pdf"
page_image = load_pdf_images(pdf_path, page_indices=[2])[0]

# Construct input batch and run OCR

input_item = BatchInputItem(image=page_image, prompt_type="ocr_layout")
results = model.generate([input_item])

# Access structured outputs

result = results[0]
print(result.markdown)  # Extracted text with formatting

print(result.html)      # HTML representation

# result.images contains extracted visual elements

This pattern mirrors the internal logic of chandra/scripts/app.py, allowing you to build custom pipelines while leveraging the same caching and error-handling strategies.

Customizing the Deployment

For production deployments or Docker containers, modify chandra/scripts/run_app.py to adjust server bindings or authentication. The default configuration disables CORS and enables headless mode suitable for reverse-proxy setups. You can override these by passing additional arguments through the module invocation or by setting environment variables before importing Streamlit.

Summary

  • The Chandra Streamlit app in chandra/scripts/app.py provides an interactive interface for single-page OCR with support for both local Hugging Face models and remote vLLM servers.
  • InferenceManager abstracts backend complexity, while caching decorators ensure efficient resource utilization across user interactions.
  • Input handling relies on load_pdf_images from chandra/input.py for PDF rendering and standard Pillow loaders for images.
  • The output pipeline in chandra/output.py converts raw model predictions into markdown, HTML, and visual layout overlays, with embed_images_in_markdown ensuring self-contained export files.
  • Launch the app via python -m chandra.scripts.run_app or direct Streamlit commands, using the provided wrapper for optimal server configuration.

Frequently Asked Questions

How does the Chandra Streamlit app handle multi-page PDFs?

The app processes one page at a time interactively. The sidebar provides a page selector that passes specific indices to load_pdf_images in chandra/input.py, rendering only the requested page as a PIL image. While the UI focuses on single-page inspection, you can automate batch processing by calling InferenceManager.generate() in a loop over multiple BatchInputItem instances programmatically.

What is the difference between the "hf" and "vllm" modes in the app?

The "hf" mode loads models directly from Hugging Face using local GPU/CPU resources through the InferenceManager class, suitable for workstations with sufficient VRAM. The "vllm" mode connects to a remote vLLM inference server, enabling distributed processing or usage of larger models hosted on separate infrastructure. Both modes use identical input schemas and output parsers defined in chandra/model/schema.py.

Can I export the OCR results without using the web interface?

Yes. The underlying functions in chandra/output.py—including parse_markdown, parse_html, and extract_images—operate independently of Streamlit. You can generate BatchInputItem objects, call model.generate(), and save the resulting markdown or HTML to disk. The embed_images_in_markdown utility ensures extracted figures remain visible in the exported files by encoding them as base-64 data-URLs.

Where is the layout visualization generated in the codebase?

The visual overlay showing bounding boxes appears in the third tab of the Streamlit interface, generated by the draw_layout function located in chandra/util.py. This function takes the raw layout coordinates from the model output and renders them on the source PIL image using standard drawing primitives, returning an annotated image suitable for immediate display or download.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →