hermes-memra 4.5.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_memra-4.5.0/.gitignore +3 -0
- hermes_memra-4.5.0/LICENSE +21 -0
- hermes_memra-4.5.0/PKG-INFO +253 -0
- hermes_memra-4.5.0/README.md +233 -0
- hermes_memra-4.5.0/install-local.sh +87 -0
- hermes_memra-4.5.0/install.sh +149 -0
- hermes_memra-4.5.0/memra/README.md +153 -0
- hermes_memra-4.5.0/memra/__init__.py +875 -0
- hermes_memra-4.5.0/memra/plugin.yaml +9 -0
- hermes_memra-4.5.0/memra/scripts/memra_doctor.py +319 -0
- hermes_memra-4.5.0/memra/scripts/migrate_tenant.py +302 -0
- hermes_memra-4.5.0/pyproject.toml +39 -0
- hermes_memra-4.5.0/tests/__init__.py +0 -0
- hermes_memra-4.5.0/tests/test_memra_provider.py +195 -0
- hermes_memra-4.5.0/tests/test_migrate_tenant.py +138 -0
- hermes_memra-4.5.0/tests/test_provider_tenant.py +408 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ali Vonsensey
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hermes-memra
|
|
3
|
+
Version: 4.5.0
|
|
4
|
+
Summary: Memra memory provider for Hermes Agent — EU-native, privacy-first persistent memory with hybrid recall, supersede chains, and staleness signals. Cloud or fully-local.
|
|
5
|
+
Project-URL: Homepage, https://usememra.com
|
|
6
|
+
Project-URL: Repository, https://github.com/usememra/hermes-memra
|
|
7
|
+
Project-URL: Documentation, https://usememra.com/hermes-memory
|
|
8
|
+
Project-URL: Changelog, https://github.com/usememra/hermes-memra/releases
|
|
9
|
+
Author-email: Ali Vonsensey <hello@usememra.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai-agents,hermes-agent,long-term-memory,memory,memory-provider,memra
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: httpx>=0.24
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Memra ↔ Hermes Agent
|
|
22
|
+
|
|
23
|
+
A [Memra](https://usememra.com) memory-provider plugin for
|
|
24
|
+
[Hermes Agent](https://hermes-agent.nousresearch.com) (Nous Research).
|
|
25
|
+
|
|
26
|
+
Hermes Agent supports pluggable, single-select external memory providers
|
|
27
|
+
(Honcho, Mem0, Hindsight, OpenViking, Holographic, RetainDB, ByteRover,
|
|
28
|
+
Supermemory). This makes **Memra** an option alongside them: hybrid
|
|
29
|
+
semantic + structured recall, typed memories, importance ranking, EU-native
|
|
30
|
+
self-hosting, and server-side compression of long-lived memories.
|
|
31
|
+
|
|
32
|
+
`memra/` is the drop-in plugin directory. In the Hermes **source tree** it
|
|
33
|
+
lives at `plugins/memory/memra/` (the bundled-provider layout, imported as the
|
|
34
|
+
package `plugins.memory.memra`). A **user install** instead goes one level deep
|
|
35
|
+
at `$HERMES_HOME/plugins/memra/` — see below.
|
|
36
|
+
|
|
37
|
+
## Install (pip — recommended)
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install hermes-memra
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Hermes Agent discovers the provider automatically via the `hermes_agent.plugins`
|
|
44
|
+
entry point — no files to copy. Then set your key and select the provider:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
export MEMRA_API_KEY=memra_live_... # free key at https://usememra.com
|
|
48
|
+
hermes memory provider memra
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Fully-local mode (no cloud): `pip install memra-local`, run `memra serve`, and
|
|
52
|
+
set `mode: local` in the plugin config — same tools, zero external calls.
|
|
53
|
+
|
|
54
|
+
The `install.sh` script remains for source-tree / $HERMES_HOME drop-in installs.
|
|
55
|
+
|
|
56
|
+
## What it does
|
|
57
|
+
|
|
58
|
+
| Hermes lifecycle | Memra behavior |
|
|
59
|
+
|------------------|----------------|
|
|
60
|
+
| `prefetch` / `queue_prefetch` | Background hybrid recall before each turn |
|
|
61
|
+
| `sync_turn` | Persists each turn as an `event` memory |
|
|
62
|
+
| `on_pre_compress` | Ships about-to-be-discarded context to Memra so it survives window compression (Memra then compresses it server-side) |
|
|
63
|
+
| `on_memory_write` | Mirrors Hermes `MEMORY.md` / `USER.md` writes into Memra |
|
|
64
|
+
| Tools | `memra_search`, `memra_remember` (store or supersede a fact), `memra_profile` |
|
|
65
|
+
|
|
66
|
+
All network calls are wrapped in a circuit breaker so a Memra outage never
|
|
67
|
+
blocks the agent loop. The plugin is self-contained (only `httpx`) — it calls
|
|
68
|
+
the Memra REST API directly, no Memra client package required.
|
|
69
|
+
|
|
70
|
+
## Install today (Hermes ≥ 0.11)
|
|
71
|
+
|
|
72
|
+
Hermes discovers user-installed providers one level deep under
|
|
73
|
+
`$HERMES_HOME/plugins/<name>/`. The easiest path is the installer:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install.sh | bash
|
|
77
|
+
hermes memory setup # select "memra", paste API key + project id
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or do it by hand — note the destination is `plugins/memra/`, not
|
|
81
|
+
`plugins/memory/memra/` (that's the in-tree bundled path):
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
cp -r memra ~/.hermes/plugins/memra
|
|
85
|
+
hermes memory setup # select "memra", paste API key + project id
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Or wire it manually:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
hermes config set memory.provider memra
|
|
92
|
+
echo "MEMRA_API_KEY=memra_live_xxx" >> ~/.hermes/.env
|
|
93
|
+
echo '{"project_id": "proj_xxx"}' > ~/.hermes/memra.json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Get an API key and project id at [usememra.com](https://usememra.com), or point
|
|
97
|
+
`base_url` at your own self-hosted Memra to keep all data on your infra.
|
|
98
|
+
|
|
99
|
+
> **Want zero cloud?** Skip the account entirely and run fully on-device — see
|
|
100
|
+
> [Run fully local](#run-fully-local-memra-local) below. It's a single
|
|
101
|
+
> `"mode": "local"` switch.
|
|
102
|
+
|
|
103
|
+
## Run fully local (memra-local)
|
|
104
|
+
|
|
105
|
+
Run everything on your own machine — no account, no API key, no network. The
|
|
106
|
+
plugin points at [`memra-local`](https://pypi.org/project/memra-local/), a
|
|
107
|
+
single-process on-device memory server (SQLite + on-device embeddings). This is
|
|
108
|
+
distinct from self-hosting the cloud API above: memra-local runs entirely
|
|
109
|
+
locally.
|
|
110
|
+
|
|
111
|
+
**Cloud vs local is one switch.** The plugin has a `mode` setting:
|
|
112
|
+
|
|
113
|
+
| `mode` | Backend | Needs an API key / account? |
|
|
114
|
+
|--------------------|-------------------------------|-----------------------------|
|
|
115
|
+
| `cloud` *(default)*| Hosted / self-hosted Memra API | Yes |
|
|
116
|
+
| `local` | memra-local on this machine | No |
|
|
117
|
+
|
|
118
|
+
Set `mode` to `local` and the plugin auto-fills the local URL
|
|
119
|
+
(`http://127.0.0.1:8765/v1`), a throwaway API key, and a default namespace —
|
|
120
|
+
nothing else to configure.
|
|
121
|
+
|
|
122
|
+
### What you'll need
|
|
123
|
+
|
|
124
|
+
- **Python 3.11+** with `pip` (to install and run memra-local).
|
|
125
|
+
- **Hermes Agent ≥ 0.11** (this plugin).
|
|
126
|
+
- **~300 MB disk** for memra-local (no PyTorch — it uses an ONNX runtime ~55 MB
|
|
127
|
+
plus a one-time ~90 MB model download). Your memories live in a local SQLite
|
|
128
|
+
file.
|
|
129
|
+
- A terminal you can leave open (or a background process) for the server — **it
|
|
130
|
+
must be running whenever you use Hermes.**
|
|
131
|
+
|
|
132
|
+
### Quick start
|
|
133
|
+
|
|
134
|
+
**Option A — one command (installs the server, configures the plugin, starts it):**
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install-local.sh | bash
|
|
138
|
+
hermes config set memory.provider memra
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
That installs `memra-local`, writes `~/.hermes/memra.json` with `"mode": "local"`,
|
|
142
|
+
and starts the server on port 8765 in the background.
|
|
143
|
+
|
|
144
|
+
**Option B — step by step (you stay in control of each piece):**
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# 1. Install the on-device server
|
|
148
|
+
pip install memra-local
|
|
149
|
+
|
|
150
|
+
# 2. Start it — LEAVE THIS RUNNING (its own terminal/tab). Port 8765.
|
|
151
|
+
memra serve --port 8765
|
|
152
|
+
|
|
153
|
+
# 3. Install the plugin in local mode (in a second terminal)
|
|
154
|
+
MEMRA_MODE=local \
|
|
155
|
+
curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install.sh | bash
|
|
156
|
+
|
|
157
|
+
# 4. Select Memra as the provider
|
|
158
|
+
hermes config set memory.provider memra
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Either way you end up with a one-line `~/.hermes/memra.json`:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{ "mode": "local", "project_id": "hermes", "tenant_id": "hermes-user" }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
To switch an existing cloud install to local, just add `"mode": "local"` to that
|
|
168
|
+
file (and start the server). To go back, set it to `"cloud"`.
|
|
169
|
+
|
|
170
|
+
### ⚠️ The server must be running
|
|
171
|
+
|
|
172
|
+
This is the one thing that trips people up: **memra-local is a separate process
|
|
173
|
+
that has to be up** (listening on `http://127.0.0.1:8765`). If it isn't, every
|
|
174
|
+
memory call fails and the plugin's circuit breaker quietly pauses retries for a
|
|
175
|
+
couple of minutes — looking like "memory just doesn't work."
|
|
176
|
+
|
|
177
|
+
- Check it's up: `curl http://127.0.0.1:8765/health` → `{"status":"healthy",...}`
|
|
178
|
+
- It does **not** auto-start on reboot. After a reboot, run `memra serve` again.
|
|
179
|
+
- Background it: `nohup memra serve --port 8765 >~/.memra-local.log 2>&1 &`
|
|
180
|
+
- Always-on: run `memra serve` as a `systemd --user` service.
|
|
181
|
+
|
|
182
|
+
### Good to know
|
|
183
|
+
|
|
184
|
+
- **`/v1`, not `/api/v1`** — memra-local mounts the API at `/v1`. `mode: local`
|
|
185
|
+
handles this for you; only matters if you set `base_url` by hand.
|
|
186
|
+
- **The API key is ignored** — memra-local doesn't authenticate. The plugin
|
|
187
|
+
still needs *some* non-empty key, so `mode: local` sets a placeholder. Don't
|
|
188
|
+
paste a real `memra_live_…` key into a local config.
|
|
189
|
+
- **`project_id` / `tenant_id` are local namespaces** — any names you like; they
|
|
190
|
+
scope memories within the on-device store.
|
|
191
|
+
- **Stale env wins:** a `MEMRA_BASE_URL` / `MEMRA_API_KEY` left in
|
|
192
|
+
`~/.hermes/.env` overrides `mode: local`. If local mode seems to hit the cloud,
|
|
193
|
+
clear those. The init line in `~/.hermes/logs/agent.log` prints the resolved
|
|
194
|
+
`mode=` and `base_url=` so you can confirm.
|
|
195
|
+
|
|
196
|
+
What runs local: the plugin's full REST contract — `memra_remember` (add and
|
|
197
|
+
supersede), `memra_search`, `memra_profile`, plus the supersession audit chain —
|
|
198
|
+
is verified end-to-end against memra-local. Recall is **semantic** (on-device
|
|
199
|
+
`all-MiniLM-L6-v2` embeddings via ONNX, with FTS5 keyword as fallback), so
|
|
200
|
+
"recall by meaning" works fully offline — no OpenAI key and no PyTorch.
|
|
201
|
+
|
|
202
|
+
## Ship to all Hermes users (PR)
|
|
203
|
+
|
|
204
|
+
To appear in Hermes's official provider list, the `memra/` directory is
|
|
205
|
+
submitted to `NousResearch/hermes-agent` under `plugins/memory/memra/`:
|
|
206
|
+
|
|
207
|
+
1. Fork `NousResearch/hermes-agent`.
|
|
208
|
+
2. Copy `memra/` → `plugins/memory/memra/`.
|
|
209
|
+
3. Add an entry to `website/docs/user-guide/features/memory-providers.md`.
|
|
210
|
+
4. Open a PR. (See `docs/developer-guide/memory-provider-plugin.md` for their
|
|
211
|
+
contribution requirements — this plugin already follows that contract.)
|
|
212
|
+
|
|
213
|
+
PR note for maintainers: unlike the other providers this one ships
|
|
214
|
+
self-contained (httpx only) rather than depending on a `memra-sdk` PyPI package,
|
|
215
|
+
because the import name collides with an unrelated existing `memra` package.
|
|
216
|
+
Happy to switch to the SDK once it's published under a non-colliding name.
|
|
217
|
+
|
|
218
|
+
## Test
|
|
219
|
+
|
|
220
|
+
The unit tests stub out the host imports, so they run standalone — no Hermes
|
|
221
|
+
checkout and no live Memra account required:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
pip install pytest
|
|
225
|
+
pytest tests/
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
They cover tenant-id resolution, the `memra_remember` supersede paths, and the
|
|
229
|
+
`migrate_tenant` cascade handling.
|
|
230
|
+
|
|
231
|
+
## Troubleshooting
|
|
232
|
+
|
|
233
|
+
**Local mode: memory does nothing / every call fails.** The memra-local server
|
|
234
|
+
isn't running. It's a separate process — start it with `memra serve --port 8765`
|
|
235
|
+
and confirm `curl http://127.0.0.1:8765/health` returns `healthy`. After a
|
|
236
|
+
reboot you must start it again. See [Run fully local](#run-fully-local-memra-local).
|
|
237
|
+
|
|
238
|
+
**Local mode seems to hit the cloud anyway.** A leftover `MEMRA_BASE_URL` or
|
|
239
|
+
`MEMRA_API_KEY` in `~/.hermes/.env` overrides `mode: local`. Clear them. The init
|
|
240
|
+
line in `~/.hermes/logs/agent.log` shows the resolved `mode=` and `base_url=`.
|
|
241
|
+
|
|
242
|
+
**Very long memories occasionally fail to store with some local models.**
|
|
243
|
+
This is a host-side (Hermes) limitation, not a Memra one. Some local models
|
|
244
|
+
(e.g. certain OpenRouter local models) emit slightly-malformed JSON when a
|
|
245
|
+
tool call carries a large argument; Hermes' tool-call sanitizer may drop the
|
|
246
|
+
argument before it reaches Memra. Memra itself accepts content up to 10,000
|
|
247
|
+
characters of any shape. Workarounds: use a well-behaved model (Claude, GPT,
|
|
248
|
+
most hosted models), or split very large memories into smaller writes. Tracked
|
|
249
|
+
in [#4](https://github.com/usememra/hermes-memra/issues/4).
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
[MIT](LICENSE) © Ali Vonsensey
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Memra ↔ Hermes Agent
|
|
2
|
+
|
|
3
|
+
A [Memra](https://usememra.com) memory-provider plugin for
|
|
4
|
+
[Hermes Agent](https://hermes-agent.nousresearch.com) (Nous Research).
|
|
5
|
+
|
|
6
|
+
Hermes Agent supports pluggable, single-select external memory providers
|
|
7
|
+
(Honcho, Mem0, Hindsight, OpenViking, Holographic, RetainDB, ByteRover,
|
|
8
|
+
Supermemory). This makes **Memra** an option alongside them: hybrid
|
|
9
|
+
semantic + structured recall, typed memories, importance ranking, EU-native
|
|
10
|
+
self-hosting, and server-side compression of long-lived memories.
|
|
11
|
+
|
|
12
|
+
`memra/` is the drop-in plugin directory. In the Hermes **source tree** it
|
|
13
|
+
lives at `plugins/memory/memra/` (the bundled-provider layout, imported as the
|
|
14
|
+
package `plugins.memory.memra`). A **user install** instead goes one level deep
|
|
15
|
+
at `$HERMES_HOME/plugins/memra/` — see below.
|
|
16
|
+
|
|
17
|
+
## Install (pip — recommended)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install hermes-memra
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Hermes Agent discovers the provider automatically via the `hermes_agent.plugins`
|
|
24
|
+
entry point — no files to copy. Then set your key and select the provider:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
export MEMRA_API_KEY=memra_live_... # free key at https://usememra.com
|
|
28
|
+
hermes memory provider memra
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Fully-local mode (no cloud): `pip install memra-local`, run `memra serve`, and
|
|
32
|
+
set `mode: local` in the plugin config — same tools, zero external calls.
|
|
33
|
+
|
|
34
|
+
The `install.sh` script remains for source-tree / $HERMES_HOME drop-in installs.
|
|
35
|
+
|
|
36
|
+
## What it does
|
|
37
|
+
|
|
38
|
+
| Hermes lifecycle | Memra behavior |
|
|
39
|
+
|------------------|----------------|
|
|
40
|
+
| `prefetch` / `queue_prefetch` | Background hybrid recall before each turn |
|
|
41
|
+
| `sync_turn` | Persists each turn as an `event` memory |
|
|
42
|
+
| `on_pre_compress` | Ships about-to-be-discarded context to Memra so it survives window compression (Memra then compresses it server-side) |
|
|
43
|
+
| `on_memory_write` | Mirrors Hermes `MEMORY.md` / `USER.md` writes into Memra |
|
|
44
|
+
| Tools | `memra_search`, `memra_remember` (store or supersede a fact), `memra_profile` |
|
|
45
|
+
|
|
46
|
+
All network calls are wrapped in a circuit breaker so a Memra outage never
|
|
47
|
+
blocks the agent loop. The plugin is self-contained (only `httpx`) — it calls
|
|
48
|
+
the Memra REST API directly, no Memra client package required.
|
|
49
|
+
|
|
50
|
+
## Install today (Hermes ≥ 0.11)
|
|
51
|
+
|
|
52
|
+
Hermes discovers user-installed providers one level deep under
|
|
53
|
+
`$HERMES_HOME/plugins/<name>/`. The easiest path is the installer:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install.sh | bash
|
|
57
|
+
hermes memory setup # select "memra", paste API key + project id
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or do it by hand — note the destination is `plugins/memra/`, not
|
|
61
|
+
`plugins/memory/memra/` (that's the in-tree bundled path):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cp -r memra ~/.hermes/plugins/memra
|
|
65
|
+
hermes memory setup # select "memra", paste API key + project id
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or wire it manually:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
hermes config set memory.provider memra
|
|
72
|
+
echo "MEMRA_API_KEY=memra_live_xxx" >> ~/.hermes/.env
|
|
73
|
+
echo '{"project_id": "proj_xxx"}' > ~/.hermes/memra.json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Get an API key and project id at [usememra.com](https://usememra.com), or point
|
|
77
|
+
`base_url` at your own self-hosted Memra to keep all data on your infra.
|
|
78
|
+
|
|
79
|
+
> **Want zero cloud?** Skip the account entirely and run fully on-device — see
|
|
80
|
+
> [Run fully local](#run-fully-local-memra-local) below. It's a single
|
|
81
|
+
> `"mode": "local"` switch.
|
|
82
|
+
|
|
83
|
+
## Run fully local (memra-local)
|
|
84
|
+
|
|
85
|
+
Run everything on your own machine — no account, no API key, no network. The
|
|
86
|
+
plugin points at [`memra-local`](https://pypi.org/project/memra-local/), a
|
|
87
|
+
single-process on-device memory server (SQLite + on-device embeddings). This is
|
|
88
|
+
distinct from self-hosting the cloud API above: memra-local runs entirely
|
|
89
|
+
locally.
|
|
90
|
+
|
|
91
|
+
**Cloud vs local is one switch.** The plugin has a `mode` setting:
|
|
92
|
+
|
|
93
|
+
| `mode` | Backend | Needs an API key / account? |
|
|
94
|
+
|--------------------|-------------------------------|-----------------------------|
|
|
95
|
+
| `cloud` *(default)*| Hosted / self-hosted Memra API | Yes |
|
|
96
|
+
| `local` | memra-local on this machine | No |
|
|
97
|
+
|
|
98
|
+
Set `mode` to `local` and the plugin auto-fills the local URL
|
|
99
|
+
(`http://127.0.0.1:8765/v1`), a throwaway API key, and a default namespace —
|
|
100
|
+
nothing else to configure.
|
|
101
|
+
|
|
102
|
+
### What you'll need
|
|
103
|
+
|
|
104
|
+
- **Python 3.11+** with `pip` (to install and run memra-local).
|
|
105
|
+
- **Hermes Agent ≥ 0.11** (this plugin).
|
|
106
|
+
- **~300 MB disk** for memra-local (no PyTorch — it uses an ONNX runtime ~55 MB
|
|
107
|
+
plus a one-time ~90 MB model download). Your memories live in a local SQLite
|
|
108
|
+
file.
|
|
109
|
+
- A terminal you can leave open (or a background process) for the server — **it
|
|
110
|
+
must be running whenever you use Hermes.**
|
|
111
|
+
|
|
112
|
+
### Quick start
|
|
113
|
+
|
|
114
|
+
**Option A — one command (installs the server, configures the plugin, starts it):**
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install-local.sh | bash
|
|
118
|
+
hermes config set memory.provider memra
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
That installs `memra-local`, writes `~/.hermes/memra.json` with `"mode": "local"`,
|
|
122
|
+
and starts the server on port 8765 in the background.
|
|
123
|
+
|
|
124
|
+
**Option B — step by step (you stay in control of each piece):**
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# 1. Install the on-device server
|
|
128
|
+
pip install memra-local
|
|
129
|
+
|
|
130
|
+
# 2. Start it — LEAVE THIS RUNNING (its own terminal/tab). Port 8765.
|
|
131
|
+
memra serve --port 8765
|
|
132
|
+
|
|
133
|
+
# 3. Install the plugin in local mode (in a second terminal)
|
|
134
|
+
MEMRA_MODE=local \
|
|
135
|
+
curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install.sh | bash
|
|
136
|
+
|
|
137
|
+
# 4. Select Memra as the provider
|
|
138
|
+
hermes config set memory.provider memra
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Either way you end up with a one-line `~/.hermes/memra.json`:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{ "mode": "local", "project_id": "hermes", "tenant_id": "hermes-user" }
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
To switch an existing cloud install to local, just add `"mode": "local"` to that
|
|
148
|
+
file (and start the server). To go back, set it to `"cloud"`.
|
|
149
|
+
|
|
150
|
+
### ⚠️ The server must be running
|
|
151
|
+
|
|
152
|
+
This is the one thing that trips people up: **memra-local is a separate process
|
|
153
|
+
that has to be up** (listening on `http://127.0.0.1:8765`). If it isn't, every
|
|
154
|
+
memory call fails and the plugin's circuit breaker quietly pauses retries for a
|
|
155
|
+
couple of minutes — looking like "memory just doesn't work."
|
|
156
|
+
|
|
157
|
+
- Check it's up: `curl http://127.0.0.1:8765/health` → `{"status":"healthy",...}`
|
|
158
|
+
- It does **not** auto-start on reboot. After a reboot, run `memra serve` again.
|
|
159
|
+
- Background it: `nohup memra serve --port 8765 >~/.memra-local.log 2>&1 &`
|
|
160
|
+
- Always-on: run `memra serve` as a `systemd --user` service.
|
|
161
|
+
|
|
162
|
+
### Good to know
|
|
163
|
+
|
|
164
|
+
- **`/v1`, not `/api/v1`** — memra-local mounts the API at `/v1`. `mode: local`
|
|
165
|
+
handles this for you; only matters if you set `base_url` by hand.
|
|
166
|
+
- **The API key is ignored** — memra-local doesn't authenticate. The plugin
|
|
167
|
+
still needs *some* non-empty key, so `mode: local` sets a placeholder. Don't
|
|
168
|
+
paste a real `memra_live_…` key into a local config.
|
|
169
|
+
- **`project_id` / `tenant_id` are local namespaces** — any names you like; they
|
|
170
|
+
scope memories within the on-device store.
|
|
171
|
+
- **Stale env wins:** a `MEMRA_BASE_URL` / `MEMRA_API_KEY` left in
|
|
172
|
+
`~/.hermes/.env` overrides `mode: local`. If local mode seems to hit the cloud,
|
|
173
|
+
clear those. The init line in `~/.hermes/logs/agent.log` prints the resolved
|
|
174
|
+
`mode=` and `base_url=` so you can confirm.
|
|
175
|
+
|
|
176
|
+
What runs local: the plugin's full REST contract — `memra_remember` (add and
|
|
177
|
+
supersede), `memra_search`, `memra_profile`, plus the supersession audit chain —
|
|
178
|
+
is verified end-to-end against memra-local. Recall is **semantic** (on-device
|
|
179
|
+
`all-MiniLM-L6-v2` embeddings via ONNX, with FTS5 keyword as fallback), so
|
|
180
|
+
"recall by meaning" works fully offline — no OpenAI key and no PyTorch.
|
|
181
|
+
|
|
182
|
+
## Ship to all Hermes users (PR)
|
|
183
|
+
|
|
184
|
+
To appear in Hermes's official provider list, the `memra/` directory is
|
|
185
|
+
submitted to `NousResearch/hermes-agent` under `plugins/memory/memra/`:
|
|
186
|
+
|
|
187
|
+
1. Fork `NousResearch/hermes-agent`.
|
|
188
|
+
2. Copy `memra/` → `plugins/memory/memra/`.
|
|
189
|
+
3. Add an entry to `website/docs/user-guide/features/memory-providers.md`.
|
|
190
|
+
4. Open a PR. (See `docs/developer-guide/memory-provider-plugin.md` for their
|
|
191
|
+
contribution requirements — this plugin already follows that contract.)
|
|
192
|
+
|
|
193
|
+
PR note for maintainers: unlike the other providers this one ships
|
|
194
|
+
self-contained (httpx only) rather than depending on a `memra-sdk` PyPI package,
|
|
195
|
+
because the import name collides with an unrelated existing `memra` package.
|
|
196
|
+
Happy to switch to the SDK once it's published under a non-colliding name.
|
|
197
|
+
|
|
198
|
+
## Test
|
|
199
|
+
|
|
200
|
+
The unit tests stub out the host imports, so they run standalone — no Hermes
|
|
201
|
+
checkout and no live Memra account required:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
pip install pytest
|
|
205
|
+
pytest tests/
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
They cover tenant-id resolution, the `memra_remember` supersede paths, and the
|
|
209
|
+
`migrate_tenant` cascade handling.
|
|
210
|
+
|
|
211
|
+
## Troubleshooting
|
|
212
|
+
|
|
213
|
+
**Local mode: memory does nothing / every call fails.** The memra-local server
|
|
214
|
+
isn't running. It's a separate process — start it with `memra serve --port 8765`
|
|
215
|
+
and confirm `curl http://127.0.0.1:8765/health` returns `healthy`. After a
|
|
216
|
+
reboot you must start it again. See [Run fully local](#run-fully-local-memra-local).
|
|
217
|
+
|
|
218
|
+
**Local mode seems to hit the cloud anyway.** A leftover `MEMRA_BASE_URL` or
|
|
219
|
+
`MEMRA_API_KEY` in `~/.hermes/.env` overrides `mode: local`. Clear them. The init
|
|
220
|
+
line in `~/.hermes/logs/agent.log` shows the resolved `mode=` and `base_url=`.
|
|
221
|
+
|
|
222
|
+
**Very long memories occasionally fail to store with some local models.**
|
|
223
|
+
This is a host-side (Hermes) limitation, not a Memra one. Some local models
|
|
224
|
+
(e.g. certain OpenRouter local models) emit slightly-malformed JSON when a
|
|
225
|
+
tool call carries a large argument; Hermes' tool-call sanitizer may drop the
|
|
226
|
+
argument before it reaches Memra. Memra itself accepts content up to 10,000
|
|
227
|
+
characters of any shape. Workarounds: use a well-behaved model (Claude, GPT,
|
|
228
|
+
most hosted models), or split very large memories into smaller writes. Tracked
|
|
229
|
+
in [#4](https://github.com/usememra/hermes-memra/issues/4).
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
[MIT](LICENSE) © Ali Vonsensey
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# One-shot installer for running Memra ↔ Hermes FULLY LOCAL (memra-local).
|
|
3
|
+
#
|
|
4
|
+
# curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install-local.sh | bash
|
|
5
|
+
#
|
|
6
|
+
# What this does, in order:
|
|
7
|
+
# 1. pip install (or upgrade) memra-local — the on-device server
|
|
8
|
+
# 2. install the Hermes plugin in local mode — delegates to install.sh
|
|
9
|
+
# (writes ~/.hermes/memra.json with "mode": "local")
|
|
10
|
+
# 3. start the memra-local server on port 8765 — REQUIRED to be running
|
|
11
|
+
#
|
|
12
|
+
# No Memra account, no API key, no network. Everything stays on this machine.
|
|
13
|
+
#
|
|
14
|
+
# This is a thin wrapper: it does NOT duplicate the plugin file-copy logic — it
|
|
15
|
+
# sets MEMRA_MODE=local and hands off to install.sh (local checkout if present,
|
|
16
|
+
# otherwise the published one). Edit install.sh for plugin-install changes.
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
|
|
20
|
+
PORT="${MEMRA_LOCAL_PORT:-8765}"
|
|
21
|
+
REPO_RAW="https://raw.githubusercontent.com/usememra/hermes-memra/main"
|
|
22
|
+
|
|
23
|
+
say() { printf '\033[36m[memra-local]\033[0m %s\n' "$1"; }
|
|
24
|
+
die() { printf '\033[31m[memra-local] %s\033[0m\n' "$1" >&2; exit 1; }
|
|
25
|
+
|
|
26
|
+
# --- 1. Python + memra-local -------------------------------------------------
|
|
27
|
+
command -v python3 >/dev/null 2>&1 || die "python3 not found. Install Python 3.11+ first."
|
|
28
|
+
PIP="python3 -m pip"
|
|
29
|
+
say "Installing memra-local (on-device memory server)…"
|
|
30
|
+
$PIP install -q --upgrade memra-local || die "pip install memra-local failed."
|
|
31
|
+
|
|
32
|
+
# The console script is 'memra' (NOT 'memra-local'). It may land in a per-user
|
|
33
|
+
# bin that isn't on PATH yet (e.g. ~/.local/bin) — surface that instead of a
|
|
34
|
+
# confusing 'command not found' later.
|
|
35
|
+
if ! command -v memra >/dev/null 2>&1; then
|
|
36
|
+
say "Note: the 'memra' command isn't on your PATH yet."
|
|
37
|
+
say " Add your user bin to PATH (e.g. export PATH=\"\$HOME/.local/bin:\$PATH\")."
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# --- 2. Hermes plugin, in local mode ----------------------------------------
|
|
41
|
+
say "Installing the Hermes plugin (local mode)…"
|
|
42
|
+
SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")" 2>/dev/null && pwd || true)"
|
|
43
|
+
if [[ -f "$SRC_DIR/install.sh" ]]; then
|
|
44
|
+
MEMRA_MODE=local bash "$SRC_DIR/install.sh"
|
|
45
|
+
else
|
|
46
|
+
MEMRA_MODE=local bash -c "$(curl -fsSL "$REPO_RAW/install.sh")"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# --- 3. Start the server -----------------------------------------------------
|
|
50
|
+
# The plugin is useless until memra-local is actually listening. Start it now in
|
|
51
|
+
# the background so a fresh install is immediately working; if it's already up
|
|
52
|
+
# we leave it alone.
|
|
53
|
+
if curl -fsS "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1; then
|
|
54
|
+
say "memra-local already running on :${PORT} — leaving it as is."
|
|
55
|
+
else
|
|
56
|
+
say "Starting memra-local on :${PORT} (background, logs → ~/.memra-local.log)…"
|
|
57
|
+
nohup memra serve --port "${PORT}" >"$HOME/.memra-local.log" 2>&1 &
|
|
58
|
+
# Give uvicorn a moment, then health-check so we fail loudly if it didn't come up.
|
|
59
|
+
for _ in 1 2 3 4 5 6 7 8 9 10; do
|
|
60
|
+
sleep 1
|
|
61
|
+
if curl -fsS "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1; then break; fi
|
|
62
|
+
done
|
|
63
|
+
if curl -fsS "http://127.0.0.1:${PORT}/health" >/dev/null 2>&1; then
|
|
64
|
+
say "memra-local is up."
|
|
65
|
+
else
|
|
66
|
+
die "memra-local did not become healthy on :${PORT}. Check ~/.memra-local.log and start it manually: memra serve --port ${PORT}"
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
cat <<EOF
|
|
71
|
+
|
|
72
|
+
$(say "Done — fully local.")
|
|
73
|
+
|
|
74
|
+
Hermes is configured for local mode (~/.hermes/memra.json has "mode": "local")
|
|
75
|
+
and the memra-local server is running on http://127.0.0.1:${PORT}.
|
|
76
|
+
|
|
77
|
+
Final step:
|
|
78
|
+
hermes config set memory.provider memra
|
|
79
|
+
|
|
80
|
+
REMEMBER: the memra-local server must be running whenever you use Hermes.
|
|
81
|
+
- It's running now (background, logs → ~/.memra-local.log).
|
|
82
|
+
- It does NOT auto-start on reboot. After a reboot, run: memra serve --port ${PORT}
|
|
83
|
+
- For an always-on setup, run it as a systemd --user service.
|
|
84
|
+
|
|
85
|
+
Verify it's working: start a Hermes chat, then check ~/.hermes/logs/agent.log
|
|
86
|
+
shows an init line containing: mode=local
|
|
87
|
+
EOF
|