Spaces:
Sleeping
Sleeping
File size: 6,315 Bytes
745f62a | 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 137 138 139 140 141 142 143 144 145 146 147 148 | #!/usr/bin/env bash
# Download the Cactus-Compute Gemma 4 E2B INT4 model and push it to a
# connected Android device running Sakhi. One command from clone β on-device
# model ready.
#
# Prerequisites:
# 1. The Sakhi APK must already be INSTALLED on the phone (debuggable),
# because we use `run-as com.sakhi.app` to write into app-private storage.
# Build/install instructions are in the main README.
# 2. `adb` must be on PATH and the phone must show as an authorized device
# (`adb devices` lists it with state "device", not "unauthorized").
# 3. `curl` and `unzip` must be available (unzip runs on-device via
# adb shell, so only the host `curl` is needed).
# 4. You must have ACCEPTED the terms on the Cactus-Compute model page:
# https://huggingface.co/Cactus-Compute/gemma-4-E2B-it
# and have a HuggingFace access token available. The script reads, in
# order: $HF_TOKEN env var, then ~/.cache/huggingface/token. If neither
# exists the script bails with instructions.
#
# Tested on: Windows 11 + Git Bash, macOS 14, Ubuntu 22.04.
# adb wireless and USB both work β the script is transport-agnostic.
set -euo pipefail
MODEL_REPO="Cactus-Compute/gemma-4-E2B-it"
MODEL_FILE="gemma-4-e2b-it-int4.zip" # NOTE: lowercase despite repo casing
MODEL_URL="https://huggingface.co/${MODEL_REPO}/resolve/main/weights/${MODEL_FILE}"
PKG="com.sakhi.app"
ON_DEVICE_DIR="files/models/gemma-4-e2b" # relative to /data/data/$PKG/
DOWNLOAD_DIR="${DOWNLOAD_DIR:-./models/cactus}"
LOCAL_ZIP="${DOWNLOAD_DIR}/${MODEL_FILE}"
# ββ Token resolution ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
resolve_hf_token() {
if [ -n "${HF_TOKEN:-}" ]; then
echo "$HF_TOKEN"
return 0
fi
if [ -f "$HOME/.cache/huggingface/token" ]; then
cat "$HOME/.cache/huggingface/token"
return 0
fi
return 1
}
if ! TOKEN=$(resolve_hf_token); then
cat >&2 <<EOF
ERROR: no HuggingFace token found.
This model is gated β you must accept the terms on HuggingFace before the
download will work.
Steps:
1. Open https://huggingface.co/${MODEL_REPO} and click "Agree and access".
2. Create a read token at https://huggingface.co/settings/tokens.
3. Export it: export HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxx
OR save to ~/.cache/huggingface/token (huggingface-cli login).
4. Re-run this script.
Without completing step 1, the download will return HTTP 403 even with a
valid token.
EOF
exit 1
fi
# ββ adb sanity checks βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if ! command -v adb >/dev/null 2>&1; then
echo "ERROR: adb not on PATH. Install Android platform-tools first." >&2
exit 1
fi
DEVICE_COUNT=$(adb devices | awk 'NR>1 && $2=="device" {count++} END {print count+0}')
if [ "$DEVICE_COUNT" -eq 0 ]; then
echo "ERROR: no authorized adb device connected. Run 'adb devices' to diagnose." >&2
exit 1
fi
if [ "$DEVICE_COUNT" -gt 1 ]; then
echo "WARNING: multiple adb devices connected β the first one will be used." >&2
echo " Set ANDROID_SERIAL=<serial> to target a specific device." >&2
fi
# Confirm Sakhi is installed and debuggable (run-as needs both).
if ! adb shell "run-as $PKG true" >/dev/null 2>&1; then
cat >&2 <<EOF
ERROR: cannot run-as $PKG on the connected device.
This means one of:
(a) The Sakhi APK is not installed. Build + install it first (see README).
(b) The installed APK is not a debuggable build (release APKs cannot run-as).
Use ./gradlew assembleDebug output, not assembleRelease.
If you just installed the APK, wait ~3 seconds and re-run.
EOF
exit 1
fi
# ββ Download (skip if already present) ββββββββββββββββββββββββββββββββββββββ
mkdir -p "$DOWNLOAD_DIR"
if [ -f "$LOCAL_ZIP" ]; then
echo "β Model zip already present at $LOCAL_ZIP β skipping download."
else
echo "β Downloading $MODEL_FILE (~4.4 GB)..."
curl -L --fail-with-body \
-H "Authorization: Bearer $TOKEN" \
-o "$LOCAL_ZIP" \
"$MODEL_URL"
echo "β Downloaded to $LOCAL_ZIP"
fi
# Size sanity β the zip is known ~4.4 GB; anything smaller is a 403 body or
# a partial transfer.
ZIP_BYTES=$(wc -c < "$LOCAL_ZIP")
if [ "$ZIP_BYTES" -lt 4000000000 ]; then
echo "ERROR: downloaded file is only $ZIP_BYTES bytes (expected ~4.4 GB)." >&2
echo " Likely HTTP error body saved as zip. Inspect: head -c 500 '$LOCAL_ZIP'" >&2
exit 1
fi
# ββ Push + extract on-device βββββββββββββββββββββββββββββββββββββββββββββββ
# Git-Bash path-mangling guard: /data/local/tmp would become C:\Program Files\Git\data\...
export MSYS_NO_PATHCONV=1
export MSYS2_ARG_CONV_EXCL="*"
TMP_ON_DEVICE="/data/local/tmp/${MODEL_FILE}"
echo "β Pushing zip to $TMP_ON_DEVICE (this can take a few minutes over USB/wireless)..."
adb push "$LOCAL_ZIP" "$TMP_ON_DEVICE"
echo "β Creating app-private model directory..."
adb shell "run-as $PKG mkdir -p $ON_DEVICE_DIR"
echo "β Extracting in-place (app-private storage)..."
adb shell "run-as $PKG sh -c 'cd $ON_DEVICE_DIR && unzip -o $TMP_ON_DEVICE'"
echo "β Removing pushed zip from /data/local/tmp..."
adb shell "rm $TMP_ON_DEVICE"
# ββ Verify βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
CONFIG_EXISTS=$(adb shell "run-as $PKG sh -c 'test -f $ON_DEVICE_DIR/config.txt && echo YES || echo NO'" | tr -d '\r\n')
if [ "$CONFIG_EXISTS" != "YES" ]; then
echo "ERROR: config.txt not found under $ON_DEVICE_DIR after extract. Unzip likely failed." >&2
exit 1
fi
FILE_COUNT=$(adb shell "run-as $PKG sh -c 'ls $ON_DEVICE_DIR | wc -l'" | tr -d '\r\n')
echo "β Model extracted: $FILE_COUNT files under /data/data/$PKG/$ON_DEVICE_DIR"
echo ""
echo "Next: open the Sakhi app β Field Mode β On-Device Probe β Check Status,"
echo " then Load Model. First load takes ~10 s."
|