mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-10-14 12:15:25 +02:00
A node served from the node cache had its writes applied but never saved: the loop only streamed them. Plain channels didn't notice, since the next checkpoint stores their value, but a `DeltaChannel` is rebuilt from the saved writes, so its saved state lost the cached node's write for good, under every durability. With `a -> b -> c` and `b` cached, a second thread read back `log` as `a, c` next to `plain` as `a, b, c`. The loop now saves a cache hit with `put_writes`, like a node that ran, in all six places it matches cached writes. A new `cached` flag keeps the hit streamed as cached and stops `put_writes` from writing it back to the cache, which would also restart its TTL. A hit now costs the saver write the node makes when it runs, and a cached `@task` call's result is saved the same way. The sync loop also stops caching a node that failed or was interrupted, as the async loop already did: a cached error made the next run with the same input skip the node without raising. Found in review of langchain-ai/langgraphjs#2969. JS twin: langchain-ai/langgraphjs#2980 ## Tests - `test_delta_channel_node_cache.py`: `b` runs on the first thread and is a cache hit on the second, under every durability, with `invoke` and `ainvoke`. Every checkpoint of the second thread reads the same in the DeltaChannel and a plain channel, and the cache is written once. All 6 cases fail on main; without the `cached` check in `put_writes`, they fail on the cache count. - `test_a_node_that_fails_is_not_cached`, with `invoke` and `ainvoke`: a cached node that raises raises again on the next thread. The sync case fails on main. - The two tests that spy on `PregelLoop.put_writes` pass the new keyword through. - `make format`, `make lint_package`, `make lint_tests`, and the `langgraph`, `prebuilt` and `cli` suites pass (without Redis).