diff --git a/docs/llm.md b/docs/llm.md index 8e39d18..b4bfb16 100644 --- a/docs/llm.md +++ b/docs/llm.md @@ -120,7 +120,22 @@ Unit `gpu-rent-ollama` читает `/mnt/swarm_data/.gpu-rent-gpu.json`: На `up`: скачать GGUF → `/mnt/swarm_data/llamacpp/models` → `llama-server` + systemd. Уже скачанные крупные файлы не трогает. -**Бинарник:** у upstream нет Linux CUDA в GitHub Releases (только Windows). `install_llamacpp.sh` собирает `llama-server` из исходников (`GGML_CUDA=ON`, arch из GPU probe). Если `nvcc` недоступен — fallback на Ubuntu Vulkan/CPU asset (без Windows). Pin: `LLAMACPP_TAG=b10545`. Override: `LLAMACPP_ASSET_URL` + `LLAMACPP_SHA256`. +**Бинарник (без compile по умолчанию):** официальные Releases **не** дают Linux CUDA — только Windows CUDA + Ubuntu CPU/Vulkan. `install_llamacpp.sh` берёт **Ubuntu Vulkan** prebuilt (~30 MB, GPU через Vulkan). CUDA-сборка из исходников — только крайний случай (prebuilt упал) или явно `LLAMACPP_BUILD_CUDA=1` (5–15 мин, тихий лог + heartbeat). + +Переменные (`.env` или `gpu-rent.vars`, пробрасываются на VM при `up`): + +| Var | Зачем | +| --- | --- | +| `LLAMACPP_TAG` | pin release (`b10545`) | +| `LLAMACPP_ASSET_URL` + `LLAMACPP_SHA256` | свой архив | +| `LLAMACPP_BUILD_CUDA=1` | сразу CUDA из исходников | +| `LLAMACPP_FORCE_REINSTALL=1` | снести бинарь и поставить заново | +| `LLAMACPP_NGL` / `LLAMACPP_CTX` | override GPU layers / context | +| `LLAMACPP_HOST` / `LLAMACPP_PORT` | bind (default `127.0.0.1:8080`) | +| `LLAMACPP_EXTRA_ARGS` | доп. флаги `llama-server` | +| `OLLAMA_VERSION` / `OLLAMA_SHA256` | pin Ollama | + +Готовые кейсы A–H — в `gpu-rent.vars.example`. ### Пресеты (меню) @@ -158,4 +173,7 @@ OLLAMA_VERSION=0.6.5 OLLAMA_SHA256= LLAMACPP_TAG=b10545 # или LLAMACPP_ASSET_URL=... + LLAMACPP_SHA256=... +# CUDA из исходников (медленно): LLAMACPP_BUILD_CUDA=1 +# Переустановка бинаря: LLAMACPP_FORCE_REINSTALL=1 +# VRAM: LLAMACPP_NGL=40 LLAMACPP_CTX=4096 LLAMACPP_EXTRA_ARGS=--flash-attn on ``` diff --git a/env.example b/env.example index b0199ef..1045288 100644 --- a/env.example +++ b/env.example @@ -46,6 +46,17 @@ OLLAMA_LOCAL_PORT=17811 LLAMACPP_LOCAL_PORT=17812 # OLLAMA_MODELS_MANIFEST= # LLAMACPP_MODELS_MANIFEST= +# Pin / тонкая настройка LLM (несecреты; удобнее в gpu-rent.vars — см. кейсы A–H): +# LLAMACPP_TAG=b10545 +# LLAMACPP_BUILD_CUDA=1 +# LLAMACPP_FORCE_REINSTALL=1 +# LLAMACPP_ASSET_URL= +# LLAMACPP_SHA256= +# LLAMACPP_NGL=40 +# LLAMACPP_CTX=4096 +# LLAMACPP_EXTRA_ARGS=--flash-attn on +# OLLAMA_VERSION=0.6.5 +# OLLAMA_SHA256= # CIVITAI_API_TOKEN= # CIVITAI_API_HOST=civitai.red # Hugging Face (GGUF / gated HF URLs / capture fallback metadata): diff --git a/gpu-rent.vars.example b/gpu-rent.vars.example index 394c46d..94f7ba9 100644 --- a/gpu-rent.vars.example +++ b/gpu-rent.vars.example @@ -1,7 +1,7 @@ # gpu-rent.vars — локальные параметры запуска (не секреты). # Скопируй в gpu-rent.vars и правь. Файл в .gitignore. # Читают: gpu-rent.ps1 / .bat / .sh и python -m gpu_rent (после .env). -# Секреты (OS_PASSWORD, CIVITAI_*) — только в .env. +# Секреты (OS_PASSWORD, CIVITAI_*, HF_TOKEN) — только в .env. # Если запуск без аргументов (двойной клик / .\gpu-rent.ps1): # GPU_RENT_DEFAULT_ARGS=up --yes @@ -9,24 +9,76 @@ # Доп. флаги ко ВСЕМ вызовам (после твоих аргументов): # GPU_RENT_EXTRA_ARGS= -# Те же имена, что в .env / env.example (несекретные дефолты): +# --------------------------------------------------------------------------- +# Базовые (несекретные дефолты; те же имена, что в env.example) +# --------------------------------------------------------------------------- # UPDATE_GIT=true # DEFAULT_SPOT=true # SCAN_POOLS=ru-6,ru-7 # FLAVOR_PREFERENCE=4090-24,4090-48,a5000,a100-40 # SWARMUI_LOCAL_PORT=17801 # DATA_VOLUME_SIZE_GB=100 -# DEFAULT_FLAVOR_ID= -# DEFAULT_SPOT=true # ENABLE_SWARMUI=true -# WORKLOAD=llm # то же что ENABLE_SWARMUI=false (только LLM) -# LLM_RUNTIME=none -# OLLAMA_LOCAL_PORT=17811 -# LLAMACPP_LOCAL_PORT=17812 # IDLE_MINUTES=60 # IDLE_GRACE_MINUTES=90 -# LOCAL_WATCHDOG_GRACE_MINUTES=10 # NOTIFY_READY=true -# PULL_OUTPUT=false # Prefer auto /32 (leave unset). Spike/WARP only: # GPU_RENT_SSH_CIDR=0.0.0.0/0 + +# --------------------------------------------------------------------------- +# LLM — кейсы тонкой настройки (раскомментируй один блок) +# Подробнее: docs/llm.md +# --------------------------------------------------------------------------- + +# --- A) Быстрый default: SwarmUI + llama.cpp, Ubuntu Vulkan prebuilt --- +# LLM_RUNTIME=llamacpp +# ENABLE_SWARMUI=true +# LLAMACPP_TAG=b10545 +# (GGUF — llamacpp-models.yaml; бинарь без compile) + +# --- B) Только LLM, без SwarmUI (дешевле по времени bootstrap / VRAM) --- +# WORKLOAD=llm +# LLM_RUNTIME=llamacpp +# LLAMACPP_TAG=b10545 + +# --- C) Максимальная скорость инференса: CUDA-сборка (5–15 мин первый раз) --- +# LLM_RUNTIME=llamacpp +# LLAMACPP_TAG=b10545 +# LLAMACPP_BUILD_CUDA=1 +# LLAMACPP_FORCE_REINSTALL=1 +# (FORCE — снести старый Vulkan/битый бинарь и переустановить) + +# --- D) Свой бинарь / pin URL (supply-chain) --- +# LLM_RUNTIME=llamacpp +# LLAMACPP_ASSET_URL=https://github.com/ggml-org/llama.cpp/releases/download/b10545/llama-b10545-bin-ubuntu-vulkan-x64.tar.gz +# LLAMACPP_SHA256= +# LLAMACPP_FORCE_REINSTALL=1 + +# --- E) Делим 4090 со SwarmUI: меньше слоёв / короче контекст --- +# LLM_RUNTIME=llamacpp +# LLAMACPP_NGL=40 +# LLAMACPP_CTX=4096 +# LLAMACPP_EXTRA_ARGS=--flash-attn on + +# --- F) Длинный контекст / почти весь VRAM под LLM (llm-only) --- +# WORKLOAD=llm +# LLM_RUNTIME=llamacpp +# LLAMACPP_NGL=99 +# LLAMACPP_CTX=32768 +# LLAMACPP_EXTRA_ARGS=--parallel 1 + +# --- G) Ollama вместо llama.cpp + pin версии --- +# LLM_RUNTIME=ollama +# OLLAMA_VERSION=0.6.5 +# OLLAMA_SHA256= +# OLLAMA_LOCAL_PORT=17811 + +# --- H) Выключить LLM (гасит unit’ы на следующем up) --- +# LLM_RUNTIME=none + +# Порты туннеля (localhost): +# OLLAMA_LOCAL_PORT=17811 +# LLAMACPP_LOCAL_PORT=17812 +# Свой путь к манифесту GGUF/pull: +# LLAMACPP_MODELS_MANIFEST= +# OLLAMA_MODELS_MANIFEST= diff --git a/src/gpu_rent/provision.py b/src/gpu_rent/provision.py index d9d57ce..41c24be 100644 --- a/src/gpu_rent/provision.py +++ b/src/gpu_rent/provision.py @@ -29,6 +29,32 @@ from gpu_rent.sync_files import pull_tree, push_tree Log = Callable[[str], None] DATA = "/mnt/swarm_data" +# Forwarded to remote install_*.sh (from .env / gpu-rent.vars → os.environ). +_OLLAMA_INSTALL_ENV = ("OLLAMA_VERSION", "OLLAMA_SHA256") +_LLAMACPP_INSTALL_ENV = ( + "LLAMACPP_TAG", + "LLAMACPP_ASSET_URL", + "LLAMACPP_SHA256", + "LLAMACPP_BUILD_CUDA", + "LLAMACPP_FORCE_REINSTALL", + "LLAMACPP_NGL", + "LLAMACPP_CTX", + "LLAMACPP_HOST", + "LLAMACPP_PORT", + "LLAMACPP_EXTRA_ARGS", +) + + +def _remote_llm_env(cfg: Config, *keys: str) -> dict[str, str]: + import os + + env: dict[str, str] = {"SWARM_USER": cfg.ssh_user} + for key in keys: + value = (os.environ.get(key) or "").strip() + if value: + env[key] = value + return env + def _pkg_text(name: str) -> str: return files("gpu_rent.remote").joinpath(name).read_text(encoding="utf-8") @@ -442,7 +468,7 @@ def provision_llm(cfg: Config, host: str, log: Log) -> None: _pkg_text("install_ollama.sh"), remote_path="/tmp/gpu-rent-install_ollama.sh", timeout=900, - env={"SWARM_USER": cfg.ssh_user}, + env=_remote_llm_env(cfg, *_OLLAMA_INSTALL_ENV), log=log, ) entries = parse_ollama_models(cfg.ollama_models_manifest) @@ -516,13 +542,16 @@ def provision_llm(cfg: Config, host: str, log: Log) -> None: else: log("llamacpp-models.yaml пуст — GGUF skip (положи вручную)") log("LLM: ставим/запускаем llama.cpp server") + import os + + build_cuda = (os.environ.get("LLAMACPP_BUILD_CUDA") or "").strip() == "1" run_script_sudo( cfg, host, _pkg_text("install_llamacpp.sh"), remote_path="/tmp/gpu-rent-install_llamacpp.sh", - timeout=1200, - env={"SWARM_USER": cfg.ssh_user}, + timeout=3600 if build_cuda else 1200, + env=_remote_llm_env(cfg, *_LLAMACPP_INSTALL_ENV), log=log, ) st = load_state() diff --git a/src/gpu_rent/remote/install_llamacpp.sh b/src/gpu_rent/remote/install_llamacpp.sh index 6f12f88..baca59b 100644 --- a/src/gpu_rent/remote/install_llamacpp.sh +++ b/src/gpu_rent/remote/install_llamacpp.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash -# Install llama-server (CUDA) for OpenAI-compatible API on loopback :8080. -# Official GitHub releases ship Windows CUDA only — on Linux we build from source. +# Install llama-server for OpenAI-compatible API on loopback :8080. +# +# Default: official Linux release asset (Ubuntu Vulkan — GPU without compile). +# CUDA source build only as last resort (or LLAMACPP_BUILD_CUDA=1). +# Pin: LLAMACPP_TAG=b10545 LLAMACPP_ASSET_URL=... LLAMACPP_SHA256=... set -euo pipefail SWARM_USER="${SWARM_USER:-ubuntu}" @@ -28,8 +31,21 @@ SERVER_BIN="${BIN_DIR}/llama-server" LLAMACPP_TAG="${LLAMACPP_TAG:-}" LLAMACPP_ASSET_URL="${LLAMACPP_ASSET_URL:-}" LLAMACPP_SHA256="${LLAMACPP_SHA256:-}" +# 1 = force CUDA compile; 0/empty = prebuilt first, build only if prebuilt fails +LLAMACPP_BUILD_CUDA="${LLAMACPP_BUILD_CUDA:-}" +LLAMACPP_FORCE_REINSTALL="${LLAMACPP_FORCE_REINSTALL:-}" +LLAMACPP_NGL="${LLAMACPP_NGL:-}" +LLAMACPP_CTX="${LLAMACPP_CTX:-}" +LLAMACPP_HOST="${LLAMACPP_HOST:-127.0.0.1}" +LLAMACPP_PORT="${LLAMACPP_PORT:-8080}" +LLAMACPP_EXTRA_ARGS="${LLAMACPP_EXTRA_ARGS:-}" -# Pick a Linux release asset (never Windows/macOS). Prefer ubuntu CUDA → vulkan → cpu. +if [[ "${LLAMACPP_FORCE_REINSTALL}" == "1" ]]; then + log "LLAMACPP_FORCE_REINSTALL=1 — удаляю старый бинарь" + rm -f "$SERVER_BIN" "$STAMP" +fi + +# Prefer ubuntu CUDA (rare) → vulkan → cpu. Never Windows/macOS/cudart-only. pick_linux_asset_url() { python3 -c ' import json,sys @@ -107,7 +123,7 @@ ensure_build_deps() { export PATH="/usr/local/cuda/bin:${PATH}" return 0 fi - log "ставлю nvidia-cuda-toolkit (нужен nvcc для сборки)…" + log "ставлю nvidia-cuda-toolkit (нужен nvcc)…" apt-get install -y -qq nvidia-cuda-toolkit >/dev/null if command -v nvcc >/dev/null 2>&1; then return 0 @@ -119,14 +135,24 @@ ensure_build_deps() { return 1 } +ensure_vulkan_runtime() { + if ldconfig -p 2>/dev/null | grep -q 'libvulkan\.so'; then + return 0 + fi + export DEBIAN_FRONTEND=noninteractive + log "ставлю libvulkan1 (для Ubuntu Vulkan prebuilt)…" + apt-get install -y -qq libvulkan1 mesa-vulkan-drivers >/dev/null 2>&1 || \ + apt-get install -y -qq libvulkan1 >/dev/null 2>&1 || true +} + install_from_archive_url() { local url="$1" local tmp tmp="$(mktemp -d)" ( cd "$tmp" - log "asset $url" - curl -fL "$url" -o pkg.bin + log "скачиваю prebuilt: $url" + curl -fL --progress-bar "$url" -o pkg.bin if [[ -n "$LLAMACPP_SHA256" ]]; then echo "${LLAMACPP_SHA256} pkg.bin" | sha256sum -c - else @@ -149,58 +175,19 @@ install_from_archive_url() { exit 1 fi install -m 755 "$found" "$SERVER_BIN" - chown "${SWARM_USER}:${SWARM_USER}" "$SERVER_BIN" + # Shared libs next to binary (release tarballs ship .so alongside). + find out -type f \( -name '*.so' -o -name '*.so.*' \) -print0 2>/dev/null \ + | while IFS= read -r -d '' so; do + install -m 755 "$so" "${BIN_DIR}/$(basename "$so")" + done + chown -R "${SWARM_USER}:${SWARM_USER}" "$BIN_DIR" ) local rc=$? rm -rf "$tmp" return "$rc" } -build_cuda_from_source() { - local tag="$1" - local arch - arch="$(cuda_architectures)" - log "собираю llama-server CUDA из исходников (tag=${tag}, arch=${arch})…" - if ! ensure_build_deps; then - log "нет nvcc — CUDA-сборку пропускаем" - return 1 - fi - log "nvcc $(nvcc --version 2>/dev/null | tail -n1 || echo '?')" - - mkdir -p "$SRC_DIR" - if [[ -d "${SRC_DIR}/.git" ]]; then - git -C "$SRC_DIR" fetch --depth 1 origin tag "$tag" 2>/dev/null || true - if ! git -C "$SRC_DIR" checkout -f "$tag" 2>/dev/null; then - rm -rf "$SRC_DIR" - git clone --depth 1 --branch "$tag" "$REPO" "$SRC_DIR" - fi - else - rm -rf "$SRC_DIR" - git clone --depth 1 --branch "$tag" "$REPO" "$SRC_DIR" - fi - - cmake -S "$SRC_DIR" -B "${SRC_DIR}/build" \ - -DCMAKE_BUILD_TYPE=Release \ - -DGGML_CUDA=ON \ - -DCMAKE_CUDA_ARCHITECTURES="${arch}" \ - -DLLAMA_CURL=ON \ - -DLLAMA_BUILD_SERVER=ON - cmake --build "${SRC_DIR}/build" -j"$(nproc)" --target llama-server - - local built="${SRC_DIR}/build/bin/llama-server" - if [[ ! -x "$built" ]]; then - log "сборка не дала ${built}" - return 1 - fi - install -m 755 "$built" "$SERVER_BIN" - chown "${SWARM_USER}:${SWARM_USER}" "$SERVER_BIN" - echo "cuda:${tag}:${arch}" >"$STAMP" - chown "${SWARM_USER}:${SWARM_USER}" "$STAMP" - log "CUDA binary → ${SERVER_BIN}" - return 0 -} - -install_linux_release_fallback() { +install_linux_release() { local tag="$1" local api url api="${API_BASE}/releases/tags/${tag}" @@ -210,36 +197,134 @@ install_linux_release_fallback() { return 1 fi if [[ "$url" == *vulkan* ]]; then - log "WARN: официального Linux CUDA нет — беру Ubuntu Vulkan prebuilt" - elif [[ "$url" != *cuda* ]]; then - log "WARN: беру Linux prebuilt без CUDA (CPU) — лучше собрать с nvcc" + log "беру Ubuntu Vulkan prebuilt (GPU без compile; CUDA-сборка — LLAMACPP_BUILD_CUDA=1)" + ensure_vulkan_runtime + elif [[ "$url" == *cuda* ]]; then + log "беру Linux CUDA prebuilt" + else + log "WARN: Linux prebuilt без GPU backend (CPU) — ${url##*/}" fi install_from_archive_url "$url" echo "asset:${tag}" >"$STAMP" chown "${SWARM_USER}:${SWARM_USER}" "$STAMP" } +# Quiet CUDA build: no cmake spam; heartbeat every 30s with last %. +build_cuda_from_source() { + local tag="$1" + local arch build_log pid pct line + arch="$(cuda_architectures)" + build_log="${LLAMA_ROOT}/build-cuda.log" + log "крайний случай: сборка CUDA из исходников (tag=${tag}, arch=${arch}, 5–15 мин)…" + log "полный лог: ${build_log}" + if ! ensure_build_deps; then + log "нет nvcc — CUDA-сборку пропускаем" + return 1 + fi + log "nvcc $(nvcc --version 2>/dev/null | tail -n1 || echo '?')" + + mkdir -p "$SRC_DIR" + export GIT_TERMINAL_PROMPT=0 + if [[ -d "${SRC_DIR}/.git" ]]; then + git -C "$SRC_DIR" -c advice.detachedHead=false fetch --depth 1 origin tag "$tag" 2>>"$build_log" || true + if ! git -C "$SRC_DIR" -c advice.detachedHead=false checkout -f "$tag" >>"$build_log" 2>&1; then + rm -rf "$SRC_DIR" + git -c advice.detachedHead=false clone --depth 1 --branch "$tag" "$REPO" "$SRC_DIR" >>"$build_log" 2>&1 + fi + else + rm -rf "$SRC_DIR" + git -c advice.detachedHead=false clone --depth 1 --branch "$tag" "$REPO" "$SRC_DIR" >>"$build_log" 2>&1 + fi + + cmake -S "$SRC_DIR" -B "${SRC_DIR}/build" \ + -DCMAKE_BUILD_TYPE=Release \ + -DGGML_CUDA=ON \ + -DCMAKE_CUDA_ARCHITECTURES="${arch}" \ + -DLLAMA_BUILD_SERVER=ON \ + -DLLAMA_BUILD_UI=OFF \ + -DLLAMA_USE_PREBUILT_UI=OFF \ + -DGGML_CCACHE=OFF \ + >>"$build_log" 2>&1 + + # Background build + heartbeat (keeps SSH stream alive without 200 cmake lines). + cmake --build "${SRC_DIR}/build" -j"$(nproc)" --target llama-server \ + >>"$build_log" 2>&1 & + pid=$! + while kill -0 "$pid" 2>/dev/null; do + pct="$(grep -oE '\[[[:space:]]*[0-9]+%\]' "$build_log" 2>/dev/null | tail -n1 || true)" + line="$(grep -E 'Building CUDA|Built target|Linking' "$build_log" 2>/dev/null | tail -n1 || true)" + if [[ -n "$pct" ]]; then + log "сборка CUDA ещё идёт… ${pct}${line:+ · ${line}}" + else + log "сборка CUDA ещё идёт… (cmake/nvcc, см. build.log)" + fi + sleep 30 + done + if ! wait "$pid"; then + log "сборка упала — хвост ${build_log}:" + tail -n 40 "$build_log" >&2 || true + return 1 + fi + + local built="${SRC_DIR}/build/bin/llama-server" + if [[ ! -x "$built" ]]; then + log "сборка не дала ${built}" + return 1 + fi + install -m 755 "$built" "$SERVER_BIN" + # CUDA build may need libs from build/bin + find "${SRC_DIR}/build/bin" -maxdepth 1 -type f \( -name '*.so' -o -name '*.so.*' \) -print0 2>/dev/null \ + | while IFS= read -r -d '' so; do + install -m 755 "$so" "${BIN_DIR}/$(basename "$so")" + done + chown -R "${SWARM_USER}:${SWARM_USER}" "$BIN_DIR" + echo "cuda:${tag}:${arch}" >"$STAMP" + chown "${SWARM_USER}:${SWARM_USER}" "$STAMP" + log "CUDA binary → ${SERVER_BIN}" + return 0 +} + +normalize_tag() { + printf '%s' "$1" | tr -d '\r' | head -n1 | awk 'NF{print; exit}' +} + if [[ -x "$SERVER_BIN" ]]; then log "llama-server уже есть: ${SERVER_BIN}" else if [[ -n "$LLAMACPP_ASSET_URL" ]]; then - log "скачиваю llama-server по LLAMACPP_ASSET_URL…" + log "скачиваю по LLAMACPP_ASSET_URL…" install_from_archive_url "$LLAMACPP_ASSET_URL" echo "asset-url" >"$STAMP" chown "${SWARM_USER}:${SWARM_USER}" "$STAMP" else if [[ -z "$LLAMACPP_TAG" ]]; then - log "WARN: LLAMACPP_TAG/ASSET_URL не заданы — берём latest (нет pin). См. docs/llm.md" + log "WARN: LLAMACPP_TAG не задан — latest (см. docs/llm.md)" fi - tag="$(resolve_release_tag)" - tag="$(printf '%s' "$tag" | tr -d '\r' | head -n1 | awk 'NF{print; exit}')" + tag="$(normalize_tag "$(resolve_release_tag)")" if [[ -z "$tag" || "$tag" == *" "* || "$tag" == *"["* ]]; then log "не удалось определить release tag (got: ${tag:-empty})" exit 1 fi - if ! build_cuda_from_source "$tag"; then - log "CUDA-сборка не удалась — fallback на Linux release asset" - install_linux_release_fallback "$tag" + + installed=0 + if [[ "${LLAMACPP_BUILD_CUDA}" == "1" ]]; then + log "LLAMACPP_BUILD_CUDA=1 — сразу CUDA-сборка" + if build_cuda_from_source "$tag"; then + installed=1 + fi + else + if install_linux_release "$tag"; then + installed=1 + else + log "prebuilt не вышел — крайний случай: CUDA из исходников" + if build_cuda_from_source "$tag"; then + installed=1 + fi + fi + fi + if [[ "$installed" != "1" ]]; then + log "не удалось поставить llama-server" + exit 1 fi fi fi @@ -292,7 +377,14 @@ else: PY )" || true fi -log "llama.cpp -ngl ${NGL} -c ${CTX}" +# Explicit overrides from gpu-rent.vars / .env (forwarded by provision). +if [[ -n "$LLAMACPP_NGL" ]]; then + NGL="$LLAMACPP_NGL" +fi +if [[ -n "$LLAMACPP_CTX" ]]; then + CTX="$LLAMACPP_CTX" +fi +log "llama.cpp -ngl ${NGL} -c ${CTX} host=${LLAMACPP_HOST} port=${LLAMACPP_PORT}${LLAMACPP_EXTRA_ARGS:+ extra=${LLAMACPP_EXTRA_ARGS}}" cat >/etc/systemd/system/${UNIT}.service <