mars: provision the Mnemosyne memory provider for Hermes #6

Merged
darman merged 8 commits from feat/mars-hermes-mnemosyne into master 2026-09-19 02:16:36 +02:00
2 changed files with 88 additions and 0 deletions
+32
View File
@@ -74,6 +74,9 @@ let
builtins.readFile ./gitea-pr-review-prompt.md builtins.readFile ./gitea-pr-review-prompt.md
); );
# Mnemosyne memory provider: third-party plugin, not in the image.
mnemosyneEnv = pkgs.callPackage ../../pkgs/mnemosyne-env.nix { };
# Wire event names (X-GitHub-Event) each route accepts — NOT the # Wire event names (X-GitHub-Event) each route accepts — NOT the
# subscription names the gitea hooks in services/dev/gitea.nix use. The two # subscription names the gitea hooks in services/dev/gitea.nix use. The two
# namespaces collide; see the long comment on the route unit below. # namespaces collide; see the long comment on the route unit below.
@@ -418,4 +421,33 @@ in
mv -f "$tmp" "$conf" mv -f "$tmp" "$conf"
''; '';
}; };
# Hermes discovers memory providers under $HERMES_HOME/plugins; the target
# is a store path, readable in the container via the /nix/store ro mount.
# wantedBy, not requiredBy: a failure leaves Hermes on built-in memory.
systemd.services.hermes-agent-mnemosyne-plugin = {
description = "Link Mnemosyne provider into the Hermes plugin dir";
before = [ "podman-hermes-agent.service" ];
wantedBy = [ "podman-hermes-agent.service" ];
after = [ "hermes-agent-prepare-dirs.service" ];
requires = [ "hermes-agent-prepare-dirs.service" ];
path = [ pkgs.coreutils ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
set -euo pipefail
pluginsDir=${hermesHome}/plugins
pluginDir=$pluginsDir/mnemosyne
target=${mnemosyneEnv}/${mnemosyneEnv.sitePackages}/hermes_memory_provider
[ -d "$target" ] || { echo "$target missing" >&2; exit 1; }
mkdir -p "$pluginsDir"
chown ${hermesUid}:${hermesGid} "$pluginsDir"
ln -sfn "$target" "$pluginDir.new"
mv -Tf "$pluginDir.new" "$pluginDir"
chown -h ${hermesUid}:${hermesGid} "$pluginDir"
'';
};
} }
+56
View File
@@ -0,0 +1,56 @@
# Mnemosyne memory provider for Hermes. Built here rather than pip-installed:
# the image's Python has no pip and is PEP 668 managed.
#
# Core deps only: the embeddings extra (fastembed/onnxruntime) is optional at
# runtime, and recall falls back to FTS5.
{
python3,
fetchPypi,
}:
let
mnemosyneMemory = python3.pkgs.buildPythonPackage rec {
pname = "mnemosyne-memory";
version = "3.15.1";
pyproject = true;
src = fetchPypi {
pname = "mnemosyne_memory";
inherit version;
hash = "sha256-lspUMxc0pUSkhSUrNdiiO5OJ1NMC/S853EYSanXtXKM=";
};
build-system = with python3.pkgs; [ setuptools ];
dependencies = with python3.pkgs; [ pyyaml ];
doCheck = false; # tests want a live Hermes + LLM key
# Ships the Hermes plugin package too, not just the core.
pythonImportsCheck = [
"mnemosyne"
"hermes_memory_provider"
];
};
mnemosyneHermes = python3.pkgs.buildPythonPackage rec {
pname = "mnemosyne-hermes";
version = "0.5.0";
pyproject = true;
src = fetchPypi {
pname = "mnemosyne_hermes";
inherit version;
hash = "sha256-CzEvnUw5oPFtT5bHQQ/GBdy2C/E7qShQn32irIRYKqw=";
};
build-system = with python3.pkgs; [ setuptools ];
# Upstream asks for mnemosyne-memory[embeddings]; see the header.
dependencies = [ mnemosyneMemory ] ++ (with python3.pkgs; [ pyyaml ]);
doCheck = false;
pythonImportsCheck = [ "mnemosyne_hermes" ];
};
in
python3.withPackages (_: [
mnemosyneMemory
mnemosyneHermes
])