DeepSeek-OCR-WebUI

neosun100MIT活跃维护

自托管的 DeepSeek-OCR-2 网页界面与 API,支持在 NVIDIA、Apple Silicon 和 CPU 上通过 vLLM 或 transformers 运行,提供逐页质量检查、PDF 流式处理和 MCP 接入。

Self-hosted DeepSeek-OCR-2 WebUI & API — vLLM / transformers on NVIDIA, Apple Silicon & CPU, per-page quality checks, PDF streaming, MCP

v5.0.0发布于 2026-11-07 02:51 UTC447star +1 / 7 天94fork同步于 2026-11-10 23:58 UTC

怎么上手

由 README 提炼,执行前请确认
git clone https://github.com/neosun100/DeepSeek-OCR-WebUI.git && cd DeepSeek-OCR-WebUI
docker compose up -d
硬件要求按 README 说明

NVIDIA GPU 可用 vLLM 或 transformers(T4/V100 走 fp16),Apple Silicon 走 MPS,也支持纯 CPU 推理;README 未给出具体显存要求。

纯 CPU 可运行支持 Apple Silicon需要 CUDA多卡 / 张量并行有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

自托管 DeepSeek-OCR-2 的 WebUI 与 API,把文档和图片转成带版面框的 Markdown

自托管的 DeepSeek-OCR-2 网页界面与 API,支持在 NVIDIA、Apple Silicon 和 CPU 上通过 vLLM 或 transformers 运行,提供逐页质量检查、PDF 流式处理和 MCP 接入。

核心能力

  • vLLM 连续批处理或 transformers 两种推理引擎可选
  • 逐页返回 ok 与 warnings 标记重复、截断、坐标异常
  • PDF 在 Web Worker 中解析一次并流式逐页返回结果
  • 支持 document、ocr、free、figure、describe、find 等多种识别模式
  • 提供 MCP 服务,可接入 Claude、Cursor 等客户端

容易踩的坑

首次启动需下载约 6.8 GB 权重,离线环境要先用 huggingface-cli 下载并设置 DSOCR_MODEL_PATH;模型加载完成前 /health 一直返回 503,别当成服务挂了;服务默认无登录且只监听 127.0.0.1,跨机访问需 SSH 隧道或带认证的反向代理。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

