lemonade: XDNA2 NPU LLM serving via nix-amd-ai

This commit is contained in:
Sergei Poljanski 2026-07-11 22:36:00 +04:00
commit 8d823f4da2
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
4 changed files with 163 additions and 1 deletions

View file

@ -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

110
flake.lock generated
View file

@ -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"
}
},

View file

@ -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: {

50
modules/lemonade.nix Normal file
View file

@ -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
'';
}