mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-26 17:42:24 +02:00
Merge pull request #2333 from langchain-ai/nc/apply-writes-order
lib: Enforce write application order in apply_writes
This commit is contained in:
@@ -941,7 +941,7 @@ class Pregel(PregelProtocol):
|
||||
if not writers:
|
||||
raise InvalidUpdateError(f"Node {as_node} has no writers")
|
||||
writes: deque[tuple[str, Any]] = deque()
|
||||
task = PregelTaskWrites(as_node, writes, [INTERRUPT])
|
||||
task = PregelTaskWrites((), as_node, writes, [INTERRUPT])
|
||||
task_id = str(uuid5(UUID(checkpoint["id"]), INTERRUPT))
|
||||
run = RunnableSequence(*writers) if len(writers) > 1 else writers[0]
|
||||
# execute task
|
||||
@@ -1121,7 +1121,7 @@ class Pregel(PregelProtocol):
|
||||
if not writers:
|
||||
raise InvalidUpdateError(f"Node {as_node} has no writers")
|
||||
writes: deque[tuple[str, Any]] = deque()
|
||||
task = PregelTaskWrites(as_node, writes, [INTERRUPT])
|
||||
task = PregelTaskWrites((), as_node, writes, [INTERRUPT])
|
||||
task_id = str(uuid5(UUID(checkpoint["id"]), INTERRUPT))
|
||||
run = RunnableSequence(*writers) if len(writers) > 1 else writers[0]
|
||||
# execute task
|
||||
|
||||
@@ -67,6 +67,9 @@ class WritesProtocol(Protocol):
|
||||
"""Protocol for objects containing writes to be applied to checkpoint.
|
||||
Implemented by PregelTaskWrites and PregelExecutableTask."""
|
||||
|
||||
@property
|
||||
def path(self) -> tuple[Union[str, int], ...]: ...
|
||||
|
||||
@property
|
||||
def name(self) -> str: ...
|
||||
|
||||
@@ -81,6 +84,7 @@ class PregelTaskWrites(NamedTuple):
|
||||
"""Simplest implementation of WritesProtocol, for usage with writes that
|
||||
don't originate from a runnable task, eg. graph input, update_state, etc."""
|
||||
|
||||
path: tuple[Union[str, int], ...]
|
||||
name: str
|
||||
writes: Sequence[tuple[str, Any]]
|
||||
triggers: Sequence[str]
|
||||
@@ -190,6 +194,9 @@ def apply_writes(
|
||||
"""Apply writes from a set of tasks (usually the tasks from a Pregel step)
|
||||
to the checkpoint and channels, and return managed values writes to be applied
|
||||
externally."""
|
||||
# sort tasks on path
|
||||
tasks = sorted(tasks, key=lambda t: t.path)
|
||||
|
||||
# update seen versions
|
||||
for task in tasks:
|
||||
checkpoint["versions_seen"].setdefault(task.name, {}).update(
|
||||
@@ -444,7 +451,9 @@ def prepare_single_task(
|
||||
checkpoint,
|
||||
channels,
|
||||
managed,
|
||||
PregelTaskWrites(packet.node, writes, triggers),
|
||||
PregelTaskWrites(
|
||||
task_path, packet.node, writes, triggers
|
||||
),
|
||||
config,
|
||||
),
|
||||
CONFIG_KEY_STORE: (
|
||||
@@ -552,7 +561,7 @@ def prepare_single_task(
|
||||
checkpoint,
|
||||
channels,
|
||||
managed,
|
||||
PregelTaskWrites(name, writes, triggers),
|
||||
PregelTaskWrites(task_path, name, writes, triggers),
|
||||
config,
|
||||
),
|
||||
CONFIG_KEY_STORE: (
|
||||
|
||||
@@ -477,7 +477,10 @@ class PregelLoop(LoopProtocol):
|
||||
mv_writes = apply_writes(
|
||||
self.checkpoint,
|
||||
self.channels,
|
||||
[*discard_tasks.values(), PregelTaskWrites(INPUT, input_writes, [])],
|
||||
[
|
||||
*discard_tasks.values(),
|
||||
PregelTaskWrites((), INPUT, input_writes, []),
|
||||
],
|
||||
self.checkpointer_get_next_version,
|
||||
)
|
||||
assert not mv_writes, "Can't write to SharedValues in graph input"
|
||||
|
||||
@@ -1787,11 +1787,11 @@ def test_concurrent_emit_sends() -> None:
|
||||
"0",
|
||||
"1",
|
||||
"1.1",
|
||||
"3.1",
|
||||
"2|1",
|
||||
"2|2",
|
||||
"2|3",
|
||||
"2|4",
|
||||
"3.1",
|
||||
"3",
|
||||
]
|
||||
|
||||
@@ -1836,13 +1836,13 @@ def test_send_sequences() -> None:
|
||||
assert graph.invoke(["0"]) == [
|
||||
"0",
|
||||
"1",
|
||||
"3.1",
|
||||
"2|Control(send=Send(node='2', arg=3))",
|
||||
"2|Control(send=Send(node='2', arg=4))",
|
||||
"3.1",
|
||||
"3",
|
||||
"2|3",
|
||||
"2|4",
|
||||
"3",
|
||||
"3",
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -2004,11 +2004,11 @@ async def test_concurrent_emit_sends() -> None:
|
||||
"0",
|
||||
"1",
|
||||
"1.1",
|
||||
"3.1",
|
||||
"2|1",
|
||||
"2|2",
|
||||
"2|3",
|
||||
"2|4",
|
||||
"3.1",
|
||||
"3",
|
||||
]
|
||||
|
||||
@@ -2053,13 +2053,13 @@ async def test_send_sequences() -> None:
|
||||
assert await graph.ainvoke(["0"]) == [
|
||||
"0",
|
||||
"1",
|
||||
"3.1",
|
||||
"2|Control(send=Send(node='2', arg=3))",
|
||||
"2|Control(send=Send(node='2', arg=4))",
|
||||
"3.1",
|
||||
"3",
|
||||
"2|3",
|
||||
"2|4",
|
||||
"3",
|
||||
"3",
|
||||
]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user