all repos — nixfiles @ e4815516ed702670d262492f28ce0cf3b926b8e7

System and user configuration, managed by nix and home-manager

local-llm: init module

Alin
commit

e4815516ed702670d262492f28ce0cf3b926b8e7

parent

0ca708a8e576f7c95802bb0ce61d4758d9950084

2 files changed, 155 insertions(+), 0 deletions(-)

changed files
A user/modules/llama-swap.nix
@@ -0,0 +1,143 @@
+{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.llama-swap; + settingsFormat = pkgs.formats.yaml { }; + configFile = + if cfg.configFile != null then + cfg.configFile + else + settingsFormat.generate "config.yaml" cfg.settings; + + args = [ + "--listen=${cfg.listenAddress}:${toString cfg.port}" + "--config=${configFile}" + ] + ++ cfg.extraArgs; +in +{ + options.services.llama-swap = { + enable = lib.mkEnableOption "the llama-swap service"; + + package = lib.mkPackageOption pkgs "llama-swap" { }; + + llamaPackage = lib.mkPackageOption pkgs "llama-cpp" { + extraDescription = "llama-cpp package added to llama-swap's PATH"; + }; + + listenAddress = lib.mkOption { + type = lib.types.str; + default = "localhost"; + example = "0.0.0.0"; + description = '' + Address that llama-swap listens on. + ''; + }; + + port = lib.mkOption { + default = 8080; + example = 11343; + type = lib.types.port; + description = '' + Port that llama-swap listens on. + ''; + }; + + configFile = lib.mkOption { + type = with lib.types; nullOr (either path str); + default = null; + description = '' + Optional configuration file path to use instead of + generating from settings + ''; + }; + + extraArgs = lib.mkOption { + type = with lib.types; listOf str; + default = [ ]; + description = '' + Additional arguments to pass on llama-swap startup + ''; + }; + + settings = lib.mkOption { + type = lib.types.submodule { freeformType = settingsFormat.type; }; + description = '' + llama-swap configuration. Refer to the [llama-swap example configuration](https://github.com/mostlygeek/llama-swap/blob/main/config.example.yaml) + for details on supported values. + ''; + example = lib.literalExpression '' + let + llama-cpp = pkgs.llama-cpp.override { rocmSupport = true; }; + llama-server = lib.getExe' llama-cpp "llama-server"; + in + { + healthCheckTimeout = 60; + models = { + "some-model" = { + cmd = "''${llama-server} --port ''${PORT} -m /var/lib/llama-cpp/models/some-model.gguf -ngl 0 --no-webui"; + aliases = [ + "the-best" + ]; + }; + "other-model" = { + proxy = "http://127.0.0.1:5555"; + cmd = "''${llama-server} --port 5555 -m /var/lib/llama-cpp/models/other-model.gguf -ngl 0 -c 4096 -np 4 --no-webui"; + concurrencyLimit = 4; + }; + }; + }; + ''; + }; + }; + config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = cfg.configFile != null -> (builtins.attrNames cfg.settings) == [ ]; + message = "configFile is incompatible with non-empty settings"; + } + ]; + + # untested + systemd.user.services.llama-swap = { + description = "Model swapping for LLaMA C++ Server (or any local OpenAPI compatible server)"; + + serviceConfig = { + Type = "exec"; + ExecStart = "${lib.getExe cfg.package} ${lib.escapeShellArgs args}"; + Restart = "on-failure"; + RestartSec = 3; + + # for GPU acceleration + PrivateDevices = false; + }; + path = [ cfg.llamaPackage ]; + }; + + launchd.agents.llama-swap = { + enable = true; + config = { + ProgramArguments = [ (lib.getExe cfg.package) ] ++ args; + KeepAlive = { + Crashed = true; + SuccessfulExit = false; + }; + ProcessType = "Background"; + EnvironmentVariables = { + PATH = lib.makeBinPath [ + cfg.llamaPackage + ]; + }; + }; + }; + + home.packages = [ + cfg.package + cfg.llamaPackage + ]; + }; +}
A user/settings/local-llm.nix
@@ -0,0 +1,12 @@
+{ ... }: +{ + imports = [ + ../modules/llama-swap.nix + ]; + + services.llama-swap = { + enable = true; + listenAddress = "[::1]"; + port = 11343; + }; +}