tests: add a test for interrupt() w/ functional API (#3065)

This commit is contained in:
Nuno Campos
2025-01-16 10:04:17 -08:00
committed by GitHub
3 changed files with 122 additions and 2 deletions
+2 -2
View File
@@ -345,11 +345,11 @@ class PregelLoop(LoopProtocol):
(PUSH, task.path, write_idx, task.id, call),
None,
checkpoint=self.checkpoint,
pending_writes=[(task.id, *w) for w in task.writes],
pending_writes=self.checkpoint_pending_writes,
processes=self.nodes,
channels=self.channels,
managed=self.managed,
config=self.config,
config=task.config,
step=self.step,
for_execution=True,
store=self.store,
+63
View File
@@ -4949,6 +4949,69 @@ def test_interrupt_loop(request: pytest.FixtureRequest, checkpointer_name: str):
]
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_SYNC)
def test_interrupt_functional(
request: pytest.FixtureRequest, checkpointer_name: str
) -> None:
checkpointer: BaseCheckpointSaver = request.getfixturevalue(
f"checkpointer_{checkpointer_name}"
)
@task
def foo(state: dict) -> dict:
return {"a": state["a"] + "foo"}
@task
def bar(state: dict) -> dict:
return {"a": state["a"] + "bar", "b": state["b"]}
@entrypoint(checkpointer=checkpointer)
def graph(inputs: dict) -> dict:
fut_foo = foo(inputs)
value = interrupt("Provide value for bar:")
bar_input = {**fut_foo.result(), "b": value}
fut_bar = bar(bar_input)
return fut_bar.result()
config = {"configurable": {"thread_id": "1"}}
# First run, interrupted at bar
graph.invoke({"a": ""}, config)
# Resume with an answer
res = graph.invoke(Command(resume="bar"), config)
assert res == {"a": "foobar", "b": "bar"}
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_SYNC)
def test_interrupt_task_functional(
request: pytest.FixtureRequest, checkpointer_name: str
) -> None:
checkpointer: BaseCheckpointSaver = request.getfixturevalue(
f"checkpointer_{checkpointer_name}"
)
@task
def foo(state: dict) -> dict:
return {"a": state["a"] + "foo"}
@task
def bar(state: dict) -> dict:
value = interrupt("Provide value for bar:")
return {"a": state["a"] + value}
@entrypoint(checkpointer=checkpointer)
def graph(inputs: dict) -> dict:
fut_foo = foo(inputs)
fut_bar = bar(fut_foo.result())
return fut_bar.result()
config = {"configurable": {"thread_id": "1"}}
# First run, interrupted at bar
graph.invoke({"a": ""}, config)
# Resume with an answer
res = graph.invoke(Command(resume="bar"), config)
assert res == {"a": "foobar"}
def test_root_mixed_return() -> None:
def my_node(state: list[str]):
return [Command(update=["a"]), ["b"]]
+57
View File
@@ -6281,6 +6281,63 @@ async def test_interrupt_loop(checkpointer_name: str):
]
@NEEDS_CONTEXTVARS
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_ASYNC)
async def test_interrupt_functional(checkpointer_name: str) -> None:
@task
async def foo(state: dict) -> dict:
return {"a": state["a"] + "foo"}
@task
async def bar(state: dict) -> dict:
return {"a": state["a"] + "bar", "b": state["b"]}
async with awith_checkpointer(checkpointer_name) as checkpointer:
@entrypoint(checkpointer=checkpointer)
async def graph(inputs: dict) -> dict:
foo_result = await foo(inputs)
value = interrupt("Provide value for bar:")
bar_input = {**foo_result, "b": value}
bar_result = await bar(bar_input)
return bar_result
config = {"configurable": {"thread_id": "1"}}
# First run, interrupted at bar
await graph.ainvoke({"a": ""}, config)
# Resume with an answer
res = await graph.ainvoke(Command(resume="bar"), config)
assert res == {"a": "foobar", "b": "bar"}
@NEEDS_CONTEXTVARS
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_ASYNC)
async def test_interrupt_task_functional(checkpointer_name: str) -> None:
@task
async def foo(state: dict) -> dict:
return {"a": state["a"] + "foo"}
@task
async def bar(state: dict) -> dict:
value = interrupt("Provide value for bar:")
return {"a": state["a"] + value}
async with awith_checkpointer(checkpointer_name) as checkpointer:
@entrypoint(checkpointer=checkpointer)
async def graph(inputs: dict) -> dict:
foo_result = await foo(inputs)
bar_result = await bar(foo_result)
return bar_result
config = {"configurable": {"thread_id": "1"}}
# First run, interrupted at bar
await graph.ainvoke({"a": ""}, config)
# Resume with an answer
res = await graph.ainvoke(Command(resume="bar"), config)
assert res == {"a": "foobar"}
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_ASYNC)
async def test_command_with_static_breakpoints(checkpointer_name: str) -> None:
"""Test that we can use Command to resume and update with static breakpoints."""