--- title: "Task: DOCX→Markdown converter notebook for Open WebUI" status: "active" folder: "07-tasks" tags: [task, notebook, openwebui, docx, markdown, converter] created: "2026-07-30" updated: "2026-07-30" version: "1.0.0" priority: "high" due: "2026-07-30" owner: "stateofshit" --- # Task: DOCX→Markdown converter notebook for Open WebUI ## Done — 2026-07-30 ## What A Colab notebook that batches `.docx` (and `.md`) inputs into clean Markdown optimized for embedding in **Open WebUI** knowledge bases. Open WebUI's RAG uses Markdown Header Splitting, so the converter preserves a clean single-H1 / nested-H2-H3 heading hierarchy — that's what makes retrieval chunks good. Output is mirrored to Google Drive so nothing is lost across Colab wipes. This notebook is the upgrade over the older `/home/user/00-incoming/docx_to_markdown.ipynb`: it adds the Gemini-Notebook setup nuance (cite markers stripped, hard bold-promoted to headings) and the dedup edge-cases we hit this week. ## Pipeline (per file) - **DOCX**: `mammoth(html) → clean_markdown → strip_masthead → strip_cite_markers → collapse_hr → promote_structure → collapse_extra_h1 → conditional synthetic H1 prepend` - **MD** (raw uploaded): same chain minus mammoth - **Fallback**: if mammoth crashes (the `_accept0` bug on weird docx), the pipeline degrades to a `python-docx` parser that still produces clean markdown - **Output**: one `.md` per input → `/drive/MyDrive/to_convert/markdown_output/` ## Headlining features | # | Feature | Why it matters | |---|---|---| | 1 | Drive-backed manifest dedup (SHA-256 of input bytes) | Renaming a file does NOT cause a reconvert. Survives Colab wipes. | | 2 | **Pre-flight fuzzy dedup** (new today — cell 5b) | Drops `(1)`/`(2)`/`Copy of` variants where the bytes differ by a few chars but the doc is really the same. Uses Jaccard similarity over 500-char normalized text chunks. Threshold `0.97` (tunable). | | 3 | Gemini masthead + cite-marker stripping | Removes `— / Created By: Due Process Demolition / APP: Gemini Notebook / DATE: …` boilerplate and `[cite: N]` artifacts so the KB doesn't fill with garbage | | 4 | Auto heading promotion | For Gemini-Notebook-style docs that use bold paragraphs (not Word heading styles) for section titles, the converter promotes `**Bold**` lines to real `##`/`###` so Open WebUI's splitter has structure | | 5 | Single-H1 enforcement | `collapse_extra_h1()` keeps the first H1 (title) and demotes any subsequent H1 → H2. Required by Open WebUI's Markdown Header Splitter | | 6 | URL extraction | Every `http(s)://` URL found in converted .md files → `sources.csv` paired with source-doc SHA + stem | | 7 | Bulk downloader (off by default, cell 11) | Once sources.csv is curated, opt-in fetch of PDFs first then HTML to `/downloads/`, tracked in `downloads.csv` | | 8 | Zip + download (cell 10) | Bundle all converted `.md` as a single zip for upload to Open WebUI | ## File | File | Purpose | |---|---| | `000-configs/notebooks/docx_to_markdown_openwebui.ipynb` | The notebook — 29 cells, nbformat-valid | | `000-configs/notebooks/README.md` | Index of all notebooks, how to add new ones | ## How to run (final user workflow) 1. Open the `.ipynb` in **Colab free tier** → `Runtime → Run all` (or run cell-by-cell). 2. Cell 2: drop the docx/md files (Colab upload picker), or it auto-extracts from `/drive/MyDrive/zzz-new_shit/*.tar`. 3. Cell 5b (idempotent): watch the pre-flight dedup report — see which `(1)`/`(2)` dupes got dropped by content overlap. 4. Cell 6: conversions run; manifest is updated with statuses (`converted` / `crashed_fallback_ok` / `crashed_skipped`). 5. Cell 7: `sources.csv` gets all URLs from the new conversions. 6. Cell 8: heading-structure audit (counts H1/H2/H3 per file — sanity check for Open WebUI chunking). 7. Cell 10: zip + download everything → upload zip into Open WebUI knowledge base. ## Drive layout (created on first run) ``` /drive/MyDrive/to_convert/ ├── manifest.csv ← SHA-256 dedup history (append-only) ├── dedup_drops.csv ← pre-flight fuzzy-dedup log (new today) ├── archive/ ← original input bytes, durable across Colab wipes ├── markdown_output/ ← converted .md files (this is what you upload) ├── sources.csv ← doc → URLs (append-only) └── downloads/ ├── pdf/ html/ other/ └── downloads.csv ← URL → saved file (append-only) ``` ## Config (cell 9 flags — tunable without touching the converter) ```python SKIP_ALREADY_SEEN = True ARCHIVE_INPUTS = True DEDUP_SIMILARITY_THRESHOLD = 0.97 # set to 0 to disable fuzzy dedup DEDUP_CHUNK_SIZE = 500 # chars per chunk in the similarity signature EXTRACT_IMAGES = True # Extract media to per-doc media/ (no embedding signal yet) ``` ## Bug fixed this session - **TypeError: 'NoneType' object is not subscriptable** in cell 6 when manifest has a hash in the seen-set but `is_seen()` returns None (race with `manifest.csv` written by another process). Guarded with `seen_row = is_seen(sha, manifest) or {}` and `.get(..., '(unknown)')` accessors. ## Outstanding - [ ] Re-run the notebook end-to-end with the user's real upload batch (the 257-file set with the `clean_email_threads.docx` + `clean_email_threads (2).docx` 25-byte-diff pair); confirm the fuzzy dedup actually drops the `(2)` variant at threshold 0.97. - [ ] If threshold is too generous (drops genuinely different docs), bump to `0.95`. If too strict (lets dupes through), drop to `0.90`. The user has the knob in cell 9 — no code changes needed. - [ ] Confirm `manifest.csv`, `dedup_drops.csv`, `sources.csv` all appear on Drive after first run and round-trip correctly across Colab sessions. - [ ] Consider adding a MinHash variant if Jaccard over fixed chunks ever gets slow on >1k inputs. ## Build provenance The notebook is built from a Python script (`/tmp/build_notebook.py`) that stitches fragments together and is then validated. That script is a build artifact and lives outside the vault (single-use); future revisions to the notebook can be made directly by editing the `.ipynb` in Colab and committing the saved file, OR by patching the build script and rerunning. The fragment files were in `/tmp/nbfrags/` at build time — restore from git history if rebuilding. ## Related - [[06-research/gemini-notebook-poweruser-briefing]] — context for the Gemini-Notebook .docx format quirks (cite markers, masthead, bold-as-heading) that this converter handles