From 36cf353d1965dcc50996445dbc9155b47b4bdb08 Mon Sep 17 00:00:00 2001 From: Nuno Campos Date: Fri, 5 Sep 2025 10:27:10 +0100 Subject: [PATCH] fix: Unwrap Required/NotRequired special forms before resolving channel/reducer annotations (#6080) --- libs/langgraph/langgraph/graph/state.py | 8 +++++++- libs/langgraph/tests/test_state.py | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/langgraph/langgraph/graph/state.py b/libs/langgraph/langgraph/graph/state.py index 2a1a194b7..3512b3fa5 100644 --- a/libs/langgraph/langgraph/graph/state.py +++ b/libs/langgraph/langgraph/graph/state.py @@ -25,7 +25,7 @@ from typing import ( from langchain_core.runnables import Runnable, RunnableConfig from pydantic import BaseModel, TypeAdapter -from typing_extensions import Self, Unpack, is_typeddict +from typing_extensions import NotRequired, Required, Self, Unpack, is_typeddict from langgraph._internal._constants import ( INTERRUPT, @@ -1334,6 +1334,12 @@ def _get_channel( def _get_channel( name: str, annotation: Any, *, allow_managed: bool = True ) -> BaseChannel | ManagedValueSpec: + # Strip out Required and NotRequired wrappers + if hasattr(annotation, "__origin__") and annotation.__origin__ in ( + Required, + NotRequired, + ): + annotation = annotation.__args__[0] if manager := _is_field_managed_value(name, annotation): if allow_managed: return manager diff --git a/libs/langgraph/tests/test_state.py b/libs/langgraph/tests/test_state.py index 82a3997de..67988ef09 100644 --- a/libs/langgraph/tests/test_state.py +++ b/libs/langgraph/tests/test_state.py @@ -10,6 +10,7 @@ from langchain_core.runnables import RunnableConfig from pydantic import BaseModel from typing_extensions import NotRequired, Required, TypedDict +from langgraph.channels.binop import BinaryOperatorAggregate from langgraph.graph.state import StateGraph, _get_node_name, _warn_invalid_state_schema @@ -137,7 +138,7 @@ def test_state_schema_optional_values(total_: bool): class InputState(SomeParentState, total=total_): # type: ignore val1: str val2: Optional[str] - val3: Required[str] + val3: Required[Annotated[dict, operator.or_]] val4: NotRequired[dict] val5: Annotated[Required[str], "foo"] val6: Annotated[NotRequired[str], "bar"] @@ -159,6 +160,8 @@ def test_state_schema_optional_values(total_: bool): graph = builder.compile() json_schema = graph.get_input_jsonschema() + assert isinstance(graph.channels["val3"], BinaryOperatorAggregate) + if total_ is False: expected_required = set() expected_optional = {"val2", "val1"}