Skip to content

feat: add seamless Reflexio integration for hosted mem0 clients - #419

Open
yyiilluu wants to merge 9 commits into
mainfrom
feat/mem0-dropin-wrapper
Open

feat: add seamless Reflexio integration for hosted mem0 clients#419
yyiilluu wants to merge 9 commits into
mainfrom
feat/mem0-dropin-wrapper

Conversation

@yyiilluu

@yyiilluu yyiilluu commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

What

Makes Reflexio a compatibility-first extension for mem0's hosted clients. Existing users switch one import; successful add() calls are mirrored to Reflexio, while ordinary search() remains exactly mem0 unless retrieval is explicitly requested.

# Before
from mem0 import MemoryClient

# After
from reflexio.mem0 import MemoryClient

result = client.search(query, filters=filters, include_reflexio=True)
memories = result["results"]
learnings = result["reflexio"]

The wrapper never rewrites a prompt or injects retrieved text. Applications keep deciding how to validate and format both mem0 memories and Reflexio learnings as prompt context.

Behavior

  • Wraps hosted MemoryClient and AsyncMemoryClient; local Memory and AsyncMemory remain exact mem0 aliases.
  • Runs mem0 first on add(), propagates mem0 failures, then attempts one bounded Reflexio publish. The exact mem0 result is returned regardless of the best-effort mirror outcome.
  • Keeps default search() behavior exact: no configuration inspection, Reflexio call, copy, or result mutation unless include_reflexio=True.
  • Opt-in search shallow-copies the mem0 dictionary and adds one stable reflexio envelope with ok, skipped, or error status and sanitized reason codes.
  • Raises ReflexioNamespaceCollisionError before retrieval if mem0 already owns the reserved top-level key.
  • Exposes read-only sync/async lifecycle facades as client.reflexio, with scoped user/session/record deletion and no organization-wide deletion methods. Inherited mem0 deletes remain mem0-only.
  • Uses one app/user/agent/run scope codec across add, search, and lifecycle operations. Explicit runs are collision-safe across scopes; no-run sessions are stable for the originating wrapper instance and scope.
  • Adds native cancellable publish_interaction_async() and search_async() methods to ReflexioClient, including an aiohttp total timeout.

Compatibility and delivery contract

  • Pins the optional extra to the validated mem0 minor line: mem0ai>=2.0,<2.1 for both reflexio-ai[mem0] and reflexio-client[mem0].
  • Wrapper-created Reflexio clients use a 5-second timeout. Injected clients own their finite timeout; combining injection with reflexio_timeout is rejected.
  • A successful publish response means accepted/queued, not learned. There is no retry, outbox, or eventual-delivery guarantee; crash gaps and caller-retry duplicates remain documented best-effort limitations.
  • delete_session_records removes stored requests/interactions but not derived learnings. clear_user_data removes encoded-user requests, interactions, profiles, and user playbooks, but shared agent playbooks require explicit deletion by ID. These are not transactional barriers against queued work.

Packaging and CI

  • Ships the integration in both full and lightweight distributions while preserving the optional-dependency import guard.
  • Adds installed-artifact CI across both wheels, mem0 2.0.0 and the newest allowed 2.0.x, and Python 3.12.
  • Adds real typed options/signature checks plus sync/async delegation, cancellation, identity isolation, envelope, timeout, configuration, and lifecycle regression coverage.

Verification

  • 96 focused mem0/client tests passed.
  • Full OSS unit/integration suite passed (5,765 tests collected), and the OSS E2E tier passed.
  • Ruff formatting/lint and Pyright passed repository-wide.
  • Built and installed reflexio-ai[mem0] against mem0 2.0.0 and reflexio-client[mem0] against mem0 2.0.18; both artifact contract verifiers passed.
  • TestSprite was not run because the existing project has no mem0 scenario and this package PR is not deployed to an allowed public target; the deterministic installed-artifact tests are the release gate for this integration.

Documentation

  • Rewrites the README and notebook around unchanged default search, opt-in namespacing, manual prompt composition, untrusted retrieved text, hosted sync/async support, and explicit lifecycle cleanup.
  • The notebook uses the actual environment/import path, asserts default result identity, requires a real learned Reflexio artifact, and fails unless scoped cleanup succeeds.

