Initial import: bare-metal stacks, Server UI, GPU fan, tutorials.
Infrastructure configs for GMKtec K11 (Docker, vLLM, LocalAI, ComfyUI, control-plane, gpu-fan agent, Server UI with CLI/file explorer/GPU fan curve). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared helpers for models.catalog.yaml (simple parser, no PyYAML required).
|
||||
|
||||
CATALOG_FILE="${CATALOG_FILE:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/models.catalog.yaml}"
|
||||
|
||||
catalog_list_ids() {
|
||||
grep '^ - id:' "${CATALOG_FILE}" | awk '{print $3}'
|
||||
}
|
||||
|
||||
catalog_get_field() {
|
||||
local model_id="$1"
|
||||
local field="$2"
|
||||
awk -v id="${model_id}" -v key="${field}:" '
|
||||
$0 ~ "^ - id: " id "$" { found=1; next }
|
||||
found && $0 ~ "^ - id:" { exit }
|
||||
found && index($0, key) == 5 { sub(/^ [^:]+: /, ""); print; exit }
|
||||
' "${CATALOG_FILE}"
|
||||
}
|
||||
|
||||
catalog_ensure_dirs() {
|
||||
local data_root="${1:-/data}"
|
||||
mkdir -p \
|
||||
"${data_root}/apps/vllm/huggingface" \
|
||||
"${data_root}/apps/gguf/qwen3.6-27b" \
|
||||
"${data_root}/apps/gguf/gemma-4-12b"
|
||||
}
|
||||
|
||||
catalog_model_downloaded() {
|
||||
local model_id="$1"
|
||||
local runtime
|
||||
runtime="$(catalog_get_field "${model_id}" runtime)"
|
||||
|
||||
case "${runtime}" in
|
||||
vllm)
|
||||
local hf_model
|
||||
hf_model="$(catalog_get_field "${model_id}" hf_model)"
|
||||
local cache="${DATA_ROOT:-/data}/apps/vllm/huggingface"
|
||||
# Heuristic: HF hub cache contains repo name
|
||||
local repo_path
|
||||
repo_path=$(echo "${hf_model}" | tr '/' '--')
|
||||
if find "${cache}/hub" -maxdepth 3 -type d -name "models--${repo_path}" 2>/dev/null | grep -q .; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
;;
|
||||
llamacpp)
|
||||
local local_path local_dir gguf_file
|
||||
local_path="$(catalog_get_field "${model_id}" local_path)"
|
||||
local_dir="$(catalog_get_field "${model_id}" local_dir)"
|
||||
gguf_file="$(catalog_get_field "${model_id}" gguf_file)"
|
||||
if [[ -n "${local_path}" && -f "${local_path}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ -n "${local_dir}" && -n "${gguf_file}" && -f "${local_dir}/${gguf_file}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
Executable
+114
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
STACK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${SCRIPT_DIR}/catalog-lib.sh"
|
||||
|
||||
cd "${STACK_DIR}"
|
||||
|
||||
if [[ $# -lt 1 ]]; then
|
||||
echo "Usage: $0 <catalog-model-id>"
|
||||
echo ""
|
||||
echo "Available models:"
|
||||
catalog_list_ids | sed 's/^/ /'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MODEL_ID="$1"
|
||||
|
||||
if ! catalog_list_ids | grep -qx "${MODEL_ID}"; then
|
||||
echo "ERROR: Unknown model id: ${MODEL_ID}"
|
||||
echo "Run: ./scripts/list-models.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f .env ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
set +a
|
||||
fi
|
||||
|
||||
DATA_ROOT="${DATA_ROOT:-/data}"
|
||||
catalog_ensure_dirs "${DATA_ROOT}"
|
||||
|
||||
RUNTIME="$(catalog_get_field "${MODEL_ID}" runtime)"
|
||||
NAME="$(catalog_get_field "${MODEL_ID}" name)"
|
||||
|
||||
echo "=== Download: ${NAME} (${MODEL_ID}) ==="
|
||||
echo "Runtime: ${RUNTIME}"
|
||||
echo ""
|
||||
|
||||
case "${RUNTIME}" in
|
||||
vllm)
|
||||
HF_MODEL="$(catalog_get_field "${MODEL_ID}" hf_model)"
|
||||
echo "Target: Hugging Face cache at ${DATA_ROOT}/apps/vllm/huggingface"
|
||||
echo "Model: ${HF_MODEL}"
|
||||
echo ""
|
||||
|
||||
HF_ENV=()
|
||||
if [[ -n "${HF_TOKEN:-}" ]]; then
|
||||
HF_ENV=(-e "HF_TOKEN=${HF_TOKEN}")
|
||||
fi
|
||||
|
||||
docker run --rm \
|
||||
"${HF_ENV[@]}" \
|
||||
-v "${DATA_ROOT}/apps/vllm/huggingface:/root/.cache/huggingface" \
|
||||
ghcr.io/huggingface/huggingface-cli:latest \
|
||||
download "${HF_MODEL}"
|
||||
;;
|
||||
llamacpp)
|
||||
DOWNLOAD_URL="$(catalog_get_field "${MODEL_ID}" download_url)"
|
||||
LOCAL_PATH="$(catalog_get_field "${MODEL_ID}" local_path)"
|
||||
LOCAL_DIR="$(catalog_get_field "${MODEL_ID}" local_dir)"
|
||||
GGUF_FILE="$(catalog_get_field "${MODEL_ID}" gguf_file)"
|
||||
MMPROJ_URL="$(catalog_get_field "${MODEL_ID}" mmproj_url)"
|
||||
MMPROJ_FILE="$(catalog_get_field "${MODEL_ID}" mmproj_file)"
|
||||
|
||||
if [[ -n "${LOCAL_PATH}" ]]; then
|
||||
DEST="${LOCAL_PATH}"
|
||||
mkdir -p "$(dirname "${DEST}")"
|
||||
if [[ -f "${DEST}" ]]; then
|
||||
echo "Already exists: ${DEST}"
|
||||
else
|
||||
echo "Downloading ${DEST} ..."
|
||||
wget -c -O "${DEST}" "${DOWNLOAD_URL}"
|
||||
fi
|
||||
elif [[ -n "${LOCAL_DIR}" && -n "${GGUF_FILE}" ]]; then
|
||||
mkdir -p "${LOCAL_DIR}"
|
||||
DEST="${LOCAL_DIR}/${GGUF_FILE}"
|
||||
if [[ -f "${DEST}" ]]; then
|
||||
echo "Already exists: ${DEST}"
|
||||
else
|
||||
echo "Downloading ${DEST} ..."
|
||||
wget -c -O "${DEST}" "${DOWNLOAD_URL}"
|
||||
fi
|
||||
if [[ -n "${MMPROJ_URL}" && -n "${MMPROJ_FILE}" ]]; then
|
||||
MMPROJ_DEST="${LOCAL_DIR}/${MMPROJ_FILE}"
|
||||
if [[ -f "${MMPROJ_DEST}" ]]; then
|
||||
echo "Already exists: ${MMPROJ_DEST}"
|
||||
else
|
||||
echo "Downloading ${MMPROJ_DEST} ..."
|
||||
wget -c -O "${MMPROJ_DEST}" "${MMPROJ_URL}"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "ERROR: Catalog entry missing local_path or local_dir/gguf_file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "GGUF saved for future llama.cpp stack (stacks/llamacpp/README.md)."
|
||||
echo "Standard vLLM cannot load .gguf files."
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unknown runtime: ${RUNTIME}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "Done."
|
||||
Executable
+46
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
STACK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${SCRIPT_DIR}/catalog-lib.sh"
|
||||
|
||||
cd "${STACK_DIR}"
|
||||
|
||||
if [[ -f .env ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
set +a
|
||||
fi
|
||||
|
||||
DATA_ROOT="${DATA_ROOT:-/data}"
|
||||
catalog_ensure_dirs "${DATA_ROOT}"
|
||||
|
||||
echo "=== Model catalog ==="
|
||||
echo "Catalog: ${CATALOG_FILE}"
|
||||
echo "Data: ${DATA_ROOT}"
|
||||
echo ""
|
||||
|
||||
printf "%-24s %-10s %-8s %s\n" "ID" "RUNTIME" "ON DISK" "NAME"
|
||||
printf "%-24s %-10s %-8s %s\n" "----" "-------" "-------" "----"
|
||||
|
||||
while IFS= read -r model_id; do
|
||||
name="$(catalog_get_field "${model_id}" name)"
|
||||
runtime="$(catalog_get_field "${model_id}" runtime)"
|
||||
if catalog_model_downloaded "${model_id}"; then
|
||||
on_disk="yes"
|
||||
else
|
||||
on_disk="no"
|
||||
fi
|
||||
printf "%-24s %-10s %-8s %s\n" "${model_id}" "${runtime}" "${on_disk}" "${name}"
|
||||
done < <(catalog_list_ids)
|
||||
|
||||
echo ""
|
||||
echo "Download on demand: ./scripts/download-model.sh <id>"
|
||||
echo "Switch vLLM profile: ./scripts/switch-model.sh <profile-name>"
|
||||
echo ""
|
||||
echo "Profiles in profiles/:"
|
||||
ls -1 "${STACK_DIR}/profiles/"*.env 2>/dev/null | xargs -n1 basename | sed 's/^/ /' || true
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
STACK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${SCRIPT_DIR}/catalog-lib.sh"
|
||||
|
||||
cd "${STACK_DIR}"
|
||||
|
||||
PROFILE_ARG="${1:-}"
|
||||
|
||||
if [[ -n "${PROFILE_ARG}" ]]; then
|
||||
PROFILE_FILE="${STACK_DIR}/profiles/${PROFILE_ARG}.env"
|
||||
if [[ ! -f "${PROFILE_FILE}" ]]; then
|
||||
echo "ERROR: Profile not found: ${PROFILE_ARG}"
|
||||
echo " Available:"
|
||||
ls -1 "${STACK_DIR}/profiles/"*.env 2>/dev/null | xargs -n1 basename | sed 's/\.env$//' | sed 's/^/ /'
|
||||
exit 1
|
||||
fi
|
||||
cp "${PROFILE_FILE}" .env
|
||||
echo "Applied profile: ${PROFILE_ARG}"
|
||||
fi
|
||||
|
||||
if [[ ! -f .env ]]; then
|
||||
echo "ERROR: .env not found. Run: cp .env.example .env"
|
||||
echo " Or use a profile: ./scripts/start.sh qwen3.6-27b-awq-128k"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
set +a
|
||||
|
||||
DATA_ROOT="${DATA_ROOT:-/data}"
|
||||
|
||||
if [[ -z "${VLLM_MODEL:-}" ]]; then
|
||||
echo "ERROR: VLLM_MODEL is empty in .env"
|
||||
echo " Set a model, use a profile, or run:"
|
||||
echo " ./scripts/download-model.sh qwen3.6-27b-awq-vllm"
|
||||
echo " ./scripts/switch-model.sh qwen3.6-27b-awq-128k"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${VLLM_MODEL}" == *".gguf"* ]]; then
|
||||
echo "ERROR: VLLM_MODEL points to a .gguf file — standard vLLM cannot load GGUF."
|
||||
echo " GGUF models are in models.catalog.yaml for future llama.cpp (stacks/llamacpp/)."
|
||||
echo " For vLLM use AWQ from Hugging Face, e.g.:"
|
||||
echo " ./scripts/download-model.sh qwen3.6-27b-awq-vllm"
|
||||
echo " ./scripts/switch-model.sh qwen3.6-27b-awq-128k"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! mountpoint -q "${DATA_ROOT}" 2>/dev/null; then
|
||||
echo "ERROR: ${DATA_ROOT} is not mounted"
|
||||
echo " Complete disk setup (tutorial 04 part A) first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
catalog_ensure_dirs "${DATA_ROOT}"
|
||||
|
||||
if ! docker info &>/dev/null; then
|
||||
echo "ERROR: Docker is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x "${SCRIPT_DIR}/vllm-entrypoint.sh" 2>/dev/null || true
|
||||
|
||||
echo "=== vLLM stack ==="
|
||||
echo "Model: ${VLLM_MODEL}"
|
||||
echo "Served as: ${SERVED_MODEL_NAME:-qwen3.6-27b}"
|
||||
echo "Context: ${MAX_MODEL_LEN:-131072} tokens"
|
||||
echo "KV cache: ${KV_CACHE_DTYPE:-fp8}"
|
||||
echo "Quantize: ${QUANTIZATION:-(none)}"
|
||||
echo "Data root: ${DATA_ROOT}"
|
||||
echo ""
|
||||
|
||||
docker compose --profile vllm pull
|
||||
docker compose --profile vllm up -d
|
||||
|
||||
echo ""
|
||||
echo "Started. Follow logs:"
|
||||
echo " docker compose --profile vllm logs -f vllm"
|
||||
echo ""
|
||||
echo "Test when ready:"
|
||||
echo " curl -s http://localhost:${VLLM_PORT:-8000}/v1/models | jq ."
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
STACK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
source "${SCRIPT_DIR}/catalog-lib.sh"
|
||||
|
||||
cd "${STACK_DIR}"
|
||||
|
||||
PROFILE="${1:-}"
|
||||
|
||||
if [[ -z "${PROFILE}" ]]; then
|
||||
echo "Usage: $0 <profile-name>"
|
||||
echo ""
|
||||
echo "Profiles:"
|
||||
ls -1 "${STACK_DIR}/profiles/"*.env 2>/dev/null | xargs -n1 basename | sed 's/\.env$//' | sed 's/^/ /'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROFILE_FILE="${STACK_DIR}/profiles/${PROFILE}.env"
|
||||
if [[ ! -f "${PROFILE_FILE}" ]]; then
|
||||
echo "ERROR: Profile not found: ${PROFILE_FILE}"
|
||||
echo ""
|
||||
echo "Available profiles:"
|
||||
ls -1 "${STACK_DIR}/profiles/"*.env 2>/dev/null | xargs -n1 basename | sed 's/\.env$//' | sed 's/^/ /'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f .env ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
set +a
|
||||
fi
|
||||
|
||||
DATA_ROOT="${DATA_ROOT:-/data}"
|
||||
catalog_ensure_dirs "${DATA_ROOT}"
|
||||
|
||||
echo "=== Switch vLLM profile: ${PROFILE} ==="
|
||||
cp "${PROFILE_FILE}" .env
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source .env
|
||||
set +a
|
||||
|
||||
if [[ "${VLLM_MODEL:-}" == *".gguf"* ]]; then
|
||||
echo "ERROR: VLLM_MODEL points to a .gguf file — vLLM does not support GGUF."
|
||||
echo " Use stacks/llamacpp (future) or an AWQ Hugging Face model."
|
||||
echo " See: ./scripts/list-models.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${VLLM_MODEL:-}" ]]; then
|
||||
echo "ERROR: Profile has empty VLLM_MODEL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Model: ${VLLM_MODEL}"
|
||||
echo "Served as: ${SERVED_MODEL_NAME:-qwen3.6-27b}"
|
||||
echo "Context: ${MAX_MODEL_LEN:-131072}"
|
||||
echo ""
|
||||
|
||||
if docker compose --profile vllm ps -q vllm 2>/dev/null | grep -q .; then
|
||||
echo "Restarting vLLM container ..."
|
||||
docker compose --profile vllm down
|
||||
"${SCRIPT_DIR}/start.sh"
|
||||
else
|
||||
echo "vLLM not running. Start with:"
|
||||
echo " ./scripts/start.sh"
|
||||
fi
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ARGS=(
|
||||
--model "${VLLM_MODEL}"
|
||||
--served-model-name "${SERVED_MODEL_NAME:-qwen3.6-27b}"
|
||||
--host 0.0.0.0
|
||||
--port 8000
|
||||
--max-model-len "${MAX_MODEL_LEN:-131072}"
|
||||
--max-num-seqs "${MAX_NUM_SEQS:-1}"
|
||||
--gpu-memory-utilization "${GPU_MEMORY_UTILIZATION:-0.95}"
|
||||
--kv-cache-dtype "${KV_CACHE_DTYPE:-fp8}"
|
||||
--trust-remote-code
|
||||
)
|
||||
|
||||
if [[ -n "${QUANTIZATION:-}" ]]; then
|
||||
ARGS+=(--quantization "${QUANTIZATION}")
|
||||
fi
|
||||
|
||||
if [[ -n "${VLLM_EXTRA_ARGS:-}" ]]; then
|
||||
# shellcheck disable=SC2206
|
||||
EXTRA=( ${VLLM_EXTRA_ARGS} )
|
||||
ARGS+=("${EXTRA[@]}")
|
||||
fi
|
||||
|
||||
exec vllm serve "${ARGS[@]}"
|
||||
Reference in New Issue
Block a user