From e2dd106f747f6df8a20c0830efe6f8881bcbfe32 Mon Sep 17 00:00:00 2001 From: ArthurErlich Date: Wed, 2 Sep 2026 23:07:53 +0200 Subject: [PATCH] feat(scripts): fold model downloads into update.sh, skip if already present Today's embedding-server crash-loop (missing nomic-embed-text GGUF) was a manual step nobody ran. update.sh now runs both downloader profiles itself, every time, before bringing services up -- no separate command to remember. - docker-compose.yml: downloader/downloader-embedding commands gain a `test -f ... && skip || curl ...` guard, so re-running update.sh never re-downloads an existing model file. - scripts/update.sh: runs both profiles after image pull/build, before service recreation. - scripts/download-model.sh removed -- folded in, redundant standalone script. - README.md / docs/memory-knowledgebase.md updated accordingly. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018WHfjWrSEcGhCoeu6dQfDa --- README.md | 3 +-- docker-compose.yml | 13 ++++++++++--- docs/memory-knowledgebase.md | 2 ++ scripts/download-model.sh | 12 ------------ scripts/update.sh | 4 ++++ 5 files changed, 17 insertions(+), 17 deletions(-) delete mode 100755 scripts/download-model.sh diff --git a/README.md b/README.md index 5a732fa..15d0460 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,10 @@ See the wayfinder map ([issue #1](https://git.arthurerlich.de/haylan/LLM-Server/ ## Quickstart ```bash -./scripts/download-model.sh ./scripts/update.sh ``` -`update.sh` creates `.env` from `.env.example` if missing, fills in every secret and per-workload virtual key it can generate itself (random secrets via `openssl`, `OPENWEBUI_LITELLM_KEY`/`LITELLM_PGVECTOR_EMBEDDING_KEY` minted through LiteLLM's own `/key/generate` API, `SEARXNG_LAN_IP` resolved from `search.home` on this host), then pulls/builds/brings up the whole stack. Safe to re-run any time — it only fills in what's still blank and only recreates what changed. See [`docs/proxy-key-onboarding.md`](docs/proxy-key-onboarding.md) if a key mint fails and needs doing by hand. +`update.sh` creates `.env` from `.env.example` if missing, fills in every secret and per-workload virtual key it can generate itself (random secrets via `openssl`, `OPENWEBUI_LITELLM_KEY`/`LITELLM_PGVECTOR_EMBEDDING_KEY` minted through LiteLLM's own `/key/generate` API, `SEARXNG_LAN_IP` resolved from `search.home` on this host), downloads both model GGUFs into the `models` volume if they're not there yet, then pulls/builds/brings up the whole stack. Safe to re-run any time — it only fills in what's still blank, skips models already downloaded, and only recreates what changed. See [`docs/proxy-key-onboarding.md`](docs/proxy-key-onboarding.md) if a key mint fails and needs doing by hand. - Open WebUI: `http://:3000` locally, or `ai.home` / `ai.haylan.ch` once routed through Nginx Proxy Manager — see [`docs/network-access.md`](docs/network-access.md). First signup becomes the admin account (`WEBUI_AUTH` is on). - llama.cpp's own API is internal-only now — everything routes through the AI proxy below. diff --git a/docker-compose.yml b/docker-compose.yml index e785a5c..1199e33 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,8 +66,10 @@ services: networks: [ai-stack] # ponytail: one-off downloader, not a standing service — run via - # `docker compose --profile tools run --rm downloader` (see scripts/download-model.sh). - # Keeps the model file inside the named `models` volume instead of a host bind-mount. + # `docker compose --profile tools run --rm downloader`. Folded into + # scripts/update.sh, which runs this every time; the `test -f` guard is + # what makes that safe to re-run without re-downloading. Keeps the model + # file inside the named `models` volume instead of a host bind-mount. downloader: image: curlimages/curl:latest profiles: ["tools"] @@ -79,11 +81,14 @@ services: entrypoint: ["sh", "-c"] command: - > + test -f /models/${LLAMA_MODEL_FILE:-Qwen3.8-27B-UD-Q4_K_XL.gguf} && + echo "already downloaded, skipping" || curl -L --fail --create-dirs -o /models/${LLAMA_MODEL_FILE:-Qwen3.8-27B-UD-Q4_K_XL.gguf} https://huggingface.co/unsloth/Qwen3.8-27B-GGUF/resolve/main/${LLAMA_MODEL_FILE:-Qwen3.8-27B-UD-Q4_K_XL.gguf} # ponytail: same one-off pattern as `downloader`, for the embedding model — - # run via `docker compose --profile tools run --rm downloader-embedding`. + # run via `docker compose --profile tools run --rm downloader-embedding`, + # also folded into scripts/update.sh. downloader-embedding: image: curlimages/curl:latest profiles: ["tools"] @@ -93,6 +98,8 @@ services: entrypoint: ["sh", "-c"] command: - > + test -f /models/${EMBEDDING_MODEL_FILE:-nomic-embed-text-v1.5.Q8_0.gguf} && + echo "already downloaded, skipping" || curl -L --fail --create-dirs -o /models/${EMBEDDING_MODEL_FILE:-nomic-embed-text-v1.5.Q8_0.gguf} https://huggingface.co/nomic-ai/nomic-embed-text-v1.5-GGUF/resolve/main/${EMBEDDING_MODEL_FILE:-nomic-embed-text-v1.5.Q8_0.gguf} diff --git a/docs/memory-knowledgebase.md b/docs/memory-knowledgebase.md index 5ad1668..3ab6f38 100644 --- a/docs/memory-knowledgebase.md +++ b/docs/memory-knowledgebase.md @@ -32,6 +32,8 @@ New pieces: ### First-time setup +`./scripts/update.sh` fetches the embedding model automatically (skips it if already downloaded). To do it by hand instead: + ```bash docker compose --profile tools run --rm downloader-embedding # fetch the embedding model docker compose up -d embedding-server pgvector-db litellm-pgvector diff --git a/scripts/download-model.sh b/scripts/download-model.sh deleted file mode 100755 index bebe067..0000000 --- a/scripts/download-model.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# Downloads the model GGUF straight into the `models` named volume via a -# one-off container — no huggingface-cli or host bind-mount needed. -# -# ponytail: hardcodes the one model this stack is built for (see the -# `downloader` service in docker-compose.yml for the actual URL/filename). -# Set LLAMA_MODEL_FILE in .env first if you're using a different quant. -set -euo pipefail -cd "$(dirname "$0")/.." - -docker compose --profile tools run --rm downloader -echo "Model downloaded into the 'models' volume." diff --git a/scripts/update.sh b/scripts/update.sh index 67755ea..c095d94 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -63,6 +63,10 @@ docker compose pull --ignore-buildable echo "==> rebuilding local-build services" docker compose build --pull +echo "==> ensuring models are downloaded (skips already-present files)" +docker compose --profile tools run --rm downloader +docker compose --profile tools run --rm downloader-embedding + echo "==> bringing up litellm (needed to mint virtual keys below)" docker compose up -d --wait litellm-db litellm