diff --git a/libs/langgraph/langgraph/pregel/io.py b/libs/langgraph/langgraph/pregel/io.py index d3fbf1023..363ff375b 100644 --- a/libs/langgraph/langgraph/pregel/io.py +++ b/libs/langgraph/langgraph/pregel/io.py @@ -10,6 +10,7 @@ from langgraph.constants import ( EMPTY_SEQ, ERROR, INTERRUPT, + MISSING, NULL_TASK_ID, RESUME, RETURN, @@ -173,7 +174,8 @@ def map_output_updates( return updated: list[tuple[str, Any]] = [] for task, writes in output_tasks: - if rtn := next((value for chan, value in writes if chan == RETURN), None): + rtn = next((value for chan, value in writes if chan == RETURN), MISSING) + if rtn is not MISSING: updated.append((task.name, rtn)) elif isinstance(output_channels, str): updated.extend( diff --git a/libs/langgraph/langgraph/pregel/runner.py b/libs/langgraph/langgraph/pregel/runner.py index 4a7114e46..87e73850c 100644 --- a/libs/langgraph/langgraph/pregel/runner.py +++ b/libs/langgraph/langgraph/pregel/runner.py @@ -25,6 +25,7 @@ from langgraph.constants import ( CONFIG_KEY_SEND, ERROR, INTERRUPT, + MISSING, NO_WRITES, PUSH, RESUME, @@ -113,9 +114,15 @@ class PregelRunner: elif next_task.writes: # if it already ran, return the result fut = concurrent.futures.Future() - if val := next(v for c, v in next_task.writes if c == RETURN): + if ( + val := next( + (v for c, v in next_task.writes if c == RETURN), MISSING + ) + ) and val is not MISSING: fut.set_result(val) - elif exc := next(v for c, v in next_task.writes if c == ERROR): + elif exc := next( + (v for c, v in next_task.writes if c == ERROR), None + ): fut.set_exception( exc if isinstance(exc, BaseException) @@ -299,9 +306,15 @@ class PregelRunner: elif next_task.writes: # if it already ran, return the result fut = asyncio.Future() - if val := next(v for c, v in next_task.writes if c == RETURN): + if ( + val := next( + (v for c, v in next_task.writes if c == RETURN), MISSING + ) + ) and val is not MISSING: fut.set_result(val) - elif exc := next(v for c, v in next_task.writes if c == ERROR): + elif exc := next( + (v for c, v in next_task.writes if c == ERROR), None + ): fut.set_exception( exc if isinstance(exc, BaseException) diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index 299121c66..c5fb33d71 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -5273,8 +5273,8 @@ def test_falsy_return_from_task() -> None: @entrypoint(checkpointer=checkpointer) def graph(state: dict) -> dict: """React tool.""" - task_result = falsy_task().result() - human_value = interrupt("test") + falsy_task().result() + interrupt("test") configurable = {"configurable": {"thread_id": uuid.uuid4()}} graph.invoke({"a": 5}, configurable) diff --git a/libs/langgraph/tests/test_pregel_async.py b/libs/langgraph/tests/test_pregel_async.py index cafd2fbb3..e4ab4b0c2 100644 --- a/libs/langgraph/tests/test_pregel_async.py +++ b/libs/langgraph/tests/test_pregel_async.py @@ -6693,3 +6693,22 @@ async def test_multiple_updates() -> None: {"node_a": [{"foo": "a1"}, {"foo": "a2"}]}, {"node_b": {"foo": "b"}}, ] + + +async def test_falsy_return_from_task() -> None: + """Test with a falsy return from a task.""" + checkpointer = MemorySaver() + + @task + async def falsy_task() -> bool: + return False + + @entrypoint(checkpointer=checkpointer) + async def graph(state: dict) -> dict: + """React tool.""" + await falsy_task() + interrupt("test") + + configurable = {"configurable": {"thread_id": uuid.uuid4()}} + await graph.ainvoke({"a": 5}, configurable) + await graph.ainvoke(Command(resume="123"), configurable)