feat(integrations): migrate and validate standalone Hermes Mem0 plugin - #7372
kartik-mem0 wants to merge 12 commits into
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
|
Copy all upstream files at 3fc36950b2b7c19cdd81c6de99f10d2cbed850af without modifications. Remove the prior implementation changes and tests, restore docs, and retain only the integrations table entry. Validation: identical Git subtree dd6a312; all five Python files compile. Pre-commit hooks were run: upstream has Ruff E741 and isort differences. Formatter edits were reverted to preserve the explicitly requested byte-identical import.
|
Gave this a thorough pass across all six areas — setup/credentials, backend, plugin core + tools, the OSS/LLM shims, licensing/docs, and tests — and verified each claimed fix against the actual code rather than the description. Short version: the import is genuinely solid, and all 15 named fixes are implemented as described. There are two things I'd fix before merge and a handful of follow-ups; details below, grouped by priority. Worth fixing before merge
Strongly worth closing (especially if this can run against a shared account)
Test gaps to be aware ofThe 15-test suite is genuinely good and non-vacuously protects ~9 of the 11 fixes. The credible holes: Smaller hardening notes
What I verified as clean
Net: the licensing, docs, structure, and the bulk of the fixes are well done — this is close. The pgvector exposure and the pre-release-version crash are the two concrete blockers; the guard fail-open and the update/delete scoping are the two I'd most want closed given shared-account deployments. (Static review — verified against the PR-branch source via the contents API since |
The setup wizard published the pgvector Docker container's port with a bare -p, exposing PostgreSQL on 0.0.0.0, and always set the same hardcoded password. Bind to 127.0.0.1 only, generate a random password per install, and scrub it from any subprocess error text before printing.
…vector-port The min-version check parsed mem0.__version__ with a bare int() per segment, raising an uncaught ValueError on pre-release strings like 2.1.0rc1 (outside the surrounding suppress(ImportError)). A non-numeric --oss-vector-port also crashed unhandled instead of producing a friendly error.
_recreate_collection_if_dims_changed wrapped its dimension detection in contextlib.suppress(Exception), so any transient connection error silently skipped the data-loss guard. Split detection into its own method, log a warning (instead of swallowing) when it can't be determined, and add support for Qdrant's host/port config alongside path/url. Also warn when an embedder's dimensions are unknown instead of skipping the guard without a trace.
mem0_update and mem0_delete mutated any memory_id without checking ownership, unlike search and add, letting one user mutate or delete another user's memory given its UUID. Add a get-and-verify-ownership step before both mutations, backed by a new get() method on each backend (Platform, self-hosted, OSS).
Add a 120s OpenAI client timeout, align OPENAI_API_BASE/OPENAI_BASE_URL resolution between the LLM and embedder shims, normalize the embedder provider guard, isolate close() failures per-object instead of aborting the whole chain, move prefetch thread start() inside its lock, avoid holding sync_lock across a 5s join, guard non-numeric sync_max_chars, and rename _recreate_collection_if_dims_changed to _reject_dimension_mismatch to match what it actually does.
…nc_max_chars Add coverage for the review gaps: DirectOpenAILLM reads its API key through the secret scope and bypasses OpenRouter, raises when no key is available, SelfHostedBackend omits X-API-Key for AUTH_DISABLED servers, and a non-numeric sync_max_chars falls back to the default instead of crashing initialize(). Update the two dimension-guard tests for the _reject_dimension_mismatch rename.
Linked Issue
N/A — this is a same-repository branch, exempt from the accepted-issue PR gate.
Description
Bring the standalone Nous Research Mem0 provider into
integrations/hermes-plugin-mem0/, retaining its provider name, configuration, storage paths, tools, and three backend modes. Use the same Apache 2.0 LICENSE as OpenClaw for Mem0 contributions, preserve the imported source's MIT copyright and permission text verbatim in the third-party section of LICENSE (no separate NOTICE file), and declare both applicable licenses in package metadata. The directory is independent of shared resources and needs no build step.Remove HANDOFF.md after reviewing it: it contains takeover instructions, not runtime code. Preserve source attribution, installation guidance, and the bundled-provider precedence warning in the README.
Fix confirmed problems in the imported code:
.envand OSS configuration, including database credentials.--user-idflag, and excludes stored credentials from dry-run output.Add the integration table entry, regression tests, and a reproducible real-host smoke. Update the Hermes documentation page, its llms.txt entry, and the plugin README with standalone installation, three backend modes, existing-user migration prerequisites, configuration precedence, capture limits, and troubleshooting. No shared runtime, other plugins, or workflows change. Existing upstream capture/truncation and concurrency behavior remains outside these fixes.
Rollout: Hermes PR #114569 is merged, but Mem0 is still bundled and absent from the catalog at verification time. After this PR merges, Hermes maintainers must approve a
mem0catalog entry pointing tohttps://github.com/mem0ai/mem0, subdirectoryintegrations/hermes-plugin-mem0, pinned to a reviewed full commit SHA, and coordinate removal of the bundled copy. Existing users keepmemory.provider: mem0,mem0.json,MEM0_*, user identity and database paths. This PR alone does not enable automatic migration. The plugin retains CLI setup/status support; it does not add a Desktop config panel or provider-specific CLI commands.Migration verification: Tested plugin
b183aadb6dd6c9bf4634fef14aa297eb9c427901against Hermes main01382698fc32ec7740b6a204d9b7a6abeac74d33. A temporary catalog entry drove the real pinned GitHub installer, scanner, dependency checks, activation, and external loader. Memories created by the actual bundled provider remained searchable after switching to the external copy; credentials, config and identity were retained, and CRUD/recall passed. Public catalog migration remains unavailable until Mem0 is admitted.Upstream multi-profile blockers found: Hermes'
migrate_all_homes()checks provider presence in the active home rather than each target home, so a second profile is skipped once the first has Mem0 installed.recover_at_startup()also suppresses further attempts by provider name across profiles in one process. Both failures reproduced with real profile discovery; installing under the second profile's explicit home scope succeeds. These host issues must be resolved or worked around before claiming seamless migration for all profiles. The scanner returned caution (7 findings), not dangerous; review is still needed at catalog admission. Model responses were simulated locally; storage and installer behavior were real.Type of Change
AI Assistance
Codex imported the upstream source, made the scoped fixes, reviewed Hermes' migration implementation, and ran the checks below. The human author has not yet attested to independent line-by-line review.
Breaking Changes
No configuration or tool-signature changes. An OSS collection dimension mismatch now fails initialization instead of deleting existing data; restore the previous embedder or explicitly migrate to a new collection.
Test Coverage
I added/updated unit tests
I added/updated integration tests
I tested manually (describe below)
No tests needed (explain why)
Six regression tests failed against the imported source before fixes. Eight further regression cases failed before the follow-up fixes. Final offline suite: 15 passed, including self-hosted HTTP request/authentication contracts, key rotation, user identity, profile credentials, dry-run secrecy, distinct model keys, and container preservation.
Real-host smoke passed on Hermes v0.21.3 (
345cd2b057a452236de401d3534b8502a7465e8d) and main (c62bd9f2078a946108f1c9d9b24bf118963277ef) with Hermes' pinnedmem0ai==2.0.10,openai==2.24.0, and localqdrant-client==1.19.1.Smoke covers external discovery, real CLI setup/status, private files, all four tools, recall, background extraction, existing user identity, authenticated embedding requests under a multiplexed profile despite conflicting process credentials, shutdown, rejection of dimension changes without data loss, and persistence across restart.
Storage/lifecycle smoke also passed with
mem0ai==2.0.20before testing the pinned version.Hermes' dependency conflict checker accepted the plugin requirements under core constraints.
Ruff, isort, pre-commit hooks, and
git diff --checkpassed.Licensing: verified LICENSE contains OpenClaw's Apache text and the upstream MIT notice verbatim, with no separate NOTICE file, and package TOML parses.
Documentation: Mintlify build validation passed under Node 22, and
python3 scripts/check-llms-txt-coverage.pyconfirmed the docs index is in sync.Smoke uses real Hermes, the real Mem0 SDK, a real temporary on-disk Qdrant database, and a localhost OpenAI-compatible test service. Model responses/embeddings are deterministic; no live cloud credentials, real model quality, running PostgreSQL, or production catalog migration are claimed. Optional spaCy/fastembed extras were absent. Reproduction commands are in the plugin README.
Checklist