# LocalAI – zentraler LLM-Server für opencode & Continue Ein Docker-Compose-Stack, der auf **alegab-pc** (RX 6950 XT 16 GB, Ryzen 9 7900X3D, 62 GB RAM) eine **OpenAI-kompatible API** bereitstellt. Clients im Heim-LAN und über NetBird nutzen sie zum Programmieren, ganz ohne Cloud-KI. ``` Client-PC (opencode / VS Code + Continue) ──LAN / NetBird──┐ ▼ caddy (Host-Netz, :8800) IP-Filter + API-Key └─► llama-swap (nur 127.0.0.1:8080) lädt/wechselt Modelle └─► llama-server (llama.cpp, ROCm) ─► GPU + RAM Modelle: ./models (GGUF, Download von Hugging Face beim ersten Aufruf) ``` | Modell-ID | Zweck | Verhalten | |---|---|---| | `qwen3.5-35b-a3b` | Hauptmodell Chat/Agent (MoE) | eines der Chat-Modelle gleichzeitig geladen | | `glm-4.7-flash` | Alternative Chat/Agent (MoE) | 〃 | | `qwen3.5-9b` | schnelles kleines Chat-Modell | 〃 | | `qwen2.5-coder-1.5b` | Autocomplete (FIM) für Continue | dauerhaft geladen (GPU) | | `nomic-embed` | Embeddings für Continue `@codebase` | dauerhaft geladen (CPU) | ## Server einrichten ```bash cd ~/workspace/localAI cp .env.example .env sed -i "s/^LLM_API_KEY=.*/LLM_API_KEY=$(openssl rand -hex 32)/" .env chmod 600 .env $EDITOR .env # ALLOWED_NETS, GIDs (getent group render video), UID/GID prüfen sudo ./scripts/firewall-ufw.sh # einmalig: Port 8800 für LAN + wt0 in ufw öffnen ./scripts/start.sh # prüfen, starten, Modelle vorladen, Verbindungsdaten zeigen ``` `scripts/start.sh` legt eine fehlende `.env` beim ersten Start auch selbst an: mit neuem API-Key, UID/GID und den GIDs von render/video. Weitere Optionen: | Aufruf | Wirkung | |---|---| | `./scripts/start.sh` | starten, Autocomplete + Embeddings + Hauptmodell vorladen | | `./scripts/start.sh --no-preload` | nur starten | | `./scripts/start.sh --model glm-4.7-flash` | anderes Chat-Modell vorladen | | `./scripts/start.sh --show-key` | API-Key im Klartext anzeigen (für Client-Setup) | | `./scripts/start.sh --pull` | Images vorher aktualisieren | **Nach einem Reboot** startet der Stack von selbst: Der Docker-Dienst ist aktiviert, und die Container haben `restart: unless-stopped`. Autocomplete und Embeddings lädt llama-swap per `hooks.on_startup.preload` ebenfalls von selbst, das Chat-Modell kommt beim ersten Request dazu. Der erste Aufruf eines Modells lädt es von Hugging Face herunter (Hauptmodell ~22 GB). Danach läuft alles offline. ### Warum Caddy im Host-Netz? Ports, die Docker veröffentlicht, **umgehen ufw**. Deshalb ist llama-swap nur an `127.0.0.1` gebunden. Caddy läuft mit `network_mode: host`, dort greift ufw, und Caddy sieht die echte Quell-IP. Daraus ergeben sich zwei Schutzschichten: 1. **ufw** lässt Port 8800 nur aus dem LAN und über `wt0` zu. 2. **Caddy** prüft zusätzlich die Quell-IP (`ALLOWED_NETS`) und den Bearer-Token (`LLM_API_KEY`). Die API läuft über plain HTTP. Über NetBird ist das unkritisch (WireGuard verschlüsselt den Tunnel), im Heim-LAN ist es akzeptabel. ## Clients einrichten - **opencode** (Terminal-Agent): [docs/opencode.md](docs/opencode.md) - **Continue** (VS-Code-Plugin): [docs/continue.md](docs/continue.md) Schnelltest von jedem Client: ```bash curl -H "Authorization: Bearer " http://:8800/v1/models ``` ## Betrieb ```bash docker compose ps # Status docker compose logs -f llama-swap # Modell-Laden, Requests docker compose logs -f caddy # Zugriffe docker compose restart # nach Änderungen an .env / Caddyfile ``` - `llama-swap/config.yaml` wird **automatisch neu geladen**, ein Neustart ist nicht nötig. - Web-Oberfläche von llama-swap (Modellstatus, Logs): `http://127.0.0.1:8080/ui`, nur lokal auf dem Server erreichbar. - VRAM-Auslastung: `rocm-smi --showmeminfo vram` ### Modell hinzufügen Einen Eintrag in `llama-swap/config.yaml` anlegen, z. B. ```yaml mein-modell: cmd: ${server} ${agent-ctx} -hf /-GGUF:Q4_K_M ttl: 3600 ``` Den Eintrag unter `routing.router.settings.groups.chat.members` aufnehmen. Danach die Client-Configs (`client/opencode.json`, `client/continue/config.yaml`) um die neue Modell-ID ergänzen. GPU-Layer und MoE-Auslagerung in den RAM verteilt llama.cpp automatisch (`--fit`, Standard: an). `--fit-target 3072` hält ~3 GB VRAM für das Autocomplete-Modell frei. ## Auf einen anderen PC übertragen 1. Den Ordner kopieren. `models/` kann man mitnehmen, um den Download zu sparen. 2. `.env` anpassen: `ALLOWED_NETS` (LAN-Subnetz, NetBird-Netz), `RENDER_GID`/`VIDEO_GID`, `HOST_UID`/`HOST_GID`. 3. Je nach GPU das Image in `.env` wählen (`LLAMA_SWAP_TAG`): | GPU | Tag-Variante | |---|---| | AMD (ROCm) | `vXXX-rocm-bYYYY` (wie hier) | | NVIDIA | `vXXX-cuda-bYYYY`. Dann in `docker-compose.yml` `devices`/`group_add`/`security_opt` durch `deploy.resources.reservations.devices` (nvidia) ersetzen. | | herstellerunabhängig | `vXXX-vulkan-bYYYY` (nur `/dev/dri` nötig) | | nur CPU | `vXXX-cpu-bYYYY` | Verfügbare Tags: https://github.com/mostlygeek/llama-swap/pkgs/container/llama-swap 4. Threads (`--threads` im Makro `server`) an die CPU anpassen. 5. Firewall des Ziel-PCs entsprechend `scripts/firewall-ufw.sh` konfigurieren. ## Dateien | Datei | Inhalt | |---|---| | `docker-compose.yml` | Dienste llama-swap + caddy | | `.env` / `.env.example` | Secrets, Netze, Ports, GIDs, Image-Tags | | `llama-swap/config.yaml` | Modelle, Startparameter, Gruppen | | `caddy/Caddyfile` | IP-Filter, API-Key, Reverse Proxy | | `client/opencode.json` | Vorlage für opencode-Clients | | `client/continue/` | Vorlage für Continue-Clients | | `scripts/start.sh` | Start mit Prüfungen, Preload und Statusausgabe | | `scripts/firewall-ufw.sh` | ufw-Freigaben | | `PLAN.md` | ursprünglicher Umsetzungsplan |