Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25e17f163e | ||
|
|
9b96d3c6f8 |
+6
-163
@@ -74,40 +74,9 @@ let
|
|||||||
builtins.readFile ./gitea-pr-review-prompt.md
|
builtins.readFile ./gitea-pr-review-prompt.md
|
||||||
);
|
);
|
||||||
|
|
||||||
# Mnemosyne memory provider (local SQLite, third-party plugin — not bundled
|
# Mnemosyne memory provider: third-party plugin, not in the image.
|
||||||
# with the official image). Fully built as a Nix derivation — see
|
|
||||||
# pkgs/mnemosyne-env.nix — and mounted READ-ONLY into the container at a
|
|
||||||
# fixed path. The oneshot near the bottom of this file only writes the
|
|
||||||
# plugin symlink Docker needs at $HERMES_HOME/plugins/mnemosyne.
|
|
||||||
mnemosyneEnv = pkgs.callPackage ../../pkgs/mnemosyne-env.nix { };
|
mnemosyneEnv = pkgs.callPackage ../../pkgs/mnemosyne-env.nix { };
|
||||||
|
|
||||||
# KittenTTS voice provider inputs (CPU-only; model + wheel hash-pinned).
|
|
||||||
# Provisioning unit near the bottom of this file; background on why the
|
|
||||||
# deps deviate from upstream's declaration lives in
|
|
||||||
# ./kittentts/requirements.txt + ./kittentts/kitten-misaki-stub.py.
|
|
||||||
kittenttsWheel = pkgs.fetchurl {
|
|
||||||
url = "https://github.com/KittenML/KittenTTS/releases/download/0.8.1/kittentts-0.8.1-py3-none-any.whl";
|
|
||||||
sha256 = "sha256-SCpDbE8fMZIVNxA3bkWf82iVF+vNp8KwUeL9QYe0GFE=";
|
|
||||||
};
|
|
||||||
kittenttsReqs = pkgs.writeText "kittentts-requirements.txt" (
|
|
||||||
builtins.readFile ./kittentts/requirements.txt
|
|
||||||
);
|
|
||||||
kittenttsStub = pkgs.writeText "kitten-tts-stub.py" (
|
|
||||||
builtins.readFile ./kittentts/kitten-misaki-stub.py
|
|
||||||
);
|
|
||||||
kittenttsModelOnnx = pkgs.fetchurl {
|
|
||||||
url = "https://huggingface.co/KittenML/kitten-tts-mini-0.8/resolve/main/kitten_tts_mini_v0_8.onnx";
|
|
||||||
sha256 = "sha256-D1u65PxIAMmNvFRKh+z6eVEN4vuCItsw0S5b/pF335E=";
|
|
||||||
};
|
|
||||||
kittenttsModelVoices = pkgs.fetchurl {
|
|
||||||
url = "https://huggingface.co/KittenML/kitten-tts-mini-0.8/resolve/main/voices.npz";
|
|
||||||
sha256 = "sha256-QK0mOJUrd7ey8wEn4mCOFp/GndJWtTvYqqNAmjMZPEI=";
|
|
||||||
};
|
|
||||||
kittenttsModelConfig = pkgs.fetchurl {
|
|
||||||
url = "https://huggingface.co/KittenML/kitten-tts-mini-0.8/resolve/main/config.json";
|
|
||||||
sha256 = "sha256-axYLybGeJOyyHoS8FPin2iH99H7HLUJFC8XPUUthgEo=";
|
|
||||||
};
|
|
||||||
|
|
||||||
# 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.
|
||||||
@@ -262,15 +231,6 @@ in
|
|||||||
"${hermesHome}:/opt/data"
|
"${hermesHome}:/opt/data"
|
||||||
"${dropboxDir}:/opt/data/dropbox"
|
"${dropboxDir}:/opt/data/dropbox"
|
||||||
|
|
||||||
# Mnemosyne memory provider — a Nix-built python env, mounted :ro.
|
|
||||||
# Nothing fetched at boot, nothing writable from inside the container.
|
|
||||||
# The plugin symlink the oneshot at the bottom of this file writes
|
|
||||||
# points at the canonical store path (site-packages passthru), which
|
|
||||||
# is visible inside thanks to the existing /nix/store ro mount, so
|
|
||||||
# this /opt/data restatement is a readability alias, not a load
|
|
||||||
# bearing path.
|
|
||||||
"${mnemosyneEnv}:/opt/data/mnemosyne-env:ro"
|
|
||||||
|
|
||||||
# luna's Obsidian vault, synced with CouchDB on jupiter by
|
# luna's Obsidian vault, synced with CouchDB on jupiter by
|
||||||
# livesync-bridge.nix. Under /opt/data so she can write notes, not just
|
# livesync-bridge.nix. Under /opt/data so she can write notes, not just
|
||||||
# read them; the bridge runs as this same uid/gid so no chown is needed.
|
# read them; the bridge runs as this same uid/gid so no chown is needed.
|
||||||
@@ -462,26 +422,9 @@ in
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# ---- Mnemosyne memory provider ----------------------------------------
|
# Hermes discovers memory providers under $HERMES_HOME/plugins; the target
|
||||||
# The provider's Python closure (mnemosyneEnv, callPackage'd
|
# is a store path, readable in the container via the /nix/store ro mount.
|
||||||
# pkgs/mnemosyne-env.nix above) is a READ-ONLY nix store path mounted into
|
# wantedBy, not requiredBy: a failure leaves Hermes on built-in memory.
|
||||||
# the container — nothing fetched at boot, nothing inside the container can
|
|
||||||
# write to it, and the mono-repo reproducibility story applies. Hermes only
|
|
||||||
# needs one mutable pickup point: $HERMES_HOME/plugins/mnemosyne, the
|
|
||||||
# symlink its discovery scan looks for. Both the plugin wrapper and its
|
|
||||||
# sibling `mnemosyne` core package live in that env's single site-packages,
|
|
||||||
# so one link covers them.
|
|
||||||
#
|
|
||||||
# Path handling: the symlink target is spelled in the CONTAINER's path
|
|
||||||
# space (/opt/data/...), because Hermes resolves the plugin from inside the
|
|
||||||
# container — the same host/container mismatch the webhook prompts already
|
|
||||||
# navigate via containerHome. Host-side the identical literal resolves
|
|
||||||
# onto the same store path through hermesHome's bind mount.
|
|
||||||
#
|
|
||||||
# Failure posture: after=, not requires= — a failed link write leaves the
|
|
||||||
# container running with whatever memory.provider falls back to Hermes's
|
|
||||||
# built-in memory, not a dead bot. The ro mount itself is evaluated at
|
|
||||||
# build time, so there is nothing provisionable to drift at runtime.
|
|
||||||
systemd.services.hermes-agent-mnemosyne-plugin = {
|
systemd.services.hermes-agent-mnemosyne-plugin = {
|
||||||
description = "Link Mnemosyne provider into the Hermes plugin dir";
|
description = "Link Mnemosyne provider into the Hermes plugin dir";
|
||||||
before = [ "podman-hermes-agent.service" ];
|
before = [ "podman-hermes-agent.service" ];
|
||||||
@@ -495,116 +438,16 @@ in
|
|||||||
};
|
};
|
||||||
script = ''
|
script = ''
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
pluginsDir=${hermesHome}/plugins
|
pluginsDir=${hermesHome}/plugins
|
||||||
pluginDir=$pluginsDir/mnemosyne
|
pluginDir=$pluginsDir/mnemosyne
|
||||||
# Target is the STORE path itself, not a /opt/data restatement: the
|
target=${mnemosyneEnv}/${mnemosyneEnv.sitePackages}/hermes_memory_provider
|
||||||
# container already ro-mounts /nix/store for git/tea (see the volumes
|
[ -d "$target" ] || { echo "$target missing" >&2; exit 1; }
|
||||||
# list), so the identical literal resolves on both sides of the bind
|
|
||||||
# mount. Using the canonical store path directly — not the
|
|
||||||
# /opt/data/mnemosyne-env mount — keeps one truth and still works
|
|
||||||
# whether Hermes resolves the link inside the container or host-side
|
|
||||||
# during debugging.
|
|
||||||
target="${mnemosyneEnv.sitePackages}/hermes_memory_provider"
|
|
||||||
|
|
||||||
if [ ! -d "$target" ]; then
|
|
||||||
echo "hermes_memory_provider not found in the mnemosyne env — unit bug, not transient" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$pluginsDir"
|
mkdir -p "$pluginsDir"
|
||||||
chown ${hermesUid}:${hermesGid} "$pluginsDir"
|
chown ${hermesUid}:${hermesGid} "$pluginsDir"
|
||||||
# Atomic swap: write to a temp name, rename over the old link. `-T`
|
|
||||||
# errors loudly if the target turned into a directory for any reason.
|
|
||||||
ln -sfn "$target" "$pluginDir.new"
|
ln -sfn "$target" "$pluginDir.new"
|
||||||
mv -Tf "$pluginDir.new" "$pluginDir"
|
mv -Tf "$pluginDir.new" "$pluginDir"
|
||||||
chown -h ${hermesUid}:${hermesGid} "$pluginDir"
|
chown -h ${hermesUid}:${hermesGid} "$pluginDir"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# ---- KittenTTS voice provider ------------------------------------------
|
|
||||||
# CPU-only onnxruntime TTS (no GPU on mars), mini model per darman. The
|
|
||||||
# upstream `misaki[en]` declaration is deliberately not honored — it pulls
|
|
||||||
# torch→CUDA (5.6 GB verified); the runtime phonemizes with espeak-ng only,
|
|
||||||
# so the dead `from misaki import en, espeak` import is satisfied by a
|
|
||||||
# .pth-registered stub (kitten-misaki-stub.py) that fails loudly if misaki
|
|
||||||
# is ever actually used.
|
|
||||||
#
|
|
||||||
# Provisioner invariants (shaped by the Mnemosyne-round review):
|
|
||||||
# - `uv venv --clear`: re-provision cannot wedge on an existing dir.
|
|
||||||
# - Root runs NO python from this venv: the unit itself does only fs
|
|
||||||
# writes; the venv is chowned to the container uid before Hermes ever
|
|
||||||
# imports from it. (Hermes executes provider code as uid 986.)
|
|
||||||
# - HF model cache is PRE-SEEDED from hash-pinned store paths, so
|
|
||||||
# HF_HUB_OFFLINE=1 gives zero boot-time network and no drift.
|
|
||||||
# - Idempotent via a stamp keyed on the requirements hash; checked
|
|
||||||
# against BOTH the venv python and model files being intact.
|
|
||||||
#
|
|
||||||
# Trust boundary: the venv lives inside hermesHome (HERMES_WRITE_SAFE_ROOT),
|
|
||||||
# so luna can technically alter her own TTS engine. Deliberate: it's her
|
|
||||||
# voice, not her jail — the webhook filter scripts remain the only
|
|
||||||
# write-protected-but-load-bearing items.
|
|
||||||
systemd.services.hermes-agent-kittentts-provision = {
|
|
||||||
description = "Provision KittenTTS voice provider (side venv + offline HF cache)";
|
|
||||||
before = [ "podman-hermes-agent.service" ];
|
|
||||||
wantedBy = [ "podman-hermes-agent.service" ];
|
|
||||||
wants = [ "network-online.target" ];
|
|
||||||
after = [ "network-online.target" ];
|
|
||||||
path = [ pkgs.uv pkgs.coreutils ];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
TimeoutStartSec = 600;
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
venv=${hermesHome}/kittentts-venv
|
|
||||||
hubDir=${hermesHome}/kittentts-hf/hub/models--KittenML--kitten-tts-mini-0.8
|
|
||||||
snap=$hubDir/snapshots/kitten
|
|
||||||
stampFile=${hermesHome}/kittentts-provision.stamp
|
|
||||||
reqHash=$(sha256sum ${kittenttsReqs} | cut -d' ' -f1)
|
|
||||||
|
|
||||||
# Idempotent early exit: stamp + venv + all three model files intact.
|
|
||||||
if [ -f "$stampFile" ] && [ "$(cat "$stampFile")" = "$reqHash" ] \
|
|
||||||
&& [ -x "$venv/bin/python" ] \
|
|
||||||
&& [ -f "$snap/kitten_tts_mini_v0_8.onnx" ] \
|
|
||||||
&& [ -f "$snap/voices.npz" ] \
|
|
||||||
&& [ -f "$venv/lib/python3.13/site-packages/kitten_tts_stub.py" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Venv (rebuilt rather than broken on --no-clear: uv exits 2 otherwise).
|
|
||||||
uv venv "$venv" --python ${pkgs.python313}/bin/python3 --clear --quiet
|
|
||||||
UV_CACHE_DIR=${hermesHome}/kittentts-uv-cache \
|
|
||||||
uv pip install --python "$venv/bin/python" --quiet \
|
|
||||||
--requirement ${kittenttsReqs}
|
|
||||||
# kittentts --no-deps: its overlay of spacy/misaki[en] is what drags in
|
|
||||||
# the CUDA tree; the requirements freeze already covers its real needs.
|
|
||||||
UV_CACHE_DIR=${hermesHome}/kittentts-uv-cache \
|
|
||||||
uv pip install --python "$venv/bin/python" --quiet --no-deps \
|
|
||||||
${kittenttsWheel}
|
|
||||||
|
|
||||||
# Dead-import shim: .pth auto-loads kitten_tts_stub at interpreter start
|
|
||||||
# so `from misaki import en, espeak` resolves without the real misaki.en.
|
|
||||||
siteDir=$("$venv/bin/python" -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')
|
|
||||||
cp ${kittenttsStub} "$siteDir/kitten_tts_stub.py"
|
|
||||||
printf 'import kitten_tts_stub\n' > "$siteDir/zz-kitten-stub.pth"
|
|
||||||
|
|
||||||
# Seed the HF cache with the hashed model files (exact hub layout;
|
|
||||||
# hf_hub_download scans refs/snapshots on disk offline).
|
|
||||||
mkdir -p "$hubDir/refs" "$snap"
|
|
||||||
install -m 0444 ${kittenttsModelOnnx} "$snap/kitten_tts_mini_v0_8.onnx"
|
|
||||||
install -m 0444 ${kittenttsModelVoices} "$snap/voices.npz"
|
|
||||||
install -m 0444 ${kittenttsModelConfig} "$snap/config.json"
|
|
||||||
|
|
||||||
# Hand ownership to the container uid BEFORE any python runs in this
|
|
||||||
# tree (root never imports from it — that was Mnemosyne review #3).
|
|
||||||
chown -R ${hermesUid}:${hermesGid} "$venv" "$hubDir"
|
|
||||||
|
|
||||||
# Stamp LAST — a half-provisioned venv fails the integrity check and
|
|
||||||
# re-provisions on next boot rather than being trusted.
|
|
||||||
printf '%s' "$reqHash" > "$stampFile"
|
|
||||||
chown ${hermesUid}:${hermesGid} "$stampFile"
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
# Runtime shim for KittenTTS on a CPU-only, disk-constrained host.
|
|
||||||
#
|
|
||||||
# kittentts/onnx_model.py opens with `from misaki import en, espeak` — a dead import:
|
|
||||||
# every code path that produces audio from text goes through phonemizer-fork's
|
|
||||||
# EspeakBackend (espeak-ng via espeakng-loader); misaki.en/espeak are never called.
|
|
||||||
# If misaki[en] were installed for real, those two imports would be a hook for the
|
|
||||||
# whole spacy → thinc → torch → CUDA chain (~5.6 GB verified). This stub registers
|
|
||||||
# `misaki.en` and `misaki.espeak` as import-time-only placeholders instead.
|
|
||||||
#
|
|
||||||
# Written into site-packages by the derivation (see pkgs/kittentts-env.nix) under
|
|
||||||
# `sitecustomize.py`-style auto-load — actually via a `kitten_tts_stub.py` + a `.pth`
|
|
||||||
# pointing at it, so any Python process in this env gets the stub registered before
|
|
||||||
# any kittentts import. If KittenTTS upstream ever starts USING misaki, this shim
|
|
||||||
# will fail loudly at import of the missing attributes (better than silent distortion),
|
|
||||||
# and the fix becomes "install the real misaki[en]" — a deliberate, reviewed change.
|
|
||||||
import sys
|
|
||||||
import types
|
|
||||||
|
|
||||||
_misaki = sys.modules.get("misaki")
|
|
||||||
if _misaki is None:
|
|
||||||
# Avoid registering a fake parent before the real misaki loads — the base misaki
|
|
||||||
# (addict/regex only) is installed normally, so usually already here.
|
|
||||||
import misaki # noqa: F401 (raises if base misaki is missing — loud, not silent)
|
|
||||||
_misaki = sys.modules["misaki"]
|
|
||||||
|
|
||||||
if getattr(_misaki, "en", None) is None:
|
|
||||||
_stub = types.ModuleType("misaki.en")
|
|
||||||
sys.modules.setdefault("misaki.en", _stub)
|
|
||||||
_misaki.en = _stub
|
|
||||||
|
|
||||||
if getattr(_misaki, "espeak", None) is None:
|
|
||||||
_stub2 = types.ModuleType("misaki.espeak")
|
|
||||||
sys.modules.setdefault("misaki.espeak", _stub2)
|
|
||||||
_misaki.espeak = _stub2
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
# Pinned requirements for the KittenTTS side-venv on mars (validated freeze, CPU-only).
|
|
||||||
#
|
|
||||||
# DEPENDENCY DISCIPLINE (important — this is NOT the upstream dependency set):
|
|
||||||
# `kittentts` declares `misaki[en]` + spacy, which transitively pull torch → the whole
|
|
||||||
# CUDA runtime (~5.6 GB with nvidia-* wheels, verified in a clean install on this box).
|
|
||||||
# NONE of that is reachable at runtime: onnx_model.py imports `from misaki import en, espeak`
|
|
||||||
# at module load but generate() phonemizes exclusively through `phonemizer-fork` +
|
|
||||||
# `espeakng-loader` (espeak-ng via EspeakBackend); misaki.en/espeak glyphs are parsed but
|
|
||||||
# never called in the ONNX path. mars has no GPU and a modest disk budget, so:
|
|
||||||
#
|
|
||||||
# - install kittentts with --no-deps and enumerate its deps manually,
|
|
||||||
# - install base `misaki` (NOT misaki[en]) — only addict/regex,
|
|
||||||
# - the misaki.en import is satisfied by a stub registered in sitecustomize
|
|
||||||
# (kitten-misaki-stub.py, written into site-packages by the derivation).
|
|
||||||
#
|
|
||||||
# Freeze captured 2026-09-19 from a verified CPU-only venv (python 3.13, i5-4460,
|
|
||||||
# generation ~= 1x realtime with the mini model, HF_HUB_OFFLINE=1).
|
|
||||||
#
|
|
||||||
# kittentts itself comes from a GitHub release wheel (0.8.1), not PyPI (PyPI's 0.1.3 is
|
|
||||||
# the old v0.1-lineage); see pkgs/kittentts-env.nix.
|
|
||||||
addict==2.4.0
|
|
||||||
anyio==4.15.1
|
|
||||||
attrs==26.1.0
|
|
||||||
babel==2.18.0
|
|
||||||
certifi==2026.7.22
|
|
||||||
cffi==2.1.1
|
|
||||||
click==8.5.0
|
|
||||||
cloudpickle==3.1.2
|
|
||||||
csvw==4.1.0
|
|
||||||
dlinfo==2.0.0
|
|
||||||
docopt==0.6.2
|
|
||||||
espeakng-loader==0.2.4
|
|
||||||
filelock==4.0.1
|
|
||||||
flatbuffers==25.12.19
|
|
||||||
fsspec==2026.9.0
|
|
||||||
h11==0.16.0
|
|
||||||
hf-xet==1.6.0
|
|
||||||
httpcore==1.0.9
|
|
||||||
httpx==0.28.1
|
|
||||||
huggingface-hub==1.32.0
|
|
||||||
idna==3.20
|
|
||||||
isodate==0.7.2
|
|
||||||
joblib==1.6.0
|
|
||||||
jsonschema-specifications==2025.9.1
|
|
||||||
jsonschema==4.26.0
|
|
||||||
language-tags==1.3.1
|
|
||||||
misaki==0.9.4
|
|
||||||
num2words==0.5.14
|
|
||||||
numpy==2.5.3
|
|
||||||
onnxruntime==1.30.0
|
|
||||||
packaging==26.3
|
|
||||||
phonemizer-fork==3.3.2
|
|
||||||
protobuf==7.36.2
|
|
||||||
pycparser==3.0
|
|
||||||
pyparsing==3.3.2
|
|
||||||
python-dateutil==2.9.0.post0
|
|
||||||
pyyaml==6.0.3
|
|
||||||
rdflib==7.6.0
|
|
||||||
referencing==0.37.0
|
|
||||||
regex==2026.9.10
|
|
||||||
rfc3986==1.5.0
|
|
||||||
rpds-py==2026.6.3
|
|
||||||
segments==2.4.0
|
|
||||||
six==1.17.0
|
|
||||||
soundfile==0.14.0
|
|
||||||
termcolor==3.3.0
|
|
||||||
tqdm==4.70.1
|
|
||||||
typing-extensions==4.16.0
|
|
||||||
uritemplate==4.2.0
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Pinned requirements for a Mnemosyne side-venv on mars.
|
|
||||||
#
|
|
||||||
# Hermes vendors its own Python (the official image's venv) and deliberately
|
|
||||||
# stays minimal: no pip module inside it, PEP 668 external-management on top.
|
|
||||||
# Installing provider packages straight into that interpreter would fight the
|
|
||||||
# image on every rebuild, so Mnemosyne (and its plugin wrapper) live in their
|
|
||||||
# own venv instead — see the provisioning unit in hosts/mars/hermes-agent.nix.
|
|
||||||
#
|
|
||||||
# Freeze captured 2026-09-19 from a verified container-side install of
|
|
||||||
# `mnemosyne-memory[embeddings]` + `mnemosyne-hermes` — side venv at
|
|
||||||
# $HERMES_HOME/mnemosyne-venv, activated via $HERMES_HOME/plugins/mnemosyne.
|
|
||||||
# Versions pinned exactly; transitive deps frozen for reproducibility
|
|
||||||
# (onnxruntime/numpy drift under a long-lived SQLite state dir is what a
|
|
||||||
# freeze is here to prevent).
|
|
||||||
#
|
|
||||||
anyio==4.15.0
|
|
||||||
certifi==2026.7.22
|
|
||||||
charset-normalizer==3.5.1
|
|
||||||
click==8.5.0
|
|
||||||
fastembed==0.8.0
|
|
||||||
filelock==3.32.5
|
|
||||||
flatbuffers==25.12.19
|
|
||||||
fsspec==2026.7.0
|
|
||||||
h11==0.16.0
|
|
||||||
hf-xet==1.6.0
|
|
||||||
httpcore==1.0.9
|
|
||||||
httpx==0.28.1
|
|
||||||
huggingface-hub==1.32.0
|
|
||||||
idna==3.19
|
|
||||||
loguru==0.7.3
|
|
||||||
mmh3==5.3.0
|
|
||||||
mnemosyne-hermes==0.5.0
|
|
||||||
mnemosyne-memory==3.15.1
|
|
||||||
numpy==2.5.3
|
|
||||||
onnxruntime==1.30.0
|
|
||||||
packaging==26.3
|
|
||||||
pillow==12.3.0
|
|
||||||
protobuf==7.36.1
|
|
||||||
py-rust-stemmers==0.1.8
|
|
||||||
pyyaml==6.0.3
|
|
||||||
requests==2.34.2
|
|
||||||
sqlite-vec==0.1.9
|
|
||||||
tokenizers==0.23.2
|
|
||||||
tqdm==4.70.0
|
|
||||||
typing-extensions==4.16.0
|
|
||||||
urllib3==2.7.0
|
|
||||||
+28
-58
@@ -1,86 +1,56 @@
|
|||||||
# Mnemosyne memory provider for Hermes on mars — packaged for real (Nix).
|
# Mnemosyne memory provider for Hermes. Built here rather than pip-installed:
|
||||||
|
# the image's Python has no pip and is PEP 668 managed.
|
||||||
#
|
#
|
||||||
# Why derivations instead of a runtime side-venv: the official Hermes image
|
# Core deps only: the embeddings extra (fastembed/onnxruntime) is optional at
|
||||||
# vendors its own Python and stays off-limits to pip (no pip module, PEP 668),
|
# runtime, and recall falls back to FTS5.
|
||||||
# and a runtime venv built host-side breaks twice over inside the container:
|
|
||||||
# the venv's pyvenv.cfg records a /nix/store python home the container never
|
|
||||||
# mounts, and a plugins symlink with an absolute host path points nowhere
|
|
||||||
# from /opt/data. Building here means nothing is fetched at boot, nothing
|
|
||||||
# under the provider's control is writable from inside the container, and
|
|
||||||
# the closure is as reproducible as the rest of the host.
|
|
||||||
#
|
|
||||||
# Package set (one shared site-packages — the plugin wrapper imports its
|
|
||||||
# sibling `mnemosyne` core package, so withPackages, not separate envs):
|
|
||||||
#
|
|
||||||
# mnemosyne-memory core engine: SQLite/FTS5 storage, recall, tools.
|
|
||||||
# Base deps only (PyYAML); the optional extras (llm,
|
|
||||||
# embeddings via fastembed/onnxruntime, mcp, sync) are
|
|
||||||
# deliberately NOT pulled — recall uses the bundled FTS5
|
|
||||||
# lexical path, and the heavyweight ML stack (~hundreds of
|
|
||||||
# MB, live network on first vector use) buys nothing for
|
|
||||||
# a first deployment. Adding the embeddings extra later
|
|
||||||
# is pinning fastembed + sqlite-vec here.
|
|
||||||
# mnemosyne-hermes the wrapper Hermes discovers under $HERMES_HOME/plugins
|
|
||||||
# (installs itself as package `hermes_memory_provider`).
|
|
||||||
# Hard dependency: mnemosyne-memory, PyYAML.
|
|
||||||
#
|
|
||||||
# Platform note: both sdists are pure Python (build no C extensions), so
|
|
||||||
# nothing here constrains the host arch beyond the interpreter itself.
|
|
||||||
{
|
{
|
||||||
python3,
|
python3,
|
||||||
fetchPypi,
|
fetchPypi,
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
python = python3;
|
mnemosyneMemory = python3.pkgs.buildPythonPackage rec {
|
||||||
|
|
||||||
mnemosyneMemory = python.pkgs.buildPythonPackage rec {
|
|
||||||
pname = "mnemosyne-memory";
|
pname = "mnemosyne-memory";
|
||||||
version = "3.15.1";
|
version = "3.15.1";
|
||||||
pyproject = true;
|
pyproject = true;
|
||||||
|
|
||||||
src = fetchPypi {
|
src = fetchPypi {
|
||||||
inherit pname version;
|
pname = "mnemosyne_memory";
|
||||||
sha256 = "sha256-lspUMxc0pUSkhSUrNdiiO5OJ1NMC/S853EYSanXtXKM=";
|
inherit version;
|
||||||
|
hash = "sha256-lspUMxc0pUSkhSUrNdiiO5OJ1NMC/S853EYSanXtXKM=";
|
||||||
};
|
};
|
||||||
|
|
||||||
build-system = with python.pkgs; [ setuptools ];
|
build-system = with python3.pkgs; [ setuptools ];
|
||||||
|
dependencies = with python3.pkgs; [ pyyaml ];
|
||||||
|
|
||||||
# Base dependency set — everything else in the upstream metadata is an
|
doCheck = false; # tests want a live Hermes + LLM key
|
||||||
# optional extra (llm / embeddings / mcp / sync / test / dev) and is not
|
# Ships the Hermes plugin package too, not just the core.
|
||||||
# installed; see the file-level comment.
|
pythonImportsCheck = [
|
||||||
dependencies = with python.pkgs; [ pyyaml ];
|
"mnemosyne"
|
||||||
|
"hermes_memory_provider"
|
||||||
doCheck = false; # upstream tests want a live Hermes + LLM key present
|
];
|
||||||
pythonImportsCheck = [ "mnemosyne" ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mnemosyneHermes = python.pkgs.buildPythonPackage rec {
|
mnemosyneHermes = python3.pkgs.buildPythonPackage rec {
|
||||||
pname = "mnemosyne-hermes";
|
pname = "mnemosyne-hermes";
|
||||||
version = "0.5.0";
|
version = "0.5.0";
|
||||||
pyproject = true;
|
pyproject = true;
|
||||||
|
|
||||||
src = fetchPypi {
|
src = fetchPypi {
|
||||||
inherit pname version;
|
pname = "mnemosyne_hermes";
|
||||||
sha256 = "sha256-CzEvnUw5oPFtT5bHQQ/GBdy2C/E7qShQn32irIRYKqw=";
|
inherit version;
|
||||||
|
hash = "sha256-CzEvnUw5oPFtT5bHQQ/GBdy2C/E7qShQn32irIRYKqw=";
|
||||||
};
|
};
|
||||||
|
|
||||||
build-system = with python.pkgs; [ setuptools ];
|
build-system = with python3.pkgs; [ setuptools ];
|
||||||
|
# Upstream asks for mnemosyne-memory[embeddings]; see the header.
|
||||||
# The wrapper declares `mnemosyne-memory[embeddings]>=3.11.1` on PyPI, but
|
dependencies = [ mnemosyneMemory ] ++ (with python3.pkgs; [ pyyaml ]);
|
||||||
# the embeddings extra is only consulted when vector recall is enabled
|
|
||||||
# (see above) — pass the core dependency explicitly rather than dragging
|
|
||||||
# in onnxruntime for nothing.
|
|
||||||
dependencies = [ mnemosyneMemory ] ++ (with python.pkgs; [ pyyaml ]);
|
|
||||||
|
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
pythonImportsCheck = [ "hermes_memory_provider" ];
|
pythonImportsCheck = [ "mnemosyne_hermes" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# The exposed value is the python env itself (a store path mounted :ro).
|
|
||||||
# Hermes only needs the site-packages dir inside it; `sitePackages` is a
|
|
||||||
# passthru the python interpreter derivation (and hence withPackages envs)
|
|
||||||
# exposes, so the caller uses `${env.sitePackages}` instead of guessing
|
|
||||||
# the python version in a path literal.
|
|
||||||
in
|
in
|
||||||
python.withPackages (_: [ mnemosyneMemory mnemosyneHermes ])
|
python3.withPackages (_: [
|
||||||
|
mnemosyneMemory
|
||||||
|
mnemosyneHermes
|
||||||
|
])
|
||||||
|
|||||||
Reference in New Issue
Block a user