Skip to content

Instantly share code, notes, and snippets.

@andrisgauracs
Created July 31, 2026 02:57
Show Gist options
  • Select an option

  • Save andrisgauracs/e84b842d0f5e301485ecbf6654b0838e to your computer and use it in GitHub Desktop.

Select an option

Save andrisgauracs/e84b842d0f5e301485ecbf6654b0838e to your computer and use it in GitHub Desktop.
Test a custom prompt against an already-trained, already-flashed ESP32-S3 model
#!/usr/bin/env bash
# esp32-ai: test a custom prompt against an already-trained, already-flashed model.
# Does NOT retrain and does NOT rewrite the 15MB model partition.
#
# Usage:
# ./run_custom_prompt.sh "Once there was a robot"
set -euo pipefail
if [ "$#" -lt 1 ]; then
echo "Usage: $0 \"Your custom prompt\""
exit 1
fi
PROMPT="$1"
REPO_DIR="esp32-ai"
if [ -f "$REPO_DIR/pyproject.toml" ]; then
cd "$REPO_DIR"
elif [ ! -f "pyproject.toml" ]; then
echo "Can't find the esp32-ai repo. Run this from the same folder as build_and_flash.sh, after it has cloned esp32-ai."
exit 1
fi
if [ ! -f "data/bpe32768.json" ]; then
echo "data/bpe32768.json not found. Run build_and_flash.sh at least once first (it trains the tokenizer)."
exit 1
fi
# ---- Detect port ----
CANDIDATE_PORTS=(/dev/cu.usbmodem*)
if [ ! -e "${CANDIDATE_PORTS[0]}" ]; then
echo "No /dev/cu.usbmodem* device found. Is the board plugged in?"
exit 1
elif [ "${#CANDIDATE_PORTS[@]}" -eq 1 ]; then
ESP32_PORT="${CANDIDATE_PORTS[0]}"
else
select choice in "${CANDIDATE_PORTS[@]}"; do
[ -n "$choice" ] && ESP32_PORT="$choice" && break
done
fi
echo ">> Using port: $ESP32_PORT"
# ---- Tokenize the custom prompt with the same tokenizer gen_assets.py uses ----
echo ">> Tokenizing prompt: \"$PROMPT\""
TOKENIZE_SCRIPT="$(mktemp /tmp/tokenize_prompt_XXXX.py)"
cat > "$TOKENIZE_SCRIPT" << 'PYEOF'
import sys
from tokenizers import Tokenizer
tok = Tokenizer.from_file("data/bpe32768.json")
prompt = sys.argv[1]
ids = tok.encode(prompt).ids
print(f'prompt "{prompt}" -> {len(ids)} ids: {ids}')
print(f"round-trip: {tok.decode(ids)!r}")
print("static const int PROMPT_IDS[] = {" + ", ".join(map(str, ids)) + "};")
PYEOF
GEN_OUTPUT="$(uv run python "$TOKENIZE_SCRIPT" "$PROMPT")"
rm -f "$TOKENIZE_SCRIPT"
echo "$GEN_OUTPUT"
PROMPT_IDS_LINE="$(echo "$GEN_OUTPUT" | grep -o 'static const int PROMPT_IDS\[\] = {[^}]*};')"
if [ -z "$PROMPT_IDS_LINE" ]; then
echo "!! Couldn't parse a PROMPT_IDS line out of the tokenizer output. Aborting."
exit 1
fi
echo ">> New prompt line: $PROMPT_IDS_LINE"
# ---- Paste it into the sketch ----
SKETCH_FILE="firmware/esp32_llm/esp32_llm.ino"
sed -i.bak -E "s/static const int PROMPT_IDS\[\] = \{[^}]*\};/${PROMPT_IDS_LINE//\//\\/}/" "$SKETCH_FILE"
# ---- Rebuild and reflash just the app (model partition untouched) ----
FQBN='esp32:esp32:esp32s3:UploadSpeed=921600,USBMode=hwcdc,CDCOnBoot=default,UploadMode=default,CPUFreq=240,FlashMode=qio,FlashSize=16M,PartitionScheme=custom,PSRAM=opi,DebugLevel=info'
echo ">> Recompiling firmware with new prompt..."
arduino-cli compile \
--fqbn "$FQBN" \
--build-property compiler.optimization_flags=-O3 \
--build-path /tmp/esp32-llm-uart \
firmware/esp32_llm
echo ">> Flashing app (model partition is untouched)..."
arduino-cli upload -p "$ESP32_PORT" \
--fqbn "$FQBN" \
--input-dir /tmp/esp32-llm-uart firmware/esp32_llm
echo ">> Opening serial monitor. Press the board's RESET button now."
arduino-cli monitor -p "$ESP32_PORT" --config baudrate=115200
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment