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.
@@ -0,0 +1,3 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .pytest_cache/
@@ -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