LocalAI/README.md
2026-10-15 00:01:26 +02:00

118 lines
5.8 KiB
Markdown
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.

# 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 <KEY>" http://<server>: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 <hf-user>/<repo>-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 |