Spaces:
Runtime error
Runtime error
Saravanakumar R Claude Sonnet 4.6 commited on
Commit ·
24fe4dc
1
Parent(s): 31ce85c
Add HF Spaces deployment openspec change
Browse filesDocuments the model-hub upload script, Dockerfile build flow, and
README deployment section (code already committed prior).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- openspec/changes/hf-spaces-deployment/.openspec.yaml +2 -0
- openspec/changes/hf-spaces-deployment/design.md +72 -0
- openspec/changes/hf-spaces-deployment/proposal.md +32 -0
- openspec/changes/hf-spaces-deployment/specs/hf-spaces-deployment/spec.md +50 -0
- openspec/changes/hf-spaces-deployment/specs/model-hub-upload/spec.md +23 -0
- openspec/changes/hf-spaces-deployment/tasks.md +15 -0
openspec/changes/hf-spaces-deployment/.openspec.yaml
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
schema: spec-driven
|
| 2 |
+
created: 2026-06-22
|
openspec/changes/hf-spaces-deployment/design.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Context
|
| 2 |
+
|
| 3 |
+
G.U.I.D.E. currently runs as two processes launched by `start.py`: FastAPI on `:8000` (model inference + agent API) and Gradio on `:7860` (UI). The Gradio frontend calls `http://localhost:8000` — an internal URL that works within any single container.
|
| 4 |
+
|
| 5 |
+
HF Spaces Dockerfile Spaces expose exactly one public port (`:7860`) but allow arbitrary internal processes. This maps cleanly onto the existing architecture without modification.
|
| 6 |
+
|
| 7 |
+
Three trained model artifacts exist locally:
|
| 8 |
+
- `models/domain_classifier/` — DistilBERT fine-tuned on CFPB data (~256MB safetensors)
|
| 9 |
+
- `models/evidence_ner/` — DistilBERT NER, root-level files only (~270MB safetensors); `checkpoint-*` subdirs are training artifacts (~3.2GB total, not needed at inference)
|
| 10 |
+
- `models/next_action/model.pt` — MLP state dict (~24KB)
|
| 11 |
+
|
| 12 |
+
## Goals / Non-Goals
|
| 13 |
+
|
| 14 |
+
**Goals:**
|
| 15 |
+
- Public HF Spaces demo accessible via browser with no local setup
|
| 16 |
+
- Full pipeline (DomainClassifier + EvidenceNER + NextActionPredictor) using trained weights
|
| 17 |
+
- One-time upload script for model weights with checkpoint filtering
|
| 18 |
+
- Two-server design preserved exactly as-is
|
| 19 |
+
|
| 20 |
+
**Non-Goals:**
|
| 21 |
+
- Modifying any `predict.py`, `start.py`, or `ui/app.py` files
|
| 22 |
+
- Single-process refactor
|
| 23 |
+
- CI/CD automation
|
| 24 |
+
- User-supplied API key input in the UI
|
| 25 |
+
|
| 26 |
+
## Decisions
|
| 27 |
+
|
| 28 |
+
### Decision 1: Dockerfile Space over SDK Space
|
| 29 |
+
|
| 30 |
+
HF SDK Spaces require `app.py` as the entrypoint and expect a single Gradio `demo.launch()` call. Wiring FastAPI as a background thread from inside `app.py` is fragile and breaks the existing process model.
|
| 31 |
+
|
| 32 |
+
Dockerfile Spaces let us use `CMD ["python", "start.py", "--no-train"]` exactly as-is.
|
| 33 |
+
|
| 34 |
+
**Alternatives considered:** SDK Space with threading — rejected because it requires changes to `ui/app.py` and creates a coupled process model that's harder to debug.
|
| 35 |
+
|
| 36 |
+
### Decision 2: Download weights at Docker build time, not runtime
|
| 37 |
+
|
| 38 |
+
Cold-start download (~800MB) would block HF Spaces visitors for 2-3 minutes. Baking weights into the image layer via `RUN huggingface-cli download` at build time means the container starts in seconds.
|
| 39 |
+
|
| 40 |
+
**Alternatives considered:** Download at container startup in `start.py` — rejected due to cold-start UX on free-tier HF Spaces.
|
| 41 |
+
|
| 42 |
+
### Decision 3: One HF Model Hub repo, three subdirectories
|
| 43 |
+
|
| 44 |
+
`your-username/guide-models` with `domain_classifier/`, `evidence_ner/`, `next_action/` subdirs. A single `huggingface-cli download` in the Dockerfile fetches everything into `models/` — matching the existing local path structure that all `predict.py` files already expect.
|
| 45 |
+
|
| 46 |
+
**Alternatives considered:** Three separate HF repos — cleaner HF Hub browsing but requires three download commands and complicates the upload script.
|
| 47 |
+
|
| 48 |
+
### Decision 4: Filter checkpoint-* at upload time via script
|
| 49 |
+
|
| 50 |
+
`models/evidence_ner/checkpoint-*/` directories are intermediate training snapshots (~3.2GB). Only the root-level `models/evidence_ner/` files are needed for inference. The upload script explicitly skips any path component matching `checkpoint-*` and `.DS_Store`.
|
| 51 |
+
|
| 52 |
+
## Risks / Trade-offs
|
| 53 |
+
|
| 54 |
+
- **[Risk] HF Spaces free-tier memory limit (~16GB RAM)** → Mitigation: spaCy `en_core_web_lg` (~750MB) + two DistilBERT models (~540MB each) + ViT + Tesseract fits within 16GB. Monitor via HF Spaces logs on first deploy.
|
| 55 |
+
- **[Risk] Build time timeout on HF Spaces** → Mitigation: `spacy download` + Hub download is ~1.5GB at build time; HF Spaces build timeout is 30 min, well within budget.
|
| 56 |
+
- **[Risk] ANTHROPIC_API_KEY exposure** → Mitigation: Key is set only via HF Spaces Secrets UI, never in Dockerfile or committed code. `ENV ANTHROPIC_API_KEY=""` in Dockerfile is a placeholder only.
|
| 57 |
+
- **[Risk] HF Hub model repo must be public for free-tier download** → Mitigation: Models contain no PII (trained on synthetic data + CFPB public dataset); public repo is acceptable for academic use.
|
| 58 |
+
|
| 59 |
+
## Migration Plan
|
| 60 |
+
|
| 61 |
+
1. Run `scripts/upload_models_to_hub.py` locally (one-time, requires `huggingface-cli login`)
|
| 62 |
+
2. Create HF Space (Dockerfile type) via HF UI or CLI
|
| 63 |
+
3. Push repo code to HF Space git remote
|
| 64 |
+
4. Set `ANTHROPIC_API_KEY` in HF Space Secrets UI
|
| 65 |
+
5. HF builds the Docker image — downloads weights, installs deps
|
| 66 |
+
6. Space goes live at `https://huggingface.co/spaces/<username>/guide`
|
| 67 |
+
|
| 68 |
+
Rollback: Delete the HF Space. No changes to the local codebase.
|
| 69 |
+
|
| 70 |
+
## Open Questions
|
| 71 |
+
|
| 72 |
+
- HF username to use for the model repo (affects the `huggingface-cli download` line in Dockerfile) — parameterise via a build ARG or hardcode after account is confirmed
|
openspec/changes/hf-spaces-deployment/proposal.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Why
|
| 2 |
+
|
| 3 |
+
G.U.I.D.E. runs locally today with no public access. Deploying to Hugging Face Spaces makes the academic demo accessible to evaluators without requiring a local setup, while keeping the existing two-server architecture (FastAPI + Gradio) intact.
|
| 4 |
+
|
| 5 |
+
## What Changes
|
| 6 |
+
|
| 7 |
+
- New `scripts/upload_models_to_hub.py` — uploads only inference-needed model files to HF Model Hub, filtering out `checkpoint-*` training artifacts
|
| 8 |
+
- New `Dockerfile` — HF Spaces Dockerfile Space entry point; downloads weights from Hub at build time, starts both servers with `--no-train`
|
| 9 |
+
- Updated `README.md` — deployment instructions and HF Spaces architecture note
|
| 10 |
+
|
| 11 |
+
## Capabilities
|
| 12 |
+
|
| 13 |
+
### New Capabilities
|
| 14 |
+
|
| 15 |
+
- `hf-spaces-deployment`: Containerised deployment to Hugging Face Spaces using a Dockerfile Space; FastAPI on `:8000` (internal) and Gradio on `:7860` (public-facing)
|
| 16 |
+
- `model-hub-upload`: One-time script to push inference weights for `domain_classifier`, `evidence_ner`, and `next_action` models to HF Model Hub, filtering training-only artifacts
|
| 17 |
+
|
| 18 |
+
### Modified Capabilities
|
| 19 |
+
|
| 20 |
+
## Non-goals
|
| 21 |
+
|
| 22 |
+
- No changes to predict.py files, start.py, or ui/app.py
|
| 23 |
+
- No single-process refactor of the two-server design
|
| 24 |
+
- No CI/CD pipeline for automated re-deployment
|
| 25 |
+
- No user-facing API key input in the UI (key is a HF Space Secret)
|
| 26 |
+
|
| 27 |
+
## Impact
|
| 28 |
+
|
| 29 |
+
- New files: `Dockerfile`, `scripts/upload_models_to_hub.py`
|
| 30 |
+
- Modified files: `README.md`
|
| 31 |
+
- New dependency at build time: `huggingface_hub` CLI (already in requirements via `huggingface_hub` transitive dep from `transformers`)
|
| 32 |
+
- `ANTHROPIC_API_KEY` must be set as an HF Spaces Secret before the Space goes live
|
openspec/changes/hf-spaces-deployment/specs/hf-spaces-deployment/spec.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## ADDED Requirements
|
| 2 |
+
|
| 3 |
+
### Requirement: Dockerfile Space entrypoint
|
| 4 |
+
The project SHALL include a `Dockerfile` at the repo root that builds a runnable HF Spaces image. The Dockerfile MUST install all system and Python dependencies, download spaCy model and HF Hub weights at build time, expose port 7860, and start both servers via `python start.py --no-train`.
|
| 5 |
+
|
| 6 |
+
#### Scenario: Successful Docker build
|
| 7 |
+
- **WHEN** `docker build` is run on the repo root
|
| 8 |
+
- **THEN** the image builds without error, all dependencies are installed, and model weights are present at `models/`
|
| 9 |
+
|
| 10 |
+
#### Scenario: Container starts both servers
|
| 11 |
+
- **WHEN** the built container is started
|
| 12 |
+
- **THEN** FastAPI is accessible at `localhost:8000` and Gradio at `localhost:7860` within 90 seconds
|
| 13 |
+
|
| 14 |
+
#### Scenario: ANTHROPIC_API_KEY injected at runtime
|
| 15 |
+
- **WHEN** the container starts with `ANTHROPIC_API_KEY` set as an environment variable (e.g., via HF Spaces Secrets)
|
| 16 |
+
- **THEN** the agent uses the injected key and no key is baked into the image
|
| 17 |
+
|
| 18 |
+
#### Scenario: No training at container startup
|
| 19 |
+
- **WHEN** the container starts
|
| 20 |
+
- **THEN** `start.py` runs with `--no-train` and no model training is attempted
|
| 21 |
+
|
| 22 |
+
### Requirement: Tesseract available in container
|
| 23 |
+
The Dockerfile SHALL install `tesseract-ocr` via `apt-get` so document OCR functions correctly.
|
| 24 |
+
|
| 25 |
+
#### Scenario: OCR on uploaded document
|
| 26 |
+
- **WHEN** a user uploads an image or PDF in the Documents tab
|
| 27 |
+
- **THEN** Tesseract processes the file without a missing-binary error
|
| 28 |
+
|
| 29 |
+
---
|
| 30 |
+
|
| 31 |
+
## ADDED Requirements
|
| 32 |
+
|
| 33 |
+
### Requirement: Model weights upload script
|
| 34 |
+
The project SHALL include `scripts/upload_models_to_hub.py` that uploads inference-only model files to a specified HF Model Hub repository. The script MUST skip `checkpoint-*` subdirectories and `.DS_Store` files.
|
| 35 |
+
|
| 36 |
+
#### Scenario: Upload filters checkpoint directories
|
| 37 |
+
- **WHEN** the upload script is run with `models/evidence_ner/` present (including `checkpoint-225/`, `checkpoint-450/`, etc.)
|
| 38 |
+
- **THEN** only root-level files (`config.json`, `model.safetensors`, `tokenizer*.json`, `training_args.bin`) are uploaded; no `checkpoint-*` files are uploaded
|
| 39 |
+
|
| 40 |
+
#### Scenario: Upload three model subdirs
|
| 41 |
+
- **WHEN** the upload script completes successfully
|
| 42 |
+
- **THEN** HF Model Hub repo contains `domain_classifier/`, `evidence_ner/`, and `next_action/` subdirectories with correct files
|
| 43 |
+
|
| 44 |
+
#### Scenario: Missing local model directory
|
| 45 |
+
- **WHEN** the upload script is run but a model directory does not exist locally
|
| 46 |
+
- **THEN** the script logs a clear warning for that model and continues uploading the others
|
| 47 |
+
|
| 48 |
+
#### Scenario: Unauthenticated run
|
| 49 |
+
- **WHEN** the upload script is run without a valid HF token (`huggingface-cli login` not done)
|
| 50 |
+
- **THEN** the script exits with a clear authentication error message before attempting any upload
|
openspec/changes/hf-spaces-deployment/specs/model-hub-upload/spec.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## ADDED Requirements
|
| 2 |
+
|
| 3 |
+
### Requirement: Inference-only file filtering
|
| 4 |
+
The upload script SHALL determine which files are inference-needed by excluding any path under a `checkpoint-*` directory and any `.DS_Store` file. It MUST upload: `config.json`, `model.safetensors`, `tokenizer_config.json`, `tokenizer.json`, `training_args.bin` for HuggingFace-format models; and `model.pt` for the next_action MLP.
|
| 5 |
+
|
| 6 |
+
#### Scenario: Checkpoint dirs excluded
|
| 7 |
+
- **WHEN** `models/evidence_ner/` contains both root-level files and `checkpoint-225/`, `checkpoint-450/`, `checkpoint-675/`, `checkpoint-900/` subdirs
|
| 8 |
+
- **THEN** the script uploads only files directly under `models/evidence_ner/` (not inside any `checkpoint-*` subdir)
|
| 9 |
+
|
| 10 |
+
#### Scenario: DS_Store excluded
|
| 11 |
+
- **WHEN** any `models/` directory contains a `.DS_Store` file
|
| 12 |
+
- **THEN** that file is not uploaded
|
| 13 |
+
|
| 14 |
+
### Requirement: HF repo target is configurable
|
| 15 |
+
The upload script SHALL accept the HF Hub repo ID (e.g., `username/guide-models`) as a CLI argument or environment variable so it can be used with any HF account.
|
| 16 |
+
|
| 17 |
+
#### Scenario: Repo ID via CLI argument
|
| 18 |
+
- **WHEN** the script is run as `python scripts/upload_models_to_hub.py --repo-id myuser/guide-models`
|
| 19 |
+
- **THEN** files are uploaded to `myuser/guide-models` on HF Hub
|
| 20 |
+
|
| 21 |
+
#### Scenario: Missing repo ID
|
| 22 |
+
- **WHEN** the script is run without `--repo-id`
|
| 23 |
+
- **THEN** the script prints usage instructions and exits with a non-zero code
|
openspec/changes/hf-spaces-deployment/tasks.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## 1. Model Upload Script
|
| 2 |
+
|
| 3 |
+
- [x] 1.1 Create `scripts/upload_models_to_hub.py` with `parse_args()` function that accepts `--repo-id` CLI argument and validates it is provided (exits with usage message if missing)
|
| 4 |
+
- [x] 1.2 Implement `filter_inference_files(model_dir: Path) -> list[Path]` that returns only root-level files (skipping any path under a `checkpoint-*` subdir and any `.DS_Store` file)
|
| 5 |
+
- [x] 1.3 Implement `upload_models(repo_id: str) -> None` that iterates over `domain_classifier/`, `evidence_ner/`, and `next_action/` under `models/`, calls `filter_inference_files()` for each, and uploads via `huggingface_hub.upload_file()` — logging a warning and continuing if a model directory does not exist locally
|
| 6 |
+
|
| 7 |
+
## 2. Dockerfile
|
| 8 |
+
|
| 9 |
+
- [x] 2.1 Create `Dockerfile` at repo root: base `python:3.11-slim`, install `tesseract-ocr` and `git` via `apt-get`, copy `requirements.txt` and run `pip install`
|
| 10 |
+
- [x] 2.2 Add `RUN python -m spacy download en_core_web_lg` layer after pip install
|
| 11 |
+
- [x] 2.3 Add `ARG HF_MODEL_REPO` and `RUN huggingface-cli download $HF_MODEL_REPO --local-dir models` layer to pull weights at build time; add `COPY . /app`, `WORKDIR /app`, `EXPOSE 7860`, `ENV ANTHROPIC_API_KEY=""`, and `CMD ["python", "start.py", "--no-train"]`
|
| 12 |
+
|
| 13 |
+
## 3. README Update
|
| 14 |
+
|
| 15 |
+
- [x] 3.1 Add a "Deploying to HF Spaces" section to `README.md` documenting: (a) upload script usage with `--repo-id`, (b) Dockerfile build ARG `HF_MODEL_REPO`, (c) setting `ANTHROPIC_API_KEY` in HF Spaces Secrets UI, and (d) the two-server port layout (FastAPI `:8000` internal, Gradio `:7860` public)
|