diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 8c93c0c1d..ae8941618 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -33,7 +33,7 @@ from langgraph.channels.dynamic_barrier_value import DynamicBarrierValue, WaitFo from langgraph.channels.ephemeral_value import EphemeralValue from langgraph.channels.last_value import LastValue from langgraph.channels.named_barrier_value import NamedBarrierValue -from langgraph.constants import EMPTY_SEQ, NS_END, NS_SEP, SELF, TAG_HIDDEN +from langgraph.constants import EMPTY_SEQ, MISSING, NS_END, NS_SEP, SELF, TAG_HIDDEN from langgraph.errors import ( ErrorCode, InvalidUpdateError, @@ -691,10 +691,23 @@ class CompiledStateGraph(CompiledGraph): updates.extend(_get_updates(i) or ()) return updates elif get_type_hints(type(input)): + # if input is a Pydantic model, only update values + # for the keys that have been explicitly set by the users + # (this is needed to avoid sending updates for fields with None defaults) + output_keys_ = output_keys + # Pydantic v2 + if hasattr(input, "model_fields_set"): + output_keys_ = [ + k for k in output_keys if k in input.model_fields_set + ] + # Pydantic v1 + elif hasattr(input, "__fields_set__"): + output_keys_ = [k for k in output_keys if k in input.__fields_set__] + return [ (k, getattr(input, k)) - for k in output_keys - if getattr(input, k, None) is not None + for k in output_keys_ + if getattr(input, k, MISSING) is not MISSING ] else: msg = create_error_message( diff --git a/libs/langgraph/tests/test_pregel.py b/libs/langgraph/tests/test_pregel.py index 6d0832944..d8a3b088a 100644 --- a/libs/langgraph/tests/test_pregel.py +++ b/libs/langgraph/tests/test_pregel.py @@ -6493,3 +6493,16 @@ def test_node_destinations() -> None: Edge(source="child", target="node_b", data="foo", conditional=True), Edge(source="child", target="node_c", data="bar", conditional=True), ] == graph.edges + + +def test_pydantic_none_state_update() -> None: + from pydantic import BaseModel + + class State(BaseModel): + foo: Optional[str] + + def node_a(state: State) -> State: + return State(foo=None) + + graph = StateGraph(State).add_node(node_a).add_edge(START, "node_a").compile() + assert graph.invoke({"foo": ""}) == {"foo": None}