Spaces:
Running
Running
| set -e | |
| echo '=== STARRY HF Space Entrypoint ===' | |
| # ── Start PostgreSQL ── | |
| echo 'Starting PostgreSQL...' | |
| /usr/lib/postgresql/15/bin/pg_ctl -D $PGDATA -l /home/node/postgresql.log start -o "-p 5432" | |
| for i in $(seq 1 15); do | |
| if /usr/lib/postgresql/15/bin/pg_isready -h 127.0.0.1 -p 5432 -q 2>/dev/null; then | |
| echo 'PostgreSQL is ready.' | |
| break | |
| fi | |
| echo " Waiting for PostgreSQL... attempt $i/15" | |
| sleep 1 | |
| done | |
| # Create database | |
| /usr/lib/postgresql/15/bin/psql -h 127.0.0.1 -p 5432 -d postgres -tc "SELECT 1 FROM pg_database WHERE datname='starry_omr'" | grep -q 1 || \ | |
| /usr/lib/postgresql/15/bin/createdb -h 127.0.0.1 -p 5432 starry_omr | |
| # ── Model path resolution from models.yaml ── | |
| MODELS_BASE="/home/node/app/models" | |
| MODELS_YAML="$MODELS_BASE/starry-dist/models.yaml" | |
| HF_REPO="k-l-lambda/starry" | |
| CURL_PROXY="" | |
| if [ -n "$https_proxy" ]; then | |
| CURL_PROXY="-x $https_proxy" | |
| fi | |
| # Download models.yaml from HuggingFace if not present locally | |
| if [ ! -f "$MODELS_YAML" ] && [ -n "$HF_TOKEN" ]; then | |
| echo "Downloading models.yaml from HuggingFace..." | |
| mkdir -p "$MODELS_BASE/starry-dist" | |
| curl -sfL $CURL_PROXY \ | |
| -H "Authorization: Bearer $HF_TOKEN" \ | |
| -o "$MODELS_YAML" \ | |
| "https://huggingface.co/${HF_REPO}/resolve/main/starry-dist/models.yaml?download=true" \ | |
| || echo "Warning: Failed to download models.yaml (token may lack access)" | |
| fi | |
| # Parse models.yaml and set env vars (only if not already set) | |
| if [ -f "$MODELS_YAML" ]; then | |
| echo "Parsing model paths from models.yaml..." | |
| # Helper: extract a field value under a section | |
| yaml_get() { | |
| sed -n "/^${1}:/,/^[a-z]/{ /^ ${2}:/{ s/.*${2}: *//; p; } }" "$MODELS_YAML" | head -1 | |
| } | |
| # bdtopo (ONNX model for regulation) | |
| if [ -z "$BDTOPO_MODEL_PATH" ]; then | |
| _bdtopo_path=$(yaml_get bdtopo path) | |
| _bdtopo_default=$(yaml_get bdtopo default) | |
| if [ -n "$_bdtopo_path" ] && [ -n "$_bdtopo_default" ]; then | |
| export BDTOPO_MODEL_PATH="$MODELS_BASE/starry-dist/$_bdtopo_path/$_bdtopo_default" | |
| echo " BDTOPO_MODEL_PATH=$BDTOPO_MODEL_PATH" | |
| fi | |
| fi | |
| # PyTorch predictors (starry-dist) | |
| if [ -z "$LAYOUT_MODEL_PATH" ]; then | |
| export LAYOUT_MODEL_PATH=$(yaml_get layout path) | |
| echo " LAYOUT_MODEL_PATH=$LAYOUT_MODEL_PATH" | |
| fi | |
| if [ -z "$MASK_MODEL_PATH" ]; then | |
| export MASK_MODEL_PATH=$(yaml_get mask path) | |
| echo " MASK_MODEL_PATH=$MASK_MODEL_PATH" | |
| fi | |
| if [ -z "$SEMANTIC_MODEL_PATH" ]; then | |
| export SEMANTIC_MODEL_PATH=$(yaml_get semantic path) | |
| echo " SEMANTIC_MODEL_PATH=$SEMANTIC_MODEL_PATH" | |
| fi | |
| if [ -z "$GAUGE_MODEL_PATH" ]; then | |
| export GAUGE_MODEL_PATH=$(yaml_get gauge path) | |
| echo " GAUGE_MODEL_PATH=$GAUGE_MODEL_PATH" | |
| fi | |
| # OCR predictors (ocr-dist) | |
| if [ -z "$LOC_MODEL_PATH" ]; then | |
| export LOC_MODEL_PATH=$(yaml_get loc path) | |
| echo " LOC_MODEL_PATH=$LOC_MODEL_PATH" | |
| fi | |
| if [ -z "$OCR_CONFIG" ]; then | |
| export OCR_CONFIG=$(yaml_get ocr config) | |
| echo " OCR_CONFIG=$OCR_CONFIG" | |
| fi | |
| if [ -z "$BRACKETS_CONFIG" ]; then | |
| export BRACKETS_CONFIG=$(yaml_get brackets config) | |
| echo " BRACKETS_CONFIG=$BRACKETS_CONFIG" | |
| fi | |
| fi | |
| # ── Download model files from HuggingFace ── | |
| hf_download() { | |
| local subdir="$1" | |
| local relpath="$2" | |
| local dest="$MODELS_BASE/$subdir/$relpath" | |
| if [ -z "$relpath" ]; then return; fi | |
| if [ -e "$dest" ]; then | |
| echo " $subdir/$relpath already present, skipping." | |
| return | |
| fi | |
| echo " Downloading $subdir/$relpath ..." | |
| mkdir -p "$(dirname "$dest")" | |
| if curl -sfL $CURL_PROXY \ | |
| -H "Authorization: Bearer $HF_TOKEN" \ | |
| -o "$dest" \ | |
| "https://huggingface.co/${HF_REPO}/resolve/main/${subdir}/${relpath}?download=true"; then | |
| echo " $subdir/$relpath downloaded OK." | |
| else | |
| rm -f "$dest" | |
| # Retry with .pt extension (some .state.yaml best: fields omit the extension) | |
| if curl -sfL $CURL_PROXY \ | |
| -H "Authorization: Bearer $HF_TOKEN" \ | |
| -o "${dest}.pt" \ | |
| "https://huggingface.co/${HF_REPO}/resolve/main/${subdir}/${relpath}.pt?download=true"; then | |
| echo " $subdir/$relpath.pt downloaded OK (extension fallback)." | |
| else | |
| rm -f "${dest}.pt" | |
| echo " WARNING: Failed to download $subdir/$relpath" | |
| fi | |
| fi | |
| } | |
| # ── Set predictor addresses (internal ZMQ) ── | |
| export PREDICTOR_LAYOUT="${PREDICTOR_LAYOUT:-tcp://127.0.0.1:12022}" | |
| export PREDICTOR_GAUGE="${PREDICTOR_GAUGE:-tcp://127.0.0.1:12023}" | |
| export PREDICTOR_GAUGE_RENDERER="${PREDICTOR_GAUGE_RENDERER:-tcp://127.0.0.1:15656}" | |
| export PREDICTOR_MASK="${PREDICTOR_MASK:-tcp://127.0.0.1:12024}" | |
| export PREDICTOR_SEMANTIC="${PREDICTOR_SEMANTIC:-tcp://127.0.0.1:12025}" | |
| export PREDICTOR_LOC="${PREDICTOR_LOC:-tcp://127.0.0.1:12026}" | |
| export PREDICTOR_OCR="${PREDICTOR_OCR:-tcp://127.0.0.1:12027}" | |
| export PREDICTOR_BRACKETS="${PREDICTOR_BRACKETS:-tcp://127.0.0.1:12028}" | |
| # ── Download models & start ML predictors (background) ── | |
| # Runs in background so Node services and nginx can start immediately. | |
| ( | |
| if [ -n "$HF_TOKEN" ] && [ -f "$MODELS_YAML" ]; then | |
| echo "Downloading model files from HuggingFace (background)..." | |
| # Download bdtopo ONNX models | |
| if [ -n "$BDTOPO_MODEL_PATH" ] && [ ! -f "$BDTOPO_MODEL_PATH" ]; then | |
| _bdtopo_path=$(yaml_get bdtopo path) | |
| _bdtopo_dir="$MODELS_BASE/starry-dist/$_bdtopo_path" | |
| mkdir -p "$_bdtopo_dir" | |
| sed -n '/^ files:/,/^[^ ]/{ /^ *- /p }' "$MODELS_YAML" | sed 's/^ *- *//' | while read -r FILE; do | |
| hf_download "starry-dist" "$_bdtopo_path/$FILE" | |
| done | |
| fi | |
| # Download PyTorch model directories (starry-dist) | |
| for model_var in LAYOUT_MODEL_PATH MASK_MODEL_PATH SEMANTIC_MODEL_PATH GAUGE_MODEL_PATH; do | |
| eval model_path=\$$model_var | |
| if [ -n "$model_path" ]; then | |
| hf_download "starry-dist" "$model_path/.state.yaml" | |
| state_yaml="$MODELS_BASE/starry-dist/$model_path/.state.yaml" | |
| if [ -f "$state_yaml" ]; then | |
| # Model weight file is in "best:" field (not "file:") | |
| model_file=$(sed -n 's/^best: *//p' "$state_yaml" | head -1) | |
| if [ -n "$model_file" ]; then | |
| hf_download "starry-dist" "$model_path/$model_file" | |
| fi | |
| # Sub-models listed under "subs:" (skip commented lines starting with #) | |
| sed -n '/^subs:/,/^[a-z]/{ /^[^#]*- /p }' "$state_yaml" | sed 's/^ *- *//' | while read -r sub; do | |
| if [ -n "$sub" ]; then | |
| hf_download "starry-dist" "$model_path/$sub/.state.yaml" | |
| sub_state="$MODELS_BASE/starry-dist/$model_path/$sub/.state.yaml" | |
| if [ -f "$sub_state" ]; then | |
| sub_file=$(sed -n 's/^best: *//p' "$sub_state" | head -1) | |
| if [ -n "$sub_file" ]; then | |
| hf_download "starry-dist" "$model_path/$sub/$sub_file" | |
| fi | |
| fi | |
| fi | |
| done | |
| fi | |
| fi | |
| done | |
| # Download LOC model (a single file, not a directory) | |
| if [ -n "$LOC_MODEL_PATH" ]; then | |
| hf_download "ocr-dist" "$LOC_MODEL_PATH" | |
| fi | |
| # Download OCR/TF config yamls and all referenced files | |
| for config_var in OCR_CONFIG BRACKETS_CONFIG; do | |
| eval config_path=\$$config_var | |
| if [ -n "$config_path" ]; then | |
| hf_download "ocr-dist" "$config_path" | |
| config_file="$MODELS_BASE/ocr-dist/$config_path" | |
| if [ -f "$config_file" ]; then | |
| _dir=$(dirname "$config_path") | |
| # Download all *_path values referenced in the yaml | |
| grep '_path:' "$config_file" | sed 's/^[^:]*: *//' | while read -r ref_path; do | |
| if [ -n "$ref_path" ]; then | |
| # Resolve ./ relative paths | |
| ref_path=$(echo "$ref_path" | sed 's|^\./||') | |
| hf_download "ocr-dist" "${_dir:+$_dir/}$ref_path" | |
| fi | |
| done | |
| fi | |
| fi | |
| done | |
| echo "Model download complete." | |
| fi | |
| # Start ML predictors after models are downloaded | |
| if [ -f /home/node/app/supervisord.conf ] && [ -d /home/node/app/backend/python-services ]; then | |
| if [ -n "$LAYOUT_MODEL_PATH" ]; then | |
| echo "Starting Python ML predictors via supervisord..." | |
| supervisord -c /home/node/app/supervisord.conf | |
| else | |
| echo "Skipping ML predictors (no model paths configured)." | |
| fi | |
| fi | |
| ) & | |
| # ── Run database migrations ── | |
| echo 'Running database migrations...' | |
| cd /home/node/app/backend/omr-service | |
| DB_HOST=127.0.0.1 DB_PORT=5432 DB_NAME=starry_omr DB_USER=node DB_PASSWORD= \ | |
| npx tsx src/db/migrate.ts 2>&1 || echo 'Warning: Migration failed or skipped' | |
| # Seed example score data + pre-computed regulation solutions | |
| if [ -f /home/node/app/seed-data.sql.gz ]; then | |
| echo 'Loading seed data (example score + regulation solutions)...' | |
| zcat /home/node/app/seed-data.sql.gz | \ | |
| /usr/lib/postgresql/15/bin/psql -h 127.0.0.1 -p 5432 -U node -d starry_omr 2>&1 | tail -1 | |
| fi | |
| # Extract pre-generated upload images (page sources + mask + background) | |
| if [ -f /home/node/app/seed-uploads.tar.gz ]; then | |
| echo 'Extracting seed upload images...' | |
| tar xzf /home/node/app/seed-uploads.tar.gz -C /tmp/starry-uploads/ | |
| fi | |
| # ── Start cluster-server (port 3999) ── | |
| echo 'Starting cluster-server on port 3999...' | |
| cd /home/node/app/backend/cluster-server | |
| PORT=3999 node dist/src/main.js & | |
| # ── Start omr-service (port 3080) ── | |
| echo 'Starting omr-service on port 3080...' | |
| cd /home/node/app/backend/omr-service | |
| export PORT=3080 | |
| export DB_HOST=127.0.0.1 | |
| export DB_PORT=5432 | |
| export DB_NAME=starry_omr | |
| export DB_USER=node | |
| export DB_PASSWORD= | |
| export REGULATION_ENABLED=${REGULATION_ENABLED:-true} | |
| npx tsx src/index.ts & | |
| # Wait for omr-service to be ready | |
| echo 'Waiting for omr-service...' | |
| for i in $(seq 1 30); do | |
| if curl -sf http://127.0.0.1:3080/health > /dev/null 2>&1; then | |
| echo 'omr-service is ready.' | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| # ── Start nginx (port 7860, foreground) ── | |
| echo 'Starting nginx on port 7860...' | |
| exec nginx -g 'daemon off;' | |