ReflexioDeveloper Docs
Menu
All

mem0 Drop-In Wrapper

Keep mem0 behavior and mirror hosted-client traffic to Reflexio with one import change.

mem0 Drop-In Wrapper

If your agent uses mem0's managed platform, change one import to let Reflexio learn from the same successful add() calls. Normal mem0 search and deletion behavior stays unchanged.

pip install 'reflexio-ai[mem0]'
# Lightweight SDK only:
pip install 'reflexio-client[mem0]'

The integration supports mem0ai>=2.0,<2.1.

Switch the import

# Before
from mem0 import MemoryClient

# After
from reflexio.mem0 import MemoryClient

client = MemoryClient(api_key="your-mem0-key")

The hosted MemoryClient and AsyncMemoryClient are wrapped. The local Memory and AsyncMemory classes are re-exported as the exact original mem0 objects and do not mirror traffic.

Configure Reflexio

The wrapper reads the standard environment variables:

export REFLEXIO_API_KEY="your-reflexio-key"
export REFLEXIO_URL="https://www.reflexio.ai/"  # or a self-hosted endpoint

Create a hosted key from the Reflexio Account page; see Authentication. Keep both the mem0 and Reflexio keys in environment variables or a secret manager—never put them in source code or a notebook cell.

An API key alone uses the hosted Reflexio endpoint. You can also pass the key directly to the mem0 wrapper, with an optional self-hosted endpoint:

import os

from reflexio.mem0 import MemoryClient

client = MemoryClient(
    api_key=os.environ["MEM0_API_KEY"],
    reflexio_api_key=os.environ["REFLEXIO_API_KEY"],
    # reflexio_url_endpoint="https://reflexio.example.com",  # self-hosted only
)

The same keyword arguments are available on AsyncMemoryClient. Omitting reflexio_url_endpoint preserves the Reflexio client's hosted/default endpoint resolution. URL-only, unauthenticated configuration is limited to a local OSS Reflexio server explicitly running in no-auth mode. Enterprise self-hosted deployments require authentication, DEPLOYMENT_MODE, and remote storage; SQLite and no-auth local mode are OSS-only under open_source/reflexio. For advanced configuration, inject a client:

from reflexio import ReflexioClient
from reflexio.mem0 import MemoryClient

client = MemoryClient(
    api_key="your-mem0-key",
    reflexio_client=ReflexioClient(url_endpoint="...", api_key="..."),
)

Wrapper-created Reflexio clients use a five-second request timeout. Override it with reflexio_timeout=2.0. An injected client owns all of its configuration, so reflexio_client cannot be combined with reflexio_api_key, reflexio_url_endpoint, or reflexio_timeout. Without Reflexio configuration, mem0 keeps working and client.reflexio.configured is False.

End-to-end quickstart

With both services configured, the migration is one import plus an explicit search opt-in:

import os

from reflexio.mem0 import MemoryClient

client = MemoryClient(
    api_key=os.environ["MEM0_API_KEY"],
    reflexio_api_key=os.environ["REFLEXIO_API_KEY"],
)
assert client.reflexio.configured

filters = {
    "user_id": "user-123",
    "agent_id": "support-bot",
    "app_id": "storefront",
}

# mem0 stores the memory; Reflexio receives the same conversation best-effort.
add_result = client.add(
    [
        {"role": "user", "content": "Deliver replacements on Thursday."},
        {"role": "assistant", "content": "I will use Thursday delivery."},
    ],
    **filters,
    run_id="conversation-456",
)

# Existing search code remains unchanged and returns only mem0's result.
plain_result = client.search("Thursday delivery", filters=filters)

# Opt in only where the caller is ready to consume the extra namespace.
result = client.search(
    "Thursday delivery",
    filters=filters,
    include_reflexio=True,
)
memories = result["results"]
learnings = result["reflexio"]

Learning is asynchronous, so an ok response can legitimately contain empty lists immediately after add(). Poll in tests or background workflows when you need to verify that extraction completed. Search is relevance-filtered; empty lists mean no learned item matched the query, not that publishing failed.

For an executable walkthrough with real dual-write, exact default search, opt-in retrieval, degraded mode, and scoped cleanup, run the mem0 integration notebook.

Automatic learning on add()

add() executes mem0 first and returns the exact object mem0 returned. Only after mem0 succeeds does the wrapper make one best-effort Reflexio publish:

