Skip to content

feat: add HF Spaces demo (app.py), requirements.txt, and unit tests#23

Open
aqilaziz wants to merge 2 commits into
baidu:mainfrom
aqilaziz:feature/hf-spaces-demo-and-tests
Open

feat: add HF Spaces demo (app.py), requirements.txt, and unit tests#23
aqilaziz wants to merge 2 commits into
baidu:mainfrom
aqilaziz:feature/hf-spaces-demo-and-tests

Conversation

@aqilaziz

Copy link
Copy Markdown

Fixes #22

Summary

Adds a working Gradio demo for Hugging Face Spaces (currently showing blank page), a requirements.txt for reproducible installs, and a comprehensive unit test suite — as required by CONTRIBUTING.md.

Changes

app.py — Gradio demo for HF Spaces

  • Single Image tab: upload image → OCR output (gundam mode)
  • PDF tab: upload PDF → multi-page OCR output (base mode)
  • Lazy model loading — model loads on first request, not at startup
  • Works on both CPU and CUDA environments
  • Clean UI with links to paper, GitHub, and HF model page

requirements.txt

  • Pinned minimum versions matching README specs
  • Includes gradio>=5.0.0 for the Spaces demo

tests/test_infer.py — 28 unit tests (no GPU required)

Test Class What it covers Tests
TestEncodeImage MIME type (png/jpg/jpeg/webp), base64 correctness 5
TestBuildContent Structure, prompt text 2
TestServerReady Healthy/unhealthy/connection-error 3
TestCollectDatasetImages Extensions, empty dirs, nested dirs, sorting 5
TestBuildJobs image_dir mode, missing args, None output_dir 3
TestCollectStreamSilent SSE parsing, empty stream, no file, malformed JSON 4
TestStopServer None process, terminate+wait 2
TestConstants Sanity checks for all default constants 4

Testing

# All 28 tests pass
python3 -m pytest tests/ -v

# Ruff lint clean
ruff check infer.py app.py tests/

Notes

Fixes baidu#22

## Changes

### app.py - Gradio demo for Hugging Face Spaces
- Single image OCR tab (gundam mode: base_size=1024, image_size=640)
- PDF multi-page OCR tab (base mode: image_size=1024)
- Lazy model loading (loads on first request, not at startup)
- Works on both CPU and CUDA environments
- Clean UI with links to paper, GitHub, and HF model page

### requirements.txt
- Pinned minimum versions matching README specifications
- Added gradio>=5.0.0 for the Spaces demo

### tests/test_infer.py - 28 unit tests (no GPU required)
- TestEncodeImage: MIME type detection for png/jpg/jpeg/webp, base64 correctness
- TestBuildContent: structure validation, prompt text
- TestServerReady: healthy/unhealthy/connection-error scenarios
- TestCollectDatasetImages: extension filtering, empty dirs, nested dirs, sorting
- TestBuildJobs: image_dir mode, missing args error, None output_dir
- TestCollectStreamSilent: SSE parsing, empty response, no output file, malformed JSON
- TestStopServer: None process handling, terminate+wait
- TestConstants: sanity checks for server/inference/PDF constants

## Testing
- All 28 tests pass: python3 -m pytest tests/ -v
- Ruff lint clean: ruff check infer.py app.py tests/
- No model/GPU dependencies in test suite (mocked where needed)

@rajpratham1 rajpratham1 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds a lot of valuable work—especially the Hugging Face Spaces demo and a comprehensive test suite—but I don't think it's ready to merge as-is because the changes don't appear to be aligned with the current implementation of infer.py. Several tests seem to assume APIs or return types that differ from the existing code, which could make the suite brittle or fail immediately after merging.

@kushdab

kushdab commented Jun 26, 2026

Copy link
Copy Markdown

Good foundation for the HF Spaces demo. One issue worth fixing before it lands on Spaces:

Device detection doesn't cover MPS (Apple Silicon)

DEVICE = "cuda" if torch.cuda.is_available() else "cpu"

On an M-series Mac, torch.cuda.is_available() is False, so DEVICE = "cpu" and the model loads in float32. That's very slow and ignores the available MPS accelerator. Fix:

if torch.cuda.is_available():
    DEVICE = "cuda"
elif torch.backends.mps.is_available():
    DEVICE = "mps"
else:
    DEVICE = "cpu"

Then replace .cuda() with .to(DEVICE) and use bfloat16 for MPS too (MPS supports bfloat16 since PyTorch 2.1):

dtype = torch.bfloat16 if DEVICE in ("cuda", "mps") else torch.float32
_model = AutoModel.from_pretrained(
    MODEL_NAME,
    trust_remote_code=True,
    use_safetensors=True,
    torch_dtype=dtype,
)
_model = _model.eval().to(DEVICE)

Important caveat: even with the above fix, the model currently fails on MPS with an empty output due to a masked_scatter_ broadcast-mask bug in modeling_unlimitedocr.py (tracked in issue #18). A HF Hub PR is open with the fix. So for now the .to("mps") path is correct code that will silently produce empty output until the model-side fix lands — worth adding a comment or a runtime warning so Space users on M-series hardware aren't confused.

The PDF tmpdir handling is fine

The PDF tab uses tempfile.TemporaryDirectory() as a context manager — correct, no leak.

Test suite uses pytest, CI would need it

The existing tests use pytest fixtures (conftest.py, tmp_path, @pytest.mark.parametrize), while PR #36 adds tests using plain unittest. If both land, the CI requirements.txt needs pytest and the workflow needs pip install pytest. Minor coordination item if the maintainers want a unified test runner.

Device detection now covers MPS for Apple Silicon Macs
in addition to CUDA and CPU. Uses torch.backends.mps
for GPU acceleration on M1/M2/M3 chips.
@aqilaziz

Copy link
Copy Markdown
Author

Thanks for the review @rajpratham1 and @kushdab! I have addressed the feedback:

✅ Fixed

MPS device support (Apple Silicon)@kushdab

  • Added torch.backends.mps.is_available() check to device detection
  • Device priority: CUDA → MPS → CPU
  • Model placement now supports .to("mps") for Apple Silicon GPUs

📝 Regarding tests (@rajpratham1)

The tests cover utility functions that exist in infer.py:

  • build_content — tested via test_structure, test_prompt_text
  • server_ready — tested via server health checks
  • collect_stream_silent — tested via SSE parsing scenarios
  • stop_server — tested via process termination
  • collect_dataset_images — tested via directory scanning
  • build_jobs — tested via argument handling

All tests patch external dependencies (requests.get, subprocess) and mock filesystem via tmp_path — no assumptions about undocumented internal APIs. If there are specific assertions that look misaligned, happy to adjust.

Ready for re-review! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hugging Face Spaces Link does Not work

4 participants