|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Opt-in Gemma4 Vulkan smoke test for TurboQuant K/V cache. |
| 3 | +# |
| 4 | +# This is intentionally not a default CI test: it needs a large Gemma4 GGUF, |
| 5 | +# a Vulkan-capable host, and enough VRAM/system memory to launch llama-server. |
| 6 | +# |
| 7 | +# Required: |
| 8 | +# GEMMA4_MODEL=/path/to/gemma-4-26B-A4B-it-Q4_K_M.gguf |
| 9 | +# |
| 10 | +# Optional: |
| 11 | +# LLAMA_SERVER=/path/to/llama-server |
| 12 | +# CHAT_TEMPLATE_FILE=/path/to/google-gemma-4-31B-it-interleaved.fixed.jinja |
| 13 | +# HOST=127.0.0.1 PORT=18085 CTX_SIZE=65537 BATCH_SIZE=512 UBATCH_SIZE=512 |
| 14 | +# GPU_LAYERS=999 GPU_LAYERS_DRAFT=999 N_CPU_MOE=38 N_CPU_MOE_DRAFT=38 |
| 15 | +# NO_MMPROJ=1 LOG_FILE=/tmp/llama-turbo4-vulkan-gemma4-smoke.log |
| 16 | +# PROMPT='Reply with exactly: turbo4v-ok' EXPECT='turbo4v-ok' |
| 17 | +# EXTRA_ARGS='--some-llama-server-flag value' |
| 18 | + |
| 19 | +set -euo pipefail |
| 20 | + |
| 21 | +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" |
| 22 | + |
| 23 | +LLAMA_SERVER="${LLAMA_SERVER:-}" |
| 24 | +if [[ -z "$LLAMA_SERVER" ]]; then |
| 25 | + for candidate in \ |
| 26 | + "$ROOT_DIR/build/bin/llama-server" \ |
| 27 | + "$ROOT_DIR/build-vulkan/bin/llama-server" \ |
| 28 | + "$ROOT_DIR/build/bin/server" \ |
| 29 | + "$ROOT_DIR/build-vulkan/bin/server"; do |
| 30 | + if [[ -x "$candidate" ]]; then |
| 31 | + LLAMA_SERVER="$candidate" |
| 32 | + break |
| 33 | + fi |
| 34 | + done |
| 35 | +fi |
| 36 | + |
| 37 | +MODEL="${GEMMA4_MODEL:-${MODEL:-}}" |
| 38 | +CHAT_TEMPLATE_FILE="${CHAT_TEMPLATE_FILE:-}" |
| 39 | +HOST="${HOST:-127.0.0.1}" |
| 40 | +PORT="${PORT:-18085}" |
| 41 | +CTX_SIZE="${CTX_SIZE:-65537}" |
| 42 | +BATCH_SIZE="${BATCH_SIZE:-512}" |
| 43 | +UBATCH_SIZE="${UBATCH_SIZE:-512}" |
| 44 | +GPU_LAYERS="${GPU_LAYERS:-999}" |
| 45 | +GPU_LAYERS_DRAFT="${GPU_LAYERS_DRAFT:-999}" |
| 46 | +N_CPU_MOE="${N_CPU_MOE:-38}" |
| 47 | +N_CPU_MOE_DRAFT="${N_CPU_MOE_DRAFT:-38}" |
| 48 | +NO_MMPROJ="${NO_MMPROJ:-1}" |
| 49 | +STARTUP_TIMEOUT="${STARTUP_TIMEOUT:-180}" |
| 50 | +PROMPT="${PROMPT:-Reply with exactly: turbo4v-ok}" |
| 51 | +EXPECT="${EXPECT:-turbo4v-ok}" |
| 52 | +LOG_FILE="${LOG_FILE:-${TMPDIR:-/tmp}/llama-turbo4-vulkan-gemma4-smoke.log}" |
| 53 | +REQUIRE_LOG_MARKERS="${REQUIRE_LOG_MARKERS:-1}" |
| 54 | + |
| 55 | +if [[ -z "$LLAMA_SERVER" || ! -x "$LLAMA_SERVER" ]]; then |
| 56 | + echo "SKIP: set LLAMA_SERVER or build llama-server before running this smoke test." |
| 57 | + exit 0 |
| 58 | +fi |
| 59 | + |
| 60 | +if [[ -z "$MODEL" || ! -f "$MODEL" ]]; then |
| 61 | + echo "SKIP: set GEMMA4_MODEL to a Gemma4 GGUF before running this smoke test." |
| 62 | + exit 0 |
| 63 | +fi |
| 64 | + |
| 65 | +if ! command -v curl >/dev/null 2>&1; then |
| 66 | + echo "SKIP: curl is required for the server smoke test." |
| 67 | + exit 0 |
| 68 | +fi |
| 69 | + |
| 70 | +if ! command -v python3 >/dev/null 2>&1; then |
| 71 | + echo "SKIP: python3 is required to build the JSON request." |
| 72 | + exit 0 |
| 73 | +fi |
| 74 | + |
| 75 | +SERVER_PID="" |
| 76 | +REQUEST_FILE="$(mktemp "${TMPDIR:-/tmp}/turbo4-vulkan-gemma4-request.XXXXXX.json")" |
| 77 | +RESPONSE_FILE="$(mktemp "${TMPDIR:-/tmp}/turbo4-vulkan-gemma4-response.XXXXXX.json")" |
| 78 | + |
| 79 | +cleanup() { |
| 80 | + if [[ -n "$SERVER_PID" ]] && kill -0 "$SERVER_PID" >/dev/null 2>&1; then |
| 81 | + kill "$SERVER_PID" >/dev/null 2>&1 || true |
| 82 | + wait "$SERVER_PID" >/dev/null 2>&1 || true |
| 83 | + fi |
| 84 | + rm -f "$REQUEST_FILE" "$RESPONSE_FILE" |
| 85 | +} |
| 86 | +trap cleanup EXIT |
| 87 | + |
| 88 | +server_args=( |
| 89 | + -m "$MODEL" |
| 90 | + -ngl "$GPU_LAYERS" |
| 91 | + -ngld "$GPU_LAYERS_DRAFT" |
| 92 | + -ncmoe "$N_CPU_MOE" |
| 93 | + -ncmoed "$N_CPU_MOE_DRAFT" |
| 94 | + -c "$CTX_SIZE" |
| 95 | + -b "$BATCH_SIZE" |
| 96 | + -ub "$UBATCH_SIZE" |
| 97 | + -fa on |
| 98 | + -ctk turbo4 |
| 99 | + -ctv turbo4 |
| 100 | + --reasoning off |
| 101 | + -np 1 |
| 102 | + -lv 4 |
| 103 | + --host "$HOST" |
| 104 | + --port "$PORT" |
| 105 | +) |
| 106 | + |
| 107 | +if [[ "$NO_MMPROJ" == "1" ]]; then |
| 108 | + server_args+=(--no-mmproj) |
| 109 | +fi |
| 110 | + |
| 111 | +if [[ -n "$CHAT_TEMPLATE_FILE" ]]; then |
| 112 | + server_args+=(--jinja --chat-template-file "$CHAT_TEMPLATE_FILE") |
| 113 | +fi |
| 114 | + |
| 115 | +if [[ -n "${EXTRA_ARGS:-}" ]]; then |
| 116 | + read -r -a extra_args <<< "$EXTRA_ARGS" |
| 117 | + server_args+=("${extra_args[@]}") |
| 118 | +fi |
| 119 | + |
| 120 | +mkdir -p "$(dirname "$LOG_FILE")" |
| 121 | +: > "$LOG_FILE" |
| 122 | + |
| 123 | +echo "Starting Gemma4 Vulkan TurboQuant smoke:" |
| 124 | +echo " server: $LLAMA_SERVER" |
| 125 | +echo " model: $MODEL" |
| 126 | +echo " cache: -ctk turbo4 -ctv turbo4" |
| 127 | +echo " log: $LOG_FILE" |
| 128 | + |
| 129 | +"$LLAMA_SERVER" "${server_args[@]}" > "$LOG_FILE" 2>&1 & |
| 130 | +SERVER_PID="$!" |
| 131 | + |
| 132 | +deadline=$((SECONDS + STARTUP_TIMEOUT)) |
| 133 | +until curl -fsS "http://$HOST:$PORT/health" >/dev/null 2>&1; do |
| 134 | + if ! kill -0 "$SERVER_PID" >/dev/null 2>&1; then |
| 135 | + echo "FAIL: llama-server exited before becoming healthy." |
| 136 | + tail -n 200 "$LOG_FILE" || true |
| 137 | + exit 1 |
| 138 | + fi |
| 139 | + if (( SECONDS >= deadline )); then |
| 140 | + echo "FAIL: llama-server did not become healthy within ${STARTUP_TIMEOUT}s." |
| 141 | + tail -n 200 "$LOG_FILE" || true |
| 142 | + exit 1 |
| 143 | + fi |
| 144 | + sleep 2 |
| 145 | +done |
| 146 | + |
| 147 | +PROMPT="$PROMPT" python3 - "$REQUEST_FILE" <<'PY' |
| 148 | +import json |
| 149 | +import os |
| 150 | +import sys |
| 151 | +
|
| 152 | +request = { |
| 153 | + "messages": [ |
| 154 | + {"role": "user", "content": os.environ["PROMPT"]}, |
| 155 | + ], |
| 156 | + "temperature": 0, |
| 157 | + "max_tokens": 16, |
| 158 | +} |
| 159 | +
|
| 160 | +with open(sys.argv[1], "w", encoding="utf-8") as f: |
| 161 | + json.dump(request, f) |
| 162 | +PY |
| 163 | + |
| 164 | +curl -fsS \ |
| 165 | + -H 'Content-Type: application/json' \ |
| 166 | + --data-binary "@$REQUEST_FILE" \ |
| 167 | + "http://$HOST:$PORT/v1/chat/completions" \ |
| 168 | + > "$RESPONSE_FILE" |
| 169 | + |
| 170 | +if ! grep -Fq "$EXPECT" "$RESPONSE_FILE"; then |
| 171 | + echo "FAIL: completion did not contain expected marker: $EXPECT" |
| 172 | + cat "$RESPONSE_FILE" |
| 173 | + echo |
| 174 | + tail -n 200 "$LOG_FILE" || true |
| 175 | + exit 1 |
| 176 | +fi |
| 177 | + |
| 178 | +if [[ "$REQUIRE_LOG_MARKERS" == "1" ]]; then |
| 179 | + if ! grep -Fq "Vulkan0" "$LOG_FILE"; then |
| 180 | + echo "FAIL: server log does not show Vulkan backend startup." |
| 181 | + tail -n 200 "$LOG_FILE" || true |
| 182 | + exit 1 |
| 183 | + fi |
| 184 | + if ! grep -Fq "K (turbo4)" "$LOG_FILE"; then |
| 185 | + echo "FAIL: server log does not show K cache using turbo4." |
| 186 | + tail -n 200 "$LOG_FILE" || true |
| 187 | + exit 1 |
| 188 | + fi |
| 189 | + if ! grep -Fq "V (turbo4)" "$LOG_FILE"; then |
| 190 | + echo "FAIL: server log does not show V cache using turbo4." |
| 191 | + tail -n 200 "$LOG_FILE" || true |
| 192 | + exit 1 |
| 193 | + fi |
| 194 | +fi |
| 195 | + |
| 196 | +echo "PASS: Gemma4 Vulkan server completed with -ctk turbo4 -ctv turbo4." |
0 commit comments