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

> Effortlessly process single-page documents with the Chandra Streamlit app. Run layout-aware OCR locally or remotely, visualizing text, layout, and markdown output instantly.

- Repository: [Datalab/chandra](https://github.com/datalab-to/chandra)
- Tags: how-to-guide
- Published: 2026-03-27

---

**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.

```bash
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`](https://github.com/datalab-to/chandra/blob/main/chandra/scripts/run_app.py), which configures headless server settings automatically:

```bash
python -m chandra.scripts.run_app

```

Alternatively, invoke Streamlit directly for custom flag configurations:

```bash
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.

## Navigating the User Interface

The front-end logic resides in **[`chandra/scripts/app.py`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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:

```python
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`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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:

```python
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`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/chandra/input.py) for PDF rendering and standard Pillow loaders for images.
- The output pipeline in [`chandra/output.py`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/chandra/model/schema.py).

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

Yes. The underlying functions in [`chandra/output.py`](https://github.com/datalab-to/chandra/blob/main/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`](https://github.com/datalab-to/chandra/blob/main/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.