Spaces:
Running
Running
| # Deploy Anti-Hallucination Chat to a Hugging Face Docker Space. | |
| # | |
| # Required: | |
| # HF_TOKEN Write-scoped token from https://huggingface.co/settings/tokens | |
| # GOOGLE_API_KEY Gemini API key (stored as a Space *secret*, never uploaded) | |
| # | |
| # Optional: | |
| # SPACE_ID e.g. your-user/anti-hallucination-chat | |
| # default: value in .hf-space-id | |
| # SPACE_PRIVATE set to 1 for a private Space (default: public) | |
| # SKIP_SECRET set to 1 to skip setting GOOGLE_API_KEY on the Space | |
| # | |
| # Usage (from repo root or this directory): | |
| # export HF_TOKEN=hf_... | |
| # export GOOGLE_API_KEY=... | |
| # bash scripts/deploy_hf_space.sh | |
| # # or: | |
| # SPACE_ID=myuser/anti-hallucination-chat bash scripts/deploy_hf_space.sh | |
| set -euo pipefail | |
| ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | |
| cd "$ROOT" | |
| TARGET_FILE="$ROOT/.hf-space-id" | |
| if [[ -z "${HF_TOKEN:-}" ]]; then | |
| echo "error: HF_TOKEN is not set" >&2 | |
| echo " Create a write token at https://huggingface.co/settings/tokens" >&2 | |
| echo " then: export HF_TOKEN=hf_..." >&2 | |
| exit 1 | |
| fi | |
| export HF_TOKEN | |
| export HUGGING_FACE_HUB_TOKEN="${HUGGING_FACE_HUB_TOKEN:-$HF_TOKEN}" | |
| HF=(uv run --with "huggingface_hub>=0.30" hf) | |
| echo "==> Checking Hugging Face auth" | |
| WHOAMI="$("${HF[@]}" auth whoami --format json 2>/dev/null || "${HF[@]}" auth whoami || true)" | |
| if [[ -z "$WHOAMI" || "$WHOAMI" == *"Not logged in"* || "$WHOAMI" == *"Error"* ]]; then | |
| echo "error: HF auth failed. Check HF_TOKEN (needs write scope)." >&2 | |
| exit 1 | |
| fi | |
| echo "$WHOAMI" | |
| if [[ -z "${SPACE_ID:-}" && -f "$TARGET_FILE" ]]; then | |
| SPACE_ID="$(tr -d '[:space:]' < "$TARGET_FILE")" | |
| fi | |
| if [[ -z "${SPACE_ID:-}" ]]; then | |
| USER_NAME="$( | |
| printf '%s\n' "$WHOAMI" | python3 -c ' | |
| import json, re, sys | |
| raw = sys.stdin.read().strip() | |
| try: | |
| data = json.loads(raw) | |
| print( | |
| data.get("name") | |
| or data.get("user") | |
| or data.get("fullname") | |
| or data.get("fullnameName") | |
| or "" | |
| ) | |
| except Exception: | |
| m = re.search(r"(?:name|user|fullname)[\"'\''\\s:]+([A-Za-z0-9_-]+)", raw, re.I) | |
| print(m.group(1) if m else "") | |
| ' | |
| )" | |
| if [[ -z "$USER_NAME" ]]; then | |
| echo "error: could not resolve HF username; set SPACE_ID=user/space-name" >&2 | |
| exit 1 | |
| fi | |
| SPACE_ID="${USER_NAME}/anti-hallucination-chat" | |
| fi | |
| if [[ ! "$SPACE_ID" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then | |
| echo "error: invalid SPACE_ID '$SPACE_ID' (expected namespace/name)" >&2 | |
| exit 1 | |
| fi | |
| echo "==> Target Space: $SPACE_ID" | |
| echo "==> Exporting requirements.txt" | |
| if command -v uv >/dev/null 2>&1; then | |
| uv export --no-hashes --no-dev -o requirements.txt | |
| else | |
| echo "warning: uv not found; using existing requirements.txt" >&2 | |
| fi | |
| VISIBILITY=(--public) | |
| if [[ "${SPACE_PRIVATE:-0}" == "1" ]]; then | |
| VISIBILITY=(--private) | |
| fi | |
| if "${HF[@]}" spaces info "$SPACE_ID" --format json >/dev/null 2>&1; then | |
| echo "==> Updating existing Docker Space" | |
| else | |
| echo "==> Creating Docker Space (exist-ok)" | |
| "${HF[@]}" repos create "$SPACE_ID" \ | |
| --type space \ | |
| --space-sdk docker \ | |
| "${VISIBILITY[@]}" \ | |
| --exist-ok | |
| fi | |
| echo "==> Uploading app (secrets and .env excluded)" | |
| "${HF[@]}" upload "$SPACE_ID" "$ROOT" . \ | |
| --type space \ | |
| --commit-message "Deploy Anti-Hallucination Chat" \ | |
| --exclude ".env" \ | |
| --exclude ".env.*" \ | |
| --exclude ".venv/**" \ | |
| --exclude "**/.venv/**" \ | |
| --exclude ".cursor/**" \ | |
| --exclude "**/.cursor/**" \ | |
| --exclude ".git/**" \ | |
| --exclude "**/__pycache__/**" \ | |
| --exclude "tests/**" \ | |
| --exclude "**/*.pyc" \ | |
| --exclude "*.tex" \ | |
| --exclude "plan.md" \ | |
| --exclude "benchmark_*.py" \ | |
| --exclude "uv.lock" \ | |
| --exclude ".python-version" | |
| if [[ "${SKIP_SECRET:-0}" != "1" ]]; then | |
| if [[ -z "${GOOGLE_API_KEY:-}" ]]; then | |
| echo "warning: GOOGLE_API_KEY not set — Space will build but chat will error until you add the secret:" >&2 | |
| echo " hf spaces secrets add $SPACE_ID --secrets GOOGLE_API_KEY=YOUR_KEY" >&2 | |
| else | |
| echo "==> Setting GOOGLE_API_KEY Space secret" | |
| "${HF[@]}" spaces secrets add "$SPACE_ID" \ | |
| --secrets "GOOGLE_API_KEY=${GOOGLE_API_KEY}" | |
| fi | |
| fi | |
| # Optional non-secret model overrides (visible in Space settings) | |
| if [[ -n "${CHAT_MODEL:-}" || -n "${AUDIT_MODEL:-}" || -n "${SEARCH_MODEL:-}" ]]; then | |
| echo "==> Setting optional model env vars on the Space" | |
| ENV_ARGS=() | |
| [[ -n "${CHAT_MODEL:-}" ]] && ENV_ARGS+=(--env "CHAT_MODEL=${CHAT_MODEL}") | |
| [[ -n "${AUDIT_MODEL:-}" ]] && ENV_ARGS+=(--env "AUDIT_MODEL=${AUDIT_MODEL}") | |
| [[ -n "${SEARCH_MODEL:-}" ]] && ENV_ARGS+=(--env "SEARCH_MODEL=${SEARCH_MODEL}") | |
| # Newer CLIs: hf spaces env / repos create --env; fall back to secrets-free note | |
| if "${HF[@]}" spaces --help 2>/dev/null | grep -q "env"; then | |
| "${HF[@]}" spaces env add "$SPACE_ID" "${ENV_ARGS[@]}" 2>/dev/null \ | |
| || echo "note: set CHAT_MODEL/AUDIT_MODEL/SEARCH_MODEL in Space Settings → Variables" >&2 | |
| else | |
| echo "note: set model overrides in Space Settings → Variables if desired" >&2 | |
| fi | |
| fi | |
| echo | |
| echo "Deployed: https://huggingface.co/spaces/${SPACE_ID}" | |
| echo "Build logs: https://huggingface.co/spaces/${SPACE_ID}/logs" | |
| echo | |
| echo "If the Space is asleep or still building, wait a minute then open the URL." | |
| echo "Free Docker Spaces may require HF Pro: https://huggingface.co/pro" | |