Skip to main content

bloat2md

Превращает документ в markdown, который не стыдно положить в промпт. PDF, docx, xlsx, pptx, OpenDocument, rtf, html, epub, csv, json, yaml, xml, ipynb, картинки и zip — на выходе один markdown, а страницы, на которых текста нет, приезжают пережатыми jpeg.

Пакет — это две половины. bloat2md без экстры ставит только SDK: класс, который умеет сходить в сервис по HTTP. bloat2md[server] добавляет парсеры и сам сервис.

Установка

pip install bloat2md
pip install 'bloat2md[server]'

SDK

from bloat2md import Client

client = Client("http://bloat2md:8084", timeout=45.0)

rendered = await client.convert("report.pdf", raw)
if rendered is not None:
    print(rendered.markdown)

convert не бросает: сервис лежит, документ не распознан, ответ невнятный — везде None и запись в лог. Вызывающему остаётся решить, что делать с документом, который прочитать не вышло, и это обычно «пропустить».

Rendered — это kind, markdown, images (список пар mime/bytes), pages, truncated и dropped. Последнее — счётчики того, что выкинули по дороге: hidden_nodes, hidden_spans, hidden_sheets, unsafe_names, bidi, zero_width и прочее.

Сервис

bloat2md

POST /convert принимает файл как есть, application/octet-stream, имя — в заголовке X-File-Name. Имя нужно только там, где по содержимому не отличить csv от txt, а yaml от чего угодно.

Код Когда
200 markdown, картинки, счётчики
413 файл больше BLOAT2MD_INPUT_LIMIT_MB
415 это не документ
422 документ, но прочитать не вышло
504 воркер не уложился в BLOAT2MD_TIMEOUT

GET /healthz отвечает {"status": "ok"} и не попадает в трейсы.

Песочница

Документ — чужой ввод, и парсеры у него на пути написаны не нами. Поэтому конвертация уходит в дочерний процесс: своя сессия, свой TMPDIR, урезанное окружение и RLIMIT_AS, RLIMIT_CPU, RLIMIT_FSIZE, RLIMIT_CORE. Воркер общается с сервисом одним JSON через stdout, а по таймауту его группа процессов получает SIGKILL.

Из этого следует, что MemoryError в парсере стоит одного запроса, а не пода.

Инъекции

Текст, которого человек в документе не увидит, до промпта не доходит: скрытые css-ом узлы html, невидимый и микроскопический текст в pdf, спрятанные листы xlsx. Bidi-управляющие символы, теги Unicode, нулевой ширины и длинные цепочки joiner'ов вырезаются, а сколько чего вырезали — видно в dropped.

Zip не распаковывается, а описывается: имена и размеры. Архив с ratio выше BLOAT2MD_MAX_ARCHIVE_RATIO отвергается целиком.

Настройки

Переменные окружения с префиксом BLOAT2MD_, читается и .env.

Переменная По умолчанию Что делает
HOST, PORT 127.0.0.1, 8084 что слушать
ENV prod окружение для трейсов
TIMEOUT 30 секунд на документ
MEMORY_LIMIT_MB 512 RLIMIT_AS воркера
CPU_SECONDS 20 RLIMIT_CPU воркера
OUTPUT_LIMIT_MB 64 RLIMIT_FSIZE воркера
INPUT_LIMIT_MB 20 больше — 413
MAX_PAGES 50 страниц pdf читаем
MAX_RENDER_PAGES 10 страниц отдаём картинкой
RENDER_EDGE 1536 длинная сторона картинки
RENDER_QUALITY 80 jpeg
MAX_MARKDOWN_CHARS 200000 дальше режем с серединой
MAX_IMAGE_PIXELS 50000000 защита от decompression bomb
MAX_ARCHIVE_MEMBERS 2000 членов в архиве
MAX_ARCHIVE_RATIO 100 во сколько раз член вправе распухнуть
MAX_ARCHIVE_BYTES 104857600 суммарный размер после распаковки
LIBREOFFICE true пускать ли soffice

LibreOffice

Doc, xls, ppt и всё, на чём споткнулись питонячьи парсеры, уходят в soffice --convert-to pdf, а дальше идут как pdf. Если бинаря нет, формат просто становится неподдерживаемым — падать по этому поводу незачем.

Образ ghcr.io/wprhvso/bloat2md собирается с libreoffice-*-nogui внутри.

CI

Раннеры здесь ubuntu-latest, а не pool, как в остальных репозиториях. Флот self-hosted раннеров разворачивается по списку реп в runners.toml, а тот лежит зашифрованным в кластере — новый репозиторий в него сам собой не попадёт, и job'ы висели бы в очереди вечно. Репозиторий публичный, GitHub считает его минуты бесплатными, так что цена нулевая. Появится строка в runners.toml — можно вернуть pool и убрать шаги установки uv и earthly.

cd собирает и публикует только тогда, когда версии в pyproject.toml ещё нет тега: PyPI по trusted publishing, образ в ghcr, следом тег vX.Y.Z.

Пин образа по digest в deploy/bloat2md/deployment.yaml CD ставит сам: сразу после пуша в ghcr он спрашивает у реестра digest тега и коммитит его в main. Руками digest не резолвится и в манифесте не выдумывается никогда.

Лицензия

MIT

Metadata

Release files for bloat2md 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bloat2md 0.1.0
File Size Uploaded
bloat2md-0.1.0.tar.gz 105.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bloat2md 0.1.0
File Interpreter ABI Platform
bloat2md-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 133.2 kB

Release files / bloat2md-0.1.0.tar.gz

Download URL bloat2md-0.1.0.tar.gz
Size 105.5 kB
Tags Source
SHA-256 checksum
How to use checksums
df4f4499550d563e968baeea62d1984b9c778050f3d2bd302ad25d7faa905299
BLAKE2b-256 checksum
How to use checksums
b4fe5bfa2ad520dc36afab7309436d0c914729a377a786d52dfd53582e71ba2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / bloat2md-0.1.0-py3-none-any.whl

Download URL bloat2md-0.1.0-py3-none-any.whl
Size 27.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
acc01be3d8a06028b0f2a2f3783a5f3ded58a046ca5eebe392da01fe2f4c49d6
BLAKE2b-256 checksum
How to use checksums
a0b74c20cfc3f4a7dd604fee0b519b7e62a92d314f86f56c7f984a73ab8b01e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.6 {"installer":{"name":"uv","version":"0.12.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page