Compare commits

..
Author SHA1 Message Date
William Fu-Hinthorn 03776f058e Add examples of configurable headres 2025-04-28 12:43:51 -07:00
Vadym BardaandGitHub 11c6a54de9 docs: small update in the manage message history how to (#4442) 2025-04-28 13:22:52 -04:00
Nuno CamposandGitHub 78581b80c2 Support multiple resume values with Command.resume (#4406)
* Adding support for mapping interrupt ids -> resume values with the
`Command.resume_map` argument, like:

```py
resume_map = {
    i.interrupt_id: f"human input for prompt {i.value}" 
    for i in parent_graph.get_state(thread_config).interrupts 
}

parent_graph.invoke(Command(resume=resume_map), config=thread_config)
```

* Adds an `interrupts` attribute on `StateSnapshot` so that we can
access that directly rather than having to do
`get_state(thread_config).tasks` and then iterate over tasks to find
interrupts

* Deprecates undocumented feature where (if interrupting a graph from
the level of an interrupt), you could pass a dict mapping task ids ->
resume values. Now we recommend and endorse the `interrupt_id` approach
above.

I'll note, from an internal perspective, I would love if we didn't have
to pass around this map, but it seems like the best way right now to
make the necessary resume information necessary at different levels in a
graph with subgraphs.

Fix https://github.com/langchain-ai/langgraph/issues/4028

Slotted to be included in our v0.4.0 release early next week!
2025-04-28 09:06:58 -07:00
Sydney Runkle 5fee0d9d66 skip yielding interrupt if input was a map 2025-04-28 11:47:51 -04:00
William FHandGitHub 68e3d70967 Add section in how-to on exclusions (#4441) 2025-04-28 08:42:32 -07:00
William FHandGitHub dc9f2b1109 Add how-to on headers (#4440)
and their configurability for configurability
2025-04-28 15:31:48 +00:00
Sydney Runkle dbf1c28ccd lint 2025-04-28 10:22:30 -04:00
Sydney Runkle cb25ef985d update loop to append mapped tasks to task specific values 2025-04-28 10:18:39 -04:00
Sydney RunkleandGitHub 5ddc24ba85 Merge branch 'main' into multi-resumes 2025-04-25 17:05:35 -07:00
Sydney Runkle 0362840d0b revert debugging note"
"
2025-04-25 17:00:26 -07:00
Sydney Runkle 44099d27c8 fixing tests 2025-04-25 16:53:37 -07:00
Sydney Runkle 50449af1a9 add convenient interrupt access to StateSnapshot 2025-04-24 14:52:54 -07:00
Sydney Runkle c2fa33e055 linting etc 2025-04-24 13:28:29 -07:00
Sydney Runkle 644a6c3b63 use resume instead of resume_map and deprecate old mapping task_id -> resume logic 2025-04-24 13:26:01 -07:00
Sydney Runkle 847b9c13ac adding docs example 2025-04-24 12:36:35 -07:00
Sydney Runkle b6963d35aa multi hitl with new hash pattern 2025-04-24 11:21:25 -07:00
17 changed files with 455 additions and 50 deletions
@@ -17,8 +17,58 @@ Here's how to customize the included and excluded headers:
}
```
The `include` and `exclude` lists accept exact header names or patterns using `*` to match any number of characters. For your security, no other regex patterns are supported.
## Using within your graph
You can access the included headers in your graph using the `config` argument of any node.
```python
def my_node(state, config):
organization_id = config["configurable"].get("x-organization-id")
...
```
Or by fetching from context (useful in tools and or within other nested functions).
```python
from langgraph.config import get_config
def search_everything(query: str):
organization_id = get_config()["configurable"].get("x-organization-id")
...
```
You can even use this to dynamically compile the graph.
```python
# my_graph.py.
import contextlib
@contextlib.asynccontextmanager
async def generate_agent(config):
organization_id = config["configurable"].get("x-organization-id")
if organization_id == "org1":
graph = ...
yield graph
else:
graph = ...
yield graph
```
```json
{
"graphs": {"agent": "my_grph.py:generate_agent"}
}
```
For more examples on how to use runtime configuration, check out the [configuration how-to](../../how-tos/configuration.ipynb).
### Opt-out of configurable headers
If you'd like to opt-out of configurable headers, you can simply set a wildcard pattern in the `exclude` list:
```json
@@ -31,4 +81,6 @@ If you'd like to opt-out of configurable headers, you can simply set a wildcard
}
```
This will exclude all headers from being added to your run's configuration.
This will exclude all headers from being added to your run's configuration.
Note that exclusions take precedence over inclusions.
+16
View File
@@ -440,6 +440,22 @@ Upon **resuming** the graph, the counter will be incremented a second time, resu
The value of counter is: 2
```
### Resuming multiple interrupts with one invocation
If you have multiple interrupts in the task queue, you can use `Command.resume` with a dictionary mapping
of interrupt ids to resume values to resume multiple interrupts with a single `invoke` / `stream` call.
For example, once your graph has been interrupted (multiple times, theoretically) and is stalled:
```python
resume_map = {
i.interrupt_id: f"human input for prompt {i.value}"
for i in parent.get_state(thread_config).interrupts
}
parent_graph.invoke(Command(resume=resume_map), config=thread_config)
```
## Common Pitfalls
### Side-effects
@@ -691,7 +691,8 @@
"\n",
"checkpointer = InMemorySaver()\n",
"graph = create_react_agent(\n",
" model,\n",
" # limit the output size to ensure consistent behavior\n",
" model.bind(max_tokens=256),\n",
" tools,\n",
" # highlight-next-line\n",
" pre_model_hook=summarization_node,\n",
+2
View File
@@ -102,6 +102,8 @@ CONF = cast(Literal["configurable"], sys.intern("configurable"))
# key for the configurable dict in RunnableConfig
NULL_TASK_ID = sys.intern("00000000-0000-0000-0000-000000000000")
# the task_id to use for writes that are not associated with a task
CONFIG_KEY_RESUME_MAP = sys.intern("__pregel_resume_map")
# holds a mapping of task ns -> resume value for resuming tasks
RESERVED = {
TAG_HIDDEN,
+19 -12
View File
@@ -853,6 +853,7 @@ class Pregel(PregelProtocol):
created_at=None,
parent_config=None,
tasks=(),
interrupts=(),
)
# migrate checkpoint if needed
@@ -937,6 +938,12 @@ class Pregel(PregelProtocol):
next_tasks[tid].writes.append((k, v))
if tasks := [t for t in next_tasks.values() if t.writes]:
apply_writes(saved.checkpoint, channels, tasks, None)
tasks_with_writes = tasks_w_writes(
next_tasks.values(),
saved.pending_writes,
task_states,
self.stream_channels_asis,
)
# assemble the state snapshot
return StateSnapshot(
read_channels(channels, self.stream_channels_asis),
@@ -945,12 +952,8 @@ class Pregel(PregelProtocol):
saved.metadata,
saved.checkpoint["ts"],
patch_checkpoint_map(saved.parent_config, saved.metadata),
tasks_w_writes(
next_tasks.values(),
saved.pending_writes,
task_states,
self.stream_channels_asis,
),
tasks_with_writes,
tuple([i for task in tasks_with_writes for i in task.interrupts]),
)
async def _aprepare_state_snapshot(
@@ -969,6 +972,7 @@ class Pregel(PregelProtocol):
created_at=None,
parent_config=None,
tasks=(),
interrupts=(),
)
# migrate checkpoint if needed
@@ -1056,6 +1060,13 @@ class Pregel(PregelProtocol):
next_tasks[tid].writes.append((k, v))
if tasks := [t for t in next_tasks.values() if t.writes]:
apply_writes(saved.checkpoint, channels, tasks, None)
tasks_with_writes = tasks_w_writes(
next_tasks.values(),
saved.pending_writes,
task_states,
self.stream_channels_asis,
)
# assemble the state snapshot
return StateSnapshot(
read_channels(channels, self.stream_channels_asis),
@@ -1064,12 +1075,8 @@ class Pregel(PregelProtocol):
saved.metadata,
saved.checkpoint["ts"],
patch_checkpoint_map(saved.parent_config, saved.metadata),
tasks_w_writes(
next_tasks.values(),
saved.pending_writes,
task_states,
self.stream_channels_asis,
),
tasks_with_writes,
tuple([i for task in tasks_with_writes for i in task.interrupts]),
)
def get_state(
+16 -1
View File
@@ -40,6 +40,7 @@ from langgraph.constants import (
CONFIG_KEY_CHECKPOINTER,
CONFIG_KEY_PREVIOUS,
CONFIG_KEY_READ,
CONFIG_KEY_RESUME_MAP,
CONFIG_KEY_SCRATCHPAD,
CONFIG_KEY_SEND,
CONFIG_KEY_STORE,
@@ -594,6 +595,8 @@ def prepare_single_task(
config[CONF].get(CONFIG_KEY_SCRATCHPAD),
pending_writes,
task_id,
xxh3_128_hexdigest(task_checkpoint_ns.encode()),
config[CONF].get(CONFIG_KEY_RESUME_MAP),
),
},
),
@@ -704,6 +707,8 @@ def prepare_single_task(
config[CONF].get(CONFIG_KEY_SCRATCHPAD),
pending_writes,
task_id,
xxh3_128_hexdigest(task_checkpoint_ns.encode()),
config[CONF].get(CONFIG_KEY_RESUME_MAP),
),
CONFIG_KEY_PREVIOUS: checkpoint["channel_values"].get(
PREVIOUS, None
@@ -830,6 +835,8 @@ def prepare_single_task(
config[CONF].get(CONFIG_KEY_SCRATCHPAD),
pending_writes,
task_id,
xxh3_128_hexdigest(task_checkpoint_ns.encode()),
config[CONF].get(CONFIG_KEY_RESUME_MAP),
),
CONFIG_KEY_PREVIOUS: checkpoint["channel_values"].get(
PREVIOUS, None
@@ -881,6 +888,8 @@ def _scratchpad(
parent_scratchpad: Optional[PregelScratchpad],
pending_writes: list[PendingWrite],
task_id: str,
namespace_hash: str,
resume_map: Optional[dict[str, Any]],
) -> PregelScratchpad:
if len(pending_writes) > 0:
# find global resume value
@@ -892,6 +901,7 @@ def _scratchpad(
# None cannot be used as a resume value, because it would be difficult to
# distinguish from missing when used over http
null_resume_write = None
# find task-specific resume value
for w in pending_writes:
if w[0] == task_id and w[1] == RESUME:
@@ -901,8 +911,13 @@ def _scratchpad(
break
else:
task_resume_write = []
# clear var
del w
# find namespace and task-specific resume value
if resume_map and namespace_hash in resume_map:
mapped_resume_write = resume_map[namespace_hash]
task_resume_write.append(mapped_resume_write)
else:
null_resume_write = None
task_resume_write = []
+2 -23
View File
@@ -1,12 +1,10 @@
from collections import Counter
from collections.abc import Iterator, Mapping, Sequence
from typing import Any, Literal, Optional, TypeVar, Union
from uuid import UUID
from langchain_core.runnables.utils import AddableDict
from langgraph.channels.base import BaseChannel, EmptyChannelError
from langgraph.checkpoint.base import PendingWrite
from langgraph.constants import (
EMPTY_SEQ,
ERROR,
@@ -24,15 +22,6 @@ from langgraph.pregel.log import logger
from langgraph.types import Command, PregelExecutableTask, Send
def is_task_id(task_id: str) -> bool:
"""Check if a string is a valid task id."""
try:
UUID(task_id)
except Exception:
return False
return True
def read_channel(
channels: Mapping[str, BaseChannel],
chan: str,
@@ -66,9 +55,7 @@ def read_channels(
return values
def map_command(
cmd: Command, pending_writes: list[PendingWrite]
) -> Iterator[tuple[str, str, Any]]:
def map_command(cmd: Command) -> Iterator[tuple[str, str, Any]]:
"""Map input chunk to a sequence of pending writes in the form (channel, value)."""
if cmd.graph == Command.PARENT:
raise InvalidUpdateError("There is no parent graph")
@@ -87,15 +74,7 @@ def map_command(
f"In Command.goto, expected Send/str, got {type(send).__name__}"
)
if cmd.resume is not None:
if isinstance(cmd.resume, dict) and all(is_task_id(k) for k in cmd.resume):
for tid, resume in cmd.resume.items():
existing: list[Any] = next(
(w[2] for w in pending_writes if w[0] == tid and w[1] == RESUME), []
)
existing.append(resume)
yield (tid, RESUME, existing)
else:
yield (NULL_TASK_ID, RESUME, cmd.resume)
yield (NULL_TASK_ID, RESUME, cmd.resume)
if cmd.update:
for k, v in cmd._update_as_tuples():
yield (NULL_TASK_ID, k, v)
+12 -4
View File
@@ -47,6 +47,7 @@ from langgraph.constants import (
CONFIG_KEY_DEDUPE_TASKS,
CONFIG_KEY_DELEGATE,
CONFIG_KEY_ENSURE_LATEST,
CONFIG_KEY_RESUME_MAP,
CONFIG_KEY_RESUMING,
CONFIG_KEY_SCRATCHPAD,
CONFIG_KEY_STREAM,
@@ -112,7 +113,7 @@ from langgraph.pregel.io import (
)
from langgraph.pregel.manager import AsyncChannelsManager, ChannelsManager
from langgraph.pregel.read import PregelNode
from langgraph.pregel.utils import get_new_channel_versions
from langgraph.pregel.utils import get_new_channel_versions, is_xxh3_128_hexdigest
from langgraph.store.base import BaseStore
from langgraph.types import (
All,
@@ -649,14 +650,21 @@ class PregelLoop(LoopProtocol):
# map command to writes
if isinstance(self.input, Command):
if self.input.resume is not None and not self.checkpointer:
if resume_is_map := (
(resume := self.input.resume) is not None
and isinstance(resume, dict)
and all(is_xxh3_128_hexdigest(k) for k in resume)
):
self.config[CONF][CONFIG_KEY_RESUME_MAP] = self.input.resume
if resume is not None and not self.checkpointer:
raise RuntimeError(
"Cannot use Command(resume=...) without checkpointer"
)
writes: defaultdict[str, list[tuple[str, Any]]] = defaultdict(list)
# group writes by task ID
for tid, c, v in map_command(self.input, self.checkpoint_pending_writes):
writes[tid].append((c, v))
for tid, c, v in map_command(cmd=self.input):
if not (c == RESUME and resume_is_map):
writes[tid].append((c, v))
if not writes:
raise EmptyInputError("Received empty Command input")
# save writes
+2 -1
View File
@@ -241,7 +241,7 @@ class RemoteGraph(PregelProtocol):
)
def _create_state_snapshot(self, state: ThreadState) -> StateSnapshot:
tasks = []
tasks: list[PregelTask] = []
for task in state["tasks"]:
interrupts = []
for interrupt in task["interrupts"]:
@@ -289,6 +289,7 @@ class RemoteGraph(PregelProtocol):
if state["parent_checkpoint"]
else None,
tasks=tuple(tasks),
interrupts=tuple([i for task in tasks for i in task.interrupts]),
)
def _get_checkpoint(self, config: Optional[RunnableConfig]) -> Optional[Checkpoint]:
+6
View File
@@ -1,5 +1,6 @@
import ast
import inspect
import re
import textwrap
from typing import Any, Callable, Optional
@@ -207,3 +208,8 @@ class NonLocals(ast.NodeVisitor):
parent = parent.value
if isinstance(parent, ast.Name):
self.loads.add(parent.id + "." + attr_expr)
def is_xxh3_128_hexdigest(value: str) -> bool:
"""Check if the given string matches the format of xxh3_128_hexdigest."""
return bool(re.fullmatch(r"[0-9a-f]{32}", value))
+13 -7
View File
@@ -147,7 +147,7 @@ class Interrupt:
"""Generate a unique ID for the interrupt based on its namespace."""
if self.ns is None:
return "placeholder-id"
return xxh3_128_hexdigest("".join(self.ns).encode())
return xxh3_128_hexdigest("|".join(self.ns).encode())
class StateUpdate(NamedTuple):
@@ -192,19 +192,21 @@ class StateSnapshot(NamedTuple):
"""Snapshot of the state of the graph at the beginning of a step."""
values: Union[dict[str, Any], Any]
"""Current values of channels"""
"""Current values of channels."""
next: tuple[str, ...]
"""The name of the node to execute in each task for this step."""
config: RunnableConfig
"""Config used to fetch this snapshot"""
"""Config used to fetch this snapshot."""
metadata: Optional[CheckpointMetadata]
"""Metadata associated with this snapshot"""
"""Metadata associated with this snapshot."""
created_at: Optional[str]
"""Timestamp of snapshot creation"""
"""Timestamp of snapshot creation."""
parent_config: Optional[RunnableConfig]
"""Config used to fetch the parent snapshot, if any"""
"""Config used to fetch the parent snapshot, if any."""
tasks: tuple[PregelTask, ...]
"""Tasks to execute in this step. If already attempted, may contain an error."""
interrupts: tuple[Interrupt, ...]
"""Interrupts that occurred in this step that are pending resolution."""
class Send:
@@ -294,6 +296,10 @@ class Command(Generic[N], ToolOutputMixin):
- Command.PARENT: closest parent graph
update: update to apply to the graph's state.
resume: value to resume execution with. To be used together with [`interrupt()`][langgraph.types.interrupt].
Can be one of the following:
- mapping of interrupt ids to resume values
- a single value with which to resume the next interrupt
goto: can be one of the following:
- name of the node to navigate to next (any node that belongs to the specified `graph`)
@@ -304,7 +310,7 @@ class Command(Generic[N], ToolOutputMixin):
graph: Optional[str] = None
update: Optional[Any] = None
resume: Optional[Union[Any, dict[str, Any]]] = None
resume: Optional[Union[dict[str, Any], Any]] = None
goto: Union[Send, Sequence[Union[Send, str]], str] = ()
def __repr__(self) -> str:
@@ -63,6 +63,7 @@ def get_expected_history(*, exc_task_results: int = 0) -> list[StateSnapshot]:
}
},
tasks=(),
interrupts=(),
),
StateSnapshot(
values={
@@ -113,6 +114,15 @@ def get_expected_history(*, exc_task_results: int = 0) -> list[StateSnapshot]:
else {"answer": "doc1,doc2,doc3,doc4"},
),
),
interrupts=()
if exc_task_results
else (
Interrupt(
value="",
resumable=True,
ns=[AnyStr("qa:")],
),
),
),
StateSnapshot(
values={
@@ -156,6 +166,7 @@ def get_expected_history(*, exc_task_results: int = 0) -> list[StateSnapshot]:
result=None if exc_task_results else {"docs": ["doc1", "doc2"]},
),
),
interrupts=(),
),
StateSnapshot(
values={"query": "query: what is weather in sf", "docs": []},
@@ -206,6 +217,7 @@ def get_expected_history(*, exc_task_results: int = 0) -> list[StateSnapshot]:
else {"docs": ["doc3", "doc4"]},
),
),
interrupts=(),
),
StateSnapshot(
values={"query": "what is weather in sf", "docs": []},
@@ -245,6 +257,7 @@ def get_expected_history(*, exc_task_results: int = 0) -> list[StateSnapshot]:
else {"query": "query: what is weather in sf"},
),
),
interrupts=(),
),
StateSnapshot(
values={"docs": []},
@@ -276,6 +289,7 @@ def get_expected_history(*, exc_task_results: int = 0) -> list[StateSnapshot]:
result={"query": "what is weather in sf"},
),
),
interrupts=(),
),
]
@@ -1731,6 +1745,7 @@ def test_saved_checkpoint_state_graph(
created_at=AnyStr(),
parent_config=latest_state.parent_config,
tasks=latest_state.tasks,
interrupts=latest_state.interrupts,
)
== history[0]
)
@@ -1802,6 +1817,7 @@ async def test_saved_checkpoint_state_graph_async(
created_at=AnyStr(),
parent_config=latest_state.parent_config,
tasks=latest_state.tasks,
interrupts=latest_state.interrupts,
)
== history[0]
)
+155
View File
@@ -144,6 +144,7 @@ def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[1].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 4, "output": 4, "input": 3},
@@ -165,6 +166,7 @@ def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[2].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 21, "output": 4, "input": 3},
@@ -186,6 +188,7 @@ def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[3].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 21, "output": 4, "input": 20},
@@ -207,6 +210,7 @@ def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[4].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 3, "output": 4, "input": 20},
@@ -228,6 +232,7 @@ def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[5].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 3, "output": 4, "input": 2},
@@ -249,6 +254,7 @@ def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[6].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 3, "input": 2},
@@ -270,6 +276,7 @@ def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[7].config,
interrupts=(),
),
StateSnapshot(
values={"input": 2},
@@ -291,6 +298,7 @@ def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
),
]
@@ -358,6 +366,7 @@ def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[1].config,
interrupts=(),
),
StateSnapshot(
values=5,
@@ -379,6 +388,7 @@ def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[2].config,
interrupts=(),
),
StateSnapshot(
values=4,
@@ -400,6 +410,7 @@ def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[3].config,
interrupts=(),
),
StateSnapshot(
values=3,
@@ -421,6 +432,7 @@ def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[4].config,
interrupts=(),
),
StateSnapshot(
values=2,
@@ -442,6 +454,7 @@ def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[5].config,
interrupts=(),
),
StateSnapshot(
values=1,
@@ -463,6 +476,7 @@ def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[6].config,
interrupts=(),
),
StateSnapshot(
values=0,
@@ -484,6 +498,7 @@ def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
),
]
@@ -776,6 +791,7 @@ def test_conditional_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert (
app_w_interrupt.checkpointer.get_tuple(config).config["configurable"][
@@ -838,6 +854,7 @@ def test_conditional_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -969,6 +986,7 @@ def test_conditional_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# test state get/update methods with interrupt_before
@@ -1035,6 +1053,7 @@ def test_conditional_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
app_w_interrupt.update_state(
@@ -1091,6 +1110,7 @@ def test_conditional_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -1222,6 +1242,7 @@ def test_conditional_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# test re-invoke to continue with interrupt_before
@@ -1288,6 +1309,7 @@ def test_conditional_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -1684,6 +1706,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
with assert_ctx_once():
@@ -1737,6 +1760,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
with assert_ctx_once():
@@ -1824,6 +1848,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# test state get/update methods with interrupt_before
@@ -1886,6 +1911,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
app_w_interrupt.update_state(
@@ -1938,6 +1964,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -2023,6 +2050,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# test w interrupt before all
@@ -2066,6 +2094,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -2116,6 +2145,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -2187,6 +2217,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -2260,6 +2291,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -2331,6 +2363,7 @@ def test_conditional_state_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -3071,6 +3104,7 @@ def test_state_graph_packets(
if "shallow" in checkpointer_name
else [*app_w_interrupt.checkpointer.list(config, limit=2)][-1].config
),
interrupts=(),
)
# modify ai message
@@ -3135,6 +3169,7 @@ def test_state_graph_packets(
if "shallow" in checkpointer_name
else [*app_w_interrupt.checkpointer.list(config, limit=2)][-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -3254,6 +3289,7 @@ def test_state_graph_packets(
if "shallow" in checkpointer_name
else [*app_w_interrupt.checkpointer.list(config, limit=2)][-1].config
),
interrupts=(),
)
app_w_interrupt.update_state(
@@ -3315,6 +3351,7 @@ def test_state_graph_packets(
if "shallow" in checkpointer_name
else [*app_w_interrupt.checkpointer.list(config, limit=2)][-1].config
),
interrupts=(),
)
# interrupt before tools
@@ -3404,6 +3441,7 @@ def test_state_graph_packets(
if "shallow" in checkpointer_name
else [*app_w_interrupt.checkpointer.list(config, limit=2)][-1].config
),
interrupts=(),
)
# modify ai message
@@ -3462,6 +3500,7 @@ def test_state_graph_packets(
if "shallow" in checkpointer_name
else [*app_w_interrupt.checkpointer.list(config, limit=2)][-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -3579,6 +3618,7 @@ def test_state_graph_packets(
if "shallow" in checkpointer_name
else [*app_w_interrupt.checkpointer.list(config, limit=2)][-1].config
),
interrupts=(),
)
app_w_interrupt.update_state(
@@ -3640,6 +3680,7 @@ def test_state_graph_packets(
if "shallow" in checkpointer_name
else [*app_w_interrupt.checkpointer.list(config, limit=2)][-1].config
),
interrupts=(),
)
@@ -3936,6 +3977,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# modify ai message
@@ -3987,6 +4029,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -4080,6 +4123,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
app_w_interrupt.update_state(
@@ -4131,6 +4175,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
app_w_interrupt = workflow.compile(
@@ -4206,6 +4251,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# modify ai message
@@ -4263,6 +4309,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -4356,6 +4403,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
app_w_interrupt.update_state(
@@ -4408,6 +4456,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# add an extra message as if it came from "tools" node
@@ -4460,6 +4509,7 @@ def test_message_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
@@ -4759,6 +4809,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# modify ai message
@@ -4810,6 +4861,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -4904,6 +4956,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
app_w_interrupt.update_state(
@@ -4956,6 +5009,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
app_w_interrupt = workflow.compile(
@@ -5031,6 +5085,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# modify ai message
@@ -5088,6 +5143,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config)] == [
@@ -5182,6 +5238,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
app_w_interrupt.update_state(
@@ -5233,6 +5290,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# add an extra message as if it came from "tools" node
@@ -5285,6 +5343,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(app_w_interrupt.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# create new graph with one more state key, reuse previous thread history
@@ -5368,6 +5427,7 @@ def test_root_graph(
if "shallow" in checkpointer_name
else list(new_app.checkpointer.list(config, limit=2))[-1].config
),
interrupts=(),
)
# new input is merged to old state
@@ -5775,6 +5835,13 @@ def test_dynamic_interrupt(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(
Interrupt(
value="Just because...",
resumable=True,
ns=[AnyStr("tool_two:")],
),
),
)
# clear the interrupt and next tasks
tool_two.update_state(thread1, None, as_node=END)
@@ -5803,6 +5870,7 @@ def test_dynamic_interrupt(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
@@ -5964,6 +6032,13 @@ def test_copy_checkpoint(
if "shallow" in checkpointer_name
else [*tool_two.checkpointer.list(thread1, limit=2)][-1].config
),
interrupts=(
Interrupt(
value="Just because...",
resumable=True,
ns=[AnyStr("tool_two:")],
),
),
)
if "shallow" in checkpointer_name:
@@ -6009,6 +6084,7 @@ def test_copy_checkpoint(
parent_config=(
[*tool_two.checkpointer.list(thread1, limit=2)][-1].parent_config
),
interrupts=(),
)
@@ -6184,6 +6260,13 @@ def test_dynamic_interrupt_subgraph(
)
)[-1].config
),
interrupts=(
Interrupt(
value="Just because...",
resumable=True,
ns=[AnyStr("tool_two:"), AnyStr("do:")],
),
),
)
# clear the interrupt and next tasks
tool_two.update_state(thread1, None, as_node=END)
@@ -6216,6 +6299,7 @@ def test_dynamic_interrupt_subgraph(
)
)[-1].config
),
interrupts=(),
)
@@ -6334,6 +6418,7 @@ def test_start_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
# resume, for same result as above
assert tool_two.invoke(None, thread1, debug=1) == {
@@ -6365,6 +6450,7 @@ def test_start_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
thread2 = {"configurable": {"thread_id": "2", "assistant_id": "a"}}
@@ -6398,6 +6484,7 @@ def test_start_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread2, limit=2))[-1].config
),
interrupts=(),
)
# resume, for same result as above
assert tool_two.invoke(None, thread2, debug=1) == {
@@ -6429,6 +6516,7 @@ def test_start_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread2, limit=2))[-1].config
),
interrupts=(),
)
thread3 = {"configurable": {"thread_id": "3", "assistant_id": "b"}}
@@ -6462,6 +6550,7 @@ def test_start_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread3, limit=2))[-1].config
),
interrupts=(),
)
# update state
tool_two.update_state(thread3, {"my_key": "key"}) # appends to my_key
@@ -6490,6 +6579,7 @@ def test_start_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread3, limit=2))[-1].config
),
interrupts=(),
)
# resume, for same result as above
assert tool_two.invoke(None, thread3, debug=1) == {
@@ -6521,6 +6611,7 @@ def test_start_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread3, limit=2))[-1].config
),
interrupts=(),
)
@@ -6900,6 +6991,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
# resume, for same result as above
assert tool_two.invoke(None, thread1, debug=1) == {
@@ -6930,6 +7022,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
thread2 = {"configurable": {"thread_id": "2"}}
@@ -6962,6 +7055,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread2, limit=2))[-1].config
),
interrupts=(),
)
# resume, for same result as above
assert tool_two.invoke(None, thread2, debug=1) == {
@@ -6992,6 +7086,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread2, limit=2))[-1].config
),
interrupts=(),
)
tool_two = tool_two_graph.compile(
@@ -7032,6 +7127,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
# update state
@@ -7063,6 +7159,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
tool_two = tool_two_graph.compile(
@@ -7103,6 +7200,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
# resume, for same result as above
assert tool_two.invoke(None, thread1, debug=1) == {
@@ -7133,6 +7231,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread1, limit=2))[-1].config
),
interrupts=(),
)
thread2 = {"configurable": {"thread_id": "22"}}
@@ -7165,6 +7264,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread2, limit=2))[-1].config
),
interrupts=(),
)
# resume, for same result as above
assert tool_two.invoke(None, thread2, debug=1) == {
@@ -7195,6 +7295,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread2, limit=2))[-1].config
),
interrupts=(),
)
thread3 = {"configurable": {"thread_id": "23"}}
@@ -7221,6 +7322,7 @@ def test_branch_then(
"thread_id": "23",
},
parent_config=None,
interrupts=(),
)
# run from this point
assert tool_two.invoke(None, thread3) == {
@@ -7252,6 +7354,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread3, limit=2))[-1].config
),
interrupts=(),
)
# resume, for same result as above
assert tool_two.invoke(None, thread3, debug=1) == {
@@ -7282,6 +7385,7 @@ def test_branch_then(
if "shallow" in checkpointer_name
else list(tool_two.checkpointer.list(thread3, limit=2))[-1].config
),
interrupts=(),
)
@@ -7423,6 +7527,7 @@ def test_send_dedupe_on_resume(
}
},
tasks=(),
interrupts=(),
),
StateSnapshot(
values=[
@@ -7469,6 +7574,7 @@ def test_send_dedupe_on_resume(
result=["3"],
),
),
interrupts=(),
),
StateSnapshot(
values=[
@@ -7536,6 +7642,7 @@ def test_send_dedupe_on_resume(
result=["3"],
),
),
interrupts=(Interrupt(value="Bahh", resumable=False, ns=None),),
),
StateSnapshot(
values=["0", "1"],
@@ -7591,6 +7698,7 @@ def test_send_dedupe_on_resume(
result=["3.1"],
),
),
interrupts=(),
),
StateSnapshot(
values=["0"],
@@ -7628,6 +7736,7 @@ def test_send_dedupe_on_resume(
result=["1"],
),
),
interrupts=(),
),
StateSnapshot(
values=[],
@@ -7648,6 +7757,7 @@ def test_send_dedupe_on_resume(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -7762,6 +7872,7 @@ def test_nested_graph_state(
}
}
),
interrupts=(),
)
# now, get_state with subgraphs state
assert app.get_state(config, subgraphs=True) == StateSnapshot(
@@ -7828,6 +7939,7 @@ def test_nested_graph_state(
}
}
),
interrupts=(),
),
),
),
@@ -7858,6 +7970,7 @@ def test_nested_graph_state(
}
}
),
interrupts=(),
)
# get_state_history returns outer graph checkpoints
history = list(app.get_state_history(config))
@@ -7904,6 +8017,7 @@ def test_nested_graph_state(
}
}
),
interrupts=(),
),
StateSnapshot(
values={"my_key": "my value"},
@@ -7938,6 +8052,7 @@ def test_nested_graph_state(
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={},
@@ -7966,6 +8081,7 @@ def test_nested_graph_state(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
),
]
@@ -8022,6 +8138,7 @@ def test_nested_graph_state(
}
}
),
interrupts=(),
tasks=(PregelTask(AnyStr(), "inner_2", (PULL, "inner_2")),),
),
StateSnapshot(
@@ -8071,6 +8188,7 @@ def test_nested_graph_state(
},
),
),
interrupts=(),
),
StateSnapshot(
values={},
@@ -8099,6 +8217,7 @@ def test_nested_graph_state(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
tasks=(
PregelTask(
AnyStr(),
@@ -8150,6 +8269,7 @@ def test_nested_graph_state(
}
}
),
interrupts=(),
)
# test full history at the end
actual_history = list(app.get_state_history(config))
@@ -8186,6 +8306,7 @@ def test_nested_graph_state(
}
}
),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value here and there"},
@@ -8220,6 +8341,7 @@ def test_nested_graph_state(
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -8257,6 +8379,7 @@ def test_nested_graph_state(
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={"my_key": "my value"},
@@ -8291,6 +8414,7 @@ def test_nested_graph_state(
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={},
@@ -8319,6 +8443,7 @@ def test_nested_graph_state(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
),
]
if "shallow" in checkpointer_name:
@@ -8439,6 +8564,7 @@ def test_doubly_nested_graph_state(
}
}
),
interrupts=(),
)
child_state = app.get_state(outer_state.tasks[0].state)
assert child_state == StateSnapshot(
@@ -8500,6 +8626,7 @@ def test_doubly_nested_graph_state(
}
}
),
interrupts=(),
)
grandchild_state = app.get_state(child_state.tasks[0].state)
assert grandchild_state == StateSnapshot(
@@ -8562,6 +8689,7 @@ def test_doubly_nested_graph_state(
}
}
),
interrupts=(),
)
# get state with subgraphs
assert app.get_state(config, subgraphs=True) == StateSnapshot(
@@ -8649,6 +8777,7 @@ def test_doubly_nested_graph_state(
}
}
),
interrupts=(),
),
),
),
@@ -8690,6 +8819,7 @@ def test_doubly_nested_graph_state(
}
}
),
interrupts=(),
),
),
),
@@ -8720,6 +8850,7 @@ def test_doubly_nested_graph_state(
}
}
),
interrupts=(),
)
# # resume
assert [c for c in app.stream(None, config, subgraphs=True)] == [
@@ -8767,6 +8898,7 @@ def test_doubly_nested_graph_state(
}
}
),
interrupts=(),
)
)
@@ -8804,6 +8936,7 @@ def test_doubly_nested_graph_state(
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value here and there"},
@@ -8838,6 +8971,7 @@ def test_doubly_nested_graph_state(
result={"my_key": "hi my value here and there and back again"},
),
),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -8878,6 +9012,7 @@ def test_doubly_nested_graph_state(
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={"my_key": "my value"},
@@ -8912,6 +9047,7 @@ def test_doubly_nested_graph_state(
result={"my_key": "hi my value"},
),
),
interrupts=(),
),
StateSnapshot(
values={},
@@ -8932,6 +9068,7 @@ def test_doubly_nested_graph_state(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -8982,6 +9119,7 @@ def test_doubly_nested_graph_state(
}
},
tasks=(),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -9033,6 +9171,7 @@ def test_doubly_nested_graph_state(
result={"my_key": "hi my value here and there"},
),
),
interrupts=(),
),
StateSnapshot(
values={},
@@ -9061,6 +9200,7 @@ def test_doubly_nested_graph_state(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -9127,6 +9267,7 @@ def test_doubly_nested_graph_state(
}
},
tasks=(),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value here"},
@@ -9188,6 +9329,7 @@ def test_doubly_nested_graph_state(
result={"my_key": "hi my value here and there"},
),
),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -9249,6 +9391,7 @@ def test_doubly_nested_graph_state(
result={"my_key": "hi my value here"},
),
),
interrupts=(),
),
StateSnapshot(
values={},
@@ -9289,6 +9432,7 @@ def test_doubly_nested_graph_state(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -9565,6 +9709,7 @@ def test_send_react_interrupt(
}
}
),
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -9628,6 +9773,7 @@ def test_send_react_interrupt(
}
}
),
interrupts=(),
tasks=(),
)
@@ -9725,6 +9871,7 @@ def test_send_react_interrupt(
}
}
),
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -9816,6 +9963,7 @@ def test_send_react_interrupt(
}
}
),
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -10046,6 +10194,7 @@ def test_send_react_interrupt_control(
result=None,
),
),
interrupts=(),
)
# remove the tool call, clearing the pending task
@@ -10098,6 +10247,7 @@ def test_send_react_interrupt_control(
}
}
),
interrupts=(),
tasks=(),
)
@@ -10300,6 +10450,7 @@ def test_weather_subgraph(
},
),
),
interrupts=(),
)
# update
@@ -10442,9 +10593,11 @@ def test_weather_subgraph(
path=(PULL, "weather_node"),
),
),
interrupts=(),
),
),
),
interrupts=(),
)
graph.update_state(
state.tasks[0].state.config,
@@ -10547,10 +10700,12 @@ def test_weather_subgraph(
}
}
),
interrupts=(),
tasks=(),
),
),
),
interrupts=(),
)
assert [
c
@@ -141,6 +141,7 @@ async def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[1].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 4, "output": 4, "input": 3},
@@ -164,6 +165,7 @@ async def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[2].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 21, "output": 4, "input": 3},
@@ -187,6 +189,7 @@ async def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[3].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 21, "output": 4, "input": 20},
@@ -208,6 +211,7 @@ async def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[4].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 3, "output": 4, "input": 20},
@@ -231,6 +235,7 @@ async def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[5].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 3, "output": 4, "input": 2},
@@ -252,6 +257,7 @@ async def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[6].config,
interrupts=(),
),
StateSnapshot(
values={"inbox": 3, "input": 2},
@@ -275,6 +281,7 @@ async def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=history[7].config,
interrupts=(),
),
StateSnapshot(
values={"input": 2},
@@ -298,6 +305,7 @@ async def test_invoke_two_processes_in_out_interrupt(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
),
]
@@ -374,6 +382,7 @@ async def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[1].config,
interrupts=(),
),
StateSnapshot(
values=5,
@@ -395,6 +404,7 @@ async def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[2].config,
interrupts=(),
),
StateSnapshot(
values=4,
@@ -416,6 +426,7 @@ async def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[3].config,
interrupts=(),
),
StateSnapshot(
values=3,
@@ -437,6 +448,7 @@ async def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[4].config,
interrupts=(),
),
StateSnapshot(
values=2,
@@ -458,6 +470,7 @@ async def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[5].config,
interrupts=(),
),
StateSnapshot(
values=1,
@@ -479,6 +492,7 @@ async def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=history[6].config,
interrupts=(),
),
StateSnapshot(
values=0,
@@ -502,6 +516,7 @@ async def test_fork_always_re_runs_nodes(
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
),
]
@@ -858,6 +873,7 @@ async def test_conditional_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
await app_w_interrupt.aupdate_state(
@@ -916,6 +932,7 @@ async def test_conditional_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
assert [c async for c in app_w_interrupt.astream(None, config)] == [
@@ -1049,6 +1066,7 @@ async def test_conditional_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
# test state get/update methods with interrupt_before
@@ -1124,6 +1142,7 @@ async def test_conditional_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
await app_w_interrupt.aupdate_state(
@@ -1182,6 +1201,7 @@ async def test_conditional_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
assert [c async for c in app_w_interrupt.astream(None, config)] == [
@@ -1315,6 +1335,7 @@ async def test_conditional_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
# test re-invoke to continue with interrupt_before
@@ -1390,6 +1411,7 @@ async def test_conditional_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
assert [c async for c in app_w_interrupt.astream(None, config)] == [
@@ -1819,6 +1841,7 @@ async def test_conditional_graph_state(
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
async with assert_ctx_once():
@@ -1874,6 +1897,7 @@ async def test_conditional_graph_state(
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
async with assert_ctx_once():
@@ -1963,6 +1987,7 @@ async def test_conditional_graph_state(
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
# test state get/update methods with interrupt_before
@@ -2031,6 +2056,7 @@ async def test_conditional_graph_state(
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
await app_w_interrupt.aupdate_state(
@@ -2085,6 +2111,7 @@ async def test_conditional_graph_state(
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
assert [c async for c in app_w_interrupt.astream(None, config)] == [
@@ -2172,6 +2199,7 @@ async def test_conditional_graph_state(
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
@@ -2796,6 +2824,7 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
# modify ai message
@@ -2854,6 +2883,7 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
assert [c async for c in app_w_interrupt.astream(None, config)] == [
@@ -2968,6 +2998,7 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
await app_w_interrupt.aupdate_state(
@@ -3022,6 +3053,7 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
# interrupt before tools
@@ -3109,6 +3141,7 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
# modify ai message
@@ -3167,6 +3200,7 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
assert [c async for c in app_w_interrupt.astream(None, config)] == [
@@ -3281,6 +3315,7 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
await app_w_interrupt.aupdate_state(
@@ -3335,6 +3370,7 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
@@ -3600,6 +3636,7 @@ async def test_message_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
# modify ai message
@@ -3654,6 +3691,7 @@ async def test_message_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
assert [c async for c in app_w_interrupt.astream(None, config)] == [
@@ -3744,6 +3782,7 @@ async def test_message_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
await app_w_interrupt.aupdate_state(
@@ -3792,6 +3831,7 @@ async def test_message_graph(checkpointer_name: str) -> None:
c async for c in app_w_interrupt.checkpointer.alist(config, limit=2)
][-1].config
),
interrupts=(),
)
@@ -4131,6 +4171,7 @@ async def test_start_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
# resume, for same result as above
assert await tool_two.ainvoke(None, thread1, debug=1) == {
@@ -4164,6 +4205,7 @@ async def test_start_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
thread2 = {"configurable": {"thread_id": "2", "assistant_id": "a"}}
@@ -4199,6 +4241,7 @@ async def test_start_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
# resume, for same result as above
assert await tool_two.ainvoke(None, thread2, debug=1) == {
@@ -4232,6 +4275,7 @@ async def test_start_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
thread3 = {"configurable": {"thread_id": "3", "assistant_id": "b"}}
@@ -4267,6 +4311,7 @@ async def test_start_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
# update state
await tool_two.aupdate_state(thread3, {"my_key": "key"}) # appends to my_key
@@ -4297,6 +4342,7 @@ async def test_start_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
# resume, for same result as above
assert await tool_two.ainvoke(None, thread3, debug=1) == {
@@ -4330,6 +4376,7 @@ async def test_start_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
@@ -4872,6 +4919,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
# resume, for same result as above
assert await tool_two.ainvoke(None, thread1, debug=1) == {
@@ -4904,6 +4952,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
thread2 = {"configurable": {"thread_id": "12"}}
@@ -4938,6 +4987,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
# resume, for same result as above
assert await tool_two.ainvoke(None, thread2, debug=1) == {
@@ -4970,6 +5020,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
tool_two = tool_two_graph.compile(
@@ -5012,6 +5063,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
# resume, for same result as above
assert await tool_two.ainvoke(None, thread1, debug=1) == {
@@ -5044,6 +5096,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
thread2 = {"configurable": {"thread_id": "22"}}
@@ -5078,6 +5131,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
# resume, for same result as above
assert await tool_two.ainvoke(None, thread2, debug=1) == {
@@ -5110,6 +5164,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
thread3 = {"configurable": {"thread_id": "23"}}
@@ -5132,6 +5187,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
"thread_id": "23",
},
parent_config=None,
interrupts=(),
)
# run from this point
assert await tool_two.ainvoke(None, thread3) == {
@@ -5159,6 +5215,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
"thread_id": "23",
},
parent_config=(None if "shallow" in checkpointer_name else uconfig),
interrupts=(),
)
# resume, for same result as above
assert await tool_two.ainvoke(None, thread3, debug=1) == {
@@ -5191,6 +5248,7 @@ async def test_branch_then(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
@@ -5287,6 +5345,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
)
# now, get_state with subgraphs state
assert await app.aget_state(config, subgraphs=True) == StateSnapshot(
@@ -5353,6 +5412,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
),
),
),
@@ -5383,6 +5443,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
)
# get_state_history returns outer graph checkpoints
history = [c async for c in app.aget_state_history(config)]
@@ -5429,6 +5490,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
),
StateSnapshot(
values={"my_key": "my value"},
@@ -5463,6 +5525,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={},
@@ -5491,6 +5554,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
),
]
@@ -5550,6 +5614,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
}
),
tasks=(PregelTask(AnyStr(), "inner_2", (PULL, "inner_2")),),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -5598,6 +5663,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
},
),
),
interrupts=(),
),
StateSnapshot(
values={},
@@ -5634,6 +5700,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
result={"my_key": "hi my value"},
),
),
interrupts=(),
),
]
@@ -5677,6 +5744,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
)
# test full history at the end
actual_history = [c async for c in app.aget_state_history(config)]
@@ -5715,6 +5783,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value here and there"},
@@ -5749,6 +5818,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -5789,6 +5859,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={"my_key": "my value"},
@@ -5823,6 +5894,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={},
@@ -5851,6 +5923,7 @@ async def test_nested_graph_state(checkpointer_name: str) -> None:
},
created_at=AnyStr(),
parent_config=None,
interrupts=(),
),
]
if "shallow" in checkpointer_name:
@@ -5970,6 +6043,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
)
child_state = await app.aget_state(outer_state.tasks[0].state)
assert child_state == StateSnapshot(
@@ -6031,6 +6105,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
)
grandchild_state = await app.aget_state(child_state.tasks[0].state)
assert grandchild_state == StateSnapshot(
@@ -6095,6 +6170,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
)
# get state with subgraphs
assert await app.aget_state(config, subgraphs=True) == StateSnapshot(
@@ -6186,6 +6262,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
),
),
),
@@ -6229,6 +6306,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
),
),
),
@@ -6259,6 +6337,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
)
# resume
assert [c async for c in app.astream(None, config, subgraphs=True)] == [
@@ -6311,6 +6390,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
}
),
interrupts=(),
)
)
@@ -6352,6 +6432,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value here and there"},
@@ -6383,6 +6464,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
id=AnyStr(), name="parent_2", path=(PULL, "parent_2")
),
),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -6422,6 +6504,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
"checkpoint_id": AnyStr(),
}
},
interrupts=(),
),
StateSnapshot(
values={"my_key": "my value"},
@@ -6453,6 +6536,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
id=AnyStr(), name="parent_1", path=(PULL, "parent_1")
),
),
interrupts=(),
),
StateSnapshot(
values={},
@@ -6478,6 +6562,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
id=AnyStr(), name="__start__", path=(PULL, "__start__")
),
),
interrupts=(),
),
][0]
)
@@ -6523,6 +6608,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
},
tasks=(),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -6574,6 +6660,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
result={"my_key": "hi my value here and there"},
),
),
interrupts=(),
),
StateSnapshot(
values={},
@@ -6610,6 +6697,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
result={"my_key": "hi my value"},
),
),
interrupts=(),
),
]
# get grandchild graph history
@@ -6674,6 +6762,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
}
},
tasks=(),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value here"},
@@ -6737,6 +6826,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
result={"my_key": "hi my value here and there"},
),
),
interrupts=(),
),
StateSnapshot(
values={"my_key": "hi my value"},
@@ -6800,6 +6890,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
result={"my_key": "hi my value here"},
),
),
interrupts=(),
),
StateSnapshot(
values={},
@@ -6850,6 +6941,7 @@ async def test_doubly_nested_graph_state(checkpointer_name: str) -> None:
result={"my_key": "hi my value"},
),
),
interrupts=(),
),
]
@@ -7131,6 +7223,7 @@ async def test_weather_subgraph(
},
),
),
interrupts=(),
)
# confirm that list() delegates to alist() correctly
assert await asyncio.to_thread(get_first_in_list) == state
@@ -7270,6 +7363,7 @@ async def test_weather_subgraph(
}
}
),
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -7280,6 +7374,7 @@ async def test_weather_subgraph(
),
),
),
interrupts=(),
)
await graph.aupdate_state(
state.tasks[0].state.config,
@@ -7319,6 +7414,7 @@ async def test_weather_subgraph(
}
}
),
interrupts=(),
tasks=(
PregelTask(
id=AnyStr(),
@@ -7385,6 +7481,7 @@ async def test_weather_subgraph(
}
),
tasks=(),
interrupts=(),
),
),
),
+2
View File
@@ -2491,6 +2491,7 @@ def test_in_one_fan_out_state_graph_waiting_edge(
"thread_id": "2",
},
parent_config=expected_parent_config,
interrupts=(),
)
assert [c for c in app_w_interrupt.stream(None, config, debug=1)] == [
@@ -5224,6 +5225,7 @@ def test_parent_command(request: pytest.FixtureRequest, checkpointer_name: str)
}
),
tasks=(),
interrupts=(),
)
+38
View File
@@ -650,6 +650,13 @@ async def test_dynamic_interrupt(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(
Interrupt(
value="Just because...",
resumable=True,
ns=[AnyStr("tool_two:")],
),
),
)
# clear the interrupt and next tasks
@@ -676,6 +683,7 @@ async def test_dynamic_interrupt(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(),
)
@@ -848,6 +856,13 @@ async def test_dynamic_interrupt_subgraph(checkpointer_name: str) -> None:
c async for c in tool_two.checkpointer.alist(thread1root, limit=2)
][-1].config
),
interrupts=(
Interrupt(
value="Just because...",
resumable=True,
ns=[AnyStr("tool_two:"), AnyStr("do:")],
),
),
)
# clear the interrupt and next tasks
@@ -874,6 +889,7 @@ async def test_dynamic_interrupt_subgraph(checkpointer_name: str) -> None:
c async for c in tool_two.checkpointer.alist(thread1root, limit=2)
][-1].config
),
interrupts=(),
)
@@ -1047,6 +1063,13 @@ async def test_copy_checkpoint(checkpointer_name: str) -> None:
-1
].config
),
interrupts=(
Interrupt(
value="Just because...",
resumable=True,
ns=[AnyStr("tool_two:")],
),
),
)
if "shallow" in checkpointer_name:
@@ -1088,6 +1111,7 @@ async def test_copy_checkpoint(checkpointer_name: str) -> None:
-1
].parent_config
),
interrupts=(),
)
@@ -2900,6 +2924,7 @@ async def test_send_dedupe_on_resume(
}
},
tasks=(),
interrupts=(),
),
StateSnapshot(
values=[
@@ -2946,6 +2971,7 @@ async def test_send_dedupe_on_resume(
result=["3"],
),
),
interrupts=(),
),
StateSnapshot(
values=[
@@ -3013,6 +3039,7 @@ async def test_send_dedupe_on_resume(
result=["3"],
),
),
interrupts=(Interrupt(value="Bahh", resumable=False, ns=None),),
),
StateSnapshot(
values=["0", "1"],
@@ -3068,6 +3095,7 @@ async def test_send_dedupe_on_resume(
result=["3.1"],
),
),
interrupts=(),
),
StateSnapshot(
values=["0"],
@@ -3105,6 +3133,7 @@ async def test_send_dedupe_on_resume(
result=["1"],
),
),
interrupts=(),
),
StateSnapshot(
values=[],
@@ -3136,6 +3165,7 @@ async def test_send_dedupe_on_resume(
result=["0"],
),
),
interrupts=(),
),
]
if checkpoint_during:
@@ -3338,6 +3368,7 @@ async def test_send_react_interrupt(checkpointer_name: str) -> None:
result=None,
),
),
interrupts=(),
)
# remove the tool call, clearing the pending task
@@ -3391,6 +3422,7 @@ async def test_send_react_interrupt(checkpointer_name: str) -> None:
}
),
tasks=(),
interrupts=(),
)
# tool call not executed
@@ -3496,6 +3528,7 @@ async def test_send_react_interrupt(checkpointer_name: str) -> None:
result=None,
),
),
interrupts=(),
)
# replace the tool call, should clear previous send, create new one
@@ -3587,6 +3620,7 @@ async def test_send_react_interrupt(checkpointer_name: str) -> None:
result=None,
),
),
interrupts=(),
)
# prev tool call not executed, new tool call is
@@ -3804,6 +3838,7 @@ async def test_send_react_interrupt_control(
result=None,
),
),
interrupts=(),
)
# remove the tool call, clearing the pending task
@@ -3857,6 +3892,7 @@ async def test_send_react_interrupt_control(
}
),
tasks=(),
interrupts=(),
)
# tool call not executed
@@ -4937,6 +4973,7 @@ async def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class(
}
}
),
interrupts=(),
)
async with assert_ctx_once():
@@ -6635,6 +6672,7 @@ async def test_parent_command(checkpointer_name: str) -> None:
}
),
tasks=(),
interrupts=(),
)
@@ -183,6 +183,7 @@ def test_get_state():
created_at="timestamp",
parent_config=None,
tasks=(),
interrupts=(),
)
@@ -240,6 +241,7 @@ async def test_aget_state():
}
},
tasks=(),
interrupts=(),
)
@@ -290,6 +292,7 @@ def test_get_state_history():
created_at="timestamp",
parent_config=None,
tasks=(),
interrupts=(),
)
@@ -343,6 +346,7 @@ async def test_aget_state_history():
created_at="timestamp",
parent_config=None,
tasks=(),
interrupts=(),
)