diff --git a/libs/langgraph/langgraph/channels/binop.py b/libs/langgraph/langgraph/channels/binop.py index 29fa2a22f..82f7e940e 100644 --- a/libs/langgraph/langgraph/channels/binop.py +++ b/libs/langgraph/langgraph/channels/binop.py @@ -37,6 +37,17 @@ def _get_overwrite(value: Any) -> tuple[bool, Any]: return False, None +def _operators_equal(a: Callable, b: Callable) -> bool: + """Return True if two reducer operators should be considered equal. + + Lambdas all share the name '' so identity comparison is + unreliable; treat any pairing that includes a lambda as equal. + """ + if a.__name__ == "" or b.__name__ == "": + return True + return a is b + + class BinaryOperatorAggregate(Generic[Value], BaseChannel[Value, Value, Value]): """Stores the result of applying a binary operator to the current value and each new value. @@ -67,11 +78,8 @@ class BinaryOperatorAggregate(Generic[Value], BaseChannel[Value, Value, Value]): self.value = MISSING def __eq__(self, value: object) -> bool: - return isinstance(value, BinaryOperatorAggregate) and ( - value.operator is self.operator - if value.operator.__name__ != "" - and self.operator.__name__ != "" - else True + return isinstance(value, BinaryOperatorAggregate) and _operators_equal( + self.operator, value.operator ) @property diff --git a/libs/langgraph/langgraph/channels/delta.py b/libs/langgraph/langgraph/channels/delta.py index 7be783013..a061e21a2 100644 --- a/libs/langgraph/langgraph/channels/delta.py +++ b/libs/langgraph/langgraph/channels/delta.py @@ -8,16 +8,15 @@ from langgraph.checkpoint.base import DELTA_SENTINEL, PendingWrite from langgraph.checkpoint.serde.types import _DeltaSnapshot from typing_extensions import Self -from langgraph._internal._constants import OVERWRITE from langgraph._internal._typing import MISSING from langgraph.channels.base import BaseChannel, Value +from langgraph.channels.binop import _get_overwrite, _operators_equal from langgraph.errors import ( EmptyChannelError, ErrorCode, InvalidUpdateError, create_error_message, ) -from langgraph.types import Overwrite __all__ = ("DeltaChannel",) @@ -29,14 +28,6 @@ def _empty(typ: Any) -> Any: return [] -def _get_overwrite(value: Any) -> tuple[bool, Any]: - if isinstance(value, Overwrite): - return True, value.value - if isinstance(value, dict) and set(value.keys()) == {OVERWRITE}: - return True, value[OVERWRITE] - return False, None - - class DeltaChannel(Generic[Value], BaseChannel[Any, Any, Any]): """Fold-reducer channel with configurable snapshot cadence. @@ -74,12 +65,7 @@ class DeltaChannel(Generic[Value], BaseChannel[Any, Any, Any]): return False if self.snapshot_frequency != other.snapshot_frequency: return False - if ( - self.operator.__name__ != "" - and other.operator.__name__ != "" - ): - return self.operator is other.operator - return True + return _operators_equal(self.operator, other.operator) @property def ValueType(self) -> Any: