From 5606258855f7937659527ffffca9d9d7ef6fadc5 Mon Sep 17 00:00:00 2001 From: Siddharth Balyan <52913345+alt-glitch@users.noreply.github.com> Date: Mon, 11 May 2026 12:23:48 +0530 Subject: [PATCH] feat(nix): add extraDependencyGroups for sealed venv extras (#21817) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the dependency-groups parameter from python.nix through hermes-agent.nix and the NixOS module, allowing users to opt into pyproject.toml optional extras (e.g. hindsight, voice, matrix) that are resolved by uv inside the sealed venv. Unlike extraPythonPackages (which appends to PYTHONPATH and requires collision checking), extraDependencyGroups resolves the full dependency graph in a single uv pass — no PYTHONPATH patching, no version conflicts, no collision risk. When to use which: - extraDependencyGroups: enable a pyproject.toml optional extra - extraPythonPackages: add an external Python plugin not in pyproject.toml Usage: services.hermes-agent.extraDependencyGroups = [ "hindsight" ]; Or via overlay: pkgs.hermes-agent.override { extraDependencyGroups = [ "hindsight" ]; } Refs: #8873, #9194 --- nix/checks.nix | 21 ++++++++++++++++++ nix/hermes-agent.nix | 6 ++++- nix/nixosModules.nix | 21 ++++++++++++++++-- website/docs/getting-started/nix-setup.md | 27 ++++++++++++++++++++++- 4 files changed, 71 insertions(+), 4 deletions(-) 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 |