Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/hermes-memory-provider.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"upstash-hermes-memory": minor
---

First release: an Upstash Redis memory provider for Hermes Agent. It recalls related memories into each turn, saves every session as a searchable transcript, mirrors the built-in `memory` tool, and gives the model `upstash_memory_save` / `_search` / `_forget` and `upstash_chat_search`. It reads and writes the same keys and Redis Search indexes as `AgentMemory` and `ChatHistory` in `@upstash/agentkit-sdk`, so memories are shared with AgentKit apps on the same database.
7 changes: 6 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ embeddings — keep that in mind when naming/among scoring.
| `@upstash/agentkit-tanstack-ai` (`packages/tanstack-ai`) | TanStack AI backends: persistence stores, `StreamDurability`, `LockStore`, `MemoryAdapter`, middlewares, search tools. |
| `@upstash/agentkit-eve-extension` (`packages/eve-extension`) | AgentKit as a mountable **eve extension** (eve ≥0.24): one `agent/extensions/<ns>.ts` file composes memory tools, search tools, a chat-history hook, and an instructions fragment under `<ns>__*`. |
| `@upstash/mcp-toolkit` (`packages/mcp-toolkit`) | Durable building blocks for the official `@modelcontextprotocol/server` v2: `/tasks` (**long-running MCP tools** — native MCP Tasks for clients that declare the extension, else start + `task_status` + `task_cancel`) and `/events` (**MCP Events**, webhook delivery). `/tasks` and `/events` are generic (the `McpTasks` / `McpEvents` classes, interfaces, memory backends); every Upstash backend for both is in `/upstash` (no `/tasks/upstash`, no `/events/upstash`). `@upstash/redis`, `@upstash/qstash` and `@upstash/workflow` are regular dependencies (the package "just works"); only `@modelcontextprotocol/server` is a peer. Renamed from `@upstash/mcp-tasks` before its first release. **Not an `agentkit-*` package** — separate name, versioned independently (the changesets `linked` glob only covers `@upstash/agentkit-*`), and it depends on none of the others. |
| `upstash-hermes-memory` (`python/hermes-memory`, **PyPI**) | Hermes Agent memory provider on Redis Search. **Scaffold at `0.0.0`** (unreleased) until the port lands (DX-3182). See **Python packages** below. |
| `upstash-hermes-memory` (`python/hermes-memory`, **PyPI**) | Hermes Agent memory provider (`UpstashMemoryProvider`, entry point `hermes_agent.memory_providers: upstash`) plus Python ports of `AgentMemory` / `ChatHistory` that write the TS keys and indexes byte for byte. See **Python packages** below. |

Examples (`examples/`): `ai-sdk-demo` (hand-written Next.js), `eve-demo` (a real `eve` CLI scaffold),
`eve-extension-demo` (a minimal eve scaffold that mounts the extension), and `mcp-toolkit-demo`
Expand Down Expand Up @@ -1434,6 +1434,11 @@ Python lives **beside** the pnpm workspace, not instead of any of it:
non-zero version merges. Nothing else to touch.
- Python tests share the test Redis (10-index cap) with vitest: run them **after** the Node tests,
never in parallel, and reuse the TS key prefixes/index names when the data is meant to be shared.
Live tests take a unique `prefix` fixture and drop their indexes; call `index.wait_indexing()`
after a write or delete before asserting on search results (`SEARCH.COUNT` lags otherwise).
- `upstash-hermes-memory` e2e: `uv run python python/hermes-memory/e2e/hermes_e2e.py` drives a real
Hermes (temporary `HERMES_HOME`, package on `PYTHONPATH`, `gpt-5-mini` via `OPENAI_API_KEY`; Hermes
sends `reasoning.effort`, so non-reasoning models like `gpt-4o-mini` fail) and checks Redis.

