Files
club-3090/scripts/launch.sh
T
noonghunna 2f8bade82c fix(docs): bump curl smoke-test max_tokens 30 → 200 (#14)
Qwen3.6 thinks before answering by default, so a "Capital of France?"
smoke with max_tokens=30 returns truncated mid-`<think>` content. apnar
hit this on a working stack (verify-full.sh all green) and wasted time
debugging a non-bug.

Bump all 7 user-facing curl examples to max_tokens=200 (covers a typical
think block + the one-sentence answer with headroom).

verify-full.sh / verify.sh / verify-stress.sh stay at max_tokens=30
because they already pass chat_template_kwargs.enable_thinking=false,
which skips the think block entirely.

EXAMPLES.md gets an inline note explaining the headroom + the alternative
(disable thinking via chat_template_kwargs) for users who want a tighter
smoke.
2026-04-30 21:59:17 +00:00

245 lines
8.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
#
# Interactive launcher for club-3090 — pick engine + workload, boot the
# right compose, run verify-full to confirm it's serving.
#
# For first-run users coming in from the README. If you already know
# what you want, use `scripts/switch.sh <variant>` directly.
#
# Usage:
# bash scripts/launch.sh # interactive wizard
# bash scripts/launch.sh --variant <name> # skip wizard, boot directly
# bash scripts/launch.sh --engine vllm --cards 1 # partial flags, ask the rest
# bash scripts/launch.sh --no-verify # skip post-launch verify-full
# bash scripts/launch.sh --no-preflight # skip docker/GPU pre-flight
#
# All flags accept the same names as `switch.sh --list` produces.
# Examples:
# bash scripts/launch.sh --variant vllm/default
# bash scripts/launch.sh --variant llamacpp/default
# bash scripts/launch.sh --variant vllm/dual
set -euo pipefail
ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
SWITCH="${SWITCH:-${ROOT_DIR}/scripts/switch.sh}"
VERIFY="${VERIFY:-${ROOT_DIR}/scripts/verify-full.sh}"
# shellcheck source=preflight.sh
source "${ROOT_DIR}/scripts/preflight.sh"
# --- arg parsing ---
ENGINE=""
CARDS=""
VARIANT=""
SKIP_VERIFY=0
SKIP_PREFLIGHT=0
while [[ $# -gt 0 ]]; do
case "$1" in
--engine) ENGINE="$2"; shift 2 ;;
--cards) CARDS="$2"; shift 2 ;;
--variant) VARIANT="$2"; shift 2 ;;
--no-verify) SKIP_VERIFY=1; shift ;;
--no-preflight) SKIP_PREFLIGHT=1; shift ;;
-h|--help)
sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'
exit 0 ;;
*) echo "Unknown flag: $1"; exit 1 ;;
esac
done
# --- pre-flight ---
if [[ $SKIP_PREFLIGHT -eq 0 ]]; then
echo "[preflight] checking environment..."
preflight_docker || exit 1
preflight_gpu 1 || exit 1
preflight_gpu_idle
preflight_running
echo "[preflight] ok."
echo ""
fi
ask() {
# ask "prompt" "default" -> echoes user input or default
local p="$1" d="${2:-}" reply
if [[ -n "$d" ]]; then
read -rp "$p [${d}]: " reply
echo "${reply:-$d}"
else
read -rp "$p: " reply
echo "$reply"
fi
}
choose() {
# choose "prompt" "label1" "value1" "label2" "value2" ... -> echoes chosen value
local prompt="$1"; shift
local i=1 labels=() values=()
while [[ $# -gt 0 ]]; do
labels+=("$1"); values+=("$2"); shift 2
done
echo "" >&2
echo "$prompt" >&2
for l in "${labels[@]}"; do
printf " %d) %s\n" "$i" "$l" >&2
i=$((i+1))
done
while true; do
local pick
read -rp "Choice [1-${#labels[@]}]: " pick
if [[ "$pick" =~ ^[0-9]+$ ]] && (( pick >= 1 && pick <= ${#labels[@]} )); then
echo "${values[$((pick-1))]}"
return
fi
echo " invalid — pick a number 1-${#labels[@]}" >&2
done
}
# --- wizard ---
# Flow: cards → workload → auto-pick engine. Newcomers can answer "how
# many GPUs" and "what do I want to do" but rarely "vLLM or llama.cpp" —
# so the engine is derived from the workload pick, not asked first.
# Manual --engine override filters the workload list to that engine.
if [[ -z "$VARIANT" ]]; then
echo ""
echo "club-3090 launcher — let's pick the right config for your workload."
echo "(Use --variant <name> next time to skip the wizard.)"
echo ""
# Step 1 — cards.
if [[ -z "$CARDS" ]]; then
CARDS=$(choose "How many RTX 3090s?" \
"1× 3090 (24 GB)" "1" \
"2× 3090 (PCIe / no NVLink)" "2")
fi
# Re-validate now that we know the requirement (preflight already ran
# with min=1; bump to actual count). Skip if --no-preflight.
if [[ $SKIP_PREFLIGHT -eq 0 ]]; then
preflight_gpu "$CARDS" || exit 1
fi
# Step 2 — workload, filtered by cards (and --engine override if set).
# Each option's value is "engine/file" so engine is implied by the pick.
if [[ "$CARDS" == "1" ]]; then
# Primary recommended options first; diagnostic / niche variants in
# an "Other" group at the end. The only single-card limitation users
# need to know: vLLM single-card crashes on a single prompt >50K
# (Cliff 2). For unpredictable inputs, use llamacpp/default.
if [[ -z "$ENGINE" || "$ENGINE" == "vllm" ]]; then
VLLM_OPTS=(
"Long ctx + vision (198K + vision, MTP) — recommended for chat/agents" "vllm/long-vision"
"Long ctx, text only (218K, MTP) — recommended for RAG/codebase" "vllm/long-text"
"Bounded thinking 218K (structured CoT — ~30× cheaper think on coding)" "vllm/bounded-thinking"
)
else
VLLM_OPTS=()
fi
if [[ -z "$ENGINE" || "$ENGINE" == "llamacpp" ]]; then
LLAMA_OPTS=(
"Bulletproof, no cliffs (262K + vision, ~21 TPS) — production-safe" "llamacpp/default"
)
else
LLAMA_OPTS=()
fi
# Diagnostic / niche fallbacks — shown last so they don't dominate the menu
if [[ -z "$ENGINE" || "$ENGINE" == "vllm" ]]; then
VLLM_FALLBACK_OPTS=(
"[fallback] Default 48K + vision (Cliff 2 unreachable; fast boot)" "vllm/default"
"[fallback] tools-text 75K FP8 (FP8 KV alternative for accuracy compare)" "vllm/tools-text"
"[fallback] minimal 32K (no Genesis, no spec-decode — diagnostic stack)" "vllm/minimal"
)
else
VLLM_FALLBACK_OPTS=()
fi
if [[ -z "$ENGINE" || "$ENGINE" == "llamacpp" ]]; then
LLAMA_FALLBACK_OPTS=(
"[fallback] llamacpp/concurrent (4 parallel slots, 192K pool, vision)" "llamacpp/concurrent"
)
else
LLAMA_FALLBACK_OPTS=()
fi
VARIANT=$(choose "What's your main workload?" \
"${VLLM_OPTS[@]}" "${LLAMA_OPTS[@]}" \
"${VLLM_FALLBACK_OPTS[@]}" "${LLAMA_FALLBACK_OPTS[@]}")
elif [[ "$CARDS" == "2" ]]; then
if [[ -n "$ENGINE" && "$ENGINE" != "vllm" ]]; then
echo "ERROR: --engine ${ENGINE} not supported on 2× cards (no llama.cpp dual recipe yet)." >&2
exit 1
fi
VARIANT=$(choose "What's your dual-card priority?" \
"Balanced default — 262K + vision + 2 streams (recommended)" "vllm/dual" \
"Multi-tenant — 4 concurrent streams @ 262K, TQ3 KV" "vllm/dual-turbo" \
"Peak code TPS with vision (185K, DFlash N=5)" "vllm/dual-dflash" \
"Peak code TPS no vision (200K, DFlash N=5)" "vllm/dual-dflash-noviz")
else
echo "ERROR: --cards ${CARDS} unsupported (expected 1 or 2)." >&2
exit 1
fi
# Step 3 — explain the auto-picked engine.
echo ""
case "$VARIANT" in
llamacpp/*)
echo "[wizard] picked llama.cpp — chosen because no prefill cliffs at 262K and the simplest"
echo "[wizard] serving path. Trade-off: ~21 TPS vs 70+ on vLLM. Right call for long-prompt"
echo "[wizard] robustness, frontier context, or anyone who wants the simplest setup."
;;
vllm/*)
echo "[wizard] picked vLLM — chosen for spec-decode (MTP), tool-call extraction, and best TPS."
echo "[wizard] Watch out for the prefill cliffs (Cliff 1 = 25K+ tool returns; Cliff 2 = 50-60K"
echo "[wizard] single prompts on TQ3) — see docs/SINGLE_CARD.md for the safe-config map."
;;
esac
fi
# --- launch + verify ---
echo ""
echo "[launch] selected variant: ${VARIANT}"
echo ""
"$SWITCH" "$VARIANT"
if [[ $SKIP_VERIFY -eq 1 ]]; then
echo "[launch] --no-verify — skipping verify-full.sh"
else
echo ""
echo "[launch] running verify-full.sh against the new server..."
echo ""
bash "$VERIFY" || {
echo ""
echo "[launch] some checks failed — see hints above. Common cases:"
echo " - 'reasoning field empty' on llama.cpp = expected (parser gap, not a bug)"
echo " - 'Genesis patches' / 'MTP acceptance' skipped on llama.cpp = expected (vLLM-only checks)"
exit 1
}
fi
# Resolve the actual endpoint port the same way switch.sh does:
# explicit $PORT > per-variant default. Mirrors VARIANT_DEFAULT_PORT in
# switch.sh — keep in sync if you add a new variant.
declare -A LAUNCH_DEFAULT_PORT=(
[vllm/default]=8020
[vllm/long-vision]=8020
[vllm/long-text]=8020
[vllm/bounded-thinking]=8020
[vllm/tools-text]=8020
[vllm/minimal]=8020
[vllm/dual]=8010
[vllm/dual-turbo]=8011
[vllm/dual-dflash]=8012
[vllm/dual-dflash-noviz]=8013
[llamacpp/default]=8020
[llamacpp/concurrent]=8020
)
ENDPOINT_PORT="${PORT:-${LAUNCH_DEFAULT_PORT[$VARIANT]:-8020}}"
ENDPOINT_URL="http://localhost:${ENDPOINT_PORT}"
echo ""
echo "[launch] done. Endpoint: ${ENDPOINT_URL}"
echo "[launch] sample request:"
echo " curl -sf ${ENDPOINT_URL}/v1/chat/completions \\"
echo " -H 'Content-Type: application/json' \\"
echo " -d '{\"model\":\"qwen3.6-27b-autoround\",\"messages\":[{\"role\":\"user\",\"content\":\"Capital of France?\"}],\"max_tokens\":200}'"
echo ""
echo "[launch] switch later with: bash scripts/switch.sh <variant>"
echo "[launch] list variants: bash scripts/switch.sh --list"