#!/usr/bin/env bash # HF Space entrypoint. Works in two modes, auto-detected from KIRANA_LLM_BASE_URL: # • Modal-primary (default for the flagship): the URL points at a remote Modal # endpoint → we run ONLY Gradio + Whisper(STT) + Veena/MMS(TTS), calling Modal # for the LLM. Needs just a T4. # • Self-contained: the URL is local (or unset) → we ALSO download a MiniCPM-V-4.6 # GGUF and start a local llama-server sidecar. Needs an L4. (The live Space uses # OpenBMB's hosted MiniCPM-V-4.6 API instead, so this branch is the opt-in path.) # app.py:main() tolerates the LLM still warming up (dashboard shows server_up=false). set -uo pipefail cd "$(dirname "$0")/.." # --- decide whether the LLM is local (we host it) or remote (Modal hosts it) LLM_URL="${KIRANA_LLM_BASE_URL:-http://127.0.0.1:8080/v1}" case "$LLM_URL" in *127.0.0.1*|*localhost*|*0.0.0.0*) LOCAL_LLM=1 ;; *) LOCAL_LLM=0 ;; esac echo "[entrypoint] LLM_URL=$LLM_URL LOCAL_LLM=$LOCAL_LLM" # --- data dir: prefer persistent /data; fall back to $HOME if not attached. if ! mkdir -p "${KIRANA_DATA_DIR:-/data}" 2>/dev/null; then export KIRANA_DATA_DIR="${HOME}/data"; mkdir -p "$KIRANA_DATA_DIR" fi echo "[entrypoint] DATA_DIR=$KIRANA_DATA_DIR" # --- CTranslate2 (faster-whisper) needs libcudnn/libcublas; point the loader at # torch's bundled CUDA libs. TORCH_LIB="$(uv run python -c 'import os,torch;print(os.path.join(os.path.dirname(torch.__file__),"lib"))' 2>/dev/null || true)" export LD_LIBRARY_PATH="${TORCH_LIB}:${LD_LIBRARY_PATH:-}" # --- install the curated demo DB shipped in the image (data/). Overwrites on every # boot so the Space always starts from the exact submission state; falls back to # seeding only if no DB was shipped. (Skips the copy when DATA_DIR is already ./data.) if [ -f "data/inventory.db" ] && [ -f "data/transactions.db" ] \ && [ "$(realpath data)" != "$(realpath "${KIRANA_DATA_DIR}")" ]; then echo "[entrypoint] installing shipped demo DB into ${KIRANA_DATA_DIR}" cp -f data/inventory.db data/transactions.db "${KIRANA_DATA_DIR}/" elif [ ! -f "${KIRANA_DATA_DIR}/inventory.db" ]; then echo "[entrypoint] seeding demo DB"; uv run python -m kirana.db --reset || true fi # --- local llama-server ONLY when we're hosting the LLM in-container. This is the # optional self-host path; the live Space talks to OpenBMB's hosted MiniCPM-V-4.6 # API instead. Repo/filenames are env-overridable (defaults = the verified files # from openbmb/MiniCPM-V-4.6-gguf: model uses "4_6", projector "mmproj-model-f16"). if [ "$LOCAL_LLM" = "1" ]; then MODELS_DIR="${HOME}/app/models/minicpm"; mkdir -p "$MODELS_DIR" LLM_REPO="${LLM_REPO:-openbmb/MiniCPM-V-4.6-gguf}" LLM_FILE="${LLM_FILE:-MiniCPM-V-4_6-Q4_K_M.gguf}" LLM_MMPROJ="${LLM_MMPROJ:-mmproj-model-f16.gguf}" export KIRANA_LOCAL_GGUF="${MODELS_DIR}/${LLM_FILE}" export KIRANA_LOCAL_MMPROJ="${MODELS_DIR}/${LLM_MMPROJ}" LLM_REPO="$LLM_REPO" LLM_FILE="$LLM_FILE" LLM_MMPROJ="$LLM_MMPROJ" \ uv run python - <<'PY' import os from huggingface_hub import hf_hub_download repo = os.environ["LLM_REPO"] d = os.path.join(os.environ["HOME"], "app", "models", "minicpm") for f in (os.environ["LLM_FILE"], os.environ["LLM_MMPROJ"]): print("[models]", hf_hub_download(repo_id=repo, filename=f, local_dir=d)) PY LLAMA="$(command -v llama-server || echo /app/llama-server)" echo "[entrypoint] starting local llama-server: $LLAMA" "$LLAMA" -m "$KIRANA_LOCAL_GGUF" --mmproj "$KIRANA_LOCAL_MMPROJ" \ --host 127.0.0.1 --port 8080 -c "${KIRANA_LLM_CTX:-8192}" -ngl 99 --jinja & else echo "[entrypoint] using remote LLM at $LLM_URL — not starting a local llama-server" fi # --- Gradio app (foreground; Whisper + Veena lazy-load on first use). exec uv run python -m kirana.app