## Commands
- **Setup gotcha — use pnpm 11.5.3, matching the `packageManager` pin.** Root `package.json` pins
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ are powered by [Upstash Redis Search](https://upstash.com/docs/redis/search/intr
| [`@upstash/agentkit-tanstack-ai`](./packages/tanstack-ai) | Production backends for [TanStack AI](https://tanstack.com/ai) — chat persistence, resumable streams, distributed locks, memory, tool caching, rate limiting and search tools. |
| [`@upstash/agentkit-eve-extension`](./packages/eve-extension) | The same capabilities as a mountable [Eve extension](https://eve.dev/docs/extensions) — one file in `agent/extensions/` adds memory tools, search tools, and durable chat history the agent can search. |
| [`@upstash/mcp-toolkit`](./packages/mcp-toolkit) | Durable building blocks for MCP servers on the official TypeScript SDK. `/tasks`: long-running tools that answer at once, as a native MCP task for clients with the Tasks extension (Claude Code) or a task id the model polls for everyone else, with the record in Redis and the work on QStash or Workflow. `/events`: MCP Events, with subscriptions in Redis and signed webhook deliveries retried by QStash. |
| [`upstash-hermes-memory`](./python/hermes-memory) (PyPI) | Memory provider for [Hermes Agent](https://hermes-agent.nousresearch.com) on Upstash Redis Search. Work in progress. |
| [`upstash-hermes-memory`](./python/hermes-memory) (PyPI) | Memory provider for [Hermes Agent](https://hermes-agent.nousresearch.com) on Upstash Redis Search, sharing memories with `@upstash/agentkit-sdk`. |

## Core features

Expand Down
64 changes: 61 additions & 3 deletions python/hermes-memory/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,65 @@
# upstash-hermes-memory

An Upstash Redis memory provider for [Hermes Agent](https://hermes-agent.nousresearch.com), built on
the same Redis Search layout as `@upstash/agentkit-sdk`'s `AgentMemory` and `ChatHistory`.
the same Redis Search layout as `@upstash/agentkit-sdk`'s `AgentMemory` and `ChatHistory`. A memory
your AI SDK app saves is recalled by your Hermes agent, and the other way round.

**Status: scaffold.** The package is at `0.0.0`, which `scripts/publish-python.mjs` treats as
unreleased, so nothing is published to PyPI yet. The provider itself is tracked in DX-3182.
## What it does in Hermes

- **Recall:** before each turn, the memories closest to the user's message are added to the prompt.
- **Chat history:** after each turn, the session's transcript is saved as a searchable document.
- **Mirroring:** facts the built-in `memory` tool adds are also saved to Redis.
- **Tools:** `upstash_memory_save`, `upstash_memory_search`, `upstash_memory_forget` and
`upstash_chat_search` (search past conversations).

Subagents, cron jobs and context flushes can read memory but never write it.

## Setup

```bash
# into the Python environment Hermes runs in
pip install upstash-hermes-memory
```

Then select it in Hermes's `config.yaml`:

```yaml
memory:
provider: upstash
```

And give it a database, in your environment or `~/.hermes/.env` (`hermes memory setup` asks for
these):

| Variable | |
| --- | --- |
| `UPSTASH_REDIS_REST_URL`, `UPSTASH_REDIS_REST_TOKEN` | Required. From the [Upstash console](https://console.upstash.com/redis). |
| `UPSTASH_HERMES_USER_ID` | Whose memories these are. Defaults to the gateway user, then the Hermes profile name, then `default`. |
| `UPSTASH_HERMES_MEMORY_PREFIX`, `UPSTASH_HERMES_CHAT_PREFIX` | Key prefixes. Default `agentkit:memory` and `agentkit:chat`, the TS SDK's defaults, so the two share memories. |

## Redis layout

| | Key | Index |
| --- | --- | --- |
| Memory | `agentkit:memory:<user>:<id>` → JSON `{text, userId, createdAt, ...metadata}` | `agentkit_memory` (`text`, `userId`) |
| Chat | `agentkit:chat:<user>:<session>` → JSON `{userId, sessionId, userMessages, modelMessages, messages, createdAt, updatedAt}` | `agentkit_chat` |

User and session ids are used as they are, so the same id reads the same keys from Hermes and the
TS SDK. Like the TS SDK, the provider refuses a user id with `:` (the key separator), and keeps no
transcript for a session id with one. Indexes are created on first use. `AgentMemory` and `ChatHistory` are importable on their own for
use outside Hermes.

## Development

```bash
uv sync --all-packages && pnpm py:lint && pnpm py:test # Redis tests need UPSTASH_REDIS_REST_* set
uv run python python/hermes-memory/e2e/hermes_e2e.py # a real Hermes session, needs OPENAI_API_KEY
```

The e2e script installs the package into a temporary directory and runs Hermes with a temporary
`HERMES_HOME`, so your own Hermes install is not touched. Hermes's built-in memory is turned off
there. One session saves two facts; a fresh session started right after must name them without
calling any tool (checked in Hermes's `state.db`), so they can only come from the automatic recall;
and a control session as another user must not know them. The script then checks the keys and
transcripts in Redis (and, if `packages/sdk` is built, that the TS `AgentMemory` recalls them too)
before cleaning up.
Loading
Loading