hermes-memory-pgvector 0.5.4__tar.gz → 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hermes_memory_pgvector-0.6.0/PKG-INFO +236 -0
- hermes_memory_pgvector-0.6.0/README.md +199 -0
- hermes_memory_pgvector-0.6.0/hermes_memory_pgvector.egg-info/PKG-INFO +236 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_memory_pgvector.egg-info/SOURCES.txt +9 -1
- hermes_memory_pgvector-0.6.0/hermes_memory_pgvector.egg-info/requires.txt +11 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/__init__.py +2149 -1612
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/__main__.py +530 -471
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/embed.py +330 -311
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/identity.py +219 -192
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/migrations/001_schema.sql +95 -95
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/migrations/002_agent_attribution.sql +158 -130
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/migrations/003_hybrid_search_fts.sql +51 -51
- hermes_memory_pgvector-0.6.0/hermes_pgvector/migrations/004_runtime_grants.sql +53 -0
- hermes_memory_pgvector-0.6.0/hermes_pgvector/migrations/005_content_md5_unique.sql +63 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/plugin.yaml +16 -16
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/store.py +1466 -1259
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_pgvector/writer.py +206 -192
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/pyproject.toml +92 -83
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/setup.cfg +4 -4
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_async_writer.py +110 -110
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_config_coercion.py +184 -112
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_embed_config.py +9 -1
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_embed_timeouts.py +106 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_empty_content.py +332 -332
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_exclude_identities_live.py +197 -197
- hermes_memory_pgvector-0.6.0/tests/test_fake_embed_server.py +164 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_hybrid_search.py +124 -123
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_identity.py +298 -298
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_install_shim.py +104 -104
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_save_config_merge.py +68 -68
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_session_switch_contract.py +140 -140
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_smoke.py +319 -316
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_store_v04.py +246 -246
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_system_prompt_block.py +51 -51
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_turn_dedup.py +280 -280
- hermes_memory_pgvector-0.6.0/tests/test_v060_hook_hardening.py +136 -0
- hermes_memory_pgvector-0.6.0/tests/test_v060_identity_writer.py +278 -0
- hermes_memory_pgvector-0.6.0/tests/test_v060_migrations_cli.py +512 -0
- hermes_memory_pgvector-0.6.0/tests/test_v060_provider_reads.py +657 -0
- hermes_memory_pgvector-0.6.0/tests/test_v060_provider_writes.py +1041 -0
- hermes_memory_pgvector-0.6.0/tests/test_v060_store.py +365 -0
- hermes_memory_pgvector-0.5.4/PKG-INFO +0 -503
- hermes_memory_pgvector-0.5.4/README.md +0 -470
- hermes_memory_pgvector-0.5.4/hermes_memory_pgvector.egg-info/PKG-INFO +0 -503
- hermes_memory_pgvector-0.5.4/hermes_memory_pgvector.egg-info/requires.txt +0 -6
- hermes_memory_pgvector-0.5.4/hermes_pgvector/migrations/004_runtime_grants.sql +0 -36
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/LICENSE +0 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_memory_pgvector.egg-info/dependency_links.txt +0 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_memory_pgvector.egg-info/entry_points.txt +0 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/hermes_memory_pgvector.egg-info/top_level.txt +0 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_loader_embed_clobber.py +0 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_read_side_gate.py +0 -0
- {hermes_memory_pgvector-0.5.4 → hermes_memory_pgvector-0.6.0}/tests/test_tool_args_hardening.py +0 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hermes-memory-pgvector
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Postgres + pgvector memory provider plugin for hermes-agent. Multi-agent storage layer with per-minion themes, identity governance, agent attribution + delegation provenance, async writer, no LLM in the memory hot path.
|
|
5
|
+
Author: Andrea Borghi
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/andreab67/hermes-memory-pgvector
|
|
8
|
+
Project-URL: Source, https://github.com/andreab67/hermes-memory-pgvector
|
|
9
|
+
Project-URL: Issues, https://github.com/andreab67/hermes-memory-pgvector/issues
|
|
10
|
+
Project-URL: Roadmap, https://github.com/andreab67/hermes-memory-pgvector/blob/main/ROADMAP.md
|
|
11
|
+
Keywords: hermes-agent,memory-provider,pgvector,postgres,multi-agent,llm-memory,semantic-search
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: System Administrators
|
|
15
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Database
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: psycopg[binary]<4,>=3.3.6
|
|
28
|
+
Requires-Dist: psycopg-pool<4,>=3.3.3
|
|
29
|
+
Requires-Dist: PyYAML<7,>=6.0
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest<9,>=7.4; extra == "test"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: build<2,>=1; extra == "dev"
|
|
34
|
+
Requires-Dist: twine<7,>=5; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff<1,>=0.5; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# hermes-memory-pgvector
|
|
39
|
+
|
|
40
|
+
**Postgres + pgvector memory provider for [hermes-agent](https://github.com/NousResearch/hermes-agent).** A shared memory substrate for a fleet of cooperating hermes-agent minions — built on Postgres and a single embedding endpoint you probably already run, with no LLM in the memory hot path.
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
each minion → X-Hermes-Session-Key: <theme>
|
|
44
|
+
→ hermes-agent gateway
|
|
45
|
+
→ pgvector plugin
|
|
46
|
+
├── memory_entries (mirrors built-in MEMORY.md / USER.md per theme)
|
|
47
|
+
└── conversations (every substantive turn, semantically searchable)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Why it exists
|
|
51
|
+
|
|
52
|
+
Existing memory providers each solve a piece of the problem; the gap for **fleet deployments** is wide:
|
|
53
|
+
|
|
54
|
+
- **Built-in `memory` tool** persists to per-host `MEMORY.md` / `USER.md`. Two minions on the same host stomp on each other; minions on different hosts have no shared substrate.
|
|
55
|
+
- **Honcho** offers cross-session user modelling but requires a full external service, an LLM in the memory hot path for its deriver + dialectic loops, and its own ontology layered on top of the built-in tool. In high-concurrency fleet use it produces retry storms, embedding-endpoint queue backups, and gateway↔Honcho circular dependencies.
|
|
56
|
+
- **Holographic** is a fine in-process fact store but uses SQLite — a poor fit for many minions writing concurrently from many hosts.
|
|
57
|
+
- **Other providers** (Mem0, Hindsight, OpenViking, ByteRover, RetainDB, Supermemory) all either require a paid cloud, require LLM mediation for memory ops, or both.
|
|
58
|
+
|
|
59
|
+
What was missing: a **storage layer** that gives the built-in `memory` model durable, multi-tenant, semantically-searchable backing, with no LLM in the hot path, scoped cleanly per-minion so a marketing agent's notes don't pollute a trading agent's recall. That's what this plugin provides.
|
|
60
|
+
|
|
61
|
+
## Design philosophy
|
|
62
|
+
|
|
63
|
+
1. **Storage layer, not a memory model.** The agent keeps using `memory(action='add', target='memory'|'user', …)`. We mirror those writes via `on_memory_write`. No new ontology for the agent to learn.
|
|
64
|
+
2. **No LLM in the memory hot path.** Embeddings are vector math, not LLM calls. There is no deriver, no dialectic, no dream cycle.
|
|
65
|
+
3. **Per-agent themes by default, cross-theme recall on explicit demand.** Every row carries `agent_identity`. Recall is scoped to the current theme unless the agent asks for `scope='all'`.
|
|
66
|
+
4. **Fail-soft everywhere.** Embed endpoint down → degrade to text-only writes. Async writer queue full → drop with a one-time warning. DB down → log + skip. No exception escapes into the agent loop.
|
|
67
|
+
5. **Admin/runtime separation.** DDL runs once with superuser via `hermes-pgvector migrate`; the runtime role gets DML only on the migrated schema.
|
|
68
|
+
|
|
69
|
+
## What it does
|
|
70
|
+
|
|
71
|
+
| Hook / surface | Behavior |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `initialize()` | Verifies schema, opens a `psycopg_pool.ConnectionPool`, bulk-imports existing `MEMORY.md` + `USER.md` content. |
|
|
74
|
+
| `on_memory_write(action, target, content, meta)` | Mirrors built-in `memory` writes into `memory_entries` (add / replace / remove). |
|
|
75
|
+
| `sync_turn(user, assistant, session_id)` / `on_session_end(messages)` | Captures every substantive chat turn into `conversations`; `on_session_end` is a dedup'd backstop. |
|
|
76
|
+
| `on_delegation(task, result, …)` | Records parent→child delegation provenance (`memory_agent_edges`) and stores the exchange as a recallable turn. |
|
|
77
|
+
| `prefetch(query)` / `queue_prefetch(query, session_id=...)` | Ambient recall: top-K similar `memory_entries` for the current theme, injected into the system prompt. `queue_prefetch` pre-computes it off the agent thread. |
|
|
78
|
+
| `recall_memory(query, scope, target, limit)` tool | Explicit cross-theme search of durable memory entries. |
|
|
79
|
+
| `recall_conversation(query, scope, limit)` tool | Explicit search over past chat turns. `scope ∈ {current, session, all, <theme>}`. |
|
|
80
|
+
|
|
81
|
+
Operator maintenance CLI: `hermes-pgvector migrate · stats · backfill · prune · cleanup · remap` (destructive commands default to dry-run) — see [docs/operations.md](docs/operations.md).
|
|
82
|
+
|
|
83
|
+
## Install
|
|
84
|
+
|
|
85
|
+
### Option 1: pip + discovery shim (recommended)
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install hermes-memory-pgvector
|
|
89
|
+
hermes-pgvector install # writes $HERMES_HOME/plugins/pgvector/ (older hosts only)
|
|
90
|
+
|
|
91
|
+
# Apply ALL migrations (schema + attribution + FTS + runtime grants + md5 unique index)
|
|
92
|
+
hermes-pgvector migrate --admin-dsn \
|
|
93
|
+
"dbname=<your-memory-db> user=postgres host=/var/run/postgresql"
|
|
94
|
+
# add --runtime-role NAME if your runtime role isn't `hermes`
|
|
95
|
+
|
|
96
|
+
hermes config set memory.provider pgvector
|
|
97
|
+
sudo systemctl restart hermes.service
|
|
98
|
+
hermes memory status # expect: Provider: pgvector; Status: available
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Why the shim?** hermes-agent resolves a provider from bundled dirs, then `$HERMES_HOME/plugins/<name>/`, then the `hermes_agent.memory_providers` pip entry-point group (declared by this package since v0.5.0). On a host that reads the entry point, `pip install` alone is enough and `install` is a no-op you can skip; the shim remains for older hosts that only scan directories. A shim *directory* always wins over the entry point when one is present.
|
|
102
|
+
|
|
103
|
+
### Option 2: clone + run the installer script (from source)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
git clone https://github.com/andreab67/hermes-memory-pgvector.git
|
|
107
|
+
cd hermes-memory-pgvector
|
|
108
|
+
./scripts/install.sh
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Installs the pinned Python deps, copies `hermes_pgvector/` into `$HERMES_HOME/plugins/pgvector/`, and prints the migrate/activate commands.
|
|
112
|
+
|
|
113
|
+
### Option 3: manual
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# Python deps
|
|
117
|
+
pip install 'psycopg[binary]>=3.3.6,<4' 'psycopg-pool>=3.3.3,<4' 'PyYAML>=6.0,<7'
|
|
118
|
+
|
|
119
|
+
# Plugin module
|
|
120
|
+
mkdir -p ~/.hermes/plugins
|
|
121
|
+
cp -r hermes_pgvector ~/.hermes/plugins/pgvector
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Then apply migrations and activate as in Option 1 above (`hermes-pgvector migrate --admin-dsn ...`, or `psql -f` each `migrations/*.sql` in order — see [docs/operations.md](docs/operations.md) for the full admin walkthrough and [docs/upgrading.md](docs/upgrading.md) if you're coming from an older version).
|
|
125
|
+
|
|
126
|
+
## Configuration
|
|
127
|
+
|
|
128
|
+
Lives in `$HERMES_HOME/config.yaml` under `plugins.pgvector`, every key optional:
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
plugins:
|
|
132
|
+
pgvector:
|
|
133
|
+
dsn: "dbname=hermes_memory user=hermes host=/var/run/postgresql"
|
|
134
|
+
embed_url: "http://localhost:11434"
|
|
135
|
+
embed_model: "nomic-embed-text"
|
|
136
|
+
allowed_themes: "marketing,sales,trading" # empty/unset = allow any theme
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Full reference for every key (type, default, since-version, effect): [docs/configuration.md](docs/configuration.md). Scaling guidance (HNSW tuning, pool sizing, embed timeouts): [docs/scaling.md](docs/scaling.md).
|
|
140
|
+
|
|
141
|
+
## Multi-agent / per-minion themes
|
|
142
|
+
|
|
143
|
+
Each systemd-run minion sets one header on its OpenAI client; everything else flows automatically:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
client = AsyncOpenAI(
|
|
147
|
+
base_url="http://127.0.0.1:8642/v1",
|
|
148
|
+
api_key=API_KEY,
|
|
149
|
+
default_headers={"X-Hermes-Session-Key": "marketing"}, # ← theme
|
|
150
|
+
)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
The gateway plumbs `X-Hermes-Session-Key` through as `gateway_session_key=…` in `MemoryProvider.initialize` kwargs, taking priority over the profile default so unprofiled API traffic doesn't collapse every minion into one shared `default` scope.
|
|
154
|
+
|
|
155
|
+
Convention: lowercase, dash-separated, stable (identities are case-folded automatically — `Marketing` and `marketing` are the same theme). Governed sinks that always exist regardless of your themes: `whatsapp-dm` (collapsed DM/session keys), `external-group` (collapsed group/channel/thread keys), `_bench` (benchmark traffic), `default` (last resort). Set `plugins.pgvector.allowed_themes` to your product/worker list to enforce an allow-list — an unknown or typo'd header then falls back to `default` (one-time warning) instead of silently minting a new theme.
|
|
156
|
+
|
|
157
|
+
## Compatibility policy
|
|
158
|
+
|
|
159
|
+
This plugin follows semantic versioning on its **public surface**:
|
|
160
|
+
|
|
161
|
+
- `plugins.pgvector.*` config keys (see [docs/configuration.md](docs/configuration.md))
|
|
162
|
+
- Tool names and parameters (`recall_memory`, `recall_conversation`)
|
|
163
|
+
- CLI commands, flags, and exit codes (`hermes-pgvector ...`)
|
|
164
|
+
- Database table/column names
|
|
165
|
+
- The `pgvector` provider name and the package's pip entry point
|
|
166
|
+
|
|
167
|
+
`MemoryStore` and every other Python class/module are **internal** — not covered by semver, safe to change between minor releases. A migration file, once released, is never edited in place; schema changes land as a new numbered migration.
|
|
168
|
+
|
|
169
|
+
## Support matrix
|
|
170
|
+
|
|
171
|
+
Tested in CI on every push/PR:
|
|
172
|
+
|
|
173
|
+
| Component | Versions |
|
|
174
|
+
|---|---|
|
|
175
|
+
| Python | 3.11, 3.12, 3.13 |
|
|
176
|
+
| PostgreSQL | 16, 17, 18 |
|
|
177
|
+
| pgvector extension | `>= 0.5.0` required; CI uses the version bundled in the `pgvector/pgvector:pg1x` images |
|
|
178
|
+
| hermes-agent | Conformance-tested against the pinned ref in [`conformance/HERMES_AGENT_REF`](conformance/HERMES_AGENT_REF), plus a non-blocking weekly drift check against upstream `main` |
|
|
179
|
+
|
|
180
|
+
## Docs
|
|
181
|
+
|
|
182
|
+
- [docs/configuration.md](docs/configuration.md) — every config key
|
|
183
|
+
- [docs/operations.md](docs/operations.md) — migrate, backfill, prune, cleanup, remap, stats, alerting
|
|
184
|
+
- [docs/scaling.md](docs/scaling.md) — HNSW tuning, pool sizing, embed latency budgets
|
|
185
|
+
- [docs/troubleshooting.md](docs/troubleshooting.md) — common failures and fixes
|
|
186
|
+
- [docs/upgrading.md](docs/upgrading.md) — version-to-version upgrade procedures
|
|
187
|
+
- [docs/release/RELEASING.md](docs/release/RELEASING.md) — maintainer release checklist
|
|
188
|
+
- [CHANGELOG.md](CHANGELOG.md) — every release, in [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format
|
|
189
|
+
- [ROADMAP.md](ROADMAP.md) — milestones, what shipped where, what's deliberately not planned
|
|
190
|
+
- [SECURITY.md](SECURITY.md) — supported versions, how to report a vulnerability
|
|
191
|
+
|
|
192
|
+
## Tests
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
pip install -e ".[test]"
|
|
196
|
+
pytest -q # no DB/embed endpoint: everything skips gracefully
|
|
197
|
+
|
|
198
|
+
# Live mode (against a throwaway Postgres + your embed endpoint)
|
|
199
|
+
export PG_TEST_DSN='dbname=hermes_test user=postgres host=/var/run/postgresql'
|
|
200
|
+
export PG_TEST_EMBED_URL='http://your-embed-endpoint:11434'
|
|
201
|
+
pytest -q
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Rollback
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
hermes config set memory.provider none
|
|
208
|
+
sudo systemctl restart hermes.service
|
|
209
|
+
|
|
210
|
+
# Optional — drop every plugin-owned object (data loss, irreversible). Their
|
|
211
|
+
# sequences and indexes are dropped automatically with the tables; DROP VIEW
|
|
212
|
+
# must run before the tables it selects from.
|
|
213
|
+
sudo -u postgres psql -d <your-memory-db> -c "
|
|
214
|
+
DROP VIEW IF EXISTS v_agent_memory;
|
|
215
|
+
DROP TABLE IF EXISTS memory_maintenance_log;
|
|
216
|
+
DROP TABLE IF EXISTS memory_agent_edges;
|
|
217
|
+
DROP TABLE IF EXISTS memory_agents;
|
|
218
|
+
DROP TABLE IF EXISTS conversations;
|
|
219
|
+
DROP TABLE IF EXISTS memory_entries;
|
|
220
|
+
"
|
|
221
|
+
|
|
222
|
+
# Optional — remove the plugin files
|
|
223
|
+
rm -rf ~/.hermes/plugins/pgvector
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Why a standalone plugin (not an upstream PR)?
|
|
227
|
+
|
|
228
|
+
Per the hermes-agent [`CONTRIBUTING.md`](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md): the set of built-in memory providers is closed, and new backends are expected to ship as standalone plugins installed into `~/.hermes/plugins/` or via a pip entry point — exactly what this package does.
|
|
229
|
+
|
|
230
|
+
## Contributing
|
|
231
|
+
|
|
232
|
+
Bug reports + PRs welcome. Open an issue describing the failure mode + your environment (hermes-agent version, Postgres version, embed endpoint), or a PR with a focused change + test.
|
|
233
|
+
|
|
234
|
+
## License
|
|
235
|
+
|
|
236
|
+
[BSD 3-Clause](LICENSE) © 2026 Green Yoga Inc
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# hermes-memory-pgvector
|
|
2
|
+
|
|
3
|
+
**Postgres + pgvector memory provider for [hermes-agent](https://github.com/NousResearch/hermes-agent).** A shared memory substrate for a fleet of cooperating hermes-agent minions — built on Postgres and a single embedding endpoint you probably already run, with no LLM in the memory hot path.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
each minion → X-Hermes-Session-Key: <theme>
|
|
7
|
+
→ hermes-agent gateway
|
|
8
|
+
→ pgvector plugin
|
|
9
|
+
├── memory_entries (mirrors built-in MEMORY.md / USER.md per theme)
|
|
10
|
+
└── conversations (every substantive turn, semantically searchable)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Why it exists
|
|
14
|
+
|
|
15
|
+
Existing memory providers each solve a piece of the problem; the gap for **fleet deployments** is wide:
|
|
16
|
+
|
|
17
|
+
- **Built-in `memory` tool** persists to per-host `MEMORY.md` / `USER.md`. Two minions on the same host stomp on each other; minions on different hosts have no shared substrate.
|
|
18
|
+
- **Honcho** offers cross-session user modelling but requires a full external service, an LLM in the memory hot path for its deriver + dialectic loops, and its own ontology layered on top of the built-in tool. In high-concurrency fleet use it produces retry storms, embedding-endpoint queue backups, and gateway↔Honcho circular dependencies.
|
|
19
|
+
- **Holographic** is a fine in-process fact store but uses SQLite — a poor fit for many minions writing concurrently from many hosts.
|
|
20
|
+
- **Other providers** (Mem0, Hindsight, OpenViking, ByteRover, RetainDB, Supermemory) all either require a paid cloud, require LLM mediation for memory ops, or both.
|
|
21
|
+
|
|
22
|
+
What was missing: a **storage layer** that gives the built-in `memory` model durable, multi-tenant, semantically-searchable backing, with no LLM in the hot path, scoped cleanly per-minion so a marketing agent's notes don't pollute a trading agent's recall. That's what this plugin provides.
|
|
23
|
+
|
|
24
|
+
## Design philosophy
|
|
25
|
+
|
|
26
|
+
1. **Storage layer, not a memory model.** The agent keeps using `memory(action='add', target='memory'|'user', …)`. We mirror those writes via `on_memory_write`. No new ontology for the agent to learn.
|
|
27
|
+
2. **No LLM in the memory hot path.** Embeddings are vector math, not LLM calls. There is no deriver, no dialectic, no dream cycle.
|
|
28
|
+
3. **Per-agent themes by default, cross-theme recall on explicit demand.** Every row carries `agent_identity`. Recall is scoped to the current theme unless the agent asks for `scope='all'`.
|
|
29
|
+
4. **Fail-soft everywhere.** Embed endpoint down → degrade to text-only writes. Async writer queue full → drop with a one-time warning. DB down → log + skip. No exception escapes into the agent loop.
|
|
30
|
+
5. **Admin/runtime separation.** DDL runs once with superuser via `hermes-pgvector migrate`; the runtime role gets DML only on the migrated schema.
|
|
31
|
+
|
|
32
|
+
## What it does
|
|
33
|
+
|
|
34
|
+
| Hook / surface | Behavior |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `initialize()` | Verifies schema, opens a `psycopg_pool.ConnectionPool`, bulk-imports existing `MEMORY.md` + `USER.md` content. |
|
|
37
|
+
| `on_memory_write(action, target, content, meta)` | Mirrors built-in `memory` writes into `memory_entries` (add / replace / remove). |
|
|
38
|
+
| `sync_turn(user, assistant, session_id)` / `on_session_end(messages)` | Captures every substantive chat turn into `conversations`; `on_session_end` is a dedup'd backstop. |
|
|
39
|
+
| `on_delegation(task, result, …)` | Records parent→child delegation provenance (`memory_agent_edges`) and stores the exchange as a recallable turn. |
|
|
40
|
+
| `prefetch(query)` / `queue_prefetch(query, session_id=...)` | Ambient recall: top-K similar `memory_entries` for the current theme, injected into the system prompt. `queue_prefetch` pre-computes it off the agent thread. |
|
|
41
|
+
| `recall_memory(query, scope, target, limit)` tool | Explicit cross-theme search of durable memory entries. |
|
|
42
|
+
| `recall_conversation(query, scope, limit)` tool | Explicit search over past chat turns. `scope ∈ {current, session, all, <theme>}`. |
|
|
43
|
+
|
|
44
|
+
Operator maintenance CLI: `hermes-pgvector migrate · stats · backfill · prune · cleanup · remap` (destructive commands default to dry-run) — see [docs/operations.md](docs/operations.md).
|
|
45
|
+
|
|
46
|
+
## Install
|
|
47
|
+
|
|
48
|
+
### Option 1: pip + discovery shim (recommended)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install hermes-memory-pgvector
|
|
52
|
+
hermes-pgvector install # writes $HERMES_HOME/plugins/pgvector/ (older hosts only)
|
|
53
|
+
|
|
54
|
+
# Apply ALL migrations (schema + attribution + FTS + runtime grants + md5 unique index)
|
|
55
|
+
hermes-pgvector migrate --admin-dsn \
|
|
56
|
+
"dbname=<your-memory-db> user=postgres host=/var/run/postgresql"
|
|
57
|
+
# add --runtime-role NAME if your runtime role isn't `hermes`
|
|
58
|
+
|
|
59
|
+
hermes config set memory.provider pgvector
|
|
60
|
+
sudo systemctl restart hermes.service
|
|
61
|
+
hermes memory status # expect: Provider: pgvector; Status: available
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Why the shim?** hermes-agent resolves a provider from bundled dirs, then `$HERMES_HOME/plugins/<name>/`, then the `hermes_agent.memory_providers` pip entry-point group (declared by this package since v0.5.0). On a host that reads the entry point, `pip install` alone is enough and `install` is a no-op you can skip; the shim remains for older hosts that only scan directories. A shim *directory* always wins over the entry point when one is present.
|
|
65
|
+
|
|
66
|
+
### Option 2: clone + run the installer script (from source)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/andreab67/hermes-memory-pgvector.git
|
|
70
|
+
cd hermes-memory-pgvector
|
|
71
|
+
./scripts/install.sh
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Installs the pinned Python deps, copies `hermes_pgvector/` into `$HERMES_HOME/plugins/pgvector/`, and prints the migrate/activate commands.
|
|
75
|
+
|
|
76
|
+
### Option 3: manual
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Python deps
|
|
80
|
+
pip install 'psycopg[binary]>=3.3.6,<4' 'psycopg-pool>=3.3.3,<4' 'PyYAML>=6.0,<7'
|
|
81
|
+
|
|
82
|
+
# Plugin module
|
|
83
|
+
mkdir -p ~/.hermes/plugins
|
|
84
|
+
cp -r hermes_pgvector ~/.hermes/plugins/pgvector
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Then apply migrations and activate as in Option 1 above (`hermes-pgvector migrate --admin-dsn ...`, or `psql -f` each `migrations/*.sql` in order — see [docs/operations.md](docs/operations.md) for the full admin walkthrough and [docs/upgrading.md](docs/upgrading.md) if you're coming from an older version).
|
|
88
|
+
|
|
89
|
+
## Configuration
|
|
90
|
+
|
|
91
|
+
Lives in `$HERMES_HOME/config.yaml` under `plugins.pgvector`, every key optional:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
plugins:
|
|
95
|
+
pgvector:
|
|
96
|
+
dsn: "dbname=hermes_memory user=hermes host=/var/run/postgresql"
|
|
97
|
+
embed_url: "http://localhost:11434"
|
|
98
|
+
embed_model: "nomic-embed-text"
|
|
99
|
+
allowed_themes: "marketing,sales,trading" # empty/unset = allow any theme
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Full reference for every key (type, default, since-version, effect): [docs/configuration.md](docs/configuration.md). Scaling guidance (HNSW tuning, pool sizing, embed timeouts): [docs/scaling.md](docs/scaling.md).
|
|
103
|
+
|
|
104
|
+
## Multi-agent / per-minion themes
|
|
105
|
+
|
|
106
|
+
Each systemd-run minion sets one header on its OpenAI client; everything else flows automatically:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
client = AsyncOpenAI(
|
|
110
|
+
base_url="http://127.0.0.1:8642/v1",
|
|
111
|
+
api_key=API_KEY,
|
|
112
|
+
default_headers={"X-Hermes-Session-Key": "marketing"}, # ← theme
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The gateway plumbs `X-Hermes-Session-Key` through as `gateway_session_key=…` in `MemoryProvider.initialize` kwargs, taking priority over the profile default so unprofiled API traffic doesn't collapse every minion into one shared `default` scope.
|
|
117
|
+
|
|
118
|
+
Convention: lowercase, dash-separated, stable (identities are case-folded automatically — `Marketing` and `marketing` are the same theme). Governed sinks that always exist regardless of your themes: `whatsapp-dm` (collapsed DM/session keys), `external-group` (collapsed group/channel/thread keys), `_bench` (benchmark traffic), `default` (last resort). Set `plugins.pgvector.allowed_themes` to your product/worker list to enforce an allow-list — an unknown or typo'd header then falls back to `default` (one-time warning) instead of silently minting a new theme.
|
|
119
|
+
|
|
120
|
+
## Compatibility policy
|
|
121
|
+
|
|
122
|
+
This plugin follows semantic versioning on its **public surface**:
|
|
123
|
+
|
|
124
|
+
- `plugins.pgvector.*` config keys (see [docs/configuration.md](docs/configuration.md))
|
|
125
|
+
- Tool names and parameters (`recall_memory`, `recall_conversation`)
|
|
126
|
+
- CLI commands, flags, and exit codes (`hermes-pgvector ...`)
|
|
127
|
+
- Database table/column names
|
|
128
|
+
- The `pgvector` provider name and the package's pip entry point
|
|
129
|
+
|
|
130
|
+
`MemoryStore` and every other Python class/module are **internal** — not covered by semver, safe to change between minor releases. A migration file, once released, is never edited in place; schema changes land as a new numbered migration.
|
|
131
|
+
|
|
132
|
+
## Support matrix
|
|
133
|
+
|
|
134
|
+
Tested in CI on every push/PR:
|
|
135
|
+
|
|
136
|
+
| Component | Versions |
|
|
137
|
+
|---|---|
|
|
138
|
+
| Python | 3.11, 3.12, 3.13 |
|
|
139
|
+
| PostgreSQL | 16, 17, 18 |
|
|
140
|
+
| pgvector extension | `>= 0.5.0` required; CI uses the version bundled in the `pgvector/pgvector:pg1x` images |
|
|
141
|
+
| hermes-agent | Conformance-tested against the pinned ref in [`conformance/HERMES_AGENT_REF`](conformance/HERMES_AGENT_REF), plus a non-blocking weekly drift check against upstream `main` |
|
|
142
|
+
|
|
143
|
+
## Docs
|
|
144
|
+
|
|
145
|
+
- [docs/configuration.md](docs/configuration.md) — every config key
|
|
146
|
+
- [docs/operations.md](docs/operations.md) — migrate, backfill, prune, cleanup, remap, stats, alerting
|
|
147
|
+
- [docs/scaling.md](docs/scaling.md) — HNSW tuning, pool sizing, embed latency budgets
|
|
148
|
+
- [docs/troubleshooting.md](docs/troubleshooting.md) — common failures and fixes
|
|
149
|
+
- [docs/upgrading.md](docs/upgrading.md) — version-to-version upgrade procedures
|
|
150
|
+
- [docs/release/RELEASING.md](docs/release/RELEASING.md) — maintainer release checklist
|
|
151
|
+
- [CHANGELOG.md](CHANGELOG.md) — every release, in [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format
|
|
152
|
+
- [ROADMAP.md](ROADMAP.md) — milestones, what shipped where, what's deliberately not planned
|
|
153
|
+
- [SECURITY.md](SECURITY.md) — supported versions, how to report a vulnerability
|
|
154
|
+
|
|
155
|
+
## Tests
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
pip install -e ".[test]"
|
|
159
|
+
pytest -q # no DB/embed endpoint: everything skips gracefully
|
|
160
|
+
|
|
161
|
+
# Live mode (against a throwaway Postgres + your embed endpoint)
|
|
162
|
+
export PG_TEST_DSN='dbname=hermes_test user=postgres host=/var/run/postgresql'
|
|
163
|
+
export PG_TEST_EMBED_URL='http://your-embed-endpoint:11434'
|
|
164
|
+
pytest -q
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Rollback
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
hermes config set memory.provider none
|
|
171
|
+
sudo systemctl restart hermes.service
|
|
172
|
+
|
|
173
|
+
# Optional — drop every plugin-owned object (data loss, irreversible). Their
|
|
174
|
+
# sequences and indexes are dropped automatically with the tables; DROP VIEW
|
|
175
|
+
# must run before the tables it selects from.
|
|
176
|
+
sudo -u postgres psql -d <your-memory-db> -c "
|
|
177
|
+
DROP VIEW IF EXISTS v_agent_memory;
|
|
178
|
+
DROP TABLE IF EXISTS memory_maintenance_log;
|
|
179
|
+
DROP TABLE IF EXISTS memory_agent_edges;
|
|
180
|
+
DROP TABLE IF EXISTS memory_agents;
|
|
181
|
+
DROP TABLE IF EXISTS conversations;
|
|
182
|
+
DROP TABLE IF EXISTS memory_entries;
|
|
183
|
+
"
|
|
184
|
+
|
|
185
|
+
# Optional — remove the plugin files
|
|
186
|
+
rm -rf ~/.hermes/plugins/pgvector
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Why a standalone plugin (not an upstream PR)?
|
|
190
|
+
|
|
191
|
+
Per the hermes-agent [`CONTRIBUTING.md`](https://github.com/NousResearch/hermes-agent/blob/main/CONTRIBUTING.md): the set of built-in memory providers is closed, and new backends are expected to ship as standalone plugins installed into `~/.hermes/plugins/` or via a pip entry point — exactly what this package does.
|
|
192
|
+
|
|
193
|
+
## Contributing
|
|
194
|
+
|
|
195
|
+
Bug reports + PRs welcome. Open an issue describing the failure mode + your environment (hermes-agent version, Postgres version, embed endpoint), or a PR with a focused change + test.
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
[BSD 3-Clause](LICENSE) © 2026 Green Yoga Inc
|