Metadata-Version: 2.4
Name: hermes-memra
Version: 4.5.0
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.
Project-URL: Homepage, https://usememra.com
Project-URL: Repository, https://github.com/usememra/hermes-memra
Project-URL: Documentation, https://usememra.com/hermes-memory
Project-URL: Changelog, https://github.com/usememra/hermes-memra/releases
Author-email: Ali Vonsensey <hello@usememra.com>
License-Expression: MIT
License-File: LICENSE
Keywords: ai-agents,hermes-agent,long-term-memory,memory,memory-provider,memra
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: httpx>=0.24
Description-Content-Type: text/markdown

# Memra ↔ Hermes Agent

A [Memra](https://usememra.com) memory-provider plugin for
[Hermes Agent](https://hermes-agent.nousresearch.com) (Nous Research).

Hermes Agent supports pluggable, single-select external memory providers
(Honcho, Mem0, Hindsight, OpenViking, Holographic, RetainDB, ByteRover,
Supermemory). This makes **Memra** an option alongside them: hybrid
semantic + structured recall, typed memories, importance ranking, EU-native
self-hosting, and server-side compression of long-lived memories.

`memra/` is the drop-in plugin directory. In the Hermes **source tree** it
lives at `plugins/memory/memra/` (the bundled-provider layout, imported as the
package `plugins.memory.memra`). A **user install** instead goes one level deep
at `$HERMES_HOME/plugins/memra/` — see below.

## Install (pip — recommended)

```bash
pip install hermes-memra
```

Hermes Agent discovers the provider automatically via the `hermes_agent.plugins`
entry point — no files to copy. Then set your key and select the provider:

```bash
export MEMRA_API_KEY=memra_live_...   # free key at https://usememra.com
hermes memory provider memra
```

Fully-local mode (no cloud): `pip install memra-local`, run `memra serve`, and
set `mode: local` in the plugin config — same tools, zero external calls.

The `install.sh` script remains for source-tree / $HERMES_HOME drop-in installs.

## What it does

| Hermes lifecycle | Memra behavior |
|------------------|----------------|
| `prefetch` / `queue_prefetch` | Background hybrid recall before each turn |
| `sync_turn` | Persists each turn as an `event` memory |
| `on_pre_compress` | Ships about-to-be-discarded context to Memra so it survives window compression (Memra then compresses it server-side) |
| `on_memory_write` | Mirrors Hermes `MEMORY.md` / `USER.md` writes into Memra |
| Tools | `memra_search`, `memra_remember` (store or supersede a fact), `memra_profile` |

All network calls are wrapped in a circuit breaker so a Memra outage never
blocks the agent loop. The plugin is self-contained (only `httpx`) — it calls
the Memra REST API directly, no Memra client package required.

## Install today (Hermes ≥ 0.11)

Hermes discovers user-installed providers one level deep under
`$HERMES_HOME/plugins/<name>/`. The easiest path is the installer:

```bash
curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install.sh | bash
hermes memory setup        # select "memra", paste API key + project id
```

Or do it by hand — note the destination is `plugins/memra/`, not
`plugins/memory/memra/` (that's the in-tree bundled path):

```bash
cp -r memra ~/.hermes/plugins/memra
hermes memory setup        # select "memra", paste API key + project id
```

Or wire it manually:

```bash
hermes config set memory.provider memra
echo "MEMRA_API_KEY=memra_live_xxx" >> ~/.hermes/.env
echo '{"project_id": "proj_xxx"}' > ~/.hermes/memra.json
```

Get an API key and project id at [usememra.com](https://usememra.com), or point
`base_url` at your own self-hosted Memra to keep all data on your infra.

> **Want zero cloud?** Skip the account entirely and run fully on-device — see
> [Run fully local](#run-fully-local-memra-local) below. It's a single
> `"mode": "local"` switch.

## Run fully local (memra-local)

Run everything on your own machine — no account, no API key, no network. The
plugin points at [`memra-local`](https://pypi.org/project/memra-local/), a
single-process on-device memory server (SQLite + on-device embeddings). This is
distinct from self-hosting the cloud API above: memra-local runs entirely
locally.

**Cloud vs local is one switch.** The plugin has a `mode` setting:

| `mode`             | Backend                       | Needs an API key / account? |
|--------------------|-------------------------------|-----------------------------|
| `cloud` *(default)*| Hosted / self-hosted Memra API | Yes                         |
| `local`            | memra-local on this machine    | No                          |

Set `mode` to `local` and the plugin auto-fills the local URL
(`http://127.0.0.1:8765/v1`), a throwaway API key, and a default namespace —
nothing else to configure.

### What you'll need

- **Python 3.11+** with `pip` (to install and run memra-local).
- **Hermes Agent ≥ 0.11** (this plugin).
- **~300 MB disk** for memra-local (no PyTorch — it uses an ONNX runtime ~55 MB
  plus a one-time ~90 MB model download). Your memories live in a local SQLite
  file.
- A terminal you can leave open (or a background process) for the server — **it
  must be running whenever you use Hermes.**

### Quick start

**Option A — one command (installs the server, configures the plugin, starts it):**

```bash
curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install-local.sh | bash
hermes config set memory.provider memra
```

That installs `memra-local`, writes `~/.hermes/memra.json` with `"mode": "local"`,
and starts the server on port 8765 in the background.

**Option B — step by step (you stay in control of each piece):**

```bash
# 1. Install the on-device server
pip install memra-local

# 2. Start it — LEAVE THIS RUNNING (its own terminal/tab). Port 8765.
memra serve --port 8765

# 3. Install the plugin in local mode (in a second terminal)
MEMRA_MODE=local \
  curl -fsSL https://raw.githubusercontent.com/usememra/hermes-memra/main/install.sh | bash

# 4. Select Memra as the provider
hermes config set memory.provider memra
```

Either way you end up with a one-line `~/.hermes/memra.json`:

```json
{ "mode": "local", "project_id": "hermes", "tenant_id": "hermes-user" }
```

To switch an existing cloud install to local, just add `"mode": "local"` to that
file (and start the server). To go back, set it to `"cloud"`.

### ⚠️ The server must be running

This is the one thing that trips people up: **memra-local is a separate process
that has to be up** (listening on `http://127.0.0.1:8765`). If it isn't, every
memory call fails and the plugin's circuit breaker quietly pauses retries for a
couple of minutes — looking like "memory just doesn't work."

- Check it's up: `curl http://127.0.0.1:8765/health` → `{"status":"healthy",...}`
- It does **not** auto-start on reboot. After a reboot, run `memra serve` again.
- Background it: `nohup memra serve --port 8765 >~/.memra-local.log 2>&1 &`
- Always-on: run `memra serve` as a `systemd --user` service.

### Good to know

- **`/v1`, not `/api/v1`** — memra-local mounts the API at `/v1`. `mode: local`
  handles this for you; only matters if you set `base_url` by hand.
- **The API key is ignored** — memra-local doesn't authenticate. The plugin
  still needs *some* non-empty key, so `mode: local` sets a placeholder. Don't
  paste a real `memra_live_…` key into a local config.
- **`project_id` / `tenant_id` are local namespaces** — any names you like; they
  scope memories within the on-device store.
- **Stale env wins:** a `MEMRA_BASE_URL` / `MEMRA_API_KEY` left in
  `~/.hermes/.env` overrides `mode: local`. If local mode seems to hit the cloud,
  clear those. The init line in `~/.hermes/logs/agent.log` prints the resolved
  `mode=` and `base_url=` so you can confirm.

What runs local: the plugin's full REST contract — `memra_remember` (add and
supersede), `memra_search`, `memra_profile`, plus the supersession audit chain —
is verified end-to-end against memra-local. Recall is **semantic** (on-device
`all-MiniLM-L6-v2` embeddings via ONNX, with FTS5 keyword as fallback), so
"recall by meaning" works fully offline — no OpenAI key and no PyTorch.

## Ship to all Hermes users (PR)

To appear in Hermes's official provider list, the `memra/` directory is
submitted to `NousResearch/hermes-agent` under `plugins/memory/memra/`:

1. Fork `NousResearch/hermes-agent`.
2. Copy `memra/` → `plugins/memory/memra/`.
3. Add an entry to `website/docs/user-guide/features/memory-providers.md`.
4. Open a PR. (See `docs/developer-guide/memory-provider-plugin.md` for their
   contribution requirements — this plugin already follows that contract.)

PR note for maintainers: unlike the other providers this one ships
self-contained (httpx only) rather than depending on a `memra-sdk` PyPI package,
because the import name collides with an unrelated existing `memra` package.
Happy to switch to the SDK once it's published under a non-colliding name.

## Test

The unit tests stub out the host imports, so they run standalone — no Hermes
checkout and no live Memra account required:

```bash
pip install pytest
pytest tests/
```

They cover tenant-id resolution, the `memra_remember` supersede paths, and the
`migrate_tenant` cascade handling.

## Troubleshooting

**Local mode: memory does nothing / every call fails.** The memra-local server
isn't running. It's a separate process — start it with `memra serve --port 8765`
and confirm `curl http://127.0.0.1:8765/health` returns `healthy`. After a
reboot you must start it again. See [Run fully local](#run-fully-local-memra-local).

**Local mode seems to hit the cloud anyway.** A leftover `MEMRA_BASE_URL` or
`MEMRA_API_KEY` in `~/.hermes/.env` overrides `mode: local`. Clear them. The init
line in `~/.hermes/logs/agent.log` shows the resolved `mode=` and `base_url=`.

**Very long memories occasionally fail to store with some local models.**
This is a host-side (Hermes) limitation, not a Memra one. Some local models
(e.g. certain OpenRouter local models) emit slightly-malformed JSON when a
tool call carries a large argument; Hermes' tool-call sanitizer may drop the
argument before it reaches Memra. Memra itself accepts content up to 10,000
characters of any shape. Workarounds: use a well-behaved model (Claude, GPT,
most hosted models), or split very large memories into smaller writes. Tracked
in [#4](https://github.com/usememra/hermes-memra/issues/4).

## License

[MIT](LICENSE) © Ali Vonsensey
