mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-29 01:31:41 +00:00
feat(nix): declarative plugin installation for NixOS module (#15953)
* feat(nix): parameterize dependency-groups in python.nix
* refactor(nix): extract package to callPackage-able hermes-agent.nix
Makes the package overridable via .override{} and adds
extraPythonPackages parameter for PYTHONPATH injection.
Includes build-time collision check using PEP 503 name
canonicalization.
* feat(nix): add overlay for external NixOS consumption
External flakes can now add overlays = [ inputs.hermes-agent.overlays.default ]
to get pkgs.hermes-agent with full .override support.
* test(nix): add check for extraPythonPackages PYTHONPATH injection
Verifies wrapper has PYTHONPATH when extras provided, and
base package has no PYTHONPATH without extras.
* feat(nix): add extraPlugins option for directory-based plugins
Symlinks plugin packages into HERMES_HOME/plugins/ at activation time.
Validates plugin.yaml presence. Asserts unique plugin names at eval time.
Hermes discovers them automatically via its directory scan.
* feat(nix): add extraPythonPackages option for entry-point plugins
Overrides the hermes package with PYTHONPATH injection when
extraPythonPackages is non-empty. Plugin .dist-info directories
become visible to importlib.metadata for entry-point discovery.
Works in both native systemd and container modes.
* docs: add NixOS declarative plugin installation to nix-setup, plugins, and build-a-plugin guides
- nix-setup.md: new Plugins section with extraPlugins/extraPythonPackages
examples, overlay usage, collision checking note, options reference rows
- plugins.md: Nix row in discovery table, NixOS declarative plugins section
- build-a-hermes-plugin.md: Distribute for NixOS section after pip section
* fix: address review feedback — remove unrelated umask, fix fetchFromGitHub naming, simplify checks
- Remove accidentally introduced umask/migration changes (unrelated to plugins)
- Add pluginName helper, fix fetchFromGitHub producing name='source'
- Show name= in extraPlugins example docs
- Simplify checks.nix: use hermes-agent.override instead of re-callPackage
- Fix fragile grep shell logic in checks
* refactor: address simplify feedback — lib.getName, drop unused inputs', Python list for extras
- Use lib.getName instead of custom pluginName helper
- Drop unused inputs' from checks.nix perSystem args
- Pass extraPythonPackages as Python list literal instead of colon-split string
* fix: walk propagatedBuildInputs for plugin PYTHONPATH and collision check
Uses python312.pkgs.requiredPythonModules to resolve the full transitive
closure of extraPythonPackages. Without this, a plugin with third-party
deps (e.g. requests) would fail at runtime if those deps weren't already
in the sealed uv2nix venv. The collision check now also scans the full
closure, catching transitive conflicts.
* cleanup: fold plugins into subdir loop, use find for symlink cleanup, inline lib.getName
- Add 'plugins' to the existing cron/sessions/logs/memories subdir loop
instead of a separate mkdir/chown/chmod block
- Replace fragile for-glob with find -delete for stale symlink cleanup
- Inline lib.getName at both call sites, remove pluginName wrapper
This commit is contained in:
parent
1fa76607c0
commit
ef41d3bd45
10 changed files with 460 additions and 117 deletions
|
|
@ -599,6 +599,93 @@ The `preStart` script creates a GC root at `${stateDir}/.gc-root` pointing to th
|
|||
|
||||
---
|
||||
|
||||
## Plugins
|
||||
|
||||
The NixOS module supports declarative plugin installation — no imperative `hermes plugins install` needed.
|
||||
|
||||
### Directory Plugins (`extraPlugins`)
|
||||
|
||||
For plugins that are just a source tree with `plugin.yaml` + `__init__.py` (e.g., [hermes-lcm](https://github.com/stephenschoettler/hermes-lcm)):
|
||||
|
||||
```nix
|
||||
services.hermes-agent.extraPlugins = [
|
||||
(pkgs.fetchFromGitHub {
|
||||
owner = "stephenschoettler";
|
||||
repo = "hermes-lcm";
|
||||
rev = "v0.7.0";
|
||||
hash = "sha256-...";
|
||||
})
|
||||
];
|
||||
```
|
||||
|
||||
Plugins are symlinked into `$HERMES_HOME/plugins/` at activation time. Hermes discovers them via its normal directory scan. Removing a plugin from the list and running `nixos-rebuild switch` removes the symlink.
|
||||
|
||||
### Entry-Point Plugins (`extraPythonPackages`)
|
||||
|
||||
For pip-packaged plugins that register via `[project.entry-points."hermes_agent.plugins"]` (e.g., [rtk-hermes](https://github.com/ogallotti/rtk-hermes)):
|
||||
|
||||
```nix
|
||||
services.hermes-agent.extraPythonPackages = [
|
||||
(pkgs.python312Packages.buildPythonPackage {
|
||||
pname = "rtk-hermes";
|
||||
version = "1.0.0";
|
||||
src = pkgs.fetchFromGitHub {
|
||||
owner = "ogallotti";
|
||||
repo = "rtk-hermes";
|
||||
rev = "v1.0.0";
|
||||
hash = "sha256-...";
|
||||
};
|
||||
format = "pyproject";
|
||||
build-system = [ pkgs.python312Packages.setuptools ];
|
||||
})
|
||||
];
|
||||
```
|
||||
|
||||
The package's `site-packages` is added to PYTHONPATH in the hermes wrapper. `importlib.metadata` discovers the entry point at session start.
|
||||
|
||||
### Combining Both
|
||||
|
||||
A directory plugin with third-party Python dependencies needs both options:
|
||||
|
||||
```nix
|
||||
services.hermes-agent = {
|
||||
extraPlugins = [ my-plugin-src ]; # plugin source
|
||||
extraPythonPackages = [ pkgs.python312Packages.redis ]; # its Python dep
|
||||
extraPackages = [ pkgs.redis ]; # system binary it needs
|
||||
};
|
||||
```
|
||||
|
||||
### Using the Overlay
|
||||
|
||||
External flakes can override the package directly:
|
||||
|
||||
```nix
|
||||
{
|
||||
inputs.hermes-agent.url = "github:NousResearch/hermes-agent";
|
||||
outputs = { hermes-agent, nixpkgs, ... }: {
|
||||
nixpkgs.overlays = [ hermes-agent.overlays.default ];
|
||||
# Then: pkgs.hermes-agent.override { extraPythonPackages = [...]; }
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Plugin Configuration
|
||||
|
||||
Plugins still need to be enabled in `config.yaml`. Add them via the declarative settings:
|
||||
|
||||
```nix
|
||||
services.hermes-agent.settings.plugins.enabled = [
|
||||
"hermes-lcm"
|
||||
"rtk-rewrite"
|
||||
];
|
||||
```
|
||||
|
||||
:::note
|
||||
A build-time collision check prevents plugin packages from shadowing core hermes dependencies. If a plugin provides a package already in the sealed venv, `nixos-rebuild` fails with a clear error.
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
### Dev Shell
|
||||
|
|
@ -721,6 +808,8 @@ nix build .#checks.x86_64-linux.config-roundtrip # merge script preserves use
|
|||
|---|---|---|---|
|
||||
| `extraArgs` | `listOf str` | `[]` | Extra args for `hermes gateway` |
|
||||
| `extraPackages` | `listOf package` | `[]` | Extra packages on service PATH (native mode only) |
|
||||
| `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` |
|
||||
| `restart` | `str` | `"always"` | systemd `Restart=` policy |
|
||||
| `restartSec` | `int` | `5` | systemd `RestartSec=` value |
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue