Spaces:
Running
Running
unknown Claude Opus 4.6 commited on
Commit ·
eb7afc8
1
Parent(s): 2cb9a77
Add Hugging Face Spaces deployment ($0/month)
Browse files- Dockerfile.hf: HF-specific Dockerfile (port 7860, UID 1000)
- scripts/deploy_hf.sh: one-command deploy to HF Spaces
- .github/workflows/keep-alive.yml: pings Space every 12h to
prevent 48h inactivity sleep
- Updated CLAUDE.md with all three deployment options
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- .github/workflows/keep-alive.yml +44 -0
- CLAUDE.md +16 -1
- Dockerfile.hf +69 -0
- scripts/deploy_hf.sh +103 -0
.github/workflows/keep-alive.yml
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Keep HF Space Alive
|
| 2 |
+
|
| 3 |
+
# Ping the Hugging Face Space every 12 hours to prevent 48h sleep.
|
| 4 |
+
# The Space URL is stored as a repository variable (not secret — it's public).
|
| 5 |
+
|
| 6 |
+
on:
|
| 7 |
+
schedule:
|
| 8 |
+
# Every 12 hours: at 06:00 and 18:00 UTC
|
| 9 |
+
- cron: "0 6,18 * * *"
|
| 10 |
+
workflow_dispatch: # Allow manual trigger
|
| 11 |
+
|
| 12 |
+
jobs:
|
| 13 |
+
ping:
|
| 14 |
+
runs-on: ubuntu-latest
|
| 15 |
+
steps:
|
| 16 |
+
- name: Ping HF Space health endpoint
|
| 17 |
+
env:
|
| 18 |
+
SPACE_URL: ${{ vars.HF_SPACE_URL }}
|
| 19 |
+
run: |
|
| 20 |
+
if [ -z "$SPACE_URL" ]; then
|
| 21 |
+
echo "HF_SPACE_URL variable not set — skipping"
|
| 22 |
+
exit 0
|
| 23 |
+
fi
|
| 24 |
+
|
| 25 |
+
echo "Pinging $SPACE_URL/api/health ..."
|
| 26 |
+
|
| 27 |
+
# First request may wake the Space (takes up to 2 min)
|
| 28 |
+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
| 29 |
+
--max-time 180 \
|
| 30 |
+
"$SPACE_URL/api/health")
|
| 31 |
+
|
| 32 |
+
echo "Response: HTTP $HTTP_CODE"
|
| 33 |
+
|
| 34 |
+
if [ "$HTTP_CODE" = "200" ]; then
|
| 35 |
+
echo "Space is alive!"
|
| 36 |
+
else
|
| 37 |
+
echo "Space returned $HTTP_CODE — it may be starting up"
|
| 38 |
+
# Retry after 60 seconds (Space may still be booting)
|
| 39 |
+
sleep 60
|
| 40 |
+
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
| 41 |
+
--max-time 180 \
|
| 42 |
+
"$SPACE_URL/api/health")
|
| 43 |
+
echo "Retry response: HTTP $HTTP_CODE"
|
| 44 |
+
fi
|
CLAUDE.md
CHANGED
|
@@ -120,7 +120,22 @@ Services: `api` (FastAPI :8000), `ollama` (LLM :11434), `ingest`/`enrich` (one-s
|
|
| 120 |
|
| 121 |
## Infrastructure (Terraform)
|
| 122 |
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
### Oracle Cloud — Always Free ($0/month, recommended for demos)
|
| 126 |
|
|
|
|
| 120 |
|
| 121 |
## Infrastructure (Terraform)
|
| 122 |
|
| 123 |
+
Three deployment options:
|
| 124 |
+
|
| 125 |
+
### Hugging Face Spaces — Free ($0/month, easiest)
|
| 126 |
+
|
| 127 |
+
`Dockerfile.hf` + `scripts/deploy_hf.sh` — Docker Space on HF free tier (2 vCPU, 16 GB RAM).
|
| 128 |
+
|
| 129 |
+
```bash
|
| 130 |
+
# One-time: create Space at huggingface.co/new-space (Docker SDK, CPU basic)
|
| 131 |
+
# Set GROQ_API_KEY as a Secret in Space Settings
|
| 132 |
+
huggingface-cli login
|
| 133 |
+
./scripts/deploy_hf.sh <username>/researchradar
|
| 134 |
+
```
|
| 135 |
+
|
| 136 |
+
- Sleeps after 48h inactivity; `.github/workflows/keep-alive.yml` pings every 12h to prevent this
|
| 137 |
+
- Set `HF_SPACE_URL` as a GitHub repo variable for the keep-alive to work
|
| 138 |
+
- Redeployments: re-run `deploy_hf.sh` or push to the HF Space repo directly
|
| 139 |
|
| 140 |
### Oracle Cloud — Always Free ($0/month, recommended for demos)
|
| 141 |
|
Dockerfile.hf
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hugging Face Spaces Dockerfile
|
| 2 |
+
# Free tier: 2 vCPU, 16 GB RAM, 50 GB ephemeral disk
|
| 3 |
+
# Must listen on port 7860, runs as UID 1000
|
| 4 |
+
|
| 5 |
+
# ── Stage 1: Build frontend ─────────────────────────────────────────
|
| 6 |
+
FROM node:22-slim AS frontend
|
| 7 |
+
|
| 8 |
+
WORKDIR /frontend
|
| 9 |
+
COPY frontend/package.json frontend/package-lock.json* ./
|
| 10 |
+
RUN npm ci --no-audit --no-fund 2>/dev/null || npm install --no-audit --no-fund
|
| 11 |
+
COPY frontend/ .
|
| 12 |
+
RUN npm run build
|
| 13 |
+
|
| 14 |
+
# ── Stage 2: Build Python dependencies ──────────────────────────────
|
| 15 |
+
FROM python:3.12-slim AS builder
|
| 16 |
+
|
| 17 |
+
WORKDIR /build
|
| 18 |
+
|
| 19 |
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 20 |
+
gcc g++ && \
|
| 21 |
+
rm -rf /var/lib/apt/lists/*
|
| 22 |
+
|
| 23 |
+
COPY requirements.txt .
|
| 24 |
+
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
|
| 25 |
+
|
| 26 |
+
# ── Stage 3: Runtime ────────────────────────────────────────────────
|
| 27 |
+
FROM python:3.12-slim
|
| 28 |
+
|
| 29 |
+
WORKDIR /app
|
| 30 |
+
|
| 31 |
+
# Copy installed packages from builder
|
| 32 |
+
COPY --from=builder /install /usr/local
|
| 33 |
+
|
| 34 |
+
# NLTK data (punkt tokenizer for BM25)
|
| 35 |
+
RUN python -m nltk.downloader -d /usr/share/nltk_data punkt punkt_tab
|
| 36 |
+
|
| 37 |
+
ENV NLTK_DATA=/usr/share/nltk_data
|
| 38 |
+
ENV PYTHONUNBUFFERED=1
|
| 39 |
+
ENV PYTHONDONTWRITEBYTECODE=1
|
| 40 |
+
|
| 41 |
+
# HF Spaces runs as UID 1000 — create matching user
|
| 42 |
+
RUN useradd -m -u 1000 user
|
| 43 |
+
|
| 44 |
+
# Copy application code (owned by user)
|
| 45 |
+
COPY --chown=user src/ ./src/
|
| 46 |
+
COPY --chown=user scripts/ ./scripts/
|
| 47 |
+
|
| 48 |
+
# Copy frontend build
|
| 49 |
+
COPY --chown=user --from=frontend /frontend/dist ./frontend/dist
|
| 50 |
+
|
| 51 |
+
# Data dir — writable by user
|
| 52 |
+
RUN mkdir -p /app/data && chown user:user /app/data
|
| 53 |
+
|
| 54 |
+
# Model cache
|
| 55 |
+
ENV HF_HOME=/app/.cache/huggingface
|
| 56 |
+
RUN mkdir -p /app/.cache/huggingface && chown -R user:user /app/.cache
|
| 57 |
+
|
| 58 |
+
# Default to Groq (cloud LLM) — key set via HF Spaces secrets
|
| 59 |
+
ENV LLM_BACKEND=groq
|
| 60 |
+
ENV SQLITE_DB_PATH=/app/data/researchradar.db
|
| 61 |
+
ENV CHROMA_DB_PATH=/app/data/chroma_db
|
| 62 |
+
|
| 63 |
+
USER user
|
| 64 |
+
ENV HOME=/home/user
|
| 65 |
+
ENV PATH=/home/user/.local/bin:$PATH
|
| 66 |
+
|
| 67 |
+
EXPOSE 7860
|
| 68 |
+
|
| 69 |
+
CMD ["uvicorn", "src.api.app:app", "--host", "0.0.0.0", "--port", "7860"]
|
scripts/deploy_hf.sh
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
# Deploy ResearchRadar to Hugging Face Spaces.
|
| 3 |
+
#
|
| 4 |
+
# Prerequisites:
|
| 5 |
+
# 1. Create a Space at huggingface.co/new-space (Docker SDK)
|
| 6 |
+
# 2. Set GROQ_API_KEY as a Secret in Space Settings
|
| 7 |
+
# 3. Install: pip install huggingface_hub
|
| 8 |
+
# 4. Login: huggingface-cli login
|
| 9 |
+
#
|
| 10 |
+
# Usage:
|
| 11 |
+
# ./scripts/deploy_hf.sh <hf_username>/<space_name>
|
| 12 |
+
#
|
| 13 |
+
# Example:
|
| 14 |
+
# ./scripts/deploy_hf.sh arkgithubforyou/researchradar
|
| 15 |
+
|
| 16 |
+
set -e
|
| 17 |
+
|
| 18 |
+
SPACE_ID="${1:?Usage: deploy_hf.sh <hf_username>/<space_name>}"
|
| 19 |
+
SPACE_REPO="https://huggingface.co/spaces/${SPACE_ID}"
|
| 20 |
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
| 21 |
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
| 22 |
+
|
| 23 |
+
echo "=== Deploying ResearchRadar to HF Space: ${SPACE_ID} ==="
|
| 24 |
+
|
| 25 |
+
# Create a temp directory for the HF Space repo
|
| 26 |
+
TMPDIR=$(mktemp -d)
|
| 27 |
+
trap "rm -rf $TMPDIR" EXIT
|
| 28 |
+
|
| 29 |
+
echo "1. Cloning HF Space repo..."
|
| 30 |
+
git clone "${SPACE_REPO}" "$TMPDIR/space" 2>/dev/null || {
|
| 31 |
+
echo " Space doesn't exist yet or clone failed."
|
| 32 |
+
echo " Create it first at: https://huggingface.co/new-space"
|
| 33 |
+
echo " - Select 'Docker' as the SDK"
|
| 34 |
+
echo " - Set hardware to 'CPU basic (Free)'"
|
| 35 |
+
exit 1
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
echo "2. Copying project files..."
|
| 39 |
+
# Copy everything needed for the Docker build
|
| 40 |
+
rsync -a --delete \
|
| 41 |
+
--exclude='.git/' \
|
| 42 |
+
--exclude='data/' \
|
| 43 |
+
--exclude='.venv/' \
|
| 44 |
+
--exclude='venv/' \
|
| 45 |
+
--exclude='__pycache__/' \
|
| 46 |
+
--exclude='*.pyc' \
|
| 47 |
+
--exclude='.env' \
|
| 48 |
+
--exclude='terraform/' \
|
| 49 |
+
--exclude='terraform-oci/' \
|
| 50 |
+
--exclude='frontend/node_modules/' \
|
| 51 |
+
--exclude='frontend/dist/' \
|
| 52 |
+
--exclude='.pytest_cache/' \
|
| 53 |
+
--exclude='.ruff_cache/' \
|
| 54 |
+
--exclude='htmlcov/' \
|
| 55 |
+
"$PROJECT_DIR/" "$TMPDIR/space/"
|
| 56 |
+
|
| 57 |
+
echo "3. Setting up HF Space config..."
|
| 58 |
+
# Use the HF-specific Dockerfile
|
| 59 |
+
cp "$TMPDIR/space/Dockerfile.hf" "$TMPDIR/space/Dockerfile"
|
| 60 |
+
|
| 61 |
+
# Create the required README.md with YAML frontmatter
|
| 62 |
+
cat > "$TMPDIR/space/README.md" << 'EOF'
|
| 63 |
+
---
|
| 64 |
+
title: ResearchRadar
|
| 65 |
+
emoji: 🔬
|
| 66 |
+
colorFrom: indigo
|
| 67 |
+
colorTo: blue
|
| 68 |
+
sdk: docker
|
| 69 |
+
app_port: 7860
|
| 70 |
+
short_description: RAG-powered NLP research paper explorer
|
| 71 |
+
startup_duration_timeout: 30m
|
| 72 |
+
pinned: false
|
| 73 |
+
---
|
| 74 |
+
|
| 75 |
+
# ResearchRadar
|
| 76 |
+
|
| 77 |
+
RAG-powered NLP/ML research paper explorer. Hybrid retrieval (BM25 + vector + cross-encoder reranking) + LLM generation over ACL Anthology papers.
|
| 78 |
+
|
| 79 |
+
**Features:**
|
| 80 |
+
- 🔍 Ask natural language questions about NLP research
|
| 81 |
+
- 📄 Browse and filter papers by venue, year, method, dataset
|
| 82 |
+
- 📊 Interactive analytics dashboard with trends, top entities, and co-occurrence
|
| 83 |
+
- ⚡ Powered by Groq API for fast LLM inference
|
| 84 |
+
|
| 85 |
+
Built with FastAPI, React, ChromaDB, and sentence-transformers.
|
| 86 |
+
EOF
|
| 87 |
+
|
| 88 |
+
echo "4. Pushing to HF Space..."
|
| 89 |
+
cd "$TMPDIR/space"
|
| 90 |
+
git add -A
|
| 91 |
+
git commit -m "Deploy ResearchRadar $(date +%Y-%m-%d)" 2>/dev/null || {
|
| 92 |
+
echo " No changes to deploy."
|
| 93 |
+
exit 0
|
| 94 |
+
}
|
| 95 |
+
git push
|
| 96 |
+
|
| 97 |
+
echo ""
|
| 98 |
+
echo "=== Deployed! ==="
|
| 99 |
+
echo "Space URL: ${SPACE_REPO}"
|
| 100 |
+
echo ""
|
| 101 |
+
echo "NOTE: First build takes ~5-10 minutes."
|
| 102 |
+
echo "Set GROQ_API_KEY as a Secret in Space Settings:"
|
| 103 |
+
echo " ${SPACE_REPO}/settings"
|