1 个版本v5.0.0
  1. v5.0.0v5.0.0 · DeepSeek-OCR-2 on every GPU you own — vLLM or transformers on CUDA, native Apple Silicon, and CPU, with per-page quality checks you can trust.最新正式版2026-11-07 · 2026-11-07 02:51 UTC

    DeepSeek-OCR-2 on every GPU you own — vLLM or transformers on CUDA, native Apple Silicon, and CPU, with per-page quality checks you can trust.

    Added

    • New backend package server/deepseek_ocr_webui (managed by uv, pyproject.toml is the single dependency source) with two engines:
      • transformers engine that runs on CUDA, Apple Silicon (MPS) and CPU. Measured with scripts/bench.py: M5 Max (MPS, bf16) 8.1 pages/min; T4 (fp16, eager) 7.8; A10G 9.5 (final verification run).
      • vLLM engine (continuous batching) that manages a local vllm serve or load-balances across several external ones (DSOCR_VLLM_URLS, least in-flight). Measured on an A10G: 46.4 pages/min at concurrency 1 and 132.1 at concurrency 16 (transformers: 9.5); both engines agree on the same pages with mean character recall 0.985. Ships with the workarounds vLLM 0.33 needs for OCR-2 (legacy V1 runner for the NGram logits processor, TRITON_ALLOW_NON_CONSTEXPR_GLOBALS=1). See docs/BENCHMARKS.md.
    • Per-page output checks in every response (ok, warnings[], finish_reason): tail repetition, repetition cut, truncation, coordinate loops, invalid boxes, empty and near-empty output. Repeated tails are stopped while generating (HF StoppingCriteria; vLLM stream closed on detection) and trimmed from raw_text.
    • Readiness you can trust: /health answers 503 until the model is loaded and a synthetic warm-up page is read back with ≥ 0.85 character recall.
    • POST /api/ocr/pdf — page-by-page NDJSON stream with heartbeats, per-page errors, and pages=1-3,5 ranges.
    • GET /api/capabilities — modes, limits, engine, and known weak spots (with public benchmark evidence).
    • Swagger is the documentation: every endpoint has a summary and description; docs/openapi.json is generated and checked in CI.
    • MCP server on mcp 2.x (deepseek-ocr-webui-mcp): ocr_image, ocr_pdf, health.
    • Docker: one Dockerfile with two targets — vllm (default) and hf; weights are downloaded to a volume on first start instead of being baked in.
    • GPU verification scripts (scripts/gpu-verify/), a throughput/quality benchmark (scripts/bench.py), generated README screenshots and badges.
    • Frontend: model-loading / model-error health states, page-level output warnings, finish reason in the raw-result panel, engine/device/dtype in the status tooltip (en / zh-CN / zh-TW / ja-JP).

    Changed

    • find mode is now an honest block-level search: DeepSeek-OCR-2 has no native "Locate" prompt (12/12 trials fell back to document parsing), so the server parses the layout and returns the blocks that contain your term. find_term is now required.
    • PDF pipeline in the browser: each PDF is parsed once in a worker and its bytes are transferred once; pages are stored as JPEG at 2304 px on the long edge (aligned with the model's 3×768 tiling). A 200-page PDF used 661.9 MB of IndexedDB before and 77.7 MB now; main-thread copies went from 491.7 MB to 0.
    • Upload limit for PDFs raised from 100 MB to 500 MB (one constant, enforced by a test) (#51).
    • Model weights pinned to revision aaa02f3 (#23); Linux torch comes from the CUDA 12.6 index (works with drivers ≥ 525 via CUDA minor-version compatibility instead of requiring ≥ 580) (#46).
    • The service binds to 127.0.0.1 by default; CORS is off unless you allow-list origins; X-Forwarded-For is only trusted with DSOCR_TRUST_PROXY=true; compose publishes on 127.0.0.1 only.
    • Errors share one shape: {"detail": {"error", "message", "actual", "limit"}, "request_id"}; the frontend shows the backend message instead of "500 Internal Server Error" (#39).
    • Logging with loguru and a request id on every line and response (X-Request-ID).
    • Development no longer sends documents to a third-party server: the dev API defaults to the same origin with a Vite proxy.

    Fixed

    • Apple Silicon and CPU inference: upstream infer() hard-codes .cuda() and bf16, so

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    DeepSeek-OCR-WebUI

    Self-hosted DeepSeek-OCR-2 for documents and images — vLLM or transformers on NVIDIA, native Apple Silicon, and CPU, with per-page quality checks.

    🌐 English · 简体中文 · 繁體中文 · 日本語

    version model runs on backend tests frontend tests e2e mutants python license

    What's new · Screenshots · Quick start · API · Architecture · Performance · Development


    ✨ What's new in v5.0

    v5 is a ground-up renovation of the backend and a hardening of the Vue 3 frontend. Every number below was measured; see CHANGELOG for the full list.

    v4.1v5.0
    Apple Silicon / CPUloads, then fails on first page (upstream code hard-codes .cuda())works — device-agnostic inference, M5 Max 8.1 pages/min
    NVIDIA enginestransformers only, one page at a timevLLM (continuous batching, 132 pages/min on an A10G) or transformers
    Old GPUs (T4 / V100, no bf16)BFloat16 vs float crash (#30 #40)fp16 + eager, verified on T4
    Driver requirementCUDA 13 image ⇒ driver ≥ 580 (#46)CUDA 12 builds
    Docker image13 GB with weights inside (#38)weights in a volume; hf image ≈ 4.0 GB unpacked
    Output qualityHTTP 200 = "success"per-page ok + warnings (repetition, truncation, coordinate loops, empty)
    Readiness/health 200 before the model can read503 until a warm-up page is read back correctly
    Large PDFs200 pages ⇒ 662 MB in IndexedDB, freezes (#31 #50)78 MB, parsed once in a worker; 500 MB upload limit (#51)
    Markdown exportdropped multi-box blocks (#49)100 % character recall on all samples
    Securitybinds 0.0.0.0, CORS * with credentials, XSS via v-html127.0.0.1 by default, CORS allow-list, DOMPurify
    Tests0 backend tests; 123 frontend tests failing on Node 26backend, frontend, e2e and mutation suites — live counts in the badges above

    📸 Screenshots

    All screenshots are generated by frontend/scripts/screenshots.mjs against a real backend (DeepSeek-OCR-2 on an Apple M5 Max). The demo document is generated by scripts/make_demo_docs.py.

    Document → Markdown with layout boxes and a rendered table

    Batch queueSecond PDF page
    Recognition modesService status (engine · device · precision)
    Swagger UIMobile (简体中文)

    🚀 Quick start

    🔒 The service has no login. Every command below listens on 127.0.0.1 only. To use it from another machine, use an SSH tunnel (ssh -L 8001:127.0.0.1:8001 you@gpu-host) or put it behind a reverse proxy that authenticates users — do not publish the port on all interfaces.

    Option 1 — Docker, NVIDIA GPU (vLLM engine, recommended)

    git clone https://github.com/neosun100/DeepSeek-OCR-WebUI.git && cd DeepSeek-OCR-WebUI
    docker compose up -d                  # builds the image; first start downloads ~6.8 GB of weights
    open http://127.0.0.1:8001            # UI ·  API docs: http://127.0.0.1:8001/docs
    

    Prebuilt images (published by CI on each release):

    docker run -d --gpus all --shm-size 8g -p 127.0.0.1:8001:8001 -v dsocr-models:/models \
      ghcr.io/neosun100/deepseek-ocr-webui:5.0.0          # vLLM engine
    docker run -d --gpus all --shm-size 8g -p 127.0.0.1:8001:8001 -v dsocr-models:/models \
      ghcr.io/neosun100/deepseek-ocr-webui:5.0.0-hf       # transformers engine (smaller, also for T4/V100)
    

    /health returns 503 while the model is loading (first start: download + load + warm-up) and 200 once a warm-up page has been read back correctly. docker ps shows healthy at that point.

    Option 2 — macOS (Apple Silicon) or Linux without Docker

    Requires uv and Node.js ≥ 20.

    git clone https://github.com/neosun100/DeepSeek-OCR-WebUI.git && cd DeepSeek-OCR-WebUI
    (cd frontend && npm ci && npm run build)       # the backend serves the built UI
    uv sync --extra hf                             # transformers + torch (MPS on macOS, CUDA 12.6 wheels on Linux)
    uv run deepseek-ocr-webui serve                # http://127.0.0.1:8001
    

    On Linux with vLLM instead: uv sync --extra vllm && uv run deepseek-ocr-webui serve --engine vllm.

    Offline / air-gapped

    Download once (huggingface-cli download deepseek-ai/DeepSeek-OCR-2 --revision aaa02f3811945a91062062994c5c4a3f4c0af2b0 --local-dir /models/ocr2), then start with DSOCR_MODEL_PATH=/models/ocr2.

    Configuration

    All settings are environment variables with the DSOCR_ prefix (config.py is the single source). The most useful ones:

    VariableDefaultMeaning
    DSOCR_HOST / DSOCR_PORT127.0.0.1 / 8001Listen address. The Docker image sets 0.0.0.0 inside the container; the published port is the boundary.
    DSOCR_ENGINEautoauto · vllm · hf (auto = vLLM if installed and CUDA is available)
    DSOCR_DEVICEautotransformers engine: cuda · mps · cpu
    DSOCR_REQUIRE_GPUfalsefail fast instead of silently falling back to CPU
    DSOCR_VLLM_URLS—comma-separated external vLLM servers (multi-GPU / multi-host, least in-flight)
    DSOCR_MODEL_PATH—local weights directory (no network)
    DSOCR_MAX_UPLOAD_MB · DSOCR_MAX_PDF_PAGES200 · 500upload limits
    DSOCR_MAX_QUEUE · DSOCR_MAX_PER_CLIENT · DSOCR_MAX_PER_IP32 · 1 · 4admission control (HTTP 429 when exceeded)
    DSOCR_CORS_ORIGINS—allow-listed origins (off by default)
    DSOCR_TRUST_PROXYfalsetrust X-Forwarded-For (only behind your own proxy)

    v4 variable names (PORT, LOCAL_MODEL_PATH, FORCE_BACKEND) still work.


    🧩 Recognition modes

    ModePromptOutput
    document (default)`<grounding
    ocr`<grounding
    freeFree OCR.plain text, fastest
    figureParse the figure.chart / figure description
    describeDescribe this image in detail.natural-language description
    finddocument parsing + server-side block searchthe layout blocks that contain find_term (block-level, see below)
    freeformyour promptanything

    About find: DeepSeek-OCR-2 has no native "locate this phrase" prompt — 12 out of 12 trials with the v1-style Locate <|ref|>…<|/ref|> prompt fell back to document parsing. v5 therefore parses the layout and returns the boxes of the blocks whose text contains your term. Precision is per block, not per word.


    🔌 API

    Interactive docs: /docs (Swagger) and /redoc. The OpenAPI spec is checked in at docs/openapi.json and kept in sync by a test.

    # One image
    curl -s -F [email protected] -F prompt_type=document http://127.0.0.1:8001/api/ocr | jq '{ok, warnings, text}'
    
    # A PDF, streamed page by page (NDJSON: start → page* → done)
    curl -sN -F [email protected] -F pages=1-3 http://127.0.0.1:8001/api/ocr/pdf
    
    # Status / capabilities
    curl -s http://127.0.0.1:8001/health | jq '{status, ready, engine, warmup}'
    curl -s http://127.0.0.1:8001/api/capabilities | jq '.limits, .known_weak'
    

    Each page result contains text, raw_text (with <|ref|>/<|det|> grounding tags), boxes (pixel coordinates), finish_reason, ok and warnings:

    WarningMeaningFails ok?
    repetition / repetition_cutthe model looped at the end; the loop is trimmed (and stopped while generating)yes
    truncatedhit the generation limityes
    coordinate_loopbox coordinates degenerated ([23,268,268,268…])yes
    empty_outputnothing was generatedyes
    near_empty≤ 20 letters/digits — usually a blank pageno
    bbox_invalidsome boxes were out of range and dropped; text keptno
    find_no_matchfind mode: no block contains the termno

    Errors always look like {"success": false, "detail": {"error", "message", "actual", "limit"}, "request_id"}. The v4 endpoints /ocr, /pdf-to-images and the v4 /health fields keep working.

    MCP (Claude, Cursor, …)

    { "mcpServers": { "deepseek-ocr": {
        "command": "uv", "args": ["run", "--directory", "/path/to/DeepSeek-OCR-WebUI", "--extra", "mcp", "deepseek-ocr-webui-mcp"],
        "env": { "DSOCR_BASE": "http://127.0.0.1:8001" } } } }
    

    Tools: ocr_image(path | image_base64, mode, find_term, custom_prompt, grounding), ocr_pdf(path, mode, pages), health().


    🏗 Architecture

    • Browser — PDFs are rendered in a Web Worker (each file parsed once, bytes transferred once), pages are kept in IndexedDB so long jobs survive reloads, and Markdown / DOCX / PDF are generated locally.
    • Service (server/deepseek_ocr_webui) — admission control, upload sniffing and pixel limits, the engine, per-page judges, grounding parsing.
    • Engines — vllm streams from vllm serve and closes the stream as soon as a repetition loop is detected; hf re-implements the upstream preprocessing so tensors follow the model's real device and dtype.

    📊 Performance

    Measured with scripts/bench.py on dedicated machines (12 pages: sample images, rendered PDF pages, synthetic pages with known text; document mode). Full record, memory, agreement and reproduction steps: docs/BENCHMARKS.md.

    HardwareEngineConcurrencyPages / minp50 latencyKnown-text recall
    NVIDIA A10G 24 GBvLLM 0.33146.40.81 s1.00
    NVIDIA A10G 24 GBvLLM 0.3316132.15.88 s1.00
    NVIDIA A10G 24 GBtransformers (bf16)19.52.05 s1.00
    NVIDIA T4 16 GBtransformers (fp16)17.82.6 s1.00
    Apple M5 Maxtransformers (MPS, bf16)18.13.3 s1.00

    vLLM and transformers agree on the same pages with a mean bidirectional character recall of 0.985 (min 0.849 on one dense page). transformers handles one page at a time, so extra concurrency only queues.


    🛡 Limits and known weak spots

    From GET /api/capabilities (known_weak lists the public benchmark evidence): DeepSeek-OCR-2 is weaker on photos with perspective / skew, handwriting, and low-resource languages. Defaults: 200 MB per upload, 80 MP per image, 500 PDF pages per request (rendered at 144 DPI), 8192 tokens per page.


    🧪 Development

    uv sync --extra hf --extra mcp --group dev
    uv run pytest -n auto tests/unit tests/regression        # FakeEngine, no GPU needed
    DSOCR_REAL_MODEL=1 uv run pytest tests/e2e/test_real_model.py   # real weights on CUDA / MPS
    DSOCR_BASE=http://127.0.0.1:8001 uv run pytest tests/e2e         # against a running server
    cd frontend && npm ci && npx vitest run && npx playwright test --project=chromium
    
    • Mutation testing — tests/mutations.ini lists deliberate bugs (judges, admission, security defaults, readiness, engines, MCP); each must turn the suite red (run with touchstone mutate.py, command in the file header).
    • GPU verification — scripts/gpu-verify/ builds both images on a GPU machine, runs the e2e suite against each, benchmarks them and compares their outputs.
    • Generated assets — node frontend/scripts/screenshots.mjs, uv run python scripts/badges.py [--check], deepseek-ocr-webui openapi > docs/openapi.json.
    • CI: GitHub Actions (backend · frontend · e2e · image build) and GitLab CI.

    Upgrading from v4

    • Same port (8001), same UI, /ocr and /health stay compatible. The Docker image no longer contains the weights: mount a volume at /models.
    • Container listens on 0.0.0.0 inside, compose publishes on 127.0.0.1 only — change the publish deliberately if you need LAN access, and add authentication in front of it.
    • find now requires find_term and returns block-level matches.

    👥 Contributors

    The Vue 3 frontend — including its E2E suite — was contributed by @cloudman6 and is the foundation v5 builds on.

    📈 Star history

    Star History Chart

    🤝 Contributing

    Issues and pull requests are welcome. Please run the backend and frontend test suites before opening a PR; CI runs them on every pull request.

    📄 License

    MIT. DeepSeek-OCR-2 weights are distributed by DeepSeek under their own license.

    🙏 Acknowledgments

    DeepSeek-AI for DeepSeek-OCR-2 · vLLM for the native OCR-2 support and NGram logits processor · @cloudman6 and everyone who reported issues.