Metadata-Version: 2.5
Name: multixtract
Version: 0.1.2
Summary: Vendor-neutral PDF/document extraction: text, tables, and vision-described images, chunked and embedded for search & RAG.
Project-URL: Homepage, https://github.com/srivnamrata/multixtract
Project-URL: Documentation, https://srivnamrata.github.io/multixtract/
Project-URL: Issues, https://github.com/srivnamrata/multixtract/issues
Project-URL: Changelog, https://github.com/srivnamrata/multixtract/blob/main/CHANGELOG.md
Author-email: Namrata Srivastava <srivnamrata@yahoo.co.in>
License: MIT License
        
        Copyright (c) 2026 Namrata Srivastava
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: azure,azure-ai-search,chunking,document-ai,document-processing,docx,embeddings,epub,extraction,gpt-4o,llm,multimodal,ocr,openai,pdf,pptx,rag,retrieval-augmented-generation,table-extraction,text-extraction,vector-search,vision,xlsx
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
Classifier: Topic :: Multimedia :: Graphics :: Capture
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Image Recognition
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing
Classifier: Topic :: Text Processing :: Indexing
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: imagehash>=4.3
Requires-Dist: pillow>=10.0
Provides-Extra: all
Requires-Dist: accelerate<2.0,>=0.27; extra == 'all'
Requires-Dist: azure-identity<2.0,>=1.15; extra == 'all'
Requires-Dist: azure-storage-blob<13.0,>=12.19; extra == 'all'
Requires-Dist: beautifulsoup4>=4.12; extra == 'all'
Requires-Dist: ebooklib>=0.18; extra == 'all'
Requires-Dist: imagecodecs>=2023.1.23; extra == 'all'
Requires-Dist: openai<3.0,>=1.57.0; extra == 'all'
Requires-Dist: openpyxl>=3.1; extra == 'all'
Requires-Dist: pdfplumber>=0.10; extra == 'all'
Requires-Dist: pymupdf>=1.23; extra == 'all'
Requires-Dist: python-docx>=1.1; extra == 'all'
Requires-Dist: python-pptx>=0.6.21; extra == 'all'
Requires-Dist: striprtf>=0.0.26; extra == 'all'
Requires-Dist: torch<3.0,>=2.1; extra == 'all'
Requires-Dist: transformers<6.0,>=4.45; extra == 'all'
Requires-Dist: transformers<6.0,>=4.49; extra == 'all'
Provides-Extra: azure
Requires-Dist: azure-identity<2.0,>=1.15; extra == 'azure'
Requires-Dist: azure-storage-blob<13.0,>=12.19; extra == 'azure'
Requires-Dist: openai<3.0,>=1.57.0; extra == 'azure'
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: mkdocs-include-markdown-plugin>=6.0; extra == 'dev'
Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Requires-Dist: types-pillow>=10.0; extra == 'dev'
Provides-Extra: docx
Requires-Dist: python-docx>=1.1; extra == 'docx'
Provides-Extra: epub
Requires-Dist: beautifulsoup4>=4.12; extra == 'epub'
Requires-Dist: ebooklib>=0.18; extra == 'epub'
Provides-Extra: html
Requires-Dist: beautifulsoup4>=4.12; extra == 'html'
Provides-Extra: imaging
Requires-Dist: imagecodecs>=2023.1.23; extra == 'imaging'
Provides-Extra: llama
Requires-Dist: accelerate<2.0,>=0.27; extra == 'llama'
Requires-Dist: torch<3.0,>=2.1; extra == 'llama'
Requires-Dist: transformers<6.0,>=4.45; extra == 'llama'
Provides-Extra: openai
Requires-Dist: openai<3.0,>=1.57.0; extra == 'openai'
Provides-Extra: pdf
Requires-Dist: pdfplumber>=0.10; extra == 'pdf'
Requires-Dist: pymupdf>=1.23; extra == 'pdf'
Provides-Extra: pptx
Requires-Dist: python-pptx>=0.6.21; extra == 'pptx'
Provides-Extra: qwen2vl
Requires-Dist: accelerate<2.0,>=0.27; extra == 'qwen2vl'
Requires-Dist: torch<3.0,>=2.1; extra == 'qwen2vl'
Requires-Dist: transformers<6.0,>=4.49; extra == 'qwen2vl'
Provides-Extra: rtf
Requires-Dist: striprtf>=0.0.26; extra == 'rtf'
Provides-Extra: smolvlm
Requires-Dist: torch<3.0,>=2.1; extra == 'smolvlm'
Requires-Dist: transformers<6.0,>=4.49; extra == 'smolvlm'
Provides-Extra: xlsx
Requires-Dist: openpyxl>=3.1; extra == 'xlsx'
Description-Content-Type: text/markdown

