fix(python-sdk): recall memories in sync LangChain calls inside a running loop - #653
Merged
Merged
Conversation
HoangDucBach
force-pushed
the
fix/langchain-sync-recall-skip
branch
from
August 15, 2026 02:39
f144009 to
2327dab
Compare
HoangDucBach
changed the base branch from
fix/sync-http-client-lifecycle
to
dev
August 15, 2026 10:32
HoangDucBach
changed the base branch from
dev
to
fix/sync-http-client-lifecycle
August 17, 2026 02:25
ducnmm
approved these changes
Aug 17, 2026
ducnmm
left a comment
Collaborator
There was a problem hiding this comment.
Review
Verdict: ✅ Approve
Clean fix for a real, sneaky bug: patched_generate silently skipped recall inside running loops, making Walrus Memory appear connected while never actually injecting context.
- Reuses
_run_blocking+_with_fresh_http_clientinstead of inventing a new mechanism — right call _run_memwallocal function keeps the pattern compact- Tests cover both plain sync path and the running-loop regression case
- Callers will now see recall latency they were silently skipping before — documented in Risks ✅
- No blocking issues. Ready to merge after #652.
HoangDucBach
force-pushed
the
fix/langchain-sync-recall-skip
branch
from
August 18, 2026 08:35
2327dab to
43391b8
Compare
…ning loop with_memwal_langchain() advertises recall-before-call, but the sync _generate() wrapper appended the untouched message list whenever an event loop was already running — the normal case in notebooks and async application hosts. The LLM call still succeeded, so callers got a plain answer with no memory context and no warning: Walrus Memory looked connected while recall never ran. Route injection through the module's existing _run_blocking() helper, which moves the coroutine onto a worker thread, so the sync LangChain wrapper now behaves like the sync OpenAI one instead of silently degrading. Fixes #607
HoangDucBach
force-pushed
the
fix/langchain-sync-recall-skip
branch
from
August 18, 2026 08:38
43391b8 to
8181429
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ticket
Fixes #607
What changed?
patched_generate()now injects through the module's existing_run_blocking()helper, which runs the coroutine on a worker thread with its own loop._run_memwal()mirroring the sync OpenAI wrapper, so both share one idiom for running memory work from sync code.Why is this needed?
The sync
_generate()wrapper appended the untouched message list whenever an event loop was already running — the normal case in notebooks and async application hosts — and neither raised, warned, nor documented the skip. The LLM call still succeeded, so callers got an ordinary answer with no memory context and no signal that Walrus Memory had been bypassed._run_blocking()already solves this for the sync OpenAI wrapper; this brings LangChain to the same behaviour rather than inventing a new mechanism.Scope
Memory injection on the sync LangChain path (
_generate).patched_ageneratealready awaits injection on the caller's loop and is untouched.Out of scope
httpx client lifecycle leak (#606), the PR this is based on.
How was this tested?
2 new tests in
TestWithMemWalLangChain: the plain sync path, and the regression case inside a running loop — an async test body is a running loop, the exact condition that disabled injection. Verified to fail against the pre-fix wrapper. Suite: 121 passed, ruff clean.How can the reviewer verify it?
To watch the test catch the bug, restore the old branch in
patched_generate()(if loop is not None and loop.is_running(): enriched.append(msg_list)) and rerun with-k sync_generate→ the running-loop test fails, the plain sync one still passes.Risks and dependencies
devbefore [Python SDK/Bug] MemWalSync._run drops cached httpx client without closing it #606 lands._generate()inside a running loop now performs a blocking recall on a worker thread where it previously returned immediately — that is the fix, but those callers will see the recall latency they were silently skipping.Author checklist