Summary by CodeRabbit

  • New Features

    • Added a Mem0-compatible memory client with optional Reflexio integration.
    • Conversations can be published to Reflexio while preserving standard Mem0 behavior.
    • Search results can include relevant Reflexio profiles and playbooks.
    • Added synchronous and asynchronous Mem0 memory exports.
    • Added graceful pass-through behavior when Reflexio is unavailable or unconfigured.
  • Documentation

    • Added installation guidance, configuration details, migration instructions, and a hands-on tutorial notebook.
  • Bug Fixes

    • Reflexio integration failures no longer interrupt Mem0 operations.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an optional mem0ai integration with synchronous and asynchronous drop-in clients. The wrapper preserves mem0 operations, publishes additions to Reflexio, optionally augments searches, supports scoped cleanup, and includes transport changes, packaging, tests, documentation, and compatibility verification.

Changes

mem0 drop-in wrapper

Layer / File(s) Summary
Package and export contract
pyproject.toml, client_dist/pyproject.toml, typings/mem0/__init__.pyi, reflexio/mem0/__init__.py, tests/mem0/*, tests/client/test_search.py
Adds the optional mem0ai dependency, packages reflexio/mem0, exposes wrapped and unchanged mem0 classes, and verifies imports, inheritance, delegation, and type surfaces.
Async Reflexio transport and publishing
reflexio/client/client.py, tests/client/*
Adds timeout-aware async requests, shared publishing validation, native async interaction publishing, and native async unified search.
Client setup and add publishing
reflexio/mem0/_wrapper.py, tests/mem0/conftest.py, tests/mem0/test_add_publish.py, tests/mem0/test_async_wrapper.py
Configures injected or environment-based Reflexio clients, resolves identities and sessions, normalizes messages, publishes interactions, preserves mem0 results, and handles Reflexio failures and cancellation.
Search augmentation and lifecycle facade
reflexio/mem0/_wrapper.py, reflexio/mem0/_facade.py, tests/mem0/test_search_augment.py, tests/mem0/test_facade.py, tests/mem0/test_async_wrapper.py
Adds opt-in Reflexio profiles and playbooks to supported searches and provides scoped synchronous and asynchronous cleanup and deletion operations.
Artifact verification and usage documentation
scripts/verify_mem0_artifact.py, .github/workflows/mem0-compat.yml, README.md, notebooks/README.md, notebooks/07_mem0_dropin_wrapper.ipynb
Verifies installed sync and async contracts across supported mem0 distributions and documents installation, usage, fallback behavior, search enrichment, polling, and cleanup.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Application
  participant MemoryClient
  participant mem0
  participant ReflexioClient
  Application->>MemoryClient: add(messages, options)
  MemoryClient->>mem0: forward add operation
  mem0-->>MemoryClient: mem0 result
  MemoryClient->>ReflexioClient: publish normalized interactions
  ReflexioClient-->>MemoryClient: publish result or error
  MemoryClient-->>Application: mem0 result
  Application->>MemoryClient: search(query, filters, include_reflexio)
  MemoryClient->>mem0: forward search operation
  mem0-->>MemoryClient: mem0 results
  MemoryClient->>ReflexioClient: search profiles and playbooks
  ReflexioClient-->>MemoryClient: Reflexio result data or error
  MemoryClient-->>Application: combined search result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding Reflexio integration for hosted mem0 clients.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mem0-dropin-wrapper

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@notebooks/07_mem0_dropin_wrapper.ipynb`:
- Around line 721-729: Extend the cleanup cell after the mem0 deletion request
to delete the Reflexio records associated with USER_ID, including any supported
derived profiles or playbooks, before calling show_success. Use the existing
Reflexio client/API symbols from the notebook and ensure cleanup is completed
for this test user before reporting success.
- Line 72: Update the notebook cleanup flow to remove Reflexio interactions,
profiles, and playbooks created during the current session, including records
identified by derived IDs, in addition to Mem0 memories. Scope deletion to those
session and derived record IDs, avoid organization-wide delete_all_* methods,
and place the logic near the existing client.delete_all() cleanup using the
imported display helpers as needed.

In `@README.md`:
- Around line 322-328: Update the README description of search() to state that
Reflexio sibling keys are included only when Reflexio is configured and used;
otherwise the original mem0 payload is returned unchanged. Clarify that “never
raises” applies only to best-effort Reflexio publishing/enrichment, while mem0
failures continue to propagate.

In `@reflexio/mem0/_wrapper.py`:
- Around line 179-199: Update the identity resolution in the mem0 add() publish
path before the user_id guard and _reflexio.publish_interaction call to read
user_id, agent_id, and run_id from effective options.filters, while preserving
the documented precedence between typed/stubbed options and kwargs. Ensure
filtered identity values enable publishing and are passed through for
attribution, then add regression coverage for options and kwargs precedence.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b2b718bb-7339-4e07-b67c-f0c66848f43d

📥 Commits

Reviewing files that changed from the base of the PR and between 027707d and 0a75736.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • README.md
  • client_dist/pyproject.toml
  • notebooks/07_mem0_dropin_wrapper.ipynb
  • notebooks/README.md
  • pyproject.toml
  • reflexio/mem0/__init__.py
  • reflexio/mem0/_wrapper.py
  • tests/mem0/__init__.py
  • tests/mem0/conftest.py
  • tests/mem0/test_add_publish.py
  • tests/mem0/test_delegation.py
  • tests/mem0/test_import_guard.py
  • tests/mem0/test_real_mem0_smoke.py
  • tests/mem0/test_search_augment.py
  • typings/mem0/__init__.pyi

Comment thread notebooks/07_mem0_dropin_wrapper.ipynb
Comment thread notebooks/07_mem0_dropin_wrapper.ipynb Outdated
Comment thread README.md Outdated
Comment thread reflexio/mem0/_wrapper.py Outdated
Customers using mem0's managed platform change one import
(from reflexio.mem0 import MemoryClient) and keep every call site:

- add() forwards to mem0, then best-effort publishes the trace to
  Reflexio (publish_interaction, wait_for_response=False) mapping
  user_id->user_id, run_id->session_id, agent_id->agent_version
- search() returns mem0's payload untouched plus reflexio_profiles /
  reflexio_user_playbooks / reflexio_agent_playbooks sibling keys from
  unified search; keys are absent on any Reflexio failure
- Reflexio errors are always logged and swallowed; mem0 errors
  propagate unchanged
- mem0ai ships as an optional extra (reflexio-ai[mem0] and
  reflexio-client[mem0], pinned >=2.0,<3.0); import reflexio works
  without it and reflexio.mem0 raises a helpful ImportError
- typings/mem0 stub keeps pyright from resolving the top-level name
  mem0 to the wrapper package itself when mem0ai is not installed
notebooks/07_mem0_dropin_wrapper.ipynb validates the reflexio.mem0
wrapper end to end against the real mem0 platform and a live local
Reflexio backend: one add() stores memories in mem0 AND publishes the
trace to Reflexio; one search() returns mem0 memories plus
reflexio_profiles/user_playbooks/agent_playbooks sibling keys; a dead
Reflexio endpoint degrades to pure-mem0 behavior. The conversation is
8 turns because the default extraction gate (stride_size=8) skips
shorter test conversations. MEM0_API_KEY is read from the environment,
never stored in the notebook.
- Pass-through mode when unconfigured: with neither REFLEXIO_API_KEY nor
  REFLEXIO_URL set (and no reflexio_client), the wrapper makes no
  Reflexio calls instead of firing doomed unauthenticated requests at
  the client's production default URL on every add()/search()
- Skip augmentation for empty/blank queries instead of failing the
  Reflexio call and warning every time
- Treat an explicit None role as 'user' rather than publishing 'None'
- Debounce failure logs: first Reflexio failure warns, subsequent ones
  log at DEBUG so an outage doesn't spam the host app's logs
- Tests for all of the above plus kwargs-over-options precedence for
  timestamp and filters
@yyiilluu
yyiilluu force-pushed the feat/mem0-dropin-wrapper branch from 0a75736 to 5a6c4c8 Compare August 11, 2026 00:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@reflexio/mem0/_wrapper.py`:
- Around line 110-119: The identity extraction logic around the filter helper
must reject conflicting values instead of returning the first match. Collect all
non-empty plain values for name from the top-level filters and every AND clause,
return the identity only when all collected values agree, and otherwise return
None; add regression coverage for conflicting user_id filters in both add() and
search().
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 931d46c3-0a42-4de2-bf1d-289f8e9f80d9

📥 Commits

Reviewing files that changed from the base of the PR and between 5a6c4c8 and 1f01f30.

📒 Files selected for processing (5)
  • README.md
  • notebooks/07_mem0_dropin_wrapper.ipynb
  • reflexio/mem0/_wrapper.py
  • tests/mem0/test_add_publish.py
  • tests/mem0/test_search_augment.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • README.md

Comment thread reflexio/mem0/_wrapper.py Outdated
Mirror successful hosted sync and async adds while preserving exact mem0 search by default. Add opt-in namespaced retrieval, scoped lifecycle facades, composite identity isolation, native async Reflexio calls, and real-wheel compatibility CI for mem0 2.0.x.
@yyiilluu yyiilluu changed the title feat: reflexio.mem0 drop-in wrapper for mem0 MemoryClient feat: add seamless Reflexio integration for hosted mem0 clients Aug 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (8)
scripts/verify_mem0_artifact.py (1)

89-99: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Check inherited parameter defaults.

The verifier checks only parameter names and kinds. A wrapper can change a base constructor or search() default and still pass. This can break existing call sites that omit optional arguments.

Proposed verification improvement
-        for name in base_init.parameters:
+        for name, base_parameter in base_init.parameters.items():
             assert name in wrapper_init.parameters
-            assert wrapper_init.parameters[name].kind == base_init.parameters[name].kind
+            wrapper_parameter = wrapper_init.parameters[name]
+            assert wrapper_parameter.kind == base_parameter.kind
+            assert wrapper_parameter.default == base_parameter.default
         signature = inspect.signature(wrapper.search)
+        base_search = inspect.signature(base.search)
+        for name, base_parameter in base_search.parameters.items():
+            wrapper_parameter = signature.parameters[name]
+            assert wrapper_parameter.kind == base_parameter.kind
+            assert wrapper_parameter.default == base_parameter.default
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify_mem0_artifact.py` around lines 89 - 99, Extend the checks in
the wrapper signature verification loop to compare each inherited constructor
parameter’s default between base_init and wrapper_init, preserving the existing
name and kind checks. Also compare the corresponding defaults for wrapper.search
parameters, including include_reflexio, so omitted optional arguments retain the
base API behavior.
reflexio/client/client.py (2)

179-188: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Update the docstring type for timeout.

The parameter type is now float. The docstring still documents timeout (int).

📝 Proposed docstring fix
-            timeout (int): Default request timeout in seconds (default 300)
+            timeout (float): Default request timeout in seconds (default 300)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reflexio/client/client.py` around lines 179 - 188, Update the timeout entry
in the Reflexio client initializer docstring to document a float type, matching
the timeout annotation in the initializer signature; leave the default and
surrounding documentation unchanged.

2878-2924: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider sharing the request construction between search and search_async.

search_async repeats the full 17-parameter signature and the identical _build_request call. A private _build_search_request(...) helper would keep the two entry points from drifting when a field is added to UnifiedSearchRequest.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reflexio/client/client.py` around lines 2878 - 2924, Extract the shared
request construction from search and search_async into a private
_build_search_request helper, preserving all 17 parameters and their forwarding
to _build_request with UnifiedSearchRequest. Update both entry points to use
this helper while leaving their synchronous and asynchronous request execution
paths unchanged.
tests/mem0/test_add_publish.py (1)

311-314: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a boolean case to the invalid-timeout parameters.

_validate_timeout has an explicit isinstance(timeout, bool) guard, but no test exercises it. True would otherwise pass the numeric check because bool subclasses int.

💚 Proposed test addition
-@pytest.mark.parametrize("timeout", [0, -1, float("inf"), float("nan"), "5"])
+@pytest.mark.parametrize("timeout", [0, -1, float("inf"), float("nan"), "5", True])
 def test_invalid_timeout_rejected(wrapped_cls, timeout):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/mem0/test_add_publish.py` around lines 311 - 314, Extend the timeout
values in test_invalid_timeout_rejected to include a boolean case such as True,
ensuring _validate_timeout’s explicit bool rejection is covered while preserving
the existing ValueError assertion.
tests/client/test_publish_interaction.py (1)

132-157: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Move the response import to module scope.

The PublishUserInteractionResponse import sits inside fake_publish, so it re-executes on every call and hides the dependency from readers. Import it with the other module-level imports.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/client/test_publish_interaction.py` around lines 132 - 157, Move the
PublishUserInteractionResponse import out of the local fake_publish function in
test_publish_interaction_async_uses_shared_validation_and_warnings and place it
with the module-level imports. Remove the nested import while preserving the
fake publisher’s return behavior.
reflexio/mem0/_wrapper.py (2)

388-391: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include the exception cause in the best-effort failure log.

_log_reflexio_failure records only the operation name. Every call site catches a broad Exception and discards it, so an operator sees Best-effort Reflexio publish failed with no cause. Diagnosing a misconfigured URL, an auth failure, or a timeout then requires a code change.

Pass the exception through and log it with exc_info at the first (warning) occurrence.

🔧 Proposed change
-    def _log_reflexio_failure(self, operation: str) -> None:
-        level = logging.DEBUG if self._reflexio_failure_logged else logging.WARNING
-        self._reflexio_failure_logged = True
-        logger.log(level, "Best-effort Reflexio %s failed", operation)
+    def _log_reflexio_failure(
+        self, operation: str, exc: BaseException | None = None
+    ) -> None:
+        first = not self._reflexio_failure_logged
+        level = logging.WARNING if first else logging.DEBUG
+        self._reflexio_failure_logged = True
+        logger.log(
+            level,
+            "Best-effort Reflexio %s failed",
+            operation,
+            exc_info=exc if first else None,
+        )

Then update each call site, for example:

-        except Exception:  # noqa: BLE001 - best-effort by contract.
-            self._log_reflexio_failure("publish")
+        except Exception as exc:  # noqa: BLE001 - best-effort by contract.
+            self._log_reflexio_failure("publish", exc)

The existing message text is unchanged, so tests/mem0/test_add_publish.py::test_repeat_failures_warn_once_then_debug still matches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reflexio/mem0/_wrapper.py` around lines 388 - 391, Update
_log_reflexio_failure to accept the caught exception, preserve the existing
operation message, and pass exc_info when logging the first warning occurrence
while retaining debug-level behavior afterward. Update every broad-Exception
call site that invokes _log_reflexio_failure to pass the caught exception
through instead of discarding it.

205-215: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify the resolver selection.

resolver = _resolve_add_identity if add else None forces a second conditional inside the comprehension and computes filters even when it is unused. A direct branch reads more plainly.

♻️ Proposed refactor
-    resolver = _resolve_add_identity if add else None
-    filters = _opt(options, kwargs, "filters")
-    identities = {
-        name: resolver(options, kwargs, name)
-        if resolver
-        else _extract_identity(filters, name)
-        for name in _IDENTITY_NAMES
-    }
+    filters = _opt(options, kwargs, "filters")
+    identities = {
+        name: (
+            _resolve_add_identity(options, kwargs, name)
+            if add
+            else _extract_identity(filters, name)
+        )
+        for name in _IDENTITY_NAMES
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@reflexio/mem0/_wrapper.py` around lines 205 - 215, Update _resolve_identities
to use separate add and non-add branches instead of assigning resolver and
conditionally invoking it inside the comprehension. In the add branch, build
identities with _resolve_add_identity and avoid retrieving filters; in the
non-add branch, retrieve filters and build identities with _extract_identity.
tests/mem0/test_async_wrapper.py (1)

76-97: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

This test passes even if the wrapper blocks the event loop.

asyncio.gather awaits both coroutines, so mark_heartbeat runs and sets heartbeat = True before gather returns regardless of whether the Reflexio call yields. The assertion cannot fail for a blocking implementation.

Assert ordering instead: record that the heartbeat completes while the search is still pending.

Two smaller points on the same block:

  • delayed_search returns reflexio_mock.search.return_value, but the wrapper calls search_async. Use reflexio_mock.search_async.return_value so the fixture wiring matches the code path under test.
  • asyncio.sleep(0.01) adds wall-clock time to the suite. A shorter sleep or an asyncio.Event is sufficient.
💚 Proposed test rewrite
-    reflexio_mock.search_async.side_effect = delayed_search
-    client = async_wrapped_cls(api_key="mk", reflexio_client=reflexio_mock)
-    heartbeat = False
-
-    async def mark_heartbeat():
-        nonlocal heartbeat
-        await asyncio.sleep(0)
-        heartbeat = True
-
-    await asyncio.gather(
-        client.search("q", filters={"user_id": "u1"}, include_reflexio=True),
-        mark_heartbeat(),
-    )
-    assert heartbeat is True
+    order: list[str] = []
+
+    async def delayed_search(**_kwargs):
+        await asyncio.sleep(0.01)
+        order.append("search")
+        return reflexio_mock.search_async.return_value
+
+    reflexio_mock.search_async.side_effect = delayed_search
+    client = async_wrapped_cls(api_key="mk", reflexio_client=reflexio_mock)
+
+    async def mark_heartbeat():
+        await asyncio.sleep(0)
+        order.append("heartbeat")
+
+    await asyncio.gather(
+        client.search("q", filters={"user_id": "u1"}, include_reflexio=True),
+        mark_heartbeat(),
+    )
+    assert order == ["heartbeat", "search"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/mem0/test_async_wrapper.py` around lines 76 - 97, Rewrite
test_async_wrapper_yields_while_reflexio_search_waits to verify ordering: record
when mark_heartbeat completes and assert it occurs before the pending
client.search task finishes, rather than only asserting heartbeat after gather.
Make delayed_search return reflexio_mock.search_async.return_value, and replace
the fixed 0.01-second delay with a shorter or event-based synchronization.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/mem0-compat.yml:
- Around line 42-45: Update the non-minimum branch of the mem0 compatibility
matrix installation command to test the full supported mem0ai range through
versions below 3.0, matching the mem0 package extra instead of restricting it
below 2.1; leave the minimum branch pinned to mem0ai 2.0.0.
- Line 18: Update the workflow’s actions/checkout step to disable credential
persistence, and configure the workflow permissions as read-only. Ensure uv
build cannot access a persisted repository token while preserving the existing
checkout behavior.

In `@reflexio/mem0/__init__.py`:
- Around line 13-19: Update the import guard around AsyncMemory and Memory to
catch only ModuleNotFoundError where exc.name equals "mem0", preserving the
existing optional-dependency message for that case. Re-raise all other import
failures unchanged, including missing internal dependencies and missing exports.

In `@tests/client/test_request_headers.py`:
- Around line 50-52: Update the FakeResponse class by importing ClassVar at
module scope and annotating its mutable headers class attribute as ClassVar with
the existing dictionary type, resolving RUF012 without changing its value or
behavior.

In `@typings/mem0/__init__.pyi`:
- Around line 10-26: Extend both MemoryClient and AsyncMemoryClient stubs with
get, delete_all, delete, delete_users, and reset methods. Use synchronous return
signatures for MemoryClient and async coroutine signatures for
AsyncMemoryClient, matching the existing method parameter and return-type
conventions in each class.

---

Nitpick comments:
In `@reflexio/client/client.py`:
- Around line 179-188: Update the timeout entry in the Reflexio client
initializer docstring to document a float type, matching the timeout annotation
in the initializer signature; leave the default and surrounding documentation
unchanged.
- Around line 2878-2924: Extract the shared request construction from search and
search_async into a private _build_search_request helper, preserving all 17
parameters and their forwarding to _build_request with UnifiedSearchRequest.
Update both entry points to use this helper while leaving their synchronous and
asynchronous request execution paths unchanged.

In `@reflexio/mem0/_wrapper.py`:
- Around line 388-391: Update _log_reflexio_failure to accept the caught
exception, preserve the existing operation message, and pass exc_info when
logging the first warning occurrence while retaining debug-level behavior
afterward. Update every broad-Exception call site that invokes
_log_reflexio_failure to pass the caught exception through instead of discarding
it.
- Around line 205-215: Update _resolve_identities to use separate add and
non-add branches instead of assigning resolver and conditionally invoking it
inside the comprehension. In the add branch, build identities with
_resolve_add_identity and avoid retrieving filters; in the non-add branch,
retrieve filters and build identities with _extract_identity.

In `@scripts/verify_mem0_artifact.py`:
- Around line 89-99: Extend the checks in the wrapper signature verification
loop to compare each inherited constructor parameter’s default between base_init
and wrapper_init, preserving the existing name and kind checks. Also compare the
corresponding defaults for wrapper.search parameters, including
include_reflexio, so omitted optional arguments retain the base API behavior.

In `@tests/client/test_publish_interaction.py`:
- Around line 132-157: Move the PublishUserInteractionResponse import out of the
local fake_publish function in
test_publish_interaction_async_uses_shared_validation_and_warnings and place it
with the module-level imports. Remove the nested import while preserving the
fake publisher’s return behavior.

In `@tests/mem0/test_add_publish.py`:
- Around line 311-314: Extend the timeout values in
test_invalid_timeout_rejected to include a boolean case such as True, ensuring
_validate_timeout’s explicit bool rejection is covered while preserving the
existing ValueError assertion.

In `@tests/mem0/test_async_wrapper.py`:
- Around line 76-97: Rewrite
test_async_wrapper_yields_while_reflexio_search_waits to verify ordering: record
when mark_heartbeat completes and assert it occurs before the pending
client.search task finishes, rather than only asserting heartbeat after gather.
Make delayed_search return reflexio_mock.search_async.return_value, and replace
the fixed 0.01-second delay with a shorter or event-based synchronization.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7ad394f9-73a5-496f-9989-405585eeccf4

📥 Commits

Reviewing files that changed from the base of the PR and between 1f01f30 and a7cca10.

⛔ Files ignored due to path filters (2)
  • client_dist/uv.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (22)
  • .github/workflows/mem0-compat.yml
  • README.md
  • client_dist/pyproject.toml
  • notebooks/07_mem0_dropin_wrapper.ipynb
  • pyproject.toml
  • reflexio/client/client.py
  • reflexio/mem0/__init__.py
  • reflexio/mem0/_facade.py
  • reflexio/mem0/_wrapper.py
  • scripts/verify_mem0_artifact.py
  • tests/client/test_publish_interaction.py
  • tests/client/test_request_headers.py
  • tests/client/test_search.py
  • tests/mem0/conftest.py
  • tests/mem0/test_add_publish.py
  • tests/mem0/test_async_wrapper.py
  • tests/mem0/test_delegation.py
  • tests/mem0/test_facade.py
  • tests/mem0/test_import_guard.py
  • tests/mem0/test_real_mem0_smoke.py
  • tests/mem0/test_search_augment.py
  • typings/mem0/__init__.pyi
🚧 Files skipped from review as they are similar to previous changes (7)
  • client_dist/pyproject.toml
  • tests/mem0/test_import_guard.py
  • README.md
  • tests/mem0/test_real_mem0_smoke.py
  • pyproject.toml
  • tests/mem0/test_delegation.py
  • notebooks/07_mem0_dropin_wrapper.ipynb

Comment thread .github/workflows/mem0-compat.yml
Comment thread .github/workflows/mem0-compat.yml
Comment thread reflexio/mem0/__init__.py
Comment thread tests/client/test_request_headers.py Outdated
Comment thread typings/mem0/__init__.pyi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant