This commit is contained in:
@@ -31,6 +31,7 @@ in
|
||||
];
|
||||
|
||||
nixpkgs.overlays = [
|
||||
hostConfig.inputs.self.overlays.default
|
||||
hostConfig.inputs.claude-code-nix.overlays.default
|
||||
];
|
||||
|
||||
|
||||
Generated
+17
@@ -267,6 +267,22 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"hindsight-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1780589667,
|
||||
"narHash": "sha256-Zb8zIdlnm9spUiVbGdByGKE/V/5q+RKjbuSbayNIDxY=",
|
||||
"owner": "vectorize-io",
|
||||
"repo": "hindsight",
|
||||
"rev": "7683f29004ab9d88e0ef3f0f18b8ffe6fc741905",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "vectorize-io",
|
||||
"repo": "hindsight",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"home-manager": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -441,6 +457,7 @@
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"hermes-agent": "hermes-agent",
|
||||
"hindsight-src": "hindsight-src",
|
||||
"home-manager": "home-manager",
|
||||
"microvm": "microvm",
|
||||
"nix-index-database": "nix-index-database",
|
||||
|
||||
@@ -94,6 +94,13 @@
|
||||
uv2nix.inputs.pyproject-nix.follows = "hermes-agent/pyproject-nix";
|
||||
};
|
||||
};
|
||||
|
||||
# Hindsight memory server (vectorize-io). No upstream flake; consumed as a
|
||||
# bare source tree and packaged via the uv2nix toolchain from hermes-agent.
|
||||
hindsight-src = {
|
||||
url = "github:vectorize-io/hindsight";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, ... }@inputs:
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
|
||||
extraPackages = with pkgs; [ nix git ripgrep fd jq codex ];
|
||||
|
||||
# Pulls in hindsight-client (the HTTP client lib the memory plugin uses).
|
||||
extraDependencyGroups = [ "hindsight" ];
|
||||
|
||||
environment = {
|
||||
SIGNAL_HTTP_URL = "http://127.0.0.1:8080";
|
||||
CODEX_HOME = "/var/lib/hermes/.codex";
|
||||
@@ -44,14 +47,14 @@
|
||||
toolsets = [ "all" ];
|
||||
terminal.backend = "local";
|
||||
|
||||
# Self-hosted memory: pure SQLite in-process, no external services or
|
||||
# API keys. db file lives under HERMES_HOME (= /var/lib/hermes/.hermes),
|
||||
# which is on the persisted bind-mount.
|
||||
memory.provider = "holographic";
|
||||
plugins.hermes-memory-store = {
|
||||
db_path = "/var/lib/hermes/.hermes/memory_store.db";
|
||||
auto_extract = true;
|
||||
default_trust = 0.5;
|
||||
# Memory lives in a sibling hindsight-api process (see systemd unit
|
||||
# below) backed by system postgres. Plugin talks HTTP to it locally.
|
||||
memory.provider = "hindsight";
|
||||
plugins.hermes-hindsight = {
|
||||
mode = "local_external";
|
||||
api_url = "http://127.0.0.1:8888";
|
||||
bank_id = "hermes";
|
||||
memory_mode = "hybrid";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -80,6 +83,8 @@
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/hermes/.codex 0700 googlebot users -"
|
||||
"C+ /var/lib/hermes/.codex/config.toml 0644 googlebot users - /etc/hermes-codex-config.toml"
|
||||
"d /var/lib/hermes/hindsight 0700 googlebot users -"
|
||||
"d /var/lib/hermes/postgresql 0750 postgres postgres -"
|
||||
];
|
||||
|
||||
# signal-cli runs an HTTP JSON-RPC daemon on localhost; hermes-agent talks to
|
||||
@@ -102,4 +107,103 @@
|
||||
RestartSec = "5";
|
||||
};
|
||||
};
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Hindsight memory backend
|
||||
#
|
||||
# Architecture:
|
||||
# hermes-agent --HTTP--> hindsight-api (port 8888) --asyncpg--> postgres
|
||||
# hindsight-worker (background async retain/reflect)
|
||||
#
|
||||
# The api + worker run as googlebot with HOME=/var/lib/hermes so the
|
||||
# `openai-codex` provider's hardcoded Path.home() / ".codex" / "auth.json"
|
||||
# resolves to the persisted codex tokens.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
package = pkgs.postgresql_17;
|
||||
extensions = ps: [ ps.pgvector ];
|
||||
# Persist DB state on the /var/lib/hermes bind mount so the hindsight
|
||||
# memory bank survives `nixos-rebuild switch` (which recreates the
|
||||
# incus container and wipes its writable layer).
|
||||
dataDir = "/var/lib/hermes/postgresql/17";
|
||||
ensureDatabases = [ "hindsight" ];
|
||||
ensureUsers = [{
|
||||
name = "googlebot";
|
||||
# Superuser so the hindsight-api alembic migrations can `CREATE EXTENSION`
|
||||
# and own the schema. Acceptable here because postgres is local-only to
|
||||
# the sandboxed workspace and only talks to hindsight-api over the unix
|
||||
# socket.
|
||||
ensureClauses.superuser = true;
|
||||
}];
|
||||
};
|
||||
|
||||
systemd.services.hindsight-api =
|
||||
let
|
||||
env = {
|
||||
# Connection — postgres is on the local unix socket; sqlalchemy gets
|
||||
# an asyncpg URL via the asyncpg+host=<socket-dir> trick.
|
||||
HINDSIGHT_API_DATABASE_URL = "postgresql+asyncpg:///hindsight?host=/run/postgresql";
|
||||
HINDSIGHT_API_HOST = "127.0.0.1";
|
||||
HINDSIGHT_API_PORT = "8888";
|
||||
|
||||
# LLM + embeddings both ride codex OAuth — no separate keys.
|
||||
HINDSIGHT_API_LLM_PROVIDER = "openai-codex";
|
||||
HINDSIGHT_API_EMBEDDINGS_PROVIDER = "openai-codex";
|
||||
|
||||
# We did not install [local-ml] extras, so disable the reranker
|
||||
# (default is "local" which requires sentence-transformers).
|
||||
HINDSIGHT_API_RERANKER_PROVIDER = "none";
|
||||
|
||||
# HOME drives where the codex auth file is read from
|
||||
# (CodexLLM/CodexOAuthEmbeddings: Path.home() / ".codex" / "auth.json").
|
||||
HOME = "/var/lib/hermes";
|
||||
};
|
||||
in
|
||||
{
|
||||
description = "Hindsight memory API server";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "postgresql.service" "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
requires = [ "postgresql.service" ];
|
||||
before = [ "hermes-agent.service" ];
|
||||
environment = env;
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "googlebot";
|
||||
Group = "users";
|
||||
ExecStart = "${pkgs.hindsight-api}/bin/hindsight-api";
|
||||
Restart = "always";
|
||||
RestartSec = "5";
|
||||
WorkingDirectory = "/var/lib/hermes/hindsight";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.hindsight-worker =
|
||||
let
|
||||
env = {
|
||||
HINDSIGHT_API_DATABASE_URL = "postgresql+asyncpg:///hindsight?host=/run/postgresql";
|
||||
HINDSIGHT_API_LLM_PROVIDER = "openai-codex";
|
||||
HINDSIGHT_API_EMBEDDINGS_PROVIDER = "openai-codex";
|
||||
HINDSIGHT_API_RERANKER_PROVIDER = "none";
|
||||
HOME = "/var/lib/hermes";
|
||||
};
|
||||
in
|
||||
{
|
||||
description = "Hindsight background worker (async retain / reflect)";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "hindsight-api.service" ];
|
||||
requires = [ "hindsight-api.service" ];
|
||||
environment = env;
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "googlebot";
|
||||
Group = "users";
|
||||
ExecStart = "${pkgs.hindsight-api}/bin/hindsight-worker";
|
||||
Restart = "always";
|
||||
RestartSec = "5";
|
||||
WorkingDirectory = "/var/lib/hermes/hindsight";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -52,4 +52,13 @@ in
|
||||
qtmultimedia qtwayland qtwebengine qcoro;
|
||||
inherit (prev) lib fetchFromGitLab pkg-config sdl3 libcec wayland;
|
||||
};
|
||||
|
||||
# Hindsight agent-memory server. Built via uv2nix against the upstream
|
||||
# workspace; uses hermes-agent's toolchain pin to avoid duplicating uv2nix.
|
||||
hindsight-api = prev.callPackage ../pkgs/hindsight {
|
||||
hindsight-src = inputs.hindsight-src;
|
||||
uv2nix = inputs.hermes-agent.inputs.uv2nix;
|
||||
pyproject-nix = inputs.hermes-agent.inputs.pyproject-nix;
|
||||
pyproject-build-systems = inputs.hermes-agent.inputs.pyproject-build-systems;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
{ lib
|
||||
, callPackage
|
||||
, python312
|
||||
, stdenv
|
||||
, makeWrapper
|
||||
|
||||
, hindsight-src
|
||||
, uv2nix
|
||||
, pyproject-nix
|
||||
, pyproject-build-systems
|
||||
}:
|
||||
|
||||
# Build the hindsight-api-slim venv from upstream's uv workspace. We pick
|
||||
# `hindsight-api-slim` (not `hindsight-api`) so we skip the `[all]` extras
|
||||
# (local-ml, embedded-db). With openai-codex provider for both LLM and
|
||||
# embeddings, and system postgres as the store, those extras are unused weight.
|
||||
let
|
||||
workspace = uv2nix.lib.workspace.loadWorkspace {
|
||||
workspaceRoot = hindsight-src;
|
||||
};
|
||||
|
||||
overlay = workspace.mkPyprojectOverlay {
|
||||
sourcePreference = "wheel";
|
||||
};
|
||||
|
||||
pythonSet =
|
||||
(callPackage pyproject-nix.build.packages {
|
||||
python = python312;
|
||||
}).overrideScope
|
||||
(lib.composeManyExtensions [
|
||||
pyproject-build-systems.overlays.default
|
||||
overlay
|
||||
]);
|
||||
|
||||
venv = pythonSet.mkVirtualEnv "hindsight-api-env" {
|
||||
hindsight-api-slim = [ ];
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
pname = "hindsight-api";
|
||||
version = "0.7.2";
|
||||
|
||||
dontUnpack = true;
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
mkdir -p $out/bin
|
||||
# The venv ships hindsight-api / hindsight-worker / hindsight-admin /
|
||||
# hindsight-local-mcp / alembic in $venv/bin. Link them out so callers
|
||||
# don't have to know about the venv layout.
|
||||
for bin in hindsight-api hindsight-worker hindsight-admin hindsight-local-mcp alembic; do
|
||||
if [ -e ${venv}/bin/$bin ]; then
|
||||
ln -s ${venv}/bin/$bin $out/bin/$bin
|
||||
fi
|
||||
done
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
passthru = {
|
||||
inherit venv pythonSet;
|
||||
pythonEnv = venv;
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
description = "Hindsight: agent memory with knowledge graph and tiered retrieval";
|
||||
homepage = "https://github.com/vectorize-io/hindsight";
|
||||
license = licenses.mit;
|
||||
mainProgram = "hindsight-api";
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user