diff --git a/configuration.nix b/configuration.nix index 1f7698b..b8c445c 100644 --- a/configuration.nix +++ b/configuration.nix @@ -18,6 +18,7 @@ ./modules/power.nix ./modules/xray.nix ./modules/ollama.nix + ./modules/lemonade.nix ./modules/gaming.nix ./modules/litecoin.nix # ./modules/hostel-wifi.nix # disabled 2026-05-08 — re-enable when needed diff --git a/flake.lock b/flake.lock index 13bf656..270706b 100644 --- a/flake.lock +++ b/flake.lock @@ -52,6 +52,40 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1767039857, + "narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=", + "owner": "NixOS", + "repo": "flake-compat", + "rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1775087534, + "narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ @@ -98,6 +132,48 @@ "type": "github" } }, + "nix-amd-ai": { + "inputs": { + "flake-compat": "flake-compat_2", + "flake-parts": "flake-parts", + "nix-darwin": "nix-darwin", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1783698851, + "narHash": "sha256-cxQ4PCAdX/yN+WRQiR+9dhvYJlpS7kJOGuNZUafliAk=", + "owner": "noamsto", + "repo": "nix-amd-ai", + "rev": "a4bf7884d964b327b65ac456abf4c29fd7ac0e8b", + "type": "github" + }, + "original": { + "owner": "noamsto", + "repo": "nix-amd-ai", + "type": "github" + } + }, + "nix-darwin": { + "inputs": { + "nixpkgs": [ + "nix-amd-ai", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1781242433, + "narHash": "sha256-bchLZZ3sRn740zyvD2icZSnNoTaanN0nw7l6fjVXO+E=", + "owner": "nix-darwin", + "repo": "nix-darwin", + "rev": "aabb2037edfc0f210723b72cd5f528aab5dd3f0b", + "type": "github" + }, + "original": { + "owner": "nix-darwin", + "repo": "nix-darwin", + "type": "github" + } + }, "nix-flatpak": { "locked": { "lastModified": 1767983141, @@ -115,6 +191,37 @@ } }, "nixpkgs": { + "locked": { + "lastModified": 1783224372, + "narHash": "sha256-8i/87eeoqiGE4yOTjwSA3Eh/ziJRQEmd/unYU+K27sk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d407951447dcd00442e97087bf374aad70c04cea", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1774748309, + "narHash": "sha256-+U7gF3qxzwD5TZuANzZPeJTZRHS29OFQgkQ2kiTJBIQ=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "333c4e0545a6da976206c74db8773a1645b5870a", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs_2": { "locked": { "lastModified": 1781577229, "narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=", @@ -157,8 +264,9 @@ "inputs": { "anotherim": "anotherim", "lanzaboote": "lanzaboote", + "nix-amd-ai": "nix-amd-ai", "nix-flatpak": "nix-flatpak", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "sops-nix": "sops-nix" } }, diff --git a/flake.nix b/flake.nix index c30001b..06fd1e6 100644 --- a/flake.nix +++ b/flake.nix @@ -16,6 +16,9 @@ inputs.nixpkgs.follows = "nixpkgs"; }; nix-flatpak.url = "github:gmodena/nix-flatpak/?ref=latest"; + # No nixpkgs.follows on purpose: the flake builds against its own pinned + # nixpkgs so its Cachix binary cache hits (README warns follows breaks it). + nix-amd-ai.url = "github:noamsto/nix-amd-ai"; }; outputs = { self, nixpkgs, ... }@inputs: { diff --git a/modules/lemonade.nix b/modules/lemonade.nix new file mode 100644 index 0000000..6153c37 --- /dev/null +++ b/modules/lemonade.nix @@ -0,0 +1,50 @@ +{ inputs, pkgs, ... }: + +{ + imports = [ inputs.nix-amd-ai.nixosModules.default ]; + + # Prebuilt XRT/FLM/lemonade from the flake's Cachix; without it the whole + # NPU stack builds from source. Must live in nix.settings, not flake + # nixConfig (non-trusted users silently ignore the latter). + nix.settings = { + substituters = [ "https://nix-amd-ai.cachix.org" ]; + trusted-public-keys = [ "nix-amd-ai.cachix.org-1:F4OU4vw/lV2oiG6SBHZ+nqjl4EFJuqI4X9A7pvaBmhQ=" ]; + }; + + # XDNA2 NPU LLM serving (FLM runtime via lemonade, OpenAI-compatible API on + # 127.0.0.1:13305). iGPU/GGUF inference stays with ollama-rocm. + hardware.amd-npu = { + enable = true; + enableImageGen = false; + lemonade = { + user = "lemonade"; + host = "127.0.0.1"; + desktopApp.enable = false; # headless server only; skips the Tauri build path + }; + }; + + # Same rationale as ollama: model store on plain-ext4 /mnt/data — model + # blobs are public data, no need to occupy LUKS root or pay dm-crypt. + # FLM/lemonade derive their model dirs from $HOME. + users.users.lemonade = { + isSystemUser = true; + group = "lemonade"; + home = "/mnt/data/lemonade"; + createHome = true; + extraGroups = [ "video" "render" ]; # /dev/accel0 is group video per module udev rule + }; + users.groups.lemonade = { }; + + systemd.services.lemond.unitConfig.RequiresMountsFor = [ "/mnt/data/lemonade" ]; + + # lemonade only looks for flm on PATH when flm.prefer_system=true; default is + # false and nix-amd-ai's defaults seed doesn't cover it, so the FLM/NPU backend + # shows "not installed" and all -FLM models vanish from the registry. Patch the + # runtime config.json (cached copy overrides defaults) until fixed upstream. + systemd.services.lemond.preStart = '' + cfg="$HOME/.cache/lemonade/config.json" + if [ -f "$cfg" ]; then + ${pkgs.jq}/bin/jq '.flm.prefer_system = true' "$cfg" > "$cfg.tmp" && mv "$cfg.tmp" "$cfg" + fi + ''; +}