Repository navigation
feat(webapp): one-click "try the bundled sample" for the local demo #17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and push PDF-QA image (GHCR) | |
| # Builds the public PDF Q&A extraction images and pushes them to the GitHub | |
| # Container Registry (ghcr.io) as a build matrix: | |
| # * :latest - CPU-only, slim (~2GB) - the default for most users | |
| # * :latest-gpu - CUDA torch + onnxruntime-gpu (~8GB) - run with `--gpus all` | |
| # to GPU-accelerate BOTH unstructured model families: layout | |
| # detection (YOLOX/detectron2_onnx on onnxruntime-gpu) and the | |
| # table-structure model (Table Transformer, PyTorch). Tesseract | |
| # OCR stays CPU. | |
| # The same images feed both Azure ML and SageMaker, so learners can `docker | |
| # pull` without a cloud account. | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - "pdf_qa_extraction/**" | |
| - ".github/workflows/build-and-push.yml" | |
| tags: ["v*"] | |
| pull_request: | |
| paths: | |
| - "pdf_qa_extraction/**" | |
| workflow_dispatch: | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }}/pdf-qa-extractor | |
| # A new push to the same ref cancels an in-flight build (saves CI minutes). | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build-and-push: | |
| name: build-and-push (${{ matrix.variant }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - variant: cpu | |
| torch_index: https://download.pytorch.org/whl/cpu | |
| onnxruntime_package: onnxruntime | |
| tag_suffix: "" | |
| - variant: gpu | |
| torch_index: https://download.pytorch.org/whl/cu124 | |
| onnxruntime_package: onnxruntime-gpu | |
| tag_suffix: "-gpu" | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| # The build still juggles a builder stage (torch/onnxruntime + build | |
| # tools) and the runtime image at once, plus the gha cache export and the | |
| # smoke-test pull. GitHub-hosted runners only ship ~14GB free on /, so | |
| # reclaim the large preinstalled toolchains we don't use up front. | |
| - name: Free up runner disk space | |
| run: | | |
| echo "::group::Disk before cleanup"; df -h /; echo "::endgroup::" | |
| sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/.ghcup \ | |
| /usr/local/lib/android /usr/local/share/boost \ | |
| /opt/hostedtoolcache/CodeQL "${AGENT_TOOLSDIRECTORY:-}" || true | |
| sudo docker image prune --all --force || true | |
| echo "::group::Disk after cleanup"; df -h /; echo "::endgroup::" | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GHCR | |
| # Skip on PRs from forks (no package write permission). | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Docker metadata (tags & labels) | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| # The GPU variant gets a `-gpu` suffix on every tag (incl. latest); | |
| # the CPU variant keeps the bare tags. e.g. :latest / :latest-gpu. | |
| flavor: | | |
| suffix=${{ matrix.tag_suffix }},onlatest=true | |
| tags: | | |
| type=ref,event=branch | |
| type=ref,event=tag | |
| type=sha,format=short | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Build and push | |
| id: build | |
| # Validate the CPU build on PRs; the ~8GB GPU variant only builds on | |
| # pushes/tags to keep PR CI fast. | |
| if: github.event_name != 'pull_request' || matrix.variant == 'cpu' | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: pdf_qa_extraction | |
| file: pdf_qa_extraction/Dockerfile | |
| # Push on branch/tag builds; PRs only validate the build. | |
| push: ${{ github.event_name != 'pull_request' }} | |
| # cpu -> whl/cpu + onnxruntime (CPU, ~2GB); gpu -> whl/cu124 CUDA torch | |
| # + onnxruntime-gpu (layout & table on GPU, ~8GB). | |
| build-args: | | |
| TORCH_INDEX_URL=${{ matrix.torch_index }} | |
| ONNXRUNTIME_PACKAGE=${{ matrix.onnxruntime_package }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| # Separate cache scope per variant so cpu/gpu don't evict each other. | |
| cache-from: type=gha,scope=${{ matrix.variant }} | |
| # mode=min caches only the final-stage layers. mode=max would also | |
| # cache the fat builder stage, doubling the gha cache blobs written to | |
| # the runner's disk during export and risking "no space left". | |
| cache-to: type=gha,mode=min,scope=${{ matrix.variant }} | |
| provenance: false | |
| - name: Smoke test & image size | |
| # Only on pushed builds (the image must exist in the registry to pull). | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| # env.IMAGE_NAME embeds github.repository, whose owner/name can carry | |
| # uppercase letters; docker refs must be lowercase (metadata-action | |
| # lowercases the pushed tags, but this ref is built by hand). | |
| IMG="$(echo '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}' | tr '[:upper:]' '[:lower:]')" | |
| REF="${IMG}@${{ steps.build.outputs.digest }}" | |
| # Reclaim the BuildKit build cache (both stages' layers) before | |
| # pulling the finished image back onto the runner. | |
| docker buildx prune --all --force || true | |
| echo "::group::Disk before pull"; df -h /; echo "::endgroup::" | |
| echo "Pulling $REF" | |
| docker pull -q "$REF" | |
| echo "::group::Image size (${{ matrix.variant }})" | |
| docker image inspect "$REF" --format '{{.Size}}' | \ | |
| awk '{printf "Uncompressed image size: %.2f GiB (%d bytes)\n", $1/1073741824, $1}' | |
| echo "::endgroup::" | |
| echo "::group::Import smoke test (${{ matrix.variant }})" | |
| docker run --rm "$REF" python -c "import importlib.metadata as md, pdf_qa, torch, cv2, unstructured, onnxruntime as ort; names={d.metadata['Name'].lower() for d in md.distributions()}; assert '${{ matrix.onnxruntime_package }}' in names, 'expected ${{ matrix.onnxruntime_package }}, found '+str(sorted(n for n in names if 'onnx' in n)); assert pdf_qa.list_personas()[0]=='professor'; assert 'feynman' in pdf_qa.list_personas(), 'personas.yaml ledger not packaged in image'; print('imports OK - ${{ matrix.variant }} | pdf_qa', pdf_qa.__version__, '| personas', pdf_qa.list_personas(), '| torch', torch.__version__, '| onnxruntime', ort.__version__, '(${{ matrix.onnxruntime_package }}) providers:', ort.get_available_providers())" | |
| echo "::endgroup::" | |
| echo "::group::Device probe (${{ matrix.variant }})" | |
| docker run --rm "$REF" python -c "import pdf_qa; print(pdf_qa.probe_device().summary())" | |
| echo "::endgroup::" | |
| echo "::group::Web app smoke (${{ matrix.variant }})" | |
| # Prove the single-node demo web app is shipped & importable in-image | |
| # (fastapi/uvicorn present + webapp package COPYed). TestClient needs | |
| # httpx (a test-only dep not in the image), so call the payload helpers | |
| # directly instead of issuing HTTP requests. | |
| docker run --rm "$REF" python -c "from webapp.app import _persona_payload, _device_payload, _meta_payload; pl=_persona_payload(); keys=[p['key'] for p in pl['personas']]; assert keys[0]=='professor', keys; assert 'memoirist' in keys and 'feynman' in keys, keys; assert len(keys)==7, keys; assert all(p['method_summary'] for p in pl['personas']); dev=_device_payload(); assert 'gpu_ready' in dev and dev['summary']; meta=_meta_payload(); assert meta['sample_available'], 'sample PDF (data/fsi_data.pdf) not packaged in image'; import fastapi, uvicorn; print('webapp OK - ${{ matrix.variant }} | fastapi', fastapi.__version__, '| personas', keys, '| sample', meta['sample_name'], '| gpu_ready', dev['gpu_ready'])" | |
| echo "::endgroup::" |