Processing unstructured data into LLM-ready formats remains fragmented. Teams commonly stitch together separate OCR, transcription, and web crawling tools, resulting in inconsistent output schemas, duplicated model management, and integration friction with RAG or fine-tuning pipelines. The pain point is operational: ingest a document, image, audio, or web page and get structured, actionable data without orchestrating a custom pipeline each time.
What This Does
OmniParse is a parsing platform that supports ~20 file types—documents, images, audio, video, and web pages—converting them to high-quality structured markdown optimized for GenAI workflows. The codebase is organized into modular packages under omniparse/:
omniparse/documents/ – document parsing with Surya OCR and Florence-2 models
omniparse/image/ – image processing and captioning
omniparse/web/ – Selenium-based web crawling with configurable strategies
omniparse/chunking/ – post-parse chunking for LLM context windows
Entry point is server.py, which orchestrates model loading and API serving. Supporting files include download.py for pre-downloading models and omniparse/utils.py for shared utilities. The python-sdk/omniparse_client/ provides a Python client for programmatic access.
How It Is Wired
Execution starts at server.py, which parses CLI flags (--documents, --media, --web) and triggers model loading via omniparse/web/model_loader.py. The web module routes through omniparse/web/router.py and depends on omniparse/web/models.py for model instantiation and omniparse/web/prompts.py for prompt templates. Document parsing flows through omniparse/documents/router.py, which coordinates Surya and Florence-2. Media transcription uses omniparse/media/router.py with Whisper. The web crawler in omniparse/web/web_crawler.py uses Selenium and respects a strategy pattern in omniparse/web/crawler_strategy.py.
A notable external dependency: the Docker image is published to Docker Hub as savatar101/omniparse, and the repo references LangChain in its workflows, suggesting integration points exist but are not deeply mapped in the current surface area.
Internal call graph: server.py → model_loader → router → (document/media/web specific handlers). The widest blast radius is in omniparse/web/router.py and omniparse/documents/router.py, as both are entry points for model initialization and can load heavy vision/LLM models.
How To Use It
Setup: Clone the repository:
git clone https://github.com/moses-y/omniparse
cd omniparse
Create a Python 3.10+ environment and install dependencies:
poetry install
# or
pip install -e .
Configuration: No environment variables are required for local mode, but model downloads depend on download.py --documents --media --web. The README notes the server only runs on Linux-based systems due to system-specific dependencies.
This loads document models (Surya OCR, Florence-2), Whisper for media, and enables the Selenium web crawler. To pre-download models:
python download.py --documents --media --web
Docker build and run are also supported via Dockerfile and the published savatar101/omniparse:0.1 image.
Real-World Use
A RAG pipeline ingesting PDFs and website URLs can offload preprocessing to OmniParse: point the parser at a directory of documents and web sources, receive structured markdown back, and feed that directly into a vector store. The web crawler (omniparse/web/web_crawler.py) extracts and converts pages in one step, eliminating a separate scraping + cleaning layer. For multimedia, omniparse/media/router.py transcribes audio/video to text that can be chunked and embedded alongside document content.
Code Health & Issues
No test files detected – repository-wide untested code paths. Critical parsing and model-loading logic has no unit or integration test coverage.
Dependencies declared without a lockfile – pyproject.toml lists dependencies but no poetry.lock or requirements.txt with pinned versions, leading to non-reproducible builds across environments.
SDLC observations: The .github/workflows/ directory contains Docker CI and Python publish pipelines, and a CODE_OF_CONDUCT.md, CONTRIBUTING.md, and issue/pr templates are present. However, the absence of a lockfile and test suite is a practical blocker for team adoption.
The Bottom Line
OmniParse delivers a functional, locally-run parsing pipeline for ~20 data types with a single CLI flag, and the Docker image is readily available. The codebase is modular but thin on testing and dependency pinning, which limits its readiness for production RAG or fine-tuning pipelines without engineering investment. Teams that need a self-hosted, no-API-parser and are comfortable managing model versions and environment compatibility will find it usable; others should expect to add test coverage and lockfile discipline before shipping to production.
What the analyser found
Deployment readiness
5/7
✓Container image
✓CI pipeline
✗Lockfile committed
✗Test suite
✓README
✓License
✓No committed secrets
Composition
63 files
Python30
Markdown20
YAML2
TOML2
Jupyter Notebook1
DockerLangChain
Architecture
Top-level areas of the codebase, sized by module count. Arrows show how many imports cross from one area into another.
Ranked by severity × confidence × production reach. Reach is the honest discriminator across a collection that is mostly other people's code: the same finding matters more in something that ships.
high4
medium7
low1
Pin third-party GitHub Actions to a commit SHAhigh3 occurrences
A tag can be moved, so the action running with your token and secrets is whatever its owner last pushed; this is how tj-actions/changed-files leaked secrets from thousands of repos.
Fix: Replace each @vN with the 40-character commit SHA, keep # vN as a comment, and let Dependabot bump the SHAs.
Add a test suite; this repository has nonehigh30 occurrences
30 source files, no test files
Any change ships with no signal that existing behaviour still holds, so a regression reaches production undetected.
Fix: Add one test per public entry point, then a CI step that runs them.
Commit a lockfile beside the manifesthigh
pyproject.toml
manifest with no lockfile in scope
An unlocked range means the artifact you tested and the artifact you ship can contain different transitive code, so a malicious patch release reaches production with no diff.
Fix: Run the package manager once and commit the generated lockfile.
Replace the wildcard CORS origin with an explicit allow listhigh
server.py
allow_origins=["*"] together with credentials enabled
Any page on the internet can call the API with the browser's cookies attached, so a logged-in visitor to an unrelated site performs authenticated requests without knowing, and the wildcard combined with credentials is the exact configuration browsers refuse for that reason.
Fix: List the origins the API actually serves, and never pair a wildcard with credentials.
Declare least-privilege permissions for GITHUB_TOKENmedium
.github/workflows/docker-image.yml
1 workflow(s) declare no permissions, 1 of them reference secrets
With no declaration the token inherits the repository default, so any injected step can push commits or mint releases from inside your own CI.
Fix: Add permissions: contents: read at the top of the workflow and widen per job only where needed.
Enable Dependabot or Renovatemedium
2 manifest(s), no update bot configured
Without a bot a published advisory sits unpatched until someone audits by hand, which across 1,322 repositories means never.
Fix: Commit .github/dependabot.yml covering the repo ecosystems plus github-actions.
An untagged or mutable base means today's build and last month's contain different libc and a different CVE set, with no record of which shipped.
Fix: Use image:tag@sha256:<digest> and enable Dependabot's docker ecosystem.
Gate pull requests on a dependency vulnerability scanmedium
.github/workflows
no dependency scan in CI
This is the one gate that would catch a known-vulnerable package before it reaches a build, and no repository in the sample had it.
Fix: Add dependency-review-action on pull_request, or osv-scanner on push and a schedule.
Set persist-credentials: false on checkoutmedium
.github/workflows/python-publish.yml
checkout keeps the token, then dependencies are installed
The token stays in .git/config for every later step, so a malicious postinstall script reads a pushable credential without one ever being passed to it.
Fix: Add with: persist-credentials: false, and pass an explicit token only to the step that pushes.
Add a non-root USER to the imagemedium
Dockerfile
CMD or ENTRYPOINT with no USER directive
A process running as root in the container is root against every mounted volume, and it turns any container escape or writable-mount mistake from a contained problem into a host one.
Fix: Create an unprivileged user, chown what it needs, and end the Dockerfile with USER.
Pin the resolution so a vulnerable version cannot installmedium187 occurrences
no lockfile, and the declared ranges reach pillow@10.1.0 CVE-2023-50447 (critical) +31 more; nltk@3.8.1 CVE-2025-14009 (critical) +30 more; torch@2.2.2 CVE-2025-32434 (critical) +22 more; and 4 more package(s)
The range includes versions with published advisories and nothing in the repository records which one was resolved, so two installs a month apart can differ in whether they are vulnerable and neither is reproducible from what is committed.
Fix: Commit the lockfile, which fixes the resolution and makes this answerable.
Set timeout-minutes on the workflow jobslow2 occurrences
.github/workflows/docker-image.yml
2 workflow(s) declare no job timeout
A wedged step runs to the six-hour platform default, which on a two-hourly schedule means three runs overlap behind it.
Fix: Add timeout-minutes with a realistic bound to each job.
Checked deterministically against the repository tree and a bounded set of its files: committed credentials, unpinned actions and base images, missing lockfiles and update bots, workflows that discard failures, published advisories against the declared dependencies, runtime configuration, licensing and notebook reproducibility. No language model is involved in this section.