markitdown-pro 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- markitdown_pro-0.1.0/PKG-INFO +367 -0
- markitdown_pro-0.1.0/README.md +325 -0
- markitdown_pro-0.1.0/markitdown_pro/__init__.py +1 -0
- markitdown_pro-0.1.0/markitdown_pro/common/__init__.py +0 -0
- markitdown_pro-0.1.0/markitdown_pro/common/logger.py +6 -0
- markitdown_pro-0.1.0/markitdown_pro/common/utils.py +59 -0
- markitdown_pro-0.1.0/markitdown_pro/conversion_pipeline.py +212 -0
- markitdown_pro-0.1.0/markitdown_pro/converters/__init__.py +0 -0
- markitdown_pro-0.1.0/markitdown_pro/converters/azure_docint.py +13 -0
- markitdown_pro-0.1.0/markitdown_pro/converters/base.py +26 -0
- markitdown_pro-0.1.0/markitdown_pro/converters/gpt4o_mini_vision.py +21 -0
- markitdown_pro-0.1.0/markitdown_pro/converters/markitdown_wrapper.py +44 -0
- markitdown_pro-0.1.0/markitdown_pro/converters/pymupdf_wrapper.py +42 -0
- markitdown_pro-0.1.0/markitdown_pro/converters/unstructured_wrapper.py +62 -0
- markitdown_pro-0.1.0/markitdown_pro/converters/youtube_wrapper.py +67 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/__init__.py +0 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/audio_handler.py +40 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/base_handler.py +16 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/email_handler.py +169 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/epub_handler.py +33 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/image_handler.py +48 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/ipynb_handler.py +31 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/markup_handler.py +75 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/office_handler.py +47 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/pdf_handler.py +122 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/pst_handler.py +153 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/tabular_handler.py +34 -0
- markitdown_pro-0.1.0/markitdown_pro/handlers/text_handler.py +38 -0
- markitdown_pro-0.1.0/markitdown_pro/services/__init__.py +0 -0
- markitdown_pro-0.1.0/markitdown_pro/services/azure_service.py +160 -0
- markitdown_pro-0.1.0/markitdown_pro/services/openai_services.py +209 -0
- markitdown_pro-0.1.0/markitdown_pro.egg-info/PKG-INFO +367 -0
- markitdown_pro-0.1.0/markitdown_pro.egg-info/SOURCES.txt +39 -0
- markitdown_pro-0.1.0/markitdown_pro.egg-info/dependency_links.txt +1 -0
- markitdown_pro-0.1.0/markitdown_pro.egg-info/requires.txt +24 -0
- markitdown_pro-0.1.0/markitdown_pro.egg-info/top_level.txt +1 -0
- markitdown_pro-0.1.0/pyproject.toml +41 -0
- markitdown_pro-0.1.0/setup.cfg +8 -0
- markitdown_pro-0.1.0/setup.py +45 -0
- markitdown_pro-0.1.0/tests/test.py +59 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: markitdown-pro
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A package that converts any file format to Markdown.
|
|
5
|
+
Author: Developer
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.12.2
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: pillow==11.1.0
|
|
12
|
+
Requires-Dist: requests
|
|
13
|
+
Requires-Dist: python-dotenv
|
|
14
|
+
Requires-Dist: azure-core
|
|
15
|
+
Requires-Dist: azure-ai-documentintelligence==1.0.0
|
|
16
|
+
Requires-Dist: azure-cognitiveservices-speech==1.42.0
|
|
17
|
+
Requires-Dist: azure-common==1.1.28
|
|
18
|
+
Requires-Dist: whisper
|
|
19
|
+
Requires-Dist: markitdown
|
|
20
|
+
Requires-Dist: langchain>=0.3.23
|
|
21
|
+
Requires-Dist: langchain-core>=0.3.51
|
|
22
|
+
Requires-Dist: langchain-openai>=0.3.12
|
|
23
|
+
Requires-Dist: langchain-text-splitters>=0.3.8
|
|
24
|
+
Requires-Dist: langdetect>=1.0.9
|
|
25
|
+
Requires-Dist: langsmith>=0.3.26
|
|
26
|
+
Requires-Dist: openai>=1.71.0
|
|
27
|
+
Requires-Dist: PyMuPDF
|
|
28
|
+
Requires-Dist: tabulate==0.9.0
|
|
29
|
+
Requires-Dist: chardet==5.2.0
|
|
30
|
+
Requires-Dist: unstructured[all-docs]
|
|
31
|
+
Requires-Dist: youtube-transcript-api
|
|
32
|
+
Requires-Dist: pyexiftool
|
|
33
|
+
Requires-Dist: pydub
|
|
34
|
+
Requires-Dist: nbformat
|
|
35
|
+
Dynamic: author
|
|
36
|
+
Dynamic: classifier
|
|
37
|
+
Dynamic: description
|
|
38
|
+
Dynamic: description-content-type
|
|
39
|
+
Dynamic: requires-dist
|
|
40
|
+
Dynamic: requires-python
|
|
41
|
+
Dynamic: summary
|
|
42
|
+
|
|
43
|
+
# MarkItDown-Pro
|
|
44
|
+
|
|
45
|
+
**MarkItDown-Pro** is an **improvement** of the **[Microsoft MarkItDown repository](https://github.com/markitdown)**, enhancing gaps and extending functionality by leveraging **Azure Document Intelligence SDK**, **Unstructured.io**, and other Azure services and libraries. The result is a comprehensive Python library and command-line tool designed to **convert diverse document formats into Markdown** with graceful fallbacks, including OCR support via GPT-4o-mini.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Table of Contents
|
|
50
|
+
|
|
51
|
+
- [Folder Structure](#folder-structure)
|
|
52
|
+
- [Features & Highlights](#features--highlights)
|
|
53
|
+
- [How It Works](#how-it-works)
|
|
54
|
+
- [File-by-File Explanation](#file-by-file-explanation)
|
|
55
|
+
- [Main Files](#main-files)
|
|
56
|
+
- [Common Utils](#common-utils)
|
|
57
|
+
- [Converters](#converters)
|
|
58
|
+
- [Handlers](#handlers)
|
|
59
|
+
- [Testing](#testing)
|
|
60
|
+
- [Usage & Examples](#usage--examples)
|
|
61
|
+
- [CLI Usage](#cli-usage)
|
|
62
|
+
- [Programmatic Usage](#programmatic-usage)
|
|
63
|
+
- [Extra: Vector Database Chunking](#extra-vector-database-chunking)
|
|
64
|
+
- [Environment Variables](#environment-variables)
|
|
65
|
+
- [FAQ](#faq)
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Folder Structure
|
|
70
|
+
|
|
71
|
+
A typical layout for **MarkItDown-Pro** might look like this:
|
|
72
|
+
```bash
|
|
73
|
+
markitdown-pro/
|
|
74
|
+
├── .env
|
|
75
|
+
├── README.md
|
|
76
|
+
├── requirements.txt
|
|
77
|
+
├── main.py
|
|
78
|
+
├── conversion_pipeline.py
|
|
79
|
+
├── common
|
|
80
|
+
│ └── utils.py
|
|
81
|
+
├── converters
|
|
82
|
+
│ ├── markitdown_wrapper.py
|
|
83
|
+
│ ├── azure_docint.py
|
|
84
|
+
│ ├── unstructured_wrapper.py
|
|
85
|
+
│ └── gpt4o_mini_vision.py
|
|
86
|
+
├── handlers
|
|
87
|
+
│ ├── pst_handler.py
|
|
88
|
+
│ ├── email_handler.py
|
|
89
|
+
│ ├── zip_handler.py
|
|
90
|
+
│ ├── audio_handler.py
|
|
91
|
+
│ └── pdf_handler.py
|
|
92
|
+
└── tests
|
|
93
|
+
├── data
|
|
94
|
+
└── test.py
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
| Folder/File | Description |
|
|
98
|
+
|----------------------------|---------------------------------------------------------------------------------------|
|
|
99
|
+
| **main.py** | Entry point for CLI usage; uses `argparse` to accept file paths. |
|
|
100
|
+
| **conversion_pipeline.py** | Orchestrates the fallback chain for converting documents to Markdown. |
|
|
101
|
+
| **common/** | Shared utility functions, e.g. for file detection, text cleanup, etc. |
|
|
102
|
+
| **converters/** | Contains modules for using various 3rd-party libraries or services to extract text. |
|
|
103
|
+
| **handlers/** | Specialized handlers for specific file types (PST, EML, ZIP, audio, PDF scanning). |
|
|
104
|
+
| **.env** | Environment variables (e.g., credentials for Azure GPT-4o-mini, Azure Doc Intelligence). |
|
|
105
|
+
| **requirements.txt** | Python dependencies needed to install and run this project. |
|
|
106
|
+
| **tests/test_markitdownpro.py**| Recursively scans /tests/data/ and attempts to convert each file using convert_document_to_md|
|
|
107
|
+
| **README.md** | This documentation file, explaining usage and details of the project. |
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Features & Highlights
|
|
112
|
+
|
|
113
|
+
1. **MarkItDown with LLM**
|
|
114
|
+
- Uses **MarkItDown** to convert documents to Markdown, optionally leveraging an OpenAI LLM to create image captions if you have an **OPENAI_API_KEY**.
|
|
115
|
+
- Auto-checks for `exiftool` if you want EXIF metadata in your images.
|
|
116
|
+
|
|
117
|
+
2. **Whisper-Based Audio Transcription**
|
|
118
|
+
- Converts audio files (`.mp3`, `.wav`, `.ogg`, etc.) into text using [OpenAI Whisper](https://github.com/openai/whisper).
|
|
119
|
+
- Gracefully falls back if Whisper is not installed.
|
|
120
|
+
|
|
121
|
+
3. **PST Extraction**
|
|
122
|
+
- Parses Outlook PST files with [`libratom`](https://github.com/rafproject/libratom), extracting emails and attachments recursively.
|
|
123
|
+
|
|
124
|
+
4. **Scanned PDF Detection & Concurrency**
|
|
125
|
+
- Identifies PDFs with no text or embedded images, and automatically performs OCR on each page with GPT-4o-mini.
|
|
126
|
+
- Offers concurrent page-by-page OCR for faster performance.
|
|
127
|
+
|
|
128
|
+
5. **Fallback to Azure Document Intelligence & Unstructured**
|
|
129
|
+
- If standard MarkItDown or specialized handlers fail or yield insufficient text, it tries Azure’s Document Intelligence to extract textual layout.
|
|
130
|
+
- Unstructured.io library for broad coverage of file types.
|
|
131
|
+
|
|
132
|
+
6. **GPT-4 Vision (or GPT-4o-mini) for Images & OCR**
|
|
133
|
+
- If an image or partially scanned PDF is detected, we can pass it to GPT-4o-mini for OCR.
|
|
134
|
+
- Supports local images (base64 encoding) or remote image URLs directly.
|
|
135
|
+
|
|
136
|
+
7. **Handles ZIP & EML**
|
|
137
|
+
- **ZIP**: Unzips and processes each file inside, concatenating the results.
|
|
138
|
+
- **EML**: Extracts email text, attachments, and processes attachments recursively.
|
|
139
|
+
|
|
140
|
+
8. **Graceful LLM Handling**
|
|
141
|
+
- If no **OPENAI_API_KEY** or GPT-4o-mini credentials are provided, it simply skips LLM-based features, logging a warning.
|
|
142
|
+
|
|
143
|
+
9. **Helper Methods for URL & Stream Conversion**
|
|
144
|
+
- `convert_document_from_url(url, output_md)`
|
|
145
|
+
- `convert_document_from_stream(stream, extension, output_md)`
|
|
146
|
+
- `convert_document_to_md(local_path, output_md)`
|
|
147
|
+
|
|
148
|
+
10. **Easy-to-Extend Architecture**
|
|
149
|
+
Each file type has its own **handler**. Each text-extraction library has its own **converter**. The main pipeline provides a centralized fallback sequence.
|
|
150
|
+
|
|
151
|
+
11. **Environment-Driven Configuration**
|
|
152
|
+
- Pulls API keys, endpoints, and paths from `.env` to keep secrets out of source code.
|
|
153
|
+
|
|
154
|
+
12. **Rich File Type Handling**
|
|
155
|
+
|
|
156
|
+
| Category | File Type(s) |
|
|
157
|
+
|-----------------------|-------------|
|
|
158
|
+
| PDF | .pdf |
|
|
159
|
+
| PowerPoint | .pot, .potm, .ppt, .pptm, .pptx |
|
|
160
|
+
| Word Processing | .abw, .doc, .docm, .docx, .dot, .dotm, .hwp, .zabw |
|
|
161
|
+
| Excel/Spreadsheet | .et, .fods, .uos1, .uos2, .wk2, .xls, .xlsb, .xlsm, .xlsx, .xlw |
|
|
162
|
+
| Images | .bmp, .gif, .heic, .jpeg, .jpg, .png, .prn, .svg, .tiff, .webp |
|
|
163
|
+
| Audio | .mp3, .wav, .ogg, .flac, .m4a, .aac, .wma, .webm, .opus |
|
|
164
|
+
| HTML | .htm, .html |
|
|
165
|
+
| Text-Based Formats | .csv, .json, .xml, .txt |
|
|
166
|
+
| ZIP Files | (Iterates over contents) |
|
|
167
|
+
| Email | .eml, .p7s |
|
|
168
|
+
| PST | .pst |
|
|
169
|
+
| EPUB | .epub |
|
|
170
|
+
| Markdown | .md |
|
|
171
|
+
| Org Mode | .org |
|
|
172
|
+
| Open Office | .odt, .sgl |
|
|
173
|
+
| Other | .eth, .mw, .pbd, .sdp, .uof, .web |
|
|
174
|
+
| Plain Text | .txt |
|
|
175
|
+
| reStructured Text | .rst |
|
|
176
|
+
| Rich Text | .rtf |
|
|
177
|
+
| StarOffice | .sxg |
|
|
178
|
+
| TSV | .tsv |
|
|
179
|
+
| Apple | .cwk, .mcw, .pages |
|
|
180
|
+
| Data Interchange | .dif |
|
|
181
|
+
| dBase | .dbf |
|
|
182
|
+
| Microsoft Office | .docx, .xlsx, .pptx |
|
|
183
|
+
| HEIF Image Format | .heif |
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## How It Works
|
|
189
|
+
|
|
190
|
+
1. **Detect File Type**: The pipeline checks the file extension or general signature (`.pdf`, `.zip`, `.eml`, `.docx`, `.mp3`, etc.).
|
|
191
|
+
2. **Specialized Handlers**: If the file is PST, EML, ZIP, or audio, it’s handed off to a dedicated module that handles that format.
|
|
192
|
+
3. **MarkItDown**: For most generic document conversions, we first try [MarkItDown](https://github.com/markitdown).
|
|
193
|
+
4. **Unstructured**: If MarkItDown fails or yields minimal text, we turn to [Unstructured.io](https://unstructured.io/) next.
|
|
194
|
+
- **Why?** It's typically **cheaper** than Azure Document Intelligence, and can handle partial OCR scenarios (via Tesseract, PaddleOCR, etc., if you configure `OCR_AGENT`).
|
|
195
|
+
5. **Azure Document Intelligence**: If Unstructured also fails or yields minimal text, we try Azure Document Intelligence (prebuilt-layout).
|
|
196
|
+
6. **GPT-4o-mini**: As a final fallback or specifically for OCR on images/scanned pages.
|
|
197
|
+
7. **Saves** the extracted text to a `.md` file once any method returns sufficient content.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## File-by-File Explanation
|
|
202
|
+
|
|
203
|
+
### Main Files
|
|
204
|
+
|
|
205
|
+
- **`conversion_pipeline.py`**
|
|
206
|
+
The core logic that orchestrates the fallback chain. Checks each handler or converter in a specific order. Once a successful conversion with enough text is found, it writes to `.md` and stops.
|
|
207
|
+
|
|
208
|
+
### Common Utils
|
|
209
|
+
|
|
210
|
+
- **`common/utils.py`**
|
|
211
|
+
- **File Detection**: Contains helper functions like `is_pdf`, `is_audio`, `detect_extension`.
|
|
212
|
+
- **Markdown Cleaning**: Functions like `clean_markdown()` and `ensure_minimum_content()` to tidy up text and ensure it’s not empty.
|
|
213
|
+
|
|
214
|
+
### Converters
|
|
215
|
+
|
|
216
|
+
- **`converters/markitdown_wrapper.py`**
|
|
217
|
+
- Wraps the [MarkItDown](https://github.com/markitdown) library for docx/image extraction, EXIF reading, and optional LLM-based image captioning.
|
|
218
|
+
- If MarkItDown is not installed, or fails, returns `None`.
|
|
219
|
+
|
|
220
|
+
- **`converters/azure_docint.py`**
|
|
221
|
+
- Leverages Azure’s Document Intelligence (prebuilt-layout) to extract text from PDFs and other document types in Markdown format.
|
|
222
|
+
|
|
223
|
+
- **`converters/unstructured_wrapper.py`**
|
|
224
|
+
- Uses the [Unstructured.io](https://www.unstructured.io/) library to parse documents. Useful for handling broad, less-common file types.
|
|
225
|
+
|
|
226
|
+
- **`converters/gpt4o_mini_vision.py`**
|
|
227
|
+
- Uses GPT-4o-mini (Azure ChatOpenAI) for OCR tasks on **images** or **scanned PDFs**.
|
|
228
|
+
- **Concurrent** or **simple** page-by-page approaches for PDFs.
|
|
229
|
+
- Can pass **URL-based images** or **local images** via Base64 encoding.
|
|
230
|
+
|
|
231
|
+
### Handlers
|
|
232
|
+
|
|
233
|
+
- **`handlers/pst_handler.py`**
|
|
234
|
+
- Parses PST archives with [`libratom`](https://github.com/rafproject/libratom) and extracts emails + attachments. Calls back into the pipeline for each attachment.
|
|
235
|
+
|
|
236
|
+
- **`handlers/email_handler.py`**
|
|
237
|
+
- Processes `.eml` files, extracting plain text, attachments, etc. Recursively processes attachments.
|
|
238
|
+
|
|
239
|
+
- **`handlers/zip_handler.py`**
|
|
240
|
+
- Unzips files, recurses into the pipeline for each contained file, and concatenates all Markdown output.
|
|
241
|
+
|
|
242
|
+
- **`handlers/audio_handler.py`**
|
|
243
|
+
- Uses [OpenAI Whisper](https://github.com/openai/whisper) to transcribe `.mp3`, `.wav`, `.ogg`, etc.
|
|
244
|
+
- Caches the model in memory to speed up repeated use.
|
|
245
|
+
|
|
246
|
+
- **`handlers/pdf_handler.py`**
|
|
247
|
+
- Utility to detect if a PDF is text-only, text+images, or fully scanned.
|
|
248
|
+
- Coordinates with GPT-4o-mini for OCR if needed.
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Installation
|
|
253
|
+
|
|
254
|
+
1. **Clone the Repo**
|
|
255
|
+
```bash
|
|
256
|
+
git clone https://github.com/YourName/markitdown-pro.git
|
|
257
|
+
cd markitdown-pro
|
|
258
|
+
```
|
|
259
|
+
2. **Create a Virtual Environment (recommended)**
|
|
260
|
+
```bash
|
|
261
|
+
python -m venv venv
|
|
262
|
+
source venv/bin/activate # or venv\Scripts\activate on Windows
|
|
263
|
+
```
|
|
264
|
+
3. **Create a Virtual Environment (recommended)**
|
|
265
|
+
```bash
|
|
266
|
+
python -m venv venv
|
|
267
|
+
source venv/bin/activate # or venv\Scripts\activate on Windows
|
|
268
|
+
```
|
|
269
|
+
4. **Install Dependencies**
|
|
270
|
+
```bash
|
|
271
|
+
pip install --upgrade pip
|
|
272
|
+
pip install -r requirements.txt
|
|
273
|
+
```
|
|
274
|
+
Note: You may also need system dependencies for libraries like PyMuPDF, libratom, etc.
|
|
275
|
+
|
|
276
|
+
5. **Set Up .env**
|
|
277
|
+
|
|
278
|
+
- Copy the sample .env to your root folder, and fill in your Azure or OpenAI API keys, etc. For example:
|
|
279
|
+
```bash
|
|
280
|
+
AZURE_DOCINTEL_ENDPOINT="https://<your-region>.api.cognitive.microsoft.com"
|
|
281
|
+
AZURE_DOCINTEL_KEY="YOUR_AZURE_KEY"
|
|
282
|
+
GPT4oMINI_DEPLOYMENT_NAME="your-azure-gpt4o-mini-deployment"
|
|
283
|
+
OPENAI_API_KEY="sk-...." # needed if you want LLM-based image captioning in MarkItDown
|
|
284
|
+
EXIFTOOL_PATH="/usr/local/bin/exiftool" # optional if exiftool is not in PATH
|
|
285
|
+
OCR_AGENT="unstructured.partition.utils.ocr_models.tesseract_ocr.OCRAgentTesseract" # or another supported agent for built-in OCR in Unstructured. S
|
|
286
|
+
```
|
|
287
|
+
Make sure to source it or ensure python-dotenv can read it.
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## Testing
|
|
291
|
+
|
|
292
|
+
We use **pytest** for running our test suite. The test files and scripts are located in the `/tests` directory:
|
|
293
|
+
```bash
|
|
294
|
+
pytest tests/test_markitdownpro.py
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Usage
|
|
300
|
+
### CLI Usage
|
|
301
|
+
1. **Basic:**
|
|
302
|
+
```bash
|
|
303
|
+
python main.py /path/to/document.pdf
|
|
304
|
+
```
|
|
305
|
+
This will produce /path/to/document.md if successful.
|
|
306
|
+
|
|
307
|
+
2. **Specify Output Path:**
|
|
308
|
+
```bash
|
|
309
|
+
python main.py /path/to/document.pst --output my_pst_output.md
|
|
310
|
+
```
|
|
311
|
+
### Programmatic Usage
|
|
312
|
+
You can import and call the pipeline directly from your Python code:
|
|
313
|
+
```python
|
|
314
|
+
from conversion_pipeline import convert_document_to_md, convert_document_from_url
|
|
315
|
+
|
|
316
|
+
# 1) Local file example
|
|
317
|
+
md_text = convert_document_to_md("/path/to/my_file.pdf")
|
|
318
|
+
print("Extracted Markdown:", md_text)
|
|
319
|
+
|
|
320
|
+
# 2) URL example
|
|
321
|
+
md_from_url = convert_document_from_url("https://example.com/my_doc.docx", output_md="output_doc.md")
|
|
322
|
+
print("Output saved to output_doc.md")
|
|
323
|
+
```
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Extra: Vector Database Chunking
|
|
327
|
+
After converting a document to Markdown, it’s common to chunk the text before sending it to a vector database. Here’s a minimal example using LangChain:
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
from langchain.text_splitter import MarkdownTextSplitter
|
|
331
|
+
|
|
332
|
+
# Load your Markdown content
|
|
333
|
+
with open('your_markdown_file.md', 'r') as file:
|
|
334
|
+
markdown_text = file.read()
|
|
335
|
+
|
|
336
|
+
# Initialize the MarkdownTextSplitter
|
|
337
|
+
markdown_splitter = MarkdownTextSplitter(chunk_size=1000, chunk_overlap=100)
|
|
338
|
+
|
|
339
|
+
# Split the text into chunks
|
|
340
|
+
chunks = markdown_splitter.create_documents([markdown_text])
|
|
341
|
+
|
|
342
|
+
# Each chunk is now ready for embedding and ingestion into your vector database
|
|
343
|
+
for chunk in chunks:
|
|
344
|
+
# e.g., push chunk.text to your VectorDB
|
|
345
|
+
pass
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
## FAQ
|
|
350
|
+
1. **What if MarkItDown or Whisper is not installed?**
|
|
351
|
+
The pipeline checks for each library’s availability. If a library is missing or fails, it gracefully moves on to the next fallback.
|
|
352
|
+
|
|
353
|
+
2. **Do I need Azure/OpenAI credentials?**
|
|
354
|
+
|
|
355
|
+
Azure: If you want to use Document Intelligence or GPT-4o-mini, yes.
|
|
356
|
+
OpenAI: If you want MarkItDown’s LLM-based image captioning or are using Whisper from openai’s library, you need appropriate credentials or local models.
|
|
357
|
+
How do I handle large PST files?
|
|
358
|
+
Large PSTs can be slow to process, especially if they contain many attachments. We parse them message-by-message, recursively handling attachments. For extremely large archives, you might want to increase concurrency or filter out attachments you don’t need.
|
|
359
|
+
|
|
360
|
+
3. **Does GPT-4o-mini require a publicly accessible image URL?**
|
|
361
|
+
|
|
362
|
+
If you provide a local file path, the code base64-encodes it. This is ideal for truly local images.
|
|
363
|
+
If you have a publicly hosted image, you can pass its URL directly.
|
|
364
|
+
|
|
365
|
+
4. **Why is Unstructured tried before Azure Doc Intelligence now?**
|
|
366
|
+
We observed that **Unstructured** is typically **lower cost** to run (especially with Tesseract or local OCR) compared to Azure’s \$10 per 1,000 pages. So if MarkItDown fails, we want to try Unstructured next to potentially save cost. If that also fails, we move to Azure.
|
|
367
|
+
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# MarkItDown-Pro
|
|
2
|
+
|
|
3
|
+
**MarkItDown-Pro** is an **improvement** of the **[Microsoft MarkItDown repository](https://github.com/markitdown)**, enhancing gaps and extending functionality by leveraging **Azure Document Intelligence SDK**, **Unstructured.io**, and other Azure services and libraries. The result is a comprehensive Python library and command-line tool designed to **convert diverse document formats into Markdown** with graceful fallbacks, including OCR support via GPT-4o-mini.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Folder Structure](#folder-structure)
|
|
10
|
+
- [Features & Highlights](#features--highlights)
|
|
11
|
+
- [How It Works](#how-it-works)
|
|
12
|
+
- [File-by-File Explanation](#file-by-file-explanation)
|
|
13
|
+
- [Main Files](#main-files)
|
|
14
|
+
- [Common Utils](#common-utils)
|
|
15
|
+
- [Converters](#converters)
|
|
16
|
+
- [Handlers](#handlers)
|
|
17
|
+
- [Testing](#testing)
|
|
18
|
+
- [Usage & Examples](#usage--examples)
|
|
19
|
+
- [CLI Usage](#cli-usage)
|
|
20
|
+
- [Programmatic Usage](#programmatic-usage)
|
|
21
|
+
- [Extra: Vector Database Chunking](#extra-vector-database-chunking)
|
|
22
|
+
- [Environment Variables](#environment-variables)
|
|
23
|
+
- [FAQ](#faq)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Folder Structure
|
|
28
|
+
|
|
29
|
+
A typical layout for **MarkItDown-Pro** might look like this:
|
|
30
|
+
```bash
|
|
31
|
+
markitdown-pro/
|
|
32
|
+
├── .env
|
|
33
|
+
├── README.md
|
|
34
|
+
├── requirements.txt
|
|
35
|
+
├── main.py
|
|
36
|
+
├── conversion_pipeline.py
|
|
37
|
+
├── common
|
|
38
|
+
│ └── utils.py
|
|
39
|
+
├── converters
|
|
40
|
+
│ ├── markitdown_wrapper.py
|
|
41
|
+
│ ├── azure_docint.py
|
|
42
|
+
│ ├── unstructured_wrapper.py
|
|
43
|
+
│ └── gpt4o_mini_vision.py
|
|
44
|
+
├── handlers
|
|
45
|
+
│ ├── pst_handler.py
|
|
46
|
+
│ ├── email_handler.py
|
|
47
|
+
│ ├── zip_handler.py
|
|
48
|
+
│ ├── audio_handler.py
|
|
49
|
+
│ └── pdf_handler.py
|
|
50
|
+
└── tests
|
|
51
|
+
├── data
|
|
52
|
+
└── test.py
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| Folder/File | Description |
|
|
56
|
+
|----------------------------|---------------------------------------------------------------------------------------|
|
|
57
|
+
| **main.py** | Entry point for CLI usage; uses `argparse` to accept file paths. |
|
|
58
|
+
| **conversion_pipeline.py** | Orchestrates the fallback chain for converting documents to Markdown. |
|
|
59
|
+
| **common/** | Shared utility functions, e.g. for file detection, text cleanup, etc. |
|
|
60
|
+
| **converters/** | Contains modules for using various 3rd-party libraries or services to extract text. |
|
|
61
|
+
| **handlers/** | Specialized handlers for specific file types (PST, EML, ZIP, audio, PDF scanning). |
|
|
62
|
+
| **.env** | Environment variables (e.g., credentials for Azure GPT-4o-mini, Azure Doc Intelligence). |
|
|
63
|
+
| **requirements.txt** | Python dependencies needed to install and run this project. |
|
|
64
|
+
| **tests/test_markitdownpro.py**| Recursively scans /tests/data/ and attempts to convert each file using convert_document_to_md|
|
|
65
|
+
| **README.md** | This documentation file, explaining usage and details of the project. |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Features & Highlights
|
|
70
|
+
|
|
71
|
+
1. **MarkItDown with LLM**
|
|
72
|
+
- Uses **MarkItDown** to convert documents to Markdown, optionally leveraging an OpenAI LLM to create image captions if you have an **OPENAI_API_KEY**.
|
|
73
|
+
- Auto-checks for `exiftool` if you want EXIF metadata in your images.
|
|
74
|
+
|
|
75
|
+
2. **Whisper-Based Audio Transcription**
|
|
76
|
+
- Converts audio files (`.mp3`, `.wav`, `.ogg`, etc.) into text using [OpenAI Whisper](https://github.com/openai/whisper).
|
|
77
|
+
- Gracefully falls back if Whisper is not installed.
|
|
78
|
+
|
|
79
|
+
3. **PST Extraction**
|
|
80
|
+
- Parses Outlook PST files with [`libratom`](https://github.com/rafproject/libratom), extracting emails and attachments recursively.
|
|
81
|
+
|
|
82
|
+
4. **Scanned PDF Detection & Concurrency**
|
|
83
|
+
- Identifies PDFs with no text or embedded images, and automatically performs OCR on each page with GPT-4o-mini.
|
|
84
|
+
- Offers concurrent page-by-page OCR for faster performance.
|
|
85
|
+
|
|
86
|
+
5. **Fallback to Azure Document Intelligence & Unstructured**
|
|
87
|
+
- If standard MarkItDown or specialized handlers fail or yield insufficient text, it tries Azure’s Document Intelligence to extract textual layout.
|
|
88
|
+
- Unstructured.io library for broad coverage of file types.
|
|
89
|
+
|
|
90
|
+
6. **GPT-4 Vision (or GPT-4o-mini) for Images & OCR**
|
|
91
|
+
- If an image or partially scanned PDF is detected, we can pass it to GPT-4o-mini for OCR.
|
|
92
|
+
- Supports local images (base64 encoding) or remote image URLs directly.
|
|
93
|
+
|
|
94
|
+
7. **Handles ZIP & EML**
|
|
95
|
+
- **ZIP**: Unzips and processes each file inside, concatenating the results.
|
|
96
|
+
- **EML**: Extracts email text, attachments, and processes attachments recursively.
|
|
97
|
+
|
|
98
|
+
8. **Graceful LLM Handling**
|
|
99
|
+
- If no **OPENAI_API_KEY** or GPT-4o-mini credentials are provided, it simply skips LLM-based features, logging a warning.
|
|
100
|
+
|
|
101
|
+
9. **Helper Methods for URL & Stream Conversion**
|
|
102
|
+
- `convert_document_from_url(url, output_md)`
|
|
103
|
+
- `convert_document_from_stream(stream, extension, output_md)`
|
|
104
|
+
- `convert_document_to_md(local_path, output_md)`
|
|
105
|
+
|
|
106
|
+
10. **Easy-to-Extend Architecture**
|
|
107
|
+
Each file type has its own **handler**. Each text-extraction library has its own **converter**. The main pipeline provides a centralized fallback sequence.
|
|
108
|
+
|
|
109
|
+
11. **Environment-Driven Configuration**
|
|
110
|
+
- Pulls API keys, endpoints, and paths from `.env` to keep secrets out of source code.
|
|
111
|
+
|
|
112
|
+
12. **Rich File Type Handling**
|
|
113
|
+
|
|
114
|
+
| Category | File Type(s) |
|
|
115
|
+
|-----------------------|-------------|
|
|
116
|
+
| PDF | .pdf |
|
|
117
|
+
| PowerPoint | .pot, .potm, .ppt, .pptm, .pptx |
|
|
118
|
+
| Word Processing | .abw, .doc, .docm, .docx, .dot, .dotm, .hwp, .zabw |
|
|
119
|
+
| Excel/Spreadsheet | .et, .fods, .uos1, .uos2, .wk2, .xls, .xlsb, .xlsm, .xlsx, .xlw |
|
|
120
|
+
| Images | .bmp, .gif, .heic, .jpeg, .jpg, .png, .prn, .svg, .tiff, .webp |
|
|
121
|
+
| Audio | .mp3, .wav, .ogg, .flac, .m4a, .aac, .wma, .webm, .opus |
|
|
122
|
+
| HTML | .htm, .html |
|
|
123
|
+
| Text-Based Formats | .csv, .json, .xml, .txt |
|
|
124
|
+
| ZIP Files | (Iterates over contents) |
|
|
125
|
+
| Email | .eml, .p7s |
|
|
126
|
+
| PST | .pst |
|
|
127
|
+
| EPUB | .epub |
|
|
128
|
+
| Markdown | .md |
|
|
129
|
+
| Org Mode | .org |
|
|
130
|
+
| Open Office | .odt, .sgl |
|
|
131
|
+
| Other | .eth, .mw, .pbd, .sdp, .uof, .web |
|
|
132
|
+
| Plain Text | .txt |
|
|
133
|
+
| reStructured Text | .rst |
|
|
134
|
+
| Rich Text | .rtf |
|
|
135
|
+
| StarOffice | .sxg |
|
|
136
|
+
| TSV | .tsv |
|
|
137
|
+
| Apple | .cwk, .mcw, .pages |
|
|
138
|
+
| Data Interchange | .dif |
|
|
139
|
+
| dBase | .dbf |
|
|
140
|
+
| Microsoft Office | .docx, .xlsx, .pptx |
|
|
141
|
+
| HEIF Image Format | .heif |
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## How It Works
|
|
147
|
+
|
|
148
|
+
1. **Detect File Type**: The pipeline checks the file extension or general signature (`.pdf`, `.zip`, `.eml`, `.docx`, `.mp3`, etc.).
|
|
149
|
+
2. **Specialized Handlers**: If the file is PST, EML, ZIP, or audio, it’s handed off to a dedicated module that handles that format.
|
|
150
|
+
3. **MarkItDown**: For most generic document conversions, we first try [MarkItDown](https://github.com/markitdown).
|
|
151
|
+
4. **Unstructured**: If MarkItDown fails or yields minimal text, we turn to [Unstructured.io](https://unstructured.io/) next.
|
|
152
|
+
- **Why?** It's typically **cheaper** than Azure Document Intelligence, and can handle partial OCR scenarios (via Tesseract, PaddleOCR, etc., if you configure `OCR_AGENT`).
|
|
153
|
+
5. **Azure Document Intelligence**: If Unstructured also fails or yields minimal text, we try Azure Document Intelligence (prebuilt-layout).
|
|
154
|
+
6. **GPT-4o-mini**: As a final fallback or specifically for OCR on images/scanned pages.
|
|
155
|
+
7. **Saves** the extracted text to a `.md` file once any method returns sufficient content.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## File-by-File Explanation
|
|
160
|
+
|
|
161
|
+
### Main Files
|
|
162
|
+
|
|
163
|
+
- **`conversion_pipeline.py`**
|
|
164
|
+
The core logic that orchestrates the fallback chain. Checks each handler or converter in a specific order. Once a successful conversion with enough text is found, it writes to `.md` and stops.
|
|
165
|
+
|
|
166
|
+
### Common Utils
|
|
167
|
+
|
|
168
|
+
- **`common/utils.py`**
|
|
169
|
+
- **File Detection**: Contains helper functions like `is_pdf`, `is_audio`, `detect_extension`.
|
|
170
|
+
- **Markdown Cleaning**: Functions like `clean_markdown()` and `ensure_minimum_content()` to tidy up text and ensure it’s not empty.
|
|
171
|
+
|
|
172
|
+
### Converters
|
|
173
|
+
|
|
174
|
+
- **`converters/markitdown_wrapper.py`**
|
|
175
|
+
- Wraps the [MarkItDown](https://github.com/markitdown) library for docx/image extraction, EXIF reading, and optional LLM-based image captioning.
|
|
176
|
+
- If MarkItDown is not installed, or fails, returns `None`.
|
|
177
|
+
|
|
178
|
+
- **`converters/azure_docint.py`**
|
|
179
|
+
- Leverages Azure’s Document Intelligence (prebuilt-layout) to extract text from PDFs and other document types in Markdown format.
|
|
180
|
+
|
|
181
|
+
- **`converters/unstructured_wrapper.py`**
|
|
182
|
+
- Uses the [Unstructured.io](https://www.unstructured.io/) library to parse documents. Useful for handling broad, less-common file types.
|
|
183
|
+
|
|
184
|
+
- **`converters/gpt4o_mini_vision.py`**
|
|
185
|
+
- Uses GPT-4o-mini (Azure ChatOpenAI) for OCR tasks on **images** or **scanned PDFs**.
|
|
186
|
+
- **Concurrent** or **simple** page-by-page approaches for PDFs.
|
|
187
|
+
- Can pass **URL-based images** or **local images** via Base64 encoding.
|
|
188
|
+
|
|
189
|
+
### Handlers
|
|
190
|
+
|
|
191
|
+
- **`handlers/pst_handler.py`**
|
|
192
|
+
- Parses PST archives with [`libratom`](https://github.com/rafproject/libratom) and extracts emails + attachments. Calls back into the pipeline for each attachment.
|
|
193
|
+
|
|
194
|
+
- **`handlers/email_handler.py`**
|
|
195
|
+
- Processes `.eml` files, extracting plain text, attachments, etc. Recursively processes attachments.
|
|
196
|
+
|
|
197
|
+
- **`handlers/zip_handler.py`**
|
|
198
|
+
- Unzips files, recurses into the pipeline for each contained file, and concatenates all Markdown output.
|
|
199
|
+
|
|
200
|
+
- **`handlers/audio_handler.py`**
|
|
201
|
+
- Uses [OpenAI Whisper](https://github.com/openai/whisper) to transcribe `.mp3`, `.wav`, `.ogg`, etc.
|
|
202
|
+
- Caches the model in memory to speed up repeated use.
|
|
203
|
+
|
|
204
|
+
- **`handlers/pdf_handler.py`**
|
|
205
|
+
- Utility to detect if a PDF is text-only, text+images, or fully scanned.
|
|
206
|
+
- Coordinates with GPT-4o-mini for OCR if needed.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Installation
|
|
211
|
+
|
|
212
|
+
1. **Clone the Repo**
|
|
213
|
+
```bash
|
|
214
|
+
git clone https://github.com/YourName/markitdown-pro.git
|
|
215
|
+
cd markitdown-pro
|
|
216
|
+
```
|
|
217
|
+
2. **Create a Virtual Environment (recommended)**
|
|
218
|
+
```bash
|
|
219
|
+
python -m venv venv
|
|
220
|
+
source venv/bin/activate # or venv\Scripts\activate on Windows
|
|
221
|
+
```
|
|
222
|
+
3. **Create a Virtual Environment (recommended)**
|
|
223
|
+
```bash
|
|
224
|
+
python -m venv venv
|
|
225
|
+
source venv/bin/activate # or venv\Scripts\activate on Windows
|
|
226
|
+
```
|
|
227
|
+
4. **Install Dependencies**
|
|
228
|
+
```bash
|
|
229
|
+
pip install --upgrade pip
|
|
230
|
+
pip install -r requirements.txt
|
|
231
|
+
```
|
|
232
|
+
Note: You may also need system dependencies for libraries like PyMuPDF, libratom, etc.
|
|
233
|
+
|
|
234
|
+
5. **Set Up .env**
|
|
235
|
+
|
|
236
|
+
- Copy the sample .env to your root folder, and fill in your Azure or OpenAI API keys, etc. For example:
|
|
237
|
+
```bash
|
|
238
|
+
AZURE_DOCINTEL_ENDPOINT="https://<your-region>.api.cognitive.microsoft.com"
|
|
239
|
+
AZURE_DOCINTEL_KEY="YOUR_AZURE_KEY"
|
|
240
|
+
GPT4oMINI_DEPLOYMENT_NAME="your-azure-gpt4o-mini-deployment"
|
|
241
|
+
OPENAI_API_KEY="sk-...." # needed if you want LLM-based image captioning in MarkItDown
|
|
242
|
+
EXIFTOOL_PATH="/usr/local/bin/exiftool" # optional if exiftool is not in PATH
|
|
243
|
+
OCR_AGENT="unstructured.partition.utils.ocr_models.tesseract_ocr.OCRAgentTesseract" # or another supported agent for built-in OCR in Unstructured. S
|
|
244
|
+
```
|
|
245
|
+
Make sure to source it or ensure python-dotenv can read it.
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Testing
|
|
249
|
+
|
|
250
|
+
We use **pytest** for running our test suite. The test files and scripts are located in the `/tests` directory:
|
|
251
|
+
```bash
|
|
252
|
+
pytest tests/test_markitdownpro.py
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Usage
|
|
258
|
+
### CLI Usage
|
|
259
|
+
1. **Basic:**
|
|
260
|
+
```bash
|
|
261
|
+
python main.py /path/to/document.pdf
|
|
262
|
+
```
|
|
263
|
+
This will produce /path/to/document.md if successful.
|
|
264
|
+
|
|
265
|
+
2. **Specify Output Path:**
|
|
266
|
+
```bash
|
|
267
|
+
python main.py /path/to/document.pst --output my_pst_output.md
|
|
268
|
+
```
|
|
269
|
+
### Programmatic Usage
|
|
270
|
+
You can import and call the pipeline directly from your Python code:
|
|
271
|
+
```python
|
|
272
|
+
from conversion_pipeline import convert_document_to_md, convert_document_from_url
|
|
273
|
+
|
|
274
|
+
# 1) Local file example
|
|
275
|
+
md_text = convert_document_to_md("/path/to/my_file.pdf")
|
|
276
|
+
print("Extracted Markdown:", md_text)
|
|
277
|
+
|
|
278
|
+
# 2) URL example
|
|
279
|
+
md_from_url = convert_document_from_url("https://example.com/my_doc.docx", output_md="output_doc.md")
|
|
280
|
+
print("Output saved to output_doc.md")
|
|
281
|
+
```
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Extra: Vector Database Chunking
|
|
285
|
+
After converting a document to Markdown, it’s common to chunk the text before sending it to a vector database. Here’s a minimal example using LangChain:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
from langchain.text_splitter import MarkdownTextSplitter
|
|
289
|
+
|
|
290
|
+
# Load your Markdown content
|
|
291
|
+
with open('your_markdown_file.md', 'r') as file:
|
|
292
|
+
markdown_text = file.read()
|
|
293
|
+
|
|
294
|
+
# Initialize the MarkdownTextSplitter
|
|
295
|
+
markdown_splitter = MarkdownTextSplitter(chunk_size=1000, chunk_overlap=100)
|
|
296
|
+
|
|
297
|
+
# Split the text into chunks
|
|
298
|
+
chunks = markdown_splitter.create_documents([markdown_text])
|
|
299
|
+
|
|
300
|
+
# Each chunk is now ready for embedding and ingestion into your vector database
|
|
301
|
+
for chunk in chunks:
|
|
302
|
+
# e.g., push chunk.text to your VectorDB
|
|
303
|
+
pass
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
## FAQ
|
|
308
|
+
1. **What if MarkItDown or Whisper is not installed?**
|
|
309
|
+
The pipeline checks for each library’s availability. If a library is missing or fails, it gracefully moves on to the next fallback.
|
|
310
|
+
|
|
311
|
+
2. **Do I need Azure/OpenAI credentials?**
|
|
312
|
+
|
|
313
|
+
Azure: If you want to use Document Intelligence or GPT-4o-mini, yes.
|
|
314
|
+
OpenAI: If you want MarkItDown’s LLM-based image captioning or are using Whisper from openai’s library, you need appropriate credentials or local models.
|
|
315
|
+
How do I handle large PST files?
|
|
316
|
+
Large PSTs can be slow to process, especially if they contain many attachments. We parse them message-by-message, recursively handling attachments. For extremely large archives, you might want to increase concurrency or filter out attachments you don’t need.
|
|
317
|
+
|
|
318
|
+
3. **Does GPT-4o-mini require a publicly accessible image URL?**
|
|
319
|
+
|
|
320
|
+
If you provide a local file path, the code base64-encodes it. This is ideal for truly local images.
|
|
321
|
+
If you have a publicly hosted image, you can pass its URL directly.
|
|
322
|
+
|
|
323
|
+
4. **Why is Unstructured tried before Azure Doc Intelligence now?**
|
|
324
|
+
We observed that **Unstructured** is typically **lower cost** to run (especially with Tesseract or local OCR) compared to Azure’s \$10 per 1,000 pages. So if MarkItDown fails, we want to try Unstructured next to potentially save cost. If that also fails, we move to Azure.
|
|
325
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from . import common, converters, handlers, services # noqa F401
|