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