Skip to content

fix(azure-ai): honor checkpoint and pending-write ordering - #1209

Draft
JiahuiGu (Jiahui-Gu) wants to merge 1 commit into
langchain-ai:mainfrom
Jiahui-Gu:Jiahui-Gu/fix/foundry-checkpoint-ordering
Draft

JiahuiGu (Jiahui-Gu) wants to merge 1 commit into
langchain-ai:mainfrom
Jiahui-Gu:Jiahui-Gu/fix/foundry-checkpoint-ordering

Conversation

@Jiahui-Gu

@Jiahui-Gu JiahuiGu (Jiahui-Gu) commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Make FoundryCheckpointSaver honor LangGraph's checkpoint/history/pending-write ordering independently of State Store enumeration order. This is a standalone SDK fix: no sample, Responses converter, dependency pin, public signature, or checkpoint storage format changes.

Root cause

LangGraph defines checkpoint IDs as unique and monotonically increasing. Its standard savers select/order by checkpoint ID, and before is an exclusive ID bound. Foundry State Store enumerates resource creation order instead. Those are different contracts: tied timestamps, delayed writes, replay, or backfill must not make an older checkpoint become "latest".

Previously aget_tuple trusted list_keys(order="desc", limit=1), and alist reused a storage item cursor for before. A missing or filtered-out boundary could return no history; creation order could also include checkpoints newer than the requested ID bound. Existing tests explicitly encoded creation order and were corrected against LangGraph's contract.

The adjacent recovery path also persisted task_path but ignored it when loading pending writes. Newer LangGraph's delta-channel replay requires (task_path, task_id, index) order; task IDs are hashes, not execution order. Otherwise an order-sensitive reducer can reconstruct a different state after reopen.

Design

  • Enumerate matching key pages, extract checkpoint IDs from the existing namespaced key format, and order globally before applying limit/before. Namespace and metadata filters remain supported.
  • Fetch checkpoint values only for selected candidates. Exact checkpoint lookup remains a direct read; exact history queries with an explicit namespace also avoid a full key scan.
  • Skip items that disappear between enumeration and retrieval; propagate actual storage errors and retain finally cleanup.
  • Treat before as an exclusive ID bound without requiring the boundary object to exist or match a tag filter.
  • Restore pending writes in (task_path, task_id, index) order, defaulting missing legacy task_path to "".

No auxiliary "latest" index/pointer is introduced: without service-side checkpoint-ID ordering it would need additional concurrency/migration rules and still would not repair history and before. This implementation favors correct existing-format reads. Latest/history queries scan matching key pages and sort their keys, so request count and memory grow with retained history. It does not download every checkpoint blob just to find the latest. This cost is documented rather than hidden.

The separate Azure SDK local-ordering fix #49617 is related but not required: this adapter must remain correct against either local implementation and against hosted storage. Neither PR needs the sample PR to merge.

Validation

Check Result
Saver suite on LangGraph 1.2.16 / checkpoint 4.3.0 20 passed
Same saver suite on LangGraph 1.2.11 / checkpoint 4.2.0 20 passed
All hosting suites with older dependency combination 485 passed
All hosting suites with newer dependency combination 483 passed, 2 pre-existing failures; see below
Real ChatOpenAI request serialization + stock HTTP host + reopened file saver approve/reject/edit x JSON/SSE: 6/6 passed
Same HTTP acceptance with the separate Core fix loaded 6/6 passed
Original frozen-clock twelve-checkpoint reproduction, unchanged published Core latest and exact both return step 11
Ruff check/format, changed-source/test mypy, compilation, diff check Pass

The original latest/history/paging/disappearing-item and local-reopen regressions failed before the fix. A pending-write path-order regression also failed before that correction. Retained tests cover reordered creation, multi-page limiting, existing/deleted/filtered boundaries, namespaces, metadata, legacy pending writes, real local saver reopen, approval execution exactly once, and an order-sensitive parallel DeltaChannel restored after reopen. No new sample-specific test framework is introduced.

Existing newer-LangGraph failures (not suppressed)

TestParallelInterrupts.test_stays_paused_until_every_branch_is_answered and test_ignores_a_repeated_answer_to_the_same_interrupt also fail against the unmodified published langchain-azure-ai==1.2.11 with this newer dependency combination. They use InMemorySaver, not the changed Foundry saver. A pure LangGraph reproduction shows the final response stream contains only b=B, while the final checkpoint correctly retains both a=A and b=B. The existing tests require both messages in the final response. They remain unchanged and failing; this PR does not silently weaken them or claim to resolve that separate streaming-output compatibility question.

Boundaries

These are local SDK and HTTP integration checks, not a new cloud deployment. Native message history and actual edited execution were verified; no approval is synthesized, no sleep/fallback masks failures, and the sample is not changed. No package publication, merge, extra evaluation job, or unrelated test repair is performed.

AI assistance: Codex assisted with implementation and verification.

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