# multixtract — Vendor-neutral document extraction, OCR, chunking, and embeddings for RAG pipelines

[![CI](https://github.com/srivnamrata/multixtract/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/srivnamrata/multixtract/actions/workflows/ci.yml)
[![Coverage](https://codecov.io/gh/srivnamrata/multixtract/branch/main/graph/badge.svg)](https://codecov.io/gh/srivnamrata/multixtract)
[![PyPI](https://img.shields.io/pypi/v/multixtract)](https://pypi.org/project/multixtract/)
[![Downloads](https://img.shields.io/pypi/dm/multixtract)](https://pypi.org/project/multixtract/)
[![Python](https://img.shields.io/pypi/pyversions/multixtract)](https://pypi.org/project/multixtract/)
[![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://srivnamrata.github.io/multixtract/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![mypy](https://img.shields.io/badge/type--checked-mypy-blue)](https://mypy-lang.org/)

Pull **text, tables, and images** out of PDFs, Word, PowerPoint, Excel/CSV and more — let any **vision model** describe the images, **chunk** everything for retrieval, **embed** it, and store the result anywhere.

The core is tiny (just `Pillow` + `ImageHash`). Every format parser and every cloud SDK is an optional extra — install only what you need.

![multixtract hero](docs/hero.svg)

---

## Highlights

✅ **Vendor-neutral** — swap OpenAI for Azure, Qwen, Llama, or your own model with one line  
✅ **Extract text, tables, and images** from 15+ file formats  
✅ **Modular** — use only extraction, or run the full extract → vision → chunk → embed → store pipeline  
✅ **Fully offline** — local vision models, no API key, no cloud  
✅ **Tiny core install** — only Pillow + ImageHash; every heavy dependency is optional  
✅ **Fully typed** — mypy and pyright compatible out of the box  

---

## Why Multixtract?

Most libraries optimise for one part of the workflow — parse documents, run OCR, generate embeddings, or store vectors. You end up stitching together five packages with incompatible interfaces and rebuilding the same pipeline on every project.

multixtract connects all of them without locking you into a provider. Swap OpenAI for Azure or a local model, swap Azure Blob for S3, add a new file format — none of it touches the rest of the pipeline.

> **What multixtract is not:** It is not a vector database, retrieval framework, or chat system. It focuses on document ingestion and preparation — getting clean, structured, chunked content into whatever AI system you're building.

| Feature | **multixtract** | Unstructured | Docling |
|---|---|---|---|
| PDF | ✅ | ✅ | ✅ |
| DOCX | ✅ | ✅ | ✅ |
| PPTX | ✅ | ✅ | ✅ |
| XLSX / CSV | ✅ | Partial | ❌ |
| EPUB / RTF / HTML / Email | ✅ | Partial | ❌ |
| Vendor-neutral vision model | ✅ | ❌ | ❌ |
| Bring your own embeddings | ✅ | ❌ | ❌ |
| Bring your own storage backend | ✅ | Partial | Partial |
| Fully modular pipeline | ✅ | Partial | Partial |
| Optional dependencies | ✅ | ❌ | ❌ |
| Offline / no-cloud mode | ✅ | ❌ | Partial |
| Core install size | **Pillow + ImageHash** | Heavy | Heavy |

---

## Quick Start

```bash
pip install "multixtract[pdf,docx,pptx,xlsx]"
```

One call. Any document. Done.

```python
from multixtract import Pipeline

Pipeline().process("report.pdf")                      # extract → filter → chunk
Pipeline().process("report.pdf", split_chunks=True)   # + write individual chunk files
```

Or stay close to the data:

```python
from multixtract import extract_document, chunk_document

document, images = extract_document("report.pdf")
chunks = chunk_document(document, base_name="report")
```

**Supported formats:** PDF · DOCX · PPTX · XLSX · CSV · EPUB · HTML · RTF · Email · Images · Plain text · Markdown — and legacy `.doc` / `.ppt` via LibreOffice.

**Vision providers:** OpenAI · Azure OpenAI · Qwen2.5-VL · Llama 3.2 Vision · SmolVLM (CPU) · bring your own.

→ [Full installation guide](https://srivnamrata.github.io/multixtract/usage/#install) · [Recipes](docs/recipes/) · [Provider setup](docs/providers/)

---

## Document Schema

Every call to `extract_document` returns the same structure regardless of input format:

```
PDF / DOCX / PPTX / XLSX / …
        │
        ▼
{
  "_base_name": "report",
  "metadata": { "format": "pdf", "page_count": 12, ... },
  "pgs": [
    {
      "pg_num": 1,
      "kind":   "page",
      "title":  "Executive Summary",
      "txt":    "The quarterly results show a 12% increase...",
      "tables": [
        [["Region", "Q1", "Q2"], ["North", "1.2M", "1.4M"], ...]
      ],
      "imgs": [
        { "image_id": "report-p1-img0", "width": 800, "height": 600 }
      ],
      "hyperlinks": ["https://example.com/data"]
    },
    ...
  ]
}
```

→ [Full data model and chunk schema](docs/data-model.md)

---

## Architecture

![multixtract architecture](docs/architecture.svg)

```
┌─────────────────────────────────────────────────────────────────┐
│                        Your document                            │
│          PDF · DOCX · PPTX · XLSX · EPUB · HTML · RTF …         │
└────────────────────────────┬────────────────────────────────────┘
                             │
                    ┌────────▼────────┐
                    │   Extractors    │  (registry — one per format)
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │              │              │
         ┌────▼────┐   ┌─────▼─────┐  ┌─────▼───┐
         │  Text   │   │  Tables   │  │ Images  │
         └────┬────┘   └─────┬─────┘  └─────┬───┘
              │              │              │
              │              │    ┌─────────▼───────────┐
              │              │    │  ImageFilterPipeline│
              │              │    │  · dimension        │
              │              │    │  · solid-color      │
              │              │    │  · icon rejection   │
              │              │    │  · logo dedup (hash)│
              │              │    └─────────┬───────────┘
              │              │              │
              │              │    ┌─────────▼───────────┐
              │              │    │     VisionModel     │
              │              │    │   OpenAI · Azure    │
              │              │    │  Qwen · Llama · CPU │
              │              │    │  (or skip entirely) │
              │              │    └─────────┬───────────┘
              │              │              │
              └──────────────┴──────────────┘
                             │
                    ┌────────▼────────┐
                    │    Chunking     │  sliding-window · table-MD · image
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │    Embedder     │  OpenAI · Azure · BYO · (skip)
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │    BlobStore    │  LocalDisk · AzureBlob · S3 · BYO
                    └─────────────────┘
```

The pipeline talks only to three **interfaces** — it never imports a vendor directly:

| Interface | Job | Built-in implementations |
|---|---|---|
| `VisionModel` | image → caption + OCR + description | `OpenAIVisionModel`, `AzureOpenAIVisionModel`, `Qwen2VLVisionModel`, `SmolVLMVisionModel`, `Llama32VisionModel` |
| `Embedder` | text → vector | `OpenAIEmbedder`, `AzureOpenAIEmbedder` |
| `BlobStore` | save bytes/JSON | `LocalDiskStore`, `AzureBlobStore` |

Add a new format with `register_extractor`. Plug in S3, GCS, or any backend by implementing three methods on `BlobStore`.

---

## Integrations

Multixtract is an **extraction and chunking layer**, not a RAG framework. It fits underneath the tools you already use:

```python
from multixtract import extract_document, chunk_document
from langchain.schema import Document as LCDocument

document, _ = extract_document("report.pdf")
chunks = chunk_document(document, base_name="report")

# LangChain
lc_docs = [LCDocument(page_content=c["content"], metadata={"pg": c["pg_num"]}) for c in chunks]

# LlamaIndex
from llama_index.core import Document as LIDocument
li_docs = [LIDocument(text=c["content"], metadata={"chunk_id": c["chunk_id"]}) for c in chunks]
```


### Example projects

| Integration | What it shows |
|---|---|
| [LangChain + Chroma](examples/langchain_chroma/) | Ingest → Chroma vector store → RetrievalQA |
| [Azure AI Search](examples/azure_ai_search/) | Ingest → hybrid keyword + vector search → GPT-4o answer |
| [LlamaIndex](examples/llamaindex/) | Ingest → LlamaIndex VectorStoreIndex → query engine |
| [pgvector](examples/pgvector/) | Ingest → PostgreSQL + pgvector → cosine similarity search |
| [Semantic Kernel](examples/semantic_kernel/) | Ingest → SK memory store → prompt function RAG |
| [Offline OCR](examples/offline_ocr/) | Tesseract OCR on images — no API key, no cloud, no GPU |

Each example is a self-contained `ingest.py` with a `--query` flag so you can extract, store, and query in one command.

---

## Features

* **Multi-format**: PDF, Word, PowerPoint, Excel/CSV, EPUB, HTML, RTF, email, images (+ legacy `.doc`/`.ppt` via LibreOffice)
* Cross-page image **deduplication** via xref tracking
* **Image filters**: solid-color / tiny-icon / dimension / reference-logo (perceptual hash)
* **Sliding-window** text chunking (~500 tokens, ~50 overlap) at sentence boundaries
* Tables serialized to **Markdown**; images embedded once and reused
* **Parallel** vision calls (`vision_workers`), **batched** embeddings
* Resume support — skip documents already in the store (`skip_if_exists`)
* **Two-stage chunking**: `_chunks.json` written automatically; pass `split_chunks=True` to also write flat individual chunk documents ready for Azure AI Search or any vector store
* `build_index_document()` — transforms a raw chunk into a flat, AI-Search-ready document (renames `embedding` → `content_vector`, flattens `metadata`)
* `safe_index_key()` — sanitizes any string to a valid Azure AI Search document key
* Fully typed — `py.typed` marker, compatible with mypy and pyright

---

## Roadmap

- [x] PDF / DOCX / PPTX / XLSX extraction
- [x] EPUB / HTML / RTF / email extraction
- [x] Azure OpenAI vision + embeddings integration
- [x] Local vision models (Qwen2.5-VL, Llama 3.2, SmolVLM)
- [x] Azure Blob Storage backend
- [x] Sliding-window chunking with sentence-boundary awareness
- [x] Smart image filtering (dimension, solid-color, logo dedup)
- [x] Document-level metadata on every chunk (`file_path`, `doc_id`, `last_updated`, …)
- [x] Individual chunk splitting — `split_chunks=True` or `split_chunks_file()` writes per-chunk documents for AI Search ingestion
- [x] `build_index_document()` — flat AI-Search-optimized output with `content_vector`, flattened `metadata`
- [ ] Figure-caption association (link extracted images to their nearest caption)
- [ ] Table-of-contents aware chunking (respect heading hierarchy)
- [ ] multisense — companion RAG pipeline library built on multixtract

PRs and feature requests welcome via [GitHub Issues](https://github.com/srivnamrata/multixtract/issues).

---

## Performance

Extracts a 50-page PDF in **~4 s** and a 100-slide PPTX in **~0.14 s** on a standard developer machine (no GPU, no API key). Chunking adds negligible overhead.

→ [Full benchmark results and methodology](docs/performance.md)

---

## Documentation

| | |
|---|---|
| [Installation](https://srivnamrata.github.io/multixtract/usage/#install) | Extras, formats, providers |
| [Recipes](docs/recipes/) | OpenAI · Azure · extract-only · chunk-only · offline OCR |
| [Providers](docs/providers/) | OpenAI · Azure · Qwen · SmolVLM · Llama |
| [Data model](docs/data-model.md) | Document schema · chunk schema · metadata fields |
| [Performance](docs/performance.md) | Benchmark results and methodology |
| [Compatibility](docs/compatibility.md) | Python · OS · torch / CUDA combinations |
| [Troubleshooting](docs/troubleshooting.md) | LibreOffice · CUDA · Azure auth · common errors |
| [API Reference](https://srivnamrata.github.io/multixtract/api/) | Full public API |

---

## Contributing

```bash
pip install -e ".[dev,pdf,docx,pptx,xlsx,epub,html,rtf]"
pytest
ruff check src tests
mypy src/multixtract --ignore-missing-imports --no-error-summary
python benchmarks/run_benchmarks.py
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. Bug reports and PRs are welcome.

---

## Example Applications

- Internal RAG systems on Azure OpenAI
- Enterprise search over mixed document libraries
- Research document processing pipelines

Using multixtract in your project? [Open a PR](https://github.com/srivnamrata/multixtract/pulls) to add it here.

---

## License

MIT — see `LICENSE`.