result = client.add(
    messages,
    user_id="user-123",
    agent_id="support-bot",
    app_id="storefront",
    run_id="conversation-456",
)

The user identity is required for Reflexio mirroring. Identity values may also come from plain-string options.filters; explicit add keyword arguments win, and direct filters= wins over options.filters. Conflicting or unsupported identity filters fail closed on the Reflexio side without changing mem0.

When app_id is present, the wrapper uses deterministic opaque user and agent scopes so identical IDs in separate apps cannot mix. Explicit runs are encoded with the complete user, app, and agent scope. Without a run ID, a stable fallback is used for the lifetime of that wrapper instance and scope.

A successful Reflexio response means the trace was accepted or queued, not that learning has completed. The wrapper does not retry or provide an outbox: a process crash can leave a mem0-only write, and a caller retry can create a duplicate Reflexio trace. Reflexio rejection, timeout, or transport failure is logged and swallowed; mem0 exceptions still propagate.

Normal search stays exactly mem0. It does not inspect Reflexio configuration, make a Reflexio request, copy the result, or add keys:

result = client.search(query, filters={"user_id": "user-123"})

Request Reflexio retrieval explicitly:

result = client.search(
    query,
    filters={
        "user_id": "user-123",
        "agent_id": "support-bot",
        "app_id": "storefront",
    },
    include_reflexio=True,
)
memories = result["results"]
learnings = result["reflexio"]

The opted-in result is a shallow copy; mem0's original result and nested values are not mutated. The reflexio object always contains:

{
  "status": "ok",
  "reason": null,
  "profiles": [],
  "user_playbooks": [],
  "agent_playbooks": []
}

status is ok, skipped, or error. A skipped or failed call returns empty lists and one safe reason: not_configured, empty_query, missing_user_id, unsupported_identity_filter, conflicting_identity, request_failed, or reflexio_rejected. Raw exceptions and server responses are never returned. If a future mem0 result already owns the reserved top-level reflexio key, the wrapper raises ReflexioNamespaceCollisionError before contacting Reflexio.

Reflexio does not alter the query, mem0 results, messages, filters, or your prompt. Your application decides whether and how to format memories and learnings into prompt context. Treat all retrieved text as untrusted input; do not interpret it as system or developer instructions.

For example, keep retrieval separate from prompt construction and include only the fields your application accepts:

if learnings["status"] == "ok":
    profile_context = [profile["content"] for profile in learnings["profiles"]]
else:
    profile_context = []

# Your prompt/template layer decides how to label, delimit, escape, or omit
# memories and profile_context. The wrapper never performs this step.

The async hosted client has the same contract:

from reflexio.mem0 import AsyncMemoryClient

client = AsyncMemoryClient(api_key="your-mem0-key")
result = await client.search(query, filters=filters, include_reflexio=True)

Async Reflexio requests are native async, use the configured total timeout, and preserve task cancellation.

Scoped Reflexio cleanup

Inherited mem0 delete* methods and reset() are intentionally untouched and delete only mem0 data. Use the read-only client.reflexio facade for mirrored Reflexio data:

client.delete_all(user_id="user-123", agent_id="support-bot")  # mem0 only

client.reflexio.delete_session_records(
    user_id="user-123",
    app_id="storefront",
    agent_id="support-bot",
    run_id="conversation-456",
)
client.reflexio.clear_user_data(user_id="user-123", app_id="storefront")
client.reflexio.delete_agent_playbooks_by_ids(agent_playbook_ids)

The async facade uses the same method names and is awaited. Explicit facade operations raise ReflexioNotConfiguredError when unconfigured and ReflexioOperationError on rejection or transport failure. Available methods are clear_user_data, delete_session_records, delete_profile, delete_interaction, delete_request, delete_user_playbook, delete_agent_playbook, and the four delete_*_by_ids methods. Organization- wide deletion is deliberately not exposed.

Deletion follows the current Reflexio lifecycle:

  • delete_session_records removes stored requests and interactions for the encoded session. It does not retract learnings already derived from them.
  • clear_user_data removes that encoded user's requests, interactions, profiles, and user playbooks. Shared agent playbooks remain.
  • Delete agent playbooks explicitly by their returned IDs.
  • These operations wait for the deletion response, but they are not a transactional barrier against learning work that was already queued.
  • A no-run fallback session can be deleted with run_id=None while the originating client exists. After it restarts, use scoped clear_user_data.

get_all() remains a normal mem0 method and is not augmented because it has no query to use for Reflexio retrieval.