Spaces:
Build error
Build error
File size: 6,562 Bytes
33ac5c6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | #!/usr/bin/env bash
# ---------------------------------------------------------------------------
# VANI Speech API β one-command deploy to Hugging Face Spaces.
#
# PURPOSE: create (or update) the Space, push this folder, wait for it to
# build, then PROVE it is live by exercising the real endpoints over https
# from outside β health, a real text-to-speech round trip, and a real
# speech-to-text round trip using the audio that text-to-speech just produced.
#
# WHEN TO RUN: from RP's own terminal, which is the only shell with a Hugging
# Face credential. Agent shells are not logged in (verified 2026-08-21: both
# `hf repos create` and `hf upload` return 401 Unauthorized).
#
# WHEN NOT TO RUN: not from an agent session, and not as a scheduled job.
# It is a deliberate, interactive deployment step.
#
# This script never asks for, prints, or stores a token. It only checks that
# a credential is already present.
# ---------------------------------------------------------------------------
set -euo pipefail
SPACE_ID="${VANI_SPACE_ID:-radhapreetam/vani-speech-api}"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SUB="$(echo "$SPACE_ID" | tr '/' '-' | tr '[:upper:]' '[:lower:]')"
BASE="https://${SUB}.hf.space"
say(){ printf '\n\033[1m== %s\033[0m\n' "$*"; }
die(){ printf '\n\033[31mFAILED: %s\033[0m\n' "$*" >&2; exit 1; }
# --- Gate 1: credential present (presence only, never the value) ------------
say "Gate 1/5 β checking you are logged in to Hugging Face"
if ! WHO="$(hf auth whoami 2>&1)"; then
die "Not logged in. Run 'hf auth login' yourself and paste your own token, then re-run this script. Never share the token."
fi
echo "Logged in as: $WHO"
# --- Gate 2: the files we are about to ship are the ones we tested ----------
say "Gate 2/5 β checking the package is complete"
for f in app.py requirements.txt SPACE_CARD.md packages.txt; do
[ -f "$HERE/$f" ] || die "missing $f"
done
# On a Space, README.md is not documentation β its YAML frontmatter IS the
# build configuration (sdk, sdk_version, app_file). The workspace automation
# scripts/folder_readme.py rewrites README.md in every folder and would strip
# that frontmatter, leaving a Space that cannot build. So README.md is
# regenerated here, at upload time, from SPACE_CARD.md β which that automation
# does not touch.
cp "$HERE/SPACE_CARD.md" "$HERE/README.md"
head -1 "$HERE/README.md" | grep -q '^---$' || die "SPACE_CARD.md has lost its YAML frontmatter β the Space will not build without it."
grep -q '^app_file: app.py$' "$HERE/README.md" || die "Space card is missing 'app_file: app.py'."
# Hugging Face rejects a Space whose short_description exceeds 60 characters, and it
# only says so at UPLOAD time - after the repo has already been created. That cost a
# real failed deploy on 2026-08-21 (61 characters, one over). Validate it HERE, before
# anything is created, so the failure is cheap and self-explaining rather than a
# mid-deploy error nobody can parse.
_desc="$(sed -n 's/^short_description:[[:space:]]*//p' README.md | head -1)"
_len=${#_desc}
if [ "$_len" -gt 60 ]; then
echo "STOP - short_description is ${_len} characters; Hugging Face allows 60."
echo " Shorten it in SPACE_CARD.md (README.md is regenerated from it), then re-run."
exit 1
fi
echo "Space card regenerated from SPACE_CARD.md; frontmatter verified (short_description ${_len}/60)."
grep -q "^mlx" "$HERE/requirements.txt" && die "requirements.txt contains an mlx package β those are Apple-silicon only and will not build on a Linux Space."
python3 -c "import ast,sys; ast.parse(open('$HERE/app.py').read())" || die "app.py does not parse"
echo "app.py parses; no Apple-silicon-only dependency present."
# --- Gate 3: create the Space if it does not exist yet ----------------------
say "Gate 3/5 β ensuring the Space exists"
if hf repos create "$SPACE_ID" --type space --sdk gradio 2>/dev/null; then
echo "Created $SPACE_ID"
else
echo "$SPACE_ID already exists (or creation was declined) β continuing to upload."
fi
# --- Gate 4: upload -------------------------------------------------------
say "Gate 4/5 β uploading the package"
hf upload "$SPACE_ID" "$HERE" . --repo-type space \
--exclude "__pycache__/*" --exclude "*.pyc" --exclude "DEPLOY.md" \
|| die "upload failed"
# --- Gate 5: prove it is actually live, from outside -----------------------
say "Gate 5/5 β proving it is live at $BASE (this includes a cold start; allow ~5 minutes)"
echo "Waiting for /health to answer over https..."
for i in $(seq 1 60); do
if curl -fsS -m 15 "$BASE/health" >/tmp/vani_health.json 2>/dev/null; then
echo "PROOF 1 β https health check answered from outside:"; cat /tmp/vani_health.json; echo; break
fi
printf '.'; sleep 15
[ "$i" -eq 60 ] && die "Space never answered /health. Open $BASE and read the build logs."
done
echo "Warming the models (first load downloads them)..."
curl -fsS -m 900 -X POST "$BASE/warmup"; echo
echo
echo "PROOF 2 β real text in, real audio out:"
curl -fsS -m 300 -X POST "$BASE/text-to-speech" \
-H 'Content-Type: application/json' \
-d '{"text":"Book the badminton court for Sunday morning."}' \
-o /tmp/vani_live.wav || die "text-to-speech failed"
file /tmp/vani_live.wav
echo
echo "PROOF 3 β real audio in, real text out (feeding back the audio just generated):"
curl -fsS -m 300 -X POST "$BASE/speech-to-text" \
-F "audio=@/tmp/vani_live.wav;type=audio/wav" || die "speech-to-text failed"
echo
echo
echo "PROOF 4 β the page a stranger can open:"
curl -fsS -m 30 -o /dev/null -w "GET %{url_effective} -> HTTP %{http_code}\n" "$BASE/"
cat <<EOF
===========================================================================
LIVE at: $BASE
demo page $BASE/
health $BASE/health
speech-to-text POST $BASE/speech-to-text (multipart, field "audio")
text-to-speech POST $BASE/text-to-speech (JSON {"text": "..."})
Open $BASE/ in a browser and walk it once yourself β press and hold the
button, say a sentence, and confirm the words appear. A browser is the only
place the microphone permission prompt can actually be exercised.
If the Space is PRIVATE, only you can open it. Make it public in
Settings -> Change visibility before sending the link to anyone else.
NEXT, SEPARATELY: the website page is prepared but NOT deployed. Review
"Working Files/Vani/website_wiring/vani-demo.hosted.patch"
and apply it to rp-website only when you want the live site to switch over.
===========================================================================
EOF
|