diff --git a/nix/checks.nix b/nix/checks.nix index 8adb56628d2..2bd4f642bbd 100644 --- a/nix/checks.nix +++ b/nix/checks.nix @@ -240,6 +240,27 @@ json.dump(sorted(leaf_paths(DEFAULT_CONFIG)), sys.stdout, indent=2) echo "ok" > $out/result ''; + # Verify extraDependencyGroups passes through to python.nix + extra-dependency-groups = let + hermesWithGroups = hermes-agent.override { + extraDependencyGroups = [ "honcho" ]; + }; + in pkgs.runCommand "hermes-extra-dependency-groups" { } '' + set -e + echo "=== Checking extraDependencyGroups override evaluates ===" + + # Eval-only: verify the override produces valid derivation paths + # without building the full venv (which is expensive and redundant + # since the mechanism is just list concatenation into python.nix). + echo "derivation: ${hermesWithGroups}" + echo "venv: ${hermesWithGroups.hermesVenv}" + echo "PASS: extraDependencyGroups override evaluates cleanly" + + echo "=== All extraDependencyGroups checks passed ===" + mkdir -p $out + echo "ok" > $out/result + ''; + # ── Config merge + round-trip test ──────────────────────────────── # Tests the merge script (Nix activation behavior) across 7 # scenarios, then verifies Python's load_config() reads correctly. diff --git a/nix/hermes-agent.nix b/nix/hermes-agent.nix index c3bde20c81c..ce8be16cfdd 100644 --- a/nix/hermes-agent.nix +++ b/nix/hermes-agent.nix @@ -1,7 +1,9 @@ # nix/hermes-agent.nix — Overridable Hermes Agent package # # callPackage auto-wires nixpkgs args; flake inputs are passed explicitly. -# Users override via: pkgs.hermes-agent.override { extraPythonPackages = [...]; } +# Users override via: +# pkgs.hermes-agent.override { extraPythonPackages = [...]; } +# pkgs.hermes-agent.override { extraDependencyGroups = [ "hindsight" ]; } { lib, stdenv, @@ -25,11 +27,13 @@ rev ? null, # Overridable parameters extraPythonPackages ? [ ], + extraDependencyGroups ? [ ], }: let nodejs = nodejs_22; hermesVenv = callPackage ./python.nix { inherit uv2nix pyproject-nix pyproject-build-systems; + dependency-groups = [ "all" ] ++ extraDependencyGroups; }; hermesNpmLib = callPackage ./lib.nix { diff --git a/nix/nixosModules.nix b/nix/nixosModules.nix index fbff28e18b6..475e5b5ba5a 100644 --- a/nix/nixosModules.nix +++ b/nix/nixosModules.nix @@ -28,8 +28,10 @@ let cfg = config.services.hermes-agent; - effectivePackage = if cfg.extraPythonPackages == [ ] then cfg.package - else cfg.package.override { inherit (cfg) extraPythonPackages; }; + effectivePackage = + if cfg.extraPythonPackages == [ ] && cfg.extraDependencyGroups == [ ] + then cfg.package + else cfg.package.override { inherit (cfg) extraPythonPackages extraDependencyGroups; }; hermes-agent = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.default; # Deep-merge config type (from 0xrsydn/nix-hermes-agent) @@ -512,6 +514,21 @@ ''; }; + extraDependencyGroups = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + Additional pyproject.toml optional-dependency groups to include in + the sealed Python venv. These are resolved by uv alongside core + dependencies — no PYTHONPATH patching or collision risk. + + Use this for optional extras already declared in hermes-agent's + pyproject.toml (e.g. "hindsight", "honcho", "voice"). + Use extraPythonPackages for external packages not in pyproject.toml. + ''; + example = [ "hindsight" ]; + }; + restart = mkOption { type = types.str; default = "always"; diff --git a/website/docs/getting-started/nix-setup.md b/website/docs/getting-started/nix-setup.md index d97961a93bd..80e8cae9746 100644 --- a/website/docs/getting-started/nix-setup.md +++ b/website/docs/getting-started/nix-setup.md @@ -645,6 +645,28 @@ services.hermes-agent.extraPythonPackages = [ The package's `site-packages` is added to PYTHONPATH in the hermes wrapper. `importlib.metadata` discovers the entry point at session start. +### Optional Dependency Groups (`extraDependencyGroups`) + +For optional extras already declared in hermes-agent's `pyproject.toml` (e.g., memory providers like `hindsight` or `honcho`), use `extraDependencyGroups` to include them in the sealed venv at build time: + +```nix +services.hermes-agent = { + extraDependencyGroups = [ "hindsight" ]; + settings.memory.provider = "hindsight"; +}; +``` + +This is resolved by uv alongside core dependencies in a single pass — no PYTHONPATH patching, no collision risk. Available groups match the `[project.optional-dependencies]` keys in `pyproject.toml` (e.g., `"hindsight"`, `"honcho"`, `"voice"`, `"matrix"`, `"mistral"`, `"bedrock"`). + +**When to use which:** + +| Need | Option | +|------|--------| +| Enable a pyproject.toml optional extra | `extraDependencyGroups` | +| Add an external Python plugin not in pyproject.toml | `extraPythonPackages` | +| Add a system binary (pandoc, jq, etc.) | `extraPackages` | +| Add a directory-based plugin source tree | `extraPlugins` | + ### Combining Both A directory plugin with third-party Python dependencies needs both options: @@ -666,7 +688,9 @@ External flakes can override the package directly: inputs.hermes-agent.url = "github:NousResearch/hermes-agent"; outputs = { hermes-agent, nixpkgs, ... }: { nixpkgs.overlays = [ hermes-agent.overlays.default ]; - # Then: pkgs.hermes-agent.override { extraPythonPackages = [...]; } + # Then: + # pkgs.hermes-agent.override { extraPythonPackages = [...]; } + # pkgs.hermes-agent.override { extraDependencyGroups = [ "hindsight" ]; } }; } ``` @@ -812,6 +836,7 @@ nix build .#checks.x86_64-linux.config-roundtrip # merge script preserves use | `extraPackages` | `listOf package` | `[]` | Extra packages available to the agent. Added to the hermes user's per-user profile so terminal commands, skills, and cron jobs all see them | | `extraPlugins` | `listOf package` | `[]` | Directory plugin packages to symlink into `$HERMES_HOME/plugins/`. Each must contain `plugin.yaml` | | `extraPythonPackages` | `listOf package` | `[]` | Python packages added to PYTHONPATH for entry-point plugin discovery. Build with `python312Packages` | +| `extraDependencyGroups` | `listOf str` | `[]` | pyproject.toml optional extras to include in the sealed venv (e.g. `["hindsight"]`). Resolved by uv — no collisions | | `restart` | `str` | `"always"` | systemd `Restart=` policy | | `restartSec` | `int` | `5` | systemd `RestartSec=` value |