terra: ollama (ROCm) + LibreChat with local persistent memory

Local LLM server on the 6800 XT (ollama-rocm, gfx1030 needs no
HSA_OVERRIDE_GFX_VERSION) fronted by a LibreChat web UI, talking to it
over the OpenAI-compatible /v1 route. Also wires up LibreChat's
persistent-memory feature, which needed its own agent+model plus a
custom extraction prompt: the default 3b model couldn't reliably tell
the user's stated facts apart from its own boilerplate, and even a
tuned prompt didn't fix that — so memory extraction now reuses
gemma4:12b, the same model as the daily-driver chat endpoint.

flake.lock bump pulls in the ollama and librechat NixOS modules.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 00:38:07 +02:00
co-authored by Claude Sonnet 5
parent 0ec3c6413c
commit 7904433d34
6 changed files with 184 additions and 44 deletions
+29
View File
@@ -17,6 +17,7 @@ in
../../services/vpn/tailscale.nix
../../services/desktop/desktop-hyprland.nix
../../services/desktop/desktop-apps.nix
../../services/desktop/librechat.nix
];
networking.hostName = "terra";
@@ -69,6 +70,34 @@ in
# whisper.cpp/llama.cpp containers in ~/Data/Dev/repos/content-trigger-scanner.
users.users.darman.extraGroups = [ "render" "video" ];
# ---- ollama (local LLM server, ROCm on the 6800 XT) ----
# Navi 21 is gfx1030 — officially supported by ROCm, so no
# rocmOverrideGfx/HSA_OVERRIDE_GFX_VERSION needed (that's for gpus ROCm
# doesn't recognize, e.g. RDNA1/gfx101x). The upstream module runs the
# service under DynamicUser with SupplementaryGroups=["render"] and
# DeviceAllow for char-kfd/char-drm/char-fb already, so unlike jellyfin's
# static user it needs no extraGroups wiring here.
services.ollama = {
enable = true;
package = pkgs.ollama-rocm;
# keep in sync with services/desktop/librechat.nix's endpoints.custom
# default model — LibreChat's config schema needs a non-empty default
# even though fetch=true replaces it with whatever's actually pulled.
# gemma4:12b: general chat/coding daily driver, fits fully in 16G VRAM —
# also doubles as the memory-extraction agent (see librechat.nix): a
# 3b model (llama3.2:3b, dropped) couldn't reliably tell the user's
# stated facts apart from its own boilerplate, e.g. saving "I am an AI
# assistant with tool calling capabilities" as the user's personal_info
# after "Hi I'm Erik Simon". Reusing gemma4:12b for both roles also means
# no second model needs to swap into VRAM while it's already the active
# chat model.
# qwen3.6:35b-a3b: MoE (3B active/36B total), ~24GB Q4_K_M — doesn't fit
# in VRAM alone, so ollama offloads the inactive experts to CPU RAM.
# Sparse activation makes that far less painful than it'd be for a dense
# model this size, but still expect it to run slower than the two above.
loadModels = [ "gemma4:12b" "qwen3.6:35b-a3b" ];
};
# ---- Dev-data disks — NOT in disko, mounted read-write, never wiped ----
fileSystems."/mnt/hdd_01" = {
device = "/dev/disk/by-uuid/b8445126-ec6d-4f88-818a-d9e13031d9a4";