| #!/usr/bin/env bash |
| |
| |
| |
| set -euo pipefail |
|
|
| cd /home/user/app |
|
|
| VENV_BIN="${VENV_BIN:-/opt/venv/bin}" |
| MODEL_DIR="${MODEL_DIR:-/home/user/app/models}" |
| MODEL_FILE="${GEMMA_MODEL_FILENAME:?GEMMA_MODEL_FILENAME must be set}" |
| MODEL_PATH="${MODEL_DIR}/${MODEL_FILE}" |
| MTP_FILE="${MTP_MODEL_FILE:-mtp-gemma-4-26B-A4B-it.gguf}" |
| MTP_PATH="${MODEL_DIR}/${MTP_FILE}" |
| LLAMA_PORT="${LLAMA_CPP_PORT:-8080}" |
| CTX="${LLAMA_CPP_CTX:-32768}" |
| APP_PORT="${APP_PORT:-7860}" |
| USE_MTP="${LLAMA_CPP_MTP:-1}" |
| N_MAX="${SPEC_DRAFT_N_MAX:-2}" |
| KV_TYPE="${LLAMA_CPP_KV_TYPE:-f16}" |
| HEALTH_TIMEOUT="${LLAMA_HEALTH_TIMEOUT:-900}" |
|
|
| |
| export LLAMA_CPP_PORT="$LLAMA_PORT" |
|
|
| LLAMA_BIN="$(command -v llama-server || echo /app/llama-server)" |
| [ -x "$LLAMA_BIN" ] || { echo "[entrypoint] FATAL: llama-server not found ($LLAMA_BIN)"; exit 1; } |
|
|
| |
| |
| |
| if [ ! -f "$MODEL_PATH" ]; then |
| : "${MODEL_REPO:?MODEL_REPO required to download the model when not baked}" |
| echo "[entrypoint] model missing at $MODEL_PATH — downloading from $MODEL_REPO" |
| "${VENV_BIN}/python" - <<'PY' |
| import os, sys |
| from huggingface_hub import hf_hub_download |
| d = os.environ["MODEL_DIR"] |
| tok = os.environ.get("HF_TOKEN") |
| try: |
| hf_hub_download(os.environ["MODEL_REPO"], os.environ["GEMMA_MODEL_FILENAME"], local_dir=d, token=tok) |
| if os.environ.get("LLAMA_CPP_MTP", "1") != "0": |
| hf_hub_download(os.environ.get("MTP_REPO", os.environ["MODEL_REPO"]), |
| os.environ["MTP_MODEL_FILE"], local_dir=d, token=tok) |
| except Exception as e: |
| print(f"[entrypoint] FATAL: model download failed: {e}", file=sys.stderr) |
| sys.exit(1) |
| PY |
| fi |
|
|
| |
| |
| |
| LLAMA_ARGS=( |
| -m "$MODEL_PATH" |
| --host 127.0.0.1 --port "$LLAMA_PORT" |
| -c "$CTX" |
| -ngl all --flash-attn on --cont-batching --cache-prompt |
| --parallel 1 --jinja --reasoning off --no-mmap --metrics |
| ) |
|
|
| |
| |
| |
| if [ "$KV_TYPE" != "f16" ]; then |
| echo "[entrypoint] KV-cache type=$KV_TYPE (quantized; flash-attn required)" |
| LLAMA_ARGS+=(--cache-type-k "$KV_TYPE" --cache-type-v "$KV_TYPE") |
| fi |
|
|
| |
| |
| if [ "$USE_MTP" != "0" ] && [ -f "$MTP_PATH" ]; then |
| echo "[entrypoint] MTP on — draft head $MTP_PATH (spec-draft-n-max=$N_MAX)" |
| LLAMA_ARGS+=(--model-draft "$MTP_PATH" --spec-type draft-mtp --spec-draft-n-max "$N_MAX") |
| elif [ "$USE_MTP" != "0" ]; then |
| echo "[entrypoint] MTP requested but draft head missing ($MTP_PATH); running without MTP" |
| fi |
|
|
| echo "[entrypoint] starting llama-server on 127.0.0.1:${LLAMA_PORT} (ctx=${CTX})" |
| "$LLAMA_BIN" "${LLAMA_ARGS[@]}" & |
| LLAMA_PID=$! |
|
|
| echo "[entrypoint] waiting up to ${HEALTH_TIMEOUT}s for llama-server /health..." |
| healthy=0 |
| elapsed=0 |
| while [ "$elapsed" -lt "$HEALTH_TIMEOUT" ]; do |
| if ! kill -0 "$LLAMA_PID" 2>/dev/null; then |
| echo "[entrypoint] FATAL: llama-server exited during startup (check flags / VRAM / model)" |
| wait "$LLAMA_PID" || true |
| exit 1 |
| fi |
| if curl -fsS --max-time 5 "http://127.0.0.1:${LLAMA_PORT}/health" >/dev/null 2>&1; then |
| healthy=1; break |
| fi |
| sleep 1 |
| elapsed=$((elapsed + 1)) |
| done |
| if [ "$healthy" != "1" ]; then |
| echo "[entrypoint] FATAL: llama-server not healthy within ${HEALTH_TIMEOUT}s" |
| kill "$LLAMA_PID" 2>/dev/null || true |
| exit 1 |
| fi |
| echo "[entrypoint] llama-server healthy — starting uvicorn on 0.0.0.0:${APP_PORT}" |
|
|
| exec "${VENV_BIN}/uvicorn" loosecanvas.main:app --host 0.0.0.0 --port "$APP_PORT" |
|
|