langgraph[change]: solidify public/private differentiations (#5252)

* public interfaces for channels
* public interfaces for func
* public interfaces for graph
* pi for managed
* first pass public interface for top level modules
* first pass at private for utils -> _internal
* private interface for pregel
* scratchpad/stream protocol move
* docs update
* backwards compat for runnable
* deprecation warning for send and interrupt
* deprecation for pregel import
This commit is contained in:
Sydney Runkle
2025-07-02 16:48:53 -04:00
committed by GitHub
parent c989f1c898
commit 8c4e698c5a
89 changed files with 4085 additions and 4079 deletions
+1 -1
View File
@@ -282,8 +282,8 @@
"from langgraph.graph import StateGraph\n",
"\n",
"from langchain_core.runnables import RunnableConfig\n",
"from langgraph.constants import Send\n",
"from langgraph.checkpoint.memory import MemorySaver\n",
"from langgraph.types import Send\n",
"\n",
"\n",
"def update_candidates(\n",
+1 -1
View File
@@ -19,7 +19,7 @@ dependencies = [
[project.optional-dependencies]
inmem = [
"langgraph-api>=0.2.67 ; python_version >= '3.11'",
"langgraph-runtime-inmem>=0.3.0 ; python_version >= '3.11'",
"langgraph-runtime-inmem>=0.3.4 ; python_version >= '3.11'",
"python-dotenv>=0.8.0",
]
+4 -4
View File
@@ -531,7 +531,7 @@ dev = [
requires-dist = [
{ name = "click", specifier = ">=8.1.7" },
{ name = "langgraph-api", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.2.67" },
{ name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3.0" },
{ name = "langgraph-runtime-inmem", marker = "python_full_version >= '3.11' and extra == 'inmem'", specifier = ">=0.3.4" },
{ name = "langgraph-sdk", marker = "python_full_version >= '3.11'", specifier = ">=0.1.0" },
{ name = "python-dotenv", marker = "extra == 'inmem'", specifier = ">=0.8.0" },
]
@@ -564,7 +564,7 @@ wheels = [
[[package]]
name = "langgraph-runtime-inmem"
version = "0.3.3"
version = "0.3.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "blockbuster", marker = "python_full_version >= '3.11'" },
@@ -574,9 +574,9 @@ dependencies = [
{ name = "starlette", marker = "python_full_version >= '3.11'" },
{ name = "structlog", marker = "python_full_version >= '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/c6/c6c515df38179517a187b11cc9506caac9bc7c25ab0adc1e21b463e338fd/langgraph_runtime_inmem-0.3.3.tar.gz", hash = "sha256:1b5bc8b05989f48c64c826d826e576a6e77f42349eca3382f38f8ed1d1f026e4", size = 77443, upload-time = "2025-06-24T19:50:36.235Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c1/17/7ff669ff44a53ab342903c2996fff75a77494af9fe56abcfbca64fe2342b/langgraph_runtime_inmem-0.3.4.tar.gz", hash = "sha256:eda7828f3ea07126e5265024b74a3fa9bf611633ad83ba3296ab9f51d89b7c0c", size = 77424, upload-time = "2025-07-01T14:45:07.465Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/36/87/1b47cf8a9a9ab8e6460203921e05f59e1e95dc0636e64a7429cd0e579a79/langgraph_runtime_inmem-0.3.3-py3-none-any.whl", hash = "sha256:ed485d520870a96a4a2e81188f2dbf993e7b96c5c65804bcdfdcb39df71be3ce", size = 29146, upload-time = "2025-06-24T19:50:35.083Z" },
{ url = "https://files.pythonhosted.org/packages/95/0e/39c13ca7229a9425a0e5744a1d3817f80d38dd5ca7703494fb9cf836ba45/langgraph_runtime_inmem-0.3.4-py3-none-any.whl", hash = "sha256:dcb9ac68ac90b3fb1ddaf666d14a367ab70e69d5bb5589b77a72c318e29104ae", size = 29139, upload-time = "2025-07-01T14:45:06.472Z" },
]
[[package]]
+2 -1
View File
@@ -3,8 +3,9 @@ from typing import Annotated
from typing_extensions import TypedDict
from langgraph.constants import END, START, Send
from langgraph.constants import END, START
from langgraph.graph.state import StateGraph
from langgraph.types import Send
def fanout_to_subgraph() -> StateGraph:
+1 -1
View File
@@ -1,7 +1,7 @@
"""Create a sequential no-op graph consisting of a few hundred nodes."""
from langgraph._internal._runnable import RunnableCallable
from langgraph.graph import MessagesState, StateGraph
from langgraph.utils.runnable import RunnableCallable
def create_sequential(number_nodes: int) -> StateGraph:
@@ -0,0 +1,4 @@
"""Internal modules for LangGraph.
This module is not part of the public API, and thus stability is not guaranteed.
"""
@@ -19,7 +19,6 @@ from langchain_core.runnables.config import (
)
from langgraph.checkpoint.base import CheckpointMetadata
from langgraph.config import get_config, get_store, get_stream_writer # noqa
from langgraph.constants import (
CONF,
CONFIG_KEY_CHECKPOINT_ID,
@@ -128,6 +128,3 @@ class SyncQueue:
return len(self._queue)
__class_getitem__ = classmethod(types.GenericAlias)
__all__ = ["AsyncQueue", "SyncQueue"]
@@ -0,0 +1,29 @@
def default_retry_on(exc: Exception) -> bool:
import httpx
import requests
if isinstance(exc, ConnectionError):
return True
if isinstance(exc, httpx.HTTPStatusError):
return 500 <= exc.response.status_code < 600
if isinstance(exc, requests.HTTPError):
return 500 <= exc.response.status_code < 600 if exc.response else True
if isinstance(
exc,
(
ValueError,
TypeError,
ArithmeticError,
ImportError,
LookupError,
NameError,
SyntaxError,
RuntimeError,
ReferenceError,
StopIteration,
StopAsyncIteration,
OSError,
),
):
return False
return True
@@ -42,6 +42,12 @@ from langchain_core.runnables.utils import Input, Output
from langchain_core.tracers.langchain import LangChainTracer
from typing_extensions import TypeGuard
from langgraph._internal._config import (
ensure_config,
get_async_callback_manager_for_config,
get_callback_manager_for_config,
patch_config,
)
from langgraph.constants import (
CONF,
CONFIG_KEY_PREVIOUS,
@@ -50,12 +56,6 @@ from langgraph.constants import (
)
from langgraph.store.base import BaseStore
from langgraph.types import StreamWriter
from langgraph.utils.config import (
ensure_config,
get_async_callback_manager_for_config,
get_callback_manager_for_config,
patch_config,
)
try:
from langchain_core.tracers._streaming import _StreamingCallbackHandler
+18 -6
View File
@@ -1,15 +1,27 @@
from langgraph.channels.any_value import AnyValue
from langgraph.channels.base import BaseChannel
from langgraph.channels.binop import BinaryOperatorAggregate
from langgraph.channels.ephemeral_value import EphemeralValue
from langgraph.channels.last_value import LastValue
from langgraph.channels.last_value import LastValue, LastValueAfterFinish
from langgraph.channels.named_barrier_value import (
NamedBarrierValue,
NamedBarrierValueAfterFinish,
)
from langgraph.channels.topic import Topic
from langgraph.channels.untracked_value import UntrackedValue
__all__ = [
__all__ = (
# base
"BaseChannel",
# value types
"AnyValue",
"LastValue",
"Topic",
"BinaryOperatorAggregate",
"LastValueAfterFinish",
"UntrackedValue",
"EphemeralValue",
"AnyValue",
]
"BinaryOperatorAggregate",
"NamedBarrierValue",
"NamedBarrierValueAfterFinish",
# topics
"Topic",
)
@@ -7,6 +7,8 @@ from langgraph.channels.base import BaseChannel, Value
from langgraph.constants import MISSING
from langgraph.errors import EmptyChannelError
__all__ = ("AnyValue",)
class AnyValue(Generic[Value], BaseChannel[Value, Value, Value]):
"""Stores the last value received, assumes that if multiple values are
+3 -8
View File
@@ -5,12 +5,14 @@ from typing import Any, Generic, TypeVar
from typing_extensions import Self
from langgraph.constants import MISSING
from langgraph.errors import EmptyChannelError, InvalidUpdateError
from langgraph.errors import EmptyChannelError
Value = TypeVar("Value")
Update = TypeVar("Update")
C = TypeVar("C")
__all__ = ("BaseChannel",)
class BaseChannel(Generic[Value, Update, C], ABC):
"""Base class for all channels."""
@@ -99,10 +101,3 @@ class BaseChannel(Generic[Value, Update, C], ABC):
Returns True if the channel was updated, False otherwise.
"""
return False
__all__ = [
"BaseChannel",
"EmptyChannelError",
"InvalidUpdateError",
]
@@ -8,6 +8,8 @@ from langgraph.channels.base import BaseChannel, Value
from langgraph.constants import MISSING
from langgraph.errors import EmptyChannelError
__all__ = ("BinaryOperatorAggregate",)
# Adapted from typing_extensions
def _strip_extras(t): # type: ignore[no-untyped-def]
@@ -7,6 +7,8 @@ from langgraph.channels.base import BaseChannel, Value
from langgraph.constants import MISSING
from langgraph.errors import EmptyChannelError, InvalidUpdateError
__all__ = ("EphemeralValue",)
class EphemeralValue(Generic[Value], BaseChannel[Value, Value, Value]):
"""Stores the value received in the step immediately preceding, clears after."""
@@ -12,6 +12,8 @@ from langgraph.errors import (
create_error_message,
)
__all__ = ("LastValue", "LastValueAfterFinish")
class LastValue(Generic[Value], BaseChannel[Value, Value, Value]):
"""Stores the last value received, can receive at most one value per step."""
@@ -7,6 +7,8 @@ from langgraph.channels.base import BaseChannel, Value
from langgraph.constants import MISSING
from langgraph.errors import EmptyChannelError, InvalidUpdateError
__all__ = ("NamedBarrierValue", "NamedBarrierValueAfterFinish")
class NamedBarrierValue(Generic[Value], BaseChannel[Value, Value, set[Value]]):
"""A channel that waits until all named values are received before making the value available."""
+4 -2
View File
@@ -9,8 +9,10 @@ from langgraph.channels.base import BaseChannel, Value
from langgraph.constants import MISSING
from langgraph.errors import EmptyChannelError
__all__ = ("Topic",)
def flatten(values: Sequence[Value | list[Value]]) -> Iterator[Value]:
def _flatten(values: Sequence[Value | list[Value]]) -> Iterator[Value]:
for value in values:
if isinstance(value, list):
yield from value
@@ -77,7 +79,7 @@ class Topic(
if not self.accumulate:
updated = bool(self.values)
self.values = list[Value]()
if flat_values := tuple(flatten(values)):
if flat_values := tuple(_flatten(values)):
updated = True
self.values.extend(flat_values)
return updated
@@ -7,6 +7,8 @@ from langgraph.channels.base import BaseChannel, Value
from langgraph.constants import MISSING
from langgraph.errors import EmptyChannelError, InvalidUpdateError
__all__ = ("UntrackedValue",)
class UntrackedValue(Generic[Value], BaseChannel[Value, Value, Value]):
"""Stores the last value received, never checkpointed."""
+27 -7
View File
@@ -1,23 +1,43 @@
import sys
from collections.abc import Mapping
from types import MappingProxyType
from typing import Any, Literal, cast
from warnings import warn
from langgraph.types import Interrupt, Send # noqa: F401
from langgraph.warnings import LangGraphDeprecatedSinceV10
# Interrupt, Send re-exported for backwards compatibility
__all__ = (
"TAG_NOSTREAM",
"TAG_HIDDEN",
"START",
"END",
"SELF",
"PREVIOUS",
)
def __getattr__(name: str) -> Any:
if name in ["Send", "Interrupt"]:
warn(
f"Importing {name} from langgraph.constants is deprecated. "
f"Please use 'from langgraph.types import {name}' instead.",
LangGraphDeprecatedSinceV10,
stacklevel=2,
)
from importlib import import_module
module = import_module("langgraph.types")
return getattr(module, name)
raise AttributeError(f"module has no attribute '{name}'")
# --- Empty read-only containers ---
EMPTY_MAP: Mapping[str, Any] = MappingProxyType({})
EMPTY_SEQ: tuple[str, ...] = tuple()
MISSING = object()
# --- Public constants ---
TAG_NOSTREAM = sys.intern("nostream")
"""Tag to disable streaming for a chat model."""
TAG_NOSTREAM_ALT = sys.intern("langsmith:nostream")
"""Tag to disable streaming for a chat model. (Deprecated in favour of "nostream")"""
TAG_HIDDEN = sys.intern("langsmith:hidden")
"""Tag to hide a node/edge from certain tracing/streaming environments."""
START = sys.intern("__start__")
+13 -1
View File
@@ -2,10 +2,22 @@ from collections.abc import Sequence
from enum import Enum
from typing import Any
# EmptyChannelError is re-exported from langgraph.channels.base
from langgraph.checkpoint.base import EmptyChannelError # noqa: F401
from langgraph.types import Command, Interrupt
# EmptyChannelError re-exported for backwards compatibility
__all__ = (
"EmptyChannelError",
"ErrorCode",
"GraphRecursionError",
"InvalidUpdateError",
"GraphBubbleUp",
"GraphInterrupt",
"NodeInterrupt",
"ParentCommand",
"EmptyInputError",
"TaskNotFound",
)
class ErrorCode(Enum):
+12 -10
View File
@@ -19,14 +19,14 @@ from typing import (
from typing_extensions import Unpack
from langgraph._typing import UNSET, DeprecatedKwargs
from langgraph._internal._typing import UNSET, DeprecatedKwargs
from langgraph.cache.base import BaseCache
from langgraph.channels.ephemeral_value import EphemeralValue
from langgraph.channels.last_value import LastValue
from langgraph.checkpoint.base import BaseCheckpointSaver
from langgraph.constants import CACHE_NS_WRITES, END, PREVIOUS, START
from langgraph.pregel import Pregel
from langgraph.pregel.call import (
from langgraph.pregel._call import (
P,
SyncAsyncFuture,
T,
@@ -34,14 +34,16 @@ from langgraph.pregel.call import (
get_runnable_for_entrypoint,
identifier,
)
from langgraph.pregel.read import PregelNode
from langgraph.pregel.write import ChannelWrite, ChannelWriteEntry
from langgraph.pregel._read import PregelNode
from langgraph.pregel._write import ChannelWrite, ChannelWriteEntry
from langgraph.store.base import BaseStore
from langgraph.types import _DC_KWARGS, CachePolicy, RetryPolicy, StreamMode
from langgraph.warnings import LangGraphDeprecatedSinceV05
__all__ = ("task", "entrypoint")
class TaskFunction(Generic[P, T]):
class _TaskFunction(Generic[P, T]):
def __init__(
self,
func: Callable[P, T],
@@ -97,14 +99,14 @@ def task(
**kwargs: Unpack[DeprecatedKwargs],
) -> Callable[
[Callable[P, Awaitable[T]] | Callable[P, T]],
TaskFunction[P, T],
_TaskFunction[P, T],
]: ...
@overload
def task(
__func_or_none__: Callable[P, Awaitable[T]] | Callable[P, T],
) -> TaskFunction[P, T]: ...
) -> _TaskFunction[P, T]: ...
def task(
@@ -115,8 +117,8 @@ def task(
cache_policy: CachePolicy[Callable[P, str | bytes]] | None = None,
**kwargs: Unpack[DeprecatedKwargs],
) -> (
Callable[[Callable[P, Awaitable[T]] | Callable[P, T]], TaskFunction[P, T]]
| TaskFunction[P, T]
Callable[[Callable[P, Awaitable[T]] | Callable[P, T]], _TaskFunction[P, T]]
| _TaskFunction[P, T]
):
"""Define a LangGraph task using the `task` decorator.
@@ -195,7 +197,7 @@ def task(
def decorator(
func: Callable[P, Awaitable[T]] | Callable[P, T],
) -> Callable[P, concurrent.futures.Future[T]] | Callable[P, asyncio.Future[T]]:
return TaskFunction(
return _TaskFunction(
func, retry_policy=retry_policies, cache_policy=cache_policy, name=name
)
+3 -3
View File
@@ -2,11 +2,11 @@ from langgraph.constants import END, START
from langgraph.graph.message import MessageGraph, MessagesState, add_messages
from langgraph.graph.state import StateGraph
__all__ = [
__all__ = (
"END",
"START",
"StateGraph",
"MessageGraph",
"add_messages",
"MessagesState",
]
"MessageGraph",
)
@@ -26,15 +26,15 @@ from langchain_core.runnables import (
RunnableLambda,
)
from langgraph.constants import END, START
from langgraph.errors import InvalidUpdateError
from langgraph.pregel.write import PASSTHROUGH, ChannelWrite, ChannelWriteEntry
from langgraph.types import Send
from langgraph.utils.runnable import (
from langgraph._internal._runnable import (
RunnableCallable,
)
from langgraph.constants import END, START
from langgraph.errors import InvalidUpdateError
from langgraph.pregel._write import PASSTHROUGH, ChannelWrite, ChannelWriteEntry
from langgraph.types import Send
Writer = Callable[
_Writer = Callable[
[Sequence[Union[str, Send]], bool],
Sequence[Union[ChannelWriteEntry, Send]],
]
@@ -82,7 +82,7 @@ def _get_branch_path_input_schema(
return input
class Branch(NamedTuple):
class BranchSpec(NamedTuple):
path: Runnable[Any, Hashable | list[Hashable]]
ends: dict[Hashable, str] | None
input_schema: type[Any] | None = None
@@ -93,7 +93,7 @@ class Branch(NamedTuple):
path: Runnable[Any, Hashable | list[Hashable]],
path_map: dict[Hashable, str] | list[str] | None,
infer_schema: bool = False,
) -> Branch:
) -> BranchSpec:
# coerce path_map to a dictionary
path_map_: dict[Hashable, str] | None = None
try:
@@ -123,7 +123,7 @@ class Branch(NamedTuple):
def run(
self,
writer: Writer,
writer: _Writer,
reader: Callable[[RunnableConfig], Any] | None = None,
) -> RunnableCallable:
return ChannelWrite.register_writer(
@@ -152,7 +152,7 @@ class Branch(NamedTuple):
config: RunnableConfig,
*,
reader: Callable[[RunnableConfig], Any] | None,
writer: Writer,
writer: _Writer,
) -> Runnable:
if reader:
value = reader(config)
@@ -175,7 +175,7 @@ class Branch(NamedTuple):
config: RunnableConfig,
*,
reader: Callable[[RunnableConfig], Any] | None,
writer: Writer,
writer: _Writer,
) -> Runnable:
if reader:
value = reader(config)
@@ -194,7 +194,7 @@ class Branch(NamedTuple):
def _finish(
self,
writer: Writer,
writer: _Writer,
input: Any,
result: Any,
config: RunnableConfig,
+84
View File
@@ -0,0 +1,84 @@
from __future__ import annotations
from collections.abc import Sequence
from typing import Any, NamedTuple, Protocol, Union
from langchain_core.runnables import Runnable, RunnableConfig
from typing_extensions import TypeAlias
from langgraph.constants import EMPTY_SEQ
from langgraph.store.base import BaseStore
from langgraph.types import CachePolicy, RetryPolicy, StreamWriter
from langgraph.typing import StateT_contra
class _Node(Protocol[StateT_contra]):
def __call__(self, state: StateT_contra) -> Any: ...
class _NodeWithConfig(Protocol[StateT_contra]):
def __call__(self, state: StateT_contra, config: RunnableConfig) -> Any: ...
class _NodeWithWriter(Protocol[StateT_contra]):
def __call__(self, state: StateT_contra, *, writer: StreamWriter) -> Any: ...
class _NodeWithStore(Protocol[StateT_contra]):
def __call__(self, state: StateT_contra, *, store: BaseStore) -> Any: ...
class _NodeWithWriterStore(Protocol[StateT_contra]):
def __call__(
self, state: StateT_contra, *, writer: StreamWriter, store: BaseStore
) -> Any: ...
class _NodeWithConfigWriter(Protocol[StateT_contra]):
def __call__(
self, state: StateT_contra, *, config: RunnableConfig, writer: StreamWriter
) -> Any: ...
class _NodeWithConfigStore(Protocol[StateT_contra]):
def __call__(
self, state: StateT_contra, *, config: RunnableConfig, store: BaseStore
) -> Any: ...
class _NodeWithConfigWriterStore(Protocol[StateT_contra]):
def __call__(
self,
state: StateT_contra,
*,
config: RunnableConfig,
writer: StreamWriter,
store: BaseStore,
) -> Any: ...
# TODO: we probably don't want to explicitly support the config / store signatures once
# we move to adding a context arg. Maybe what we do is we add support for kwargs with param spec
# this is purely for typing purposes though, so can easily change in the coming weeks.
StateNode: TypeAlias = Union[
_Node[StateT_contra],
_NodeWithConfig[StateT_contra],
_NodeWithWriter[StateT_contra],
_NodeWithStore[StateT_contra],
_NodeWithWriterStore[StateT_contra],
_NodeWithConfigWriter[StateT_contra],
_NodeWithConfigStore[StateT_contra],
_NodeWithConfigWriterStore[StateT_contra],
Runnable[StateT_contra, Any],
]
# TODO: use a dataclass generic on NodeInputType
class StateNodeSpec(NamedTuple):
runnable: StateNode
metadata: dict[str, Any] | None
input_schema: type[Any]
retry_policy: RetryPolicy | Sequence[RetryPolicy] | None
cache_policy: CachePolicy | None
ends: tuple[str, ...] | dict[str, str] | None = EMPTY_SEQ
defer: bool = False
+7 -1
View File
@@ -27,6 +27,12 @@ from typing_extensions import TypedDict
from langgraph.constants import CONF, CONFIG_KEY_SEND
from langgraph.graph.state import StateGraph
__all__ = (
"add_messages",
"MessagesState",
"MessageGraph",
)
Messages = Union[list[MessageLikeRepresentation], MessageLikeRepresentation]
REMOVE_ALL_MESSAGES = "__remove_all__"
@@ -315,7 +321,7 @@ def push_message(
from langgraph.config import get_config
from langgraph.constants import NS_SEP
from langgraph.pregel.messages import StreamMessagesHandler
from langgraph.pregel._messages import StreamMessagesHandler
config = get_config()
message = next(x for x in convert_to_messages([message]))
+31 -107
View File
@@ -14,8 +14,6 @@ from typing import (
Callable,
Generic,
Literal,
NamedTuple,
Protocol,
Union,
cast,
get_args,
@@ -26,9 +24,16 @@ from typing import (
from langchain_core.runnables import Runnable, RunnableConfig
from pydantic import BaseModel
from typing_extensions import Self, TypeAlias, Unpack
from typing_extensions import Self, Unpack
from langgraph._typing import UNSET, DeprecatedKwargs
from langgraph._internal._fields import (
get_cached_annotated_keys,
get_field_default,
get_update_as_tuples,
)
from langgraph._internal._pydantic import create_model
from langgraph._internal._runnable import coerce_to_runnable
from langgraph._internal._typing import UNSET, DeprecatedKwargs
from langgraph.cache.base import BaseCache
from langgraph.channels.base import BaseChannel
from langgraph.channels.binop import BinaryOperatorAggregate
@@ -56,14 +61,15 @@ from langgraph.errors import (
ParentCommand,
create_error_message,
)
from langgraph.graph.branch import Branch
from langgraph.graph._branch import BranchSpec
from langgraph.graph._node import StateNode, StateNodeSpec
from langgraph.managed.base import (
ManagedValueSpec,
is_managed_value,
)
from langgraph.pregel import Pregel
from langgraph.pregel.read import ChannelRead, PregelNode
from langgraph.pregel.write import (
from langgraph.pregel._read import ChannelRead, PregelNode
from langgraph.pregel._write import (
ChannelWrite,
ChannelWriteEntry,
ChannelWriteTupleEntry,
@@ -76,20 +82,16 @@ from langgraph.types import (
Command,
RetryPolicy,
Send,
StreamWriter,
)
from langgraph.typing import InputT, OutputT, StateT, StateT_contra
from langgraph.utils.fields import (
get_cached_annotated_keys,
get_field_default,
get_update_as_tuples,
)
from langgraph.utils.pydantic import create_model
from langgraph.utils.runnable import coerce_to_runnable
from langgraph.typing import InputT, OutputT, StateT
from langgraph.warnings import LangGraphDeprecatedSinceV05
__all__ = ("StateGraph", "CompiledStateGraph")
logger = logging.getLogger(__name__)
_CHANNEL_BRANCH_TO = "branch:to:{}"
def _warn_invalid_state_schema(schema: type[Any] | Any) -> None:
if isinstance(schema, type):
@@ -103,67 +105,6 @@ def _warn_invalid_state_schema(schema: type[Any] | Any) -> None:
)
class _StateNode(Protocol[StateT_contra]):
def __call__(self, state: StateT_contra) -> Any: ...
class _NodeWithConfig(Protocol[StateT_contra]):
def __call__(self, state: StateT_contra, config: RunnableConfig) -> Any: ...
class _NodeWithWriter(Protocol[StateT_contra]):
def __call__(self, state: StateT_contra, *, writer: StreamWriter) -> Any: ...
class _NodeWithStore(Protocol[StateT_contra]):
def __call__(self, state: StateT_contra, *, store: BaseStore) -> Any: ...
class _NodeWithWriterStore(Protocol[StateT_contra]):
def __call__(
self, state: StateT_contra, *, writer: StreamWriter, store: BaseStore
) -> Any: ...
class _NodeWithConfigWriter(Protocol[StateT_contra]):
def __call__(
self, state: StateT_contra, *, config: RunnableConfig, writer: StreamWriter
) -> Any: ...
class _NodeWithConfigStore(Protocol[StateT_contra]):
def __call__(
self, state: StateT_contra, *, config: RunnableConfig, store: BaseStore
) -> Any: ...
class _NodeWithConfigWriterStore(Protocol[StateT_contra]):
def __call__(
self,
state: StateT_contra,
*,
config: RunnableConfig,
writer: StreamWriter,
store: BaseStore,
) -> Any: ...
# TODO: we probably don't want to explicitly support the config / store signatures once
# we move to adding a context arg. Maybe what we do is we add support for kwargs with param spec
# this is purely for typing purposes though, so can easily change in the coming weeks.
StateNode: TypeAlias = Union[
_StateNode[StateT_contra],
_NodeWithConfig[StateT_contra],
_NodeWithWriter[StateT_contra],
_NodeWithStore[StateT_contra],
_NodeWithWriterStore[StateT_contra],
_NodeWithConfigWriter[StateT_contra],
_NodeWithConfigStore[StateT_contra],
_NodeWithConfigWriterStore[StateT_contra],
Runnable[StateT_contra, Any],
]
def _get_node_name(node: StateNode) -> str:
try:
return getattr(node, "__name__", node.__class__.__name__)
@@ -171,20 +112,6 @@ def _get_node_name(node: StateNode) -> str:
raise TypeError(f"Unsupported node type: {type(node)}")
class StateNodeSpec(NamedTuple):
# TODO: rename this callable, also move away from NamedTuple so that we can use
# a generic StateNode, so maybe a dataclass
runnable: StateNode
metadata: dict[str, Any] | None
# TODO: rename to input_schema, though we really just want to modify this structure to
# be a dataclass
input: type[Any]
retry_policy: RetryPolicy | Sequence[RetryPolicy] | None
cache_policy: CachePolicy | None
ends: tuple[str, ...] | dict[str, str] | None = EMPTY_SEQ
defer: bool = False
class StateGraph(Generic[StateT, InputT, OutputT]):
"""A graph whose nodes communicate by reading and writing to a shared state.
The signature of each node is State -> Partial<State>.
@@ -239,7 +166,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]):
edges: set[tuple[str, str]]
nodes: dict[str, StateNodeSpec]
branches: defaultdict[str, dict[str, Branch]]
branches: defaultdict[str, dict[str, BranchSpec]]
channels: dict[str, BaseChannel]
managed: dict[str, ManagedValueSpec]
schemas: dict[type[Any], dict[str, BaseChannel | ManagedValueSpec]]
@@ -538,7 +465,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]):
self.nodes[node] = StateNodeSpec(
coerce_to_runnable(action, name=node, trace=False),
metadata,
input=input_schema or self.state_schema,
input_schema=input_schema or self.state_schema,
retry_policy=retry_policy,
cache_policy=cache_policy,
ends=ends,
@@ -641,7 +568,7 @@ class StateGraph(Generic[StateT, InputT, OutputT]):
f"Branch with name `{path.name}` already exists for node `{source}`"
)
# save it
self.branches[source][name] = Branch.from_path(path, path_map, True)
self.branches[source][name] = BranchSpec.from_path(path, path_map, True)
if schema := self.branches[source][name].input_schema:
self._add_schema(schema)
return self
@@ -994,7 +921,7 @@ class CompiledStateGraph(
writers=[ChannelWrite(write_entries)],
)
elif node is not None:
input_schema = node.input if node else self.builder._state_schema
input_schema = node.input_schema if node else self.builder._state_schema
input_channels = list(self.builder.schemas[input_schema])
is_single_input = len(input_channels) == 1 and "__root__" in input_channels
if input_schema in self.schema_to_mapper:
@@ -1003,7 +930,7 @@ class CompiledStateGraph(
mapper = _pick_mapper(input_channels, input_schema)
self.schema_to_mapper[input_schema] = mapper
branch_channel = CHANNEL_BRANCH_TO.format(key)
branch_channel = _CHANNEL_BRANCH_TO.format(key)
self.channels[branch_channel] = (
LastValueAfterFinish(Any)
if node.defer
@@ -1031,7 +958,7 @@ class CompiledStateGraph(
if end != END:
self.nodes[starts].writers.append(
ChannelWrite(
(ChannelWriteEntry(CHANNEL_BRANCH_TO.format(end), None),)
(ChannelWriteEntry(_CHANNEL_BRANCH_TO.format(end), None),)
)
)
elif end != END:
@@ -1052,7 +979,7 @@ class CompiledStateGraph(
)
def attach_branch(
self, start: str, name: str, branch: Branch, *, with_reader: bool = True
self, start: str, name: str, branch: BranchSpec, *, with_reader: bool = True
) -> None:
def get_writes(
packets: Sequence[str | Send], static: bool = False
@@ -1060,7 +987,7 @@ class CompiledStateGraph(
writes = [
(
ChannelWriteEntry(
p if p == END else CHANNEL_BRANCH_TO.format(p), None
p if p == END else _CHANNEL_BRANCH_TO.format(p), None
)
if not isinstance(p, Send)
else p
@@ -1075,7 +1002,7 @@ class CompiledStateGraph(
if with_reader:
# get schema
schema = branch.input_schema or (
self.builder.nodes[start].input
self.builder.nodes[start].input_schema
if start in self.builder.nodes
else self.builder.state_schema
)
@@ -1237,12 +1164,12 @@ def _control_branch(value: Any) -> Sequence[tuple[str, Any]]:
if isinstance(command.goto, Send):
rtn.append((TASKS, command.goto))
elif isinstance(command.goto, str):
rtn.append((CHANNEL_BRANCH_TO.format(command.goto), None))
rtn.append((_CHANNEL_BRANCH_TO.format(command.goto), None))
else:
rtn.extend(
(TASKS, go)
if isinstance(go, Send)
else (CHANNEL_BRANCH_TO.format(go), None)
else (_CHANNEL_BRANCH_TO.format(go), None)
for go in command.goto
)
return rtn
@@ -1253,12 +1180,12 @@ def _control_static(
) -> Sequence[tuple[str, Any, str | None]]:
if isinstance(ends, dict):
return [
(k if k == END else CHANNEL_BRANCH_TO.format(k), None, label)
(k if k == END else _CHANNEL_BRANCH_TO.format(k), None, label)
for k, label in ends.items()
]
else:
return [
(e if e == END else CHANNEL_BRANCH_TO.format(e), None, None) for e in ends
(e if e == END else _CHANNEL_BRANCH_TO.format(e), None, None) for e in ends
]
@@ -1415,6 +1342,3 @@ def _get_schema(
if k in channels and isinstance(channels[k], BaseChannel)
},
)
CHANNEL_BRANCH_TO = "branch:to:{}"
+10 -1
View File
@@ -6,8 +6,17 @@ from uuid import uuid4
from langchain_core.messages import AnyMessage
from typing_extensions import TypedDict
from langgraph.config import get_config, get_stream_writer
from langgraph.constants import CONF, CONFIG_KEY_SEND
from langgraph.utils.config import get_config, get_stream_writer
__all__ = (
"UIMessage",
"RemoveUIMessage",
"AnyUIMessage",
"push_ui_message",
"delete_ui_message",
"ui_message_reducer",
)
class UIMessage(TypedDict):
+1 -1
View File
@@ -1,3 +1,3 @@
from langgraph.managed.is_last_step import IsLastStep, RemainingSteps
__all__ = ["IsLastStep", "RemainingSteps"]
__all__ = ("IsLastStep", "RemainingSteps")
+3 -1
View File
@@ -8,11 +8,13 @@ from typing import (
from typing_extensions import TypeGuard
from langgraph.types import PregelScratchpad
from langgraph.pregel._scratchpad import PregelScratchpad
V = TypeVar("V")
U = TypeVar("U")
__all__ = ("ManagedValueSpec", "ManagedValueMapping")
class ManagedValue(ABC, Generic[V]):
@staticmethod
@@ -1,7 +1,9 @@
from typing import Annotated
from langgraph.managed.base import ManagedValue
from langgraph.types import PregelScratchpad
from langgraph.pregel._scratchpad import PregelScratchpad
__all__ = ("IsLastStep", "RemainingStepsManager")
class IsLastStepManager(ManagedValue[bool]):
File diff suppressed because it is too large Load Diff
@@ -25,6 +25,7 @@ from langchain_core.callbacks.manager import AsyncParentRunManager, ParentRunMan
from langchain_core.runnables.config import RunnableConfig
from xxhash import xxh3_128_hexdigest
from langgraph._internal._config import merge_configs, patch_config
from langgraph.channels.base import BaseChannel
from langgraph.channels.topic import Topic
from langgraph.checkpoint.base import (
@@ -64,24 +65,23 @@ from langgraph.constants import (
RETURN,
TAG_HIDDEN,
TASKS,
Send,
)
from langgraph.managed.base import ManagedValueMapping
from langgraph.pregel.call import get_runnable_for_task, identifier
from langgraph.pregel.io import read_channels
from langgraph.pregel.log import logger
from langgraph.pregel.read import INPUT_CACHE_KEY_TYPE, PregelNode
from langgraph.pregel._call import get_runnable_for_task, identifier
from langgraph.pregel._io import read_channels
from langgraph.pregel._log import logger
from langgraph.pregel._read import INPUT_CACHE_KEY_TYPE, PregelNode
from langgraph.pregel._scratchpad import PregelScratchpad
from langgraph.store.base import BaseStore
from langgraph.types import (
All,
CacheKey,
CachePolicy,
PregelExecutableTask,
PregelScratchpad,
PregelTask,
RetryPolicy,
Send,
)
from langgraph.utils.config import merge_configs, patch_config
GetNextVersion = Callable[[Optional[V], None], V]
SUPPORTS_EXC_NOTES = sys.version_info >= (3, 11)
@@ -13,16 +13,16 @@ from typing import Any, Callable, Generic, TypeVar, cast
from langchain_core.runnables import Runnable
from typing_extensions import ParamSpec
from langgraph.constants import CONF, CONFIG_KEY_CALL, RETURN
from langgraph.pregel.write import ChannelWrite, ChannelWriteEntry
from langgraph.types import CachePolicy, RetryPolicy
from langgraph.utils.config import get_config
from langgraph.utils.runnable import (
from langgraph._internal._runnable import (
RunnableCallable,
RunnableSeq,
is_async_callable,
run_in_executor,
)
from langgraph.config import get_config
from langgraph.constants import CONF, CONFIG_KEY_CALL, RETURN
from langgraph.pregel._write import ChannelWrite, ChannelWriteEntry
from langgraph.types import CachePolicy, RetryPolicy
##
# Utilities borrowed from cloudpickle.
@@ -78,8 +78,8 @@ def _whichmodule(obj: Any, name: str) -> str | None:
def identifier(obj: Any, name: str | None = None) -> str | None:
"""Return the module and name of an object."""
from langgraph.pregel.read import PregelNode
from langgraph.utils.runnable import RunnableCallable, RunnableSeq
from langgraph._internal._runnable import RunnableCallable, RunnableSeq
from langgraph.pregel._read import PregelNode
if isinstance(obj, PregelNode):
obj = obj.bound
@@ -11,16 +11,16 @@ from langgraph.channels.base import BaseChannel
from langgraph.checkpoint.base import BaseCheckpointSaver
from langgraph.constants import CONF, CONFIG_KEY_SEND, END, INPUT, START
from langgraph.managed.base import ManagedValueSpec
from langgraph.pregel.algo import (
from langgraph.pregel._algo import (
PregelTaskWrites,
apply_writes,
increment,
prepare_next_tasks,
)
from langgraph.pregel.checkpoint import channels_from_checkpoint, empty_checkpoint
from langgraph.pregel.io import map_input
from langgraph.pregel.read import PregelNode
from langgraph.pregel.write import ChannelWrite
from langgraph.pregel._checkpoint import channels_from_checkpoint, empty_checkpoint
from langgraph.pregel._io import map_input
from langgraph.pregel._read import PregelNode
from langgraph.pregel._write import ChannelWrite
from langgraph.types import All, Checkpointer
@@ -18,8 +18,8 @@ from langchain_core.runnables import RunnableConfig
from langchain_core.runnables.config import get_executor_for_config
from typing_extensions import ParamSpec
from langgraph._internal._future import CONTEXT_NOT_SUPPORTED, run_coroutine_threadsafe
from langgraph.errors import GraphBubbleUp
from langgraph.utils.future import CONTEXT_NOT_SUPPORTED, run_coroutine_threadsafe
P = ParamSpec("P")
T = TypeVar("T")
@@ -18,7 +18,7 @@ from langgraph.constants import (
TASKS,
)
from langgraph.errors import InvalidUpdateError
from langgraph.pregel.log import logger
from langgraph.pregel._log import logger
from langgraph.types import Command, PregelExecutableTask, Send
@@ -27,6 +27,7 @@ from langchain_core.callbacks import AsyncParentRunManager, ParentRunManager
from langchain_core.runnables import RunnableConfig
from typing_extensions import ParamSpec, Self
from langgraph._internal._config import patch_configurable
from langgraph.cache.base import BaseCache
from langgraph.channels.base import BaseChannel
from langgraph.checkpoint.base import (
@@ -69,7 +70,7 @@ from langgraph.managed.base import (
ManagedValueMapping,
ManagedValueSpec,
)
from langgraph.pregel.algo import (
from langgraph.pregel._algo import (
Call,
GetNextVersion,
PregelTaskWrites,
@@ -81,44 +82,42 @@ from langgraph.pregel.algo import (
should_interrupt,
task_path_str,
)
from langgraph.pregel.checkpoint import (
from langgraph.pregel._checkpoint import (
channels_from_checkpoint,
copy_checkpoint,
create_checkpoint,
empty_checkpoint,
)
from langgraph.pregel.debug import (
map_debug_checkpoint,
map_debug_task_results,
map_debug_tasks,
)
from langgraph.pregel.executor import (
from langgraph.pregel._executor import (
AsyncBackgroundExecutor,
BackgroundExecutor,
Submit,
)
from langgraph.pregel.io import (
from langgraph.pregel._io import (
map_command,
map_input,
map_output_updates,
map_output_values,
read_channels,
)
from langgraph.pregel.read import PregelNode
from langgraph.pregel.utils import get_new_channel_versions, is_xxh3_128_hexdigest
from langgraph.pregel._read import PregelNode
from langgraph.pregel._scratchpad import PregelScratchpad
from langgraph.pregel._utils import get_new_channel_versions, is_xxh3_128_hexdigest
from langgraph.pregel.debug import (
map_debug_checkpoint,
map_debug_task_results,
map_debug_tasks,
)
from langgraph.pregel.protocol import StreamChunk, StreamProtocol
from langgraph.store.base import BaseStore
from langgraph.types import (
All,
CachePolicy,
Command,
PregelExecutableTask,
PregelScratchpad,
RetryPolicy,
StreamChunk,
StreamMode,
StreamProtocol,
)
from langgraph.utils.config import patch_configurable
V = TypeVar("V")
P = ParamSpec("P")
@@ -13,8 +13,9 @@ from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.messages import BaseMessage
from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, LLMResult
from langgraph.constants import NS_SEP, TAG_HIDDEN, TAG_NOSTREAM, TAG_NOSTREAM_ALT
from langgraph.types import Command, StreamChunk
from langgraph.constants import NS_SEP, TAG_HIDDEN, TAG_NOSTREAM
from langgraph.pregel.protocol import StreamChunk
from langgraph.types import Command
try:
from langchain_core.tracers._streaming import _StreamingCallbackHandler
@@ -94,9 +95,7 @@ class StreamMessagesHandler(BaseCallbackHandler, _StreamingCallbackHandler):
metadata: dict[str, Any] | None = None,
**kwargs: Any,
) -> Any:
if metadata and (
not tags or (TAG_NOSTREAM not in tags and TAG_NOSTREAM_ALT not in tags)
):
if metadata and (not tags or (TAG_NOSTREAM not in tags)):
ns = tuple(cast(str, metadata["langgraph_checkpoint_ns"]).split(NS_SEP))[
:-1
]
@@ -10,13 +10,13 @@ from typing import (
from langchain_core.runnables import Runnable, RunnableConfig
from langgraph._internal._config import merge_configs
from langgraph._internal._runnable import RunnableCallable, RunnableSeq
from langgraph.constants import CONF, CONFIG_KEY_READ
from langgraph.pregel._utils import find_subgraph_pregel
from langgraph.pregel._write import ChannelWrite
from langgraph.pregel.protocol import PregelProtocol
from langgraph.pregel.utils import find_subgraph_pregel
from langgraph.pregel.write import ChannelWrite
from langgraph.types import CachePolicy, RetryPolicy
from langgraph.utils.config import merge_configs
from langgraph.utils.runnable import RunnableCallable, RunnableSeq
READ_TYPE = Callable[[Union[str, Sequence[str]], bool], Union[Any, dict[str, Any]]]
INPUT_CACHE_KEY_TYPE = tuple[Callable[..., Any], tuple[str, ...]]
@@ -9,6 +9,7 @@ from collections.abc import Awaitable, Sequence
from dataclasses import replace
from typing import Any, Callable
from langgraph._internal._config import patch_configurable
from langgraph.constants import (
CONF,
CONFIG_KEY_CHECKPOINT_NS,
@@ -17,7 +18,6 @@ from langgraph.constants import (
)
from langgraph.errors import GraphBubbleUp, ParentCommand
from langgraph.types import Command, PregelExecutableTask, RetryPolicy
from langgraph.utils.config import patch_configurable
logger = logging.getLogger(__name__)
SUPPORTS_EXC_NOTES = sys.version_info >= (3, 11)
@@ -19,6 +19,7 @@ from typing import (
from langchain_core.callbacks import Callbacks
from langgraph._internal._future import chain_future, run_coroutine_threadsafe
from langgraph.constants import (
CONF,
CONFIG_KEY_CALL,
@@ -32,16 +33,15 @@ from langgraph.constants import (
TAG_HIDDEN,
)
from langgraph.errors import GraphBubbleUp, GraphInterrupt
from langgraph.pregel.algo import Call
from langgraph.pregel.executor import Submit
from langgraph.pregel.retry import arun_with_retry, run_with_retry
from langgraph.pregel._algo import Call
from langgraph.pregel._executor import Submit
from langgraph.pregel._retry import arun_with_retry, run_with_retry
from langgraph.pregel._scratchpad import PregelScratchpad
from langgraph.types import (
CachePolicy,
PregelExecutableTask,
PregelScratchpad,
RetryPolicy,
)
from langgraph.utils.future import chain_future, run_coroutine_threadsafe
F = TypeVar("F", concurrent.futures.Future, asyncio.Future)
E = TypeVar("E", threading.Event, asyncio.Event)
@@ -0,0 +1,18 @@
import dataclasses
from typing import Any, Callable
from langgraph.types import _DC_KWARGS
@dataclasses.dataclass(**_DC_KWARGS)
class PregelScratchpad:
step: int
stop: int
# call
call_counter: Callable[[], int]
# interrupt
interrupt_counter: Callable[[], int]
get_null_resume: Callable[[bool], Any]
resume: list[Any]
# subgraph
subgraph_counter: Callable[[], int]
@@ -6,12 +6,12 @@ import re
import textwrap
from typing import Any, Callable
from langchain_core.runnables import RunnableLambda, RunnableSequence
from langchain_core.runnables import Runnable, RunnableLambda, RunnableSequence
from typing_extensions import override
from langgraph._internal._runnable import RunnableCallable, RunnableSeq
from langgraph.checkpoint.base import ChannelVersions
from langgraph.pregel.protocol import PregelProtocol
from langgraph.utils.runnable import Runnable, RunnableCallable, RunnableSeq
def get_new_channel_versions(
@@ -6,7 +6,7 @@ from typing import Any
from langgraph.channels.base import BaseChannel
from langgraph.constants import RESERVED
from langgraph.managed.base import ManagedValueMapping
from langgraph.pregel.read import PregelNode
from langgraph.pregel._read import PregelNode
from langgraph.types import All
@@ -13,9 +13,10 @@ from typing import (
from langchain_core.runnables import Runnable, RunnableConfig
from langgraph.constants import CONF, CONFIG_KEY_SEND, MISSING, TASKS, Send
from langgraph._internal._runnable import RunnableCallable
from langgraph.constants import CONF, CONFIG_KEY_SEND, MISSING, TASKS
from langgraph.errors import InvalidUpdateError
from langgraph.utils.runnable import RunnableCallable
from langgraph.types import Send
TYPE_SEND = Callable[[Sequence[tuple[str, Any]]], None]
R = TypeVar("R", bound=Runnable)
+5 -2
View File
@@ -5,8 +5,10 @@ from dataclasses import asdict
from typing import Any
from uuid import UUID
from langchain_core.runnables import RunnableConfig
from typing_extensions import TypedDict
from langgraph._internal._config import patch_checkpoint_map
from langgraph.channels.base import BaseChannel
from langgraph.checkpoint.base import CheckpointMetadata, PendingWrite
from langgraph.constants import (
@@ -20,9 +22,10 @@ from langgraph.constants import (
RETURN,
TAG_HIDDEN,
)
from langgraph.pregel.io import read_channels
from langgraph.pregel._io import read_channels
from langgraph.types import PregelExecutableTask, PregelTask, StateSnapshot
from langgraph.utils.config import RunnableConfig, patch_checkpoint_map
__all__ = ("TaskPayload", "TaskResultPayload", "CheckpointTask", "CheckpointPayload")
class TaskPayload(TypedDict):
File diff suppressed because it is too large Load Diff
+23 -3
View File
@@ -2,17 +2,18 @@ from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import AsyncIterator, Iterator, Sequence
from typing import Any, Generic
from typing import Any, Callable, Generic, cast
from langchain_core.runnables import Runnable, RunnableConfig
from langchain_core.runnables.graph import Graph as DrawableGraph
from typing_extensions import Self
from langgraph.pregel.types import All, StateSnapshot, StateUpdate, StreamMode
from langgraph.types import All, StateSnapshot, StateUpdate, StreamMode
from langgraph.typing import InputT, OutputT, StateT
__all__ = ("PregelProtocol", "StreamProtocol")
# TODO: remove Runnable inheritance here!
class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT], ABC):
@abstractmethod
def with_config(
@@ -138,3 +139,22 @@ class PregelProtocol(Runnable[InputT, Any], Generic[StateT, InputT, OutputT], AB
interrupt_before: All | Sequence[str] | None = None,
interrupt_after: All | Sequence[str] | None = None,
) -> dict[str, Any] | Any: ...
StreamChunk = tuple[tuple[str, ...], str, Any]
class StreamProtocol:
__slots__ = ("modes", "__call__")
modes: set[StreamMode]
__call__: Callable[[Self, StreamChunk], None]
def __init__(
self,
__call__: Callable[[StreamChunk], None],
modes: set[StreamMode],
) -> None:
self.__call__ = cast(Callable[[Self, StreamChunk], None], __call__)
self.modes = modes
+19 -11
View File
@@ -29,6 +29,7 @@ from langgraph_sdk.schema import Command as CommandSDK
from langgraph_sdk.schema import StreamMode as StreamModeSDK
from typing_extensions import Self
from langgraph._internal._config import merge_configs
from langgraph.checkpoint.base import CheckpointMetadata
from langgraph.constants import (
CONF,
@@ -41,12 +42,19 @@ from langgraph.constants import (
NS_SEP,
)
from langgraph.errors import GraphInterrupt
from langgraph.pregel.protocol import PregelProtocol
from langgraph.pregel.types import All, PregelTask, StateSnapshot, StreamMode
from langgraph.types import Command, Interrupt, StreamProtocol
from langgraph.utils.config import merge_configs
from langgraph.pregel.protocol import PregelProtocol, StreamProtocol
from langgraph.types import (
All,
Command,
Interrupt,
PregelTask,
StateSnapshot,
StreamMode,
)
CONF_DROPLIST = frozenset(
__all__ = ("RemoteGraph", "RemoteException")
_CONF_DROPLIST = frozenset(
(
CONFIG_KEY_CHECKPOINT_MAP,
CONFIG_KEY_CHECKPOINT_ID,
@@ -56,7 +64,7 @@ CONF_DROPLIST = frozenset(
)
def sanitize_config_value(v: Any) -> Any:
def _sanitize_config_value(v: Any) -> Any:
"""Recursively sanitize a config value to ensure it contains only primitives."""
if isinstance(v, (str, int, float, bool)):
return v
@@ -64,14 +72,14 @@ def sanitize_config_value(v: Any) -> Any:
sanitized_dict = {}
for k, val in v.items():
if isinstance(k, str):
sanitized_value = sanitize_config_value(val)
sanitized_value = _sanitize_config_value(val)
if sanitized_value is not None:
sanitized_dict[k] = sanitized_value
return sanitized_dict
elif isinstance(v, (list, tuple)):
sanitized_list = []
for item in v:
sanitized_item = sanitize_config_value(item)
sanitized_item = _sanitize_config_value(item)
if sanitized_item is not None:
sanitized_list.append(sanitized_item)
return sanitized_list
@@ -347,7 +355,7 @@ class RemoteGraph(PregelProtocol):
for k, v in config["metadata"].items():
if (
isinstance(k, str)
and (sanitized_value := sanitize_config_value(v)) is not None
and (sanitized_value := _sanitize_config_value(v)) is not None
):
sanitized["metadata"][k] = sanitized_value
@@ -356,8 +364,8 @@ class RemoteGraph(PregelProtocol):
for k, v in config["configurable"].items():
if (
isinstance(k, str)
and k not in CONF_DROPLIST
and (sanitized_value := sanitize_config_value(v)) is not None
and k not in _CONF_DROPLIST
and (sanitized_value := _sanitize_config_value(v)) is not None
):
sanitized["configurable"][k] = sanitized_value
+11
View File
@@ -25,3 +25,14 @@ __all__ = [
"StreamWriter",
"default_retry_on",
]
from warnings import warn
from langgraph.warnings import LangGraphDeprecatedSinceV10
warn(
"Importing from langgraph.pregel.types is deprecated. "
"Please use 'from langgraph.types import ...' instead.",
LangGraphDeprecatedSinceV10,
stacklevel=2,
)
+22 -68
View File
@@ -18,12 +18,12 @@ from typing import (
)
from langchain_core.runnables import Runnable, RunnableConfig
from typing_extensions import Self
from xxhash import xxh3_128_hexdigest
from langgraph._internal._cache import default_cache_key
from langgraph._internal._fields import get_cached_annotated_keys, get_update_as_tuples
from langgraph._internal._retry import default_retry_on
from langgraph.checkpoint.base import BaseCheckpointSaver, CheckpointMetadata
from langgraph.utils.cache import default_cache_key
from langgraph.utils.fields import get_cached_annotated_keys, get_update_as_tuples
if TYPE_CHECKING:
from langgraph.pregel.protocol import PregelProtocol
@@ -37,6 +37,24 @@ except ImportError:
pass
__all__ = (
"All",
"Checkpointer",
"StreamMode",
"StreamWriter",
"RetryPolicy",
"CachePolicy",
"Interrupt",
"StateUpdate",
"PregelTask",
"PregelExecutableTask",
"StateSnapshot",
"Send",
"Command",
"interrupt",
)
All = Literal["*"]
"""Special value to indicate that graph should interrupt on all nodes."""
@@ -73,37 +91,6 @@ else:
_DC_KWARGS = {"frozen": True}
def default_retry_on(exc: Exception) -> bool:
import httpx
import requests
if isinstance(exc, ConnectionError):
return True
if isinstance(exc, httpx.HTTPStatusError):
return 500 <= exc.response.status_code < 600
if isinstance(exc, requests.HTTPError):
return 500 <= exc.response.status_code < 600 if exc.response else True
if isinstance(
exc,
(
ValueError,
TypeError,
ArithmeticError,
ImportError,
LookupError,
NameError,
SyntaxError,
RuntimeError,
ReferenceError,
StopIteration,
StopAsyncIteration,
OSError,
),
):
return False
return True
class RetryPolicy(NamedTuple):
"""Configuration for retrying nodes.
@@ -364,39 +351,6 @@ class Command(Generic[N], ToolOutputMixin):
PARENT: ClassVar[Literal["__parent__"]] = "__parent__"
StreamChunk = tuple[tuple[str, ...], str, Any]
class StreamProtocol:
__slots__ = ("modes", "__call__")
modes: set[StreamMode]
__call__: Callable[[Self, StreamChunk], None]
def __init__(
self,
__call__: Callable[[StreamChunk], None],
modes: set[StreamMode],
) -> None:
self.__call__ = cast(Callable[[Self, StreamChunk], None], __call__)
self.modes = modes
@dataclasses.dataclass(**_DC_KWARGS)
class PregelScratchpad:
step: int
stop: int
# call
call_counter: Callable[[], int]
# interrupt
interrupt_counter: Callable[[], int]
get_null_resume: Callable[[bool], Any]
resume: list[Any]
# subgraph
subgraph_counter: Callable[[], int]
def interrupt(value: Any) -> Any:
"""Interrupt the graph with a resumable exception from within a node.
@@ -504,7 +458,7 @@ def interrupt(value: Any) -> Any:
conf = get_config()["configurable"]
# track interrupt index
scratchpad: PregelScratchpad = conf[CONFIG_KEY_SCRATCHPAD]
scratchpad = conf[CONFIG_KEY_SCRATCHPAD]
idx = scratchpad.interrupt_counter()
# find previous resume values
if scratchpad.resume:
+9 -7
View File
@@ -4,7 +4,15 @@ from typing import Union
from typing_extensions import TypeVar
from langgraph._typing import StateLike
from langgraph._internal._typing import StateLike
__all__ = (
"StateT",
"StateT_co",
"StateT_contra",
"InputT",
"OutputT",
)
StateT = TypeVar("StateT", bound=StateLike)
"""Type variable used to represent the state in a graph."""
@@ -19,12 +27,6 @@ InputT = TypeVar("InputT", bound=StateLike, default=StateT)
Defaults to `StateT`.
"""
ResolvedInputT = TypeVar("ResolvedInputT", bound=StateLike)
"""Type variable used to represent the resolved input to a state graph.
No default.
"""
OutputT = TypeVar("OutputT", bound=Union[StateLike, None], default=StateT)
"""Type variable used to represent the output of a state graph."""
+2
View File
@@ -2,6 +2,8 @@
from importlib import metadata
__all__ = ("__version__",)
try:
__version__ = metadata.version(__package__)
except metadata.PackageNotFoundError:
+13
View File
@@ -2,6 +2,12 @@
from __future__ import annotations
__all__ = (
"LangGraphDeprecationWarning",
"LangGraphDeprecatedSinceV05",
"LangGraphDeprecatedSinceV10",
)
class LangGraphDeprecationWarning(DeprecationWarning):
"""A LangGraph specific deprecation warning.
@@ -46,3 +52,10 @@ class LangGraphDeprecatedSinceV05(LangGraphDeprecationWarning):
def __init__(self, message: str, *args: object) -> None:
super().__init__(message, *args, since=(0, 5), expected_removal=(2, 0))
class LangGraphDeprecatedSinceV10(LangGraphDeprecationWarning):
"""A specific `LangGraphDeprecationWarning` subclass defining functionality deprecated since LangGraph v1.0.0"""
def __init__(self, message: str, *args: object) -> None:
super().__init__(message, *args, since=(1, 0), expected_removal=(2, 0))
@@ -1,105 +1,4 @@
# serializer version: 1
# name: test_conditional_graph[memory]
'''
{
"nodes": [
{
"id": "agent",
"type": "runnable",
"data": {
"id": [
"langchain",
"schema",
"runnable",
"RunnableAssign"
],
"name": "agent"
}
},
{
"id": "tools",
"type": "runnable",
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"RunnableCallable"
],
"name": "tools"
},
"metadata": {
"parents": {},
"version": 2,
"variant": "b"
}
},
{
"id": "__start__"
},
{
"id": "__end__"
}
],
"edges": [
{
"source": "__start__",
"target": "agent"
},
{
"source": "agent",
"target": "__end__",
"data": "exit",
"conditional": true
},
{
"source": "agent",
"target": "tools",
"data": "continue",
"conditional": true
},
{
"source": "tools",
"target": "agent"
}
]
}
'''
# ---
# name: test_conditional_graph[memory].1
'''
graph TD;
__start__ --> agent;
agent -. &nbsp;exit&nbsp; .-> __end__;
agent -. &nbsp;continue&nbsp; .-> tools;
tools --> agent;
'''
# ---
# name: test_conditional_graph[memory].2
'''
---
config:
flowchart:
curve: linear
---
graph TD;
agent(agent)
tools(tools<hr/><small><em>parents = {}
version = 2
variant = b</em></small>)
__start__([<p>__start__</p>]):::first
__end__([<p>__end__</p>]):::last
__start__ --> agent;
agent -. &nbsp;exit&nbsp; .-> __end__;
agent -. &nbsp;continue&nbsp; .-> tools;
tools --> agent;
classDef default fill:#f2f0ff,line-height:1.2
classDef first fill-opacity:0
classDef last fill:#bfb6fc
'''
# ---
# name: test_conditional_state_graph[memory]
'{"$defs": {"AgentAction": {"description": "Represents a request to execute an action by an agent.\\n\\nThe action consists of the name of the tool to execute and the input to pass\\nto the tool. The log is used to pass along extra information about the action.", "properties": {"tool": {"title": "Tool", "type": "string"}, "tool_input": {"anyOf": [{"type": "string"}, {"type": "object"}], "title": "Tool Input"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentAction", "default": "AgentAction", "enum": ["AgentAction"], "title": "Type", "type": "string"}}, "required": ["tool", "tool_input", "log"], "title": "AgentAction", "type": "object"}, "AgentFinish": {"description": "Final return value of an ActionAgent.\\n\\nAgents return an AgentFinish when they have reached a stopping condition.", "properties": {"return_values": {"title": "Return Values", "type": "object"}, "log": {"title": "Log", "type": "string"}, "type": {"const": "AgentFinish", "default": "AgentFinish", "enum": ["AgentFinish"], "title": "Type", "type": "string"}}, "required": ["return_values", "log"], "title": "AgentFinish", "type": "object"}}, "properties": {"input": {"default": null, "title": "Input", "type": "string"}, "agent_outcome": {"anyOf": [{"$ref": "#/$defs/AgentAction"}, {"$ref": "#/$defs/AgentFinish"}, {"type": "null"}], "default": null, "title": "Agent Outcome"}, "intermediate_steps": {"default": null, "items": {"maxItems": 2, "minItems": 2, "prefixItems": [{"$ref": "#/$defs/AgentAction"}, {"type": "string"}], "type": "array"}, "title": "Intermediate Steps", "type": "array"}}, "title": "LangGraphInput", "type": "object"}'
# ---
@@ -116,8 +15,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -142,8 +41,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "tools"
@@ -204,8 +103,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -291,8 +190,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -304,8 +203,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "agent"
@@ -1,93 +1,4 @@
# serializer version: 1
# name: test_conditional_entrypoint_graph
'{"title": "LangGraphInput"}'
# ---
# name: test_conditional_entrypoint_graph.1
'{"title": "LangGraphOutput"}'
# ---
# name: test_conditional_entrypoint_graph.2
'''
{
"nodes": [
{
"id": "left",
"type": "runnable",
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"RunnableCallable"
],
"name": "left"
}
},
{
"id": "right",
"type": "runnable",
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"RunnableCallable"
],
"name": "right"
}
},
{
"id": "__start__",
"type": "runnable",
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"RunnableCallable"
],
"name": "__start__"
}
},
{
"id": "__end__"
}
],
"edges": [
{
"source": "__start__",
"target": "left",
"data": "go-left",
"conditional": true
},
{
"source": "__start__",
"target": "right",
"data": "go-right",
"conditional": true
},
{
"source": "left",
"target": "__end__",
"conditional": true
},
{
"source": "right",
"target": "__end__"
}
]
}
'''
# ---
# name: test_conditional_entrypoint_graph.3
'''
graph TD;
__start__ -. &nbsp;go-left&nbsp; .-> left;
__start__ -. &nbsp;go-right&nbsp; .-> right;
left -.-> __end__;
right --> __end__;
'''
# ---
# name: test_conditional_entrypoint_graph_state
'{"properties": {"input": {"default": null, "title": "Input", "type": "string"}, "output": {"default": null, "title": "Output", "type": "string"}, "steps": {"default": null, "items": {"type": "string"}, "title": "Steps", "type": "array"}}, "title": "LangGraphInput", "type": "object"}'
# ---
@@ -104,8 +15,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -117,8 +28,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "left"
@@ -130,8 +41,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "right"
@@ -193,8 +104,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -206,8 +117,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "get_weather"
@@ -249,8 +160,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -262,8 +173,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "A"
@@ -275,8 +186,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "B"
@@ -327,8 +238,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -340,8 +251,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "human"
@@ -353,8 +264,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "agent"
@@ -406,8 +317,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -461,8 +372,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "__start__"
@@ -474,8 +385,8 @@
"data": {
"id": [
"langgraph",
"utils",
"runnable",
"_internal",
"_runnable",
"RunnableCallable"
],
"name": "worker_node"
@@ -767,8 +678,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': '__start__',
@@ -780,8 +691,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'tool_one',
@@ -793,8 +704,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'tool_three',
@@ -809,8 +720,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'tool_two:__start__',
@@ -822,8 +733,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'tool_two:tool_two_slow',
@@ -835,8 +746,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'tool_two:tool_two_fast',
@@ -1020,8 +931,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': '__start__',
@@ -1033,8 +944,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'ask_question',
@@ -1046,8 +957,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'answer_question',
@@ -1087,8 +998,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': '__start__',
@@ -1100,8 +1011,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'generate_analysts',
@@ -1126,8 +1037,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'generate_sections',
@@ -1185,8 +1096,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': '__start__',
@@ -1198,8 +1109,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'generate_analysts',
@@ -1211,8 +1122,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'generate_sections',
@@ -1227,8 +1138,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'conduct_interview:__start__',
@@ -1240,8 +1151,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'conduct_interview:ask_question',
@@ -1253,8 +1164,8 @@
'data': dict({
'id': list([
'langgraph',
'utils',
'runnable',
'_internal',
'_runnable',
'RunnableCallable',
]),
'name': 'conduct_interview:answer_question',
+2 -2
View File
@@ -1,6 +1,6 @@
from langgraph.constants import PULL, PUSH
from langgraph.pregel.algo import prepare_next_tasks, task_path_str
from langgraph.pregel.checkpoint import channels_from_checkpoint, empty_checkpoint
from langgraph.pregel._algo import prepare_next_tasks, task_path_str
from langgraph.pregel._checkpoint import channels_from_checkpoint, empty_checkpoint
def test_prepare_next_tasks() -> None:
@@ -7,11 +7,11 @@ from typing import Annotated, Literal, Optional, Union
import pytest
from typing_extensions import TypedDict
from langgraph._internal._config import patch_configurable
from langgraph.checkpoint.base import BaseCheckpointSaver, CheckpointTuple
from langgraph.graph.state import StateGraph
from langgraph.pregel.checkpoint import copy_checkpoint
from langgraph.pregel._checkpoint import copy_checkpoint
from langgraph.types import Command, Interrupt, PregelTask, StateSnapshot, interrupt
from langgraph.utils.config import patch_configurable
from tests.any_int import AnyInt
from tests.any_str import AnyDict, AnyObject, AnyStr
+1 -1
View File
@@ -1,7 +1,7 @@
import pytest
from langchain_core.callbacks import AsyncCallbackManager
from langgraph.utils.config import get_async_callback_manager_for_config
from langgraph._internal._config import get_async_callback_manager_for_config
pytestmark = pytest.mark.anyio
+23 -1
View File
@@ -4,7 +4,7 @@ from typing_extensions import TypedDict
from langgraph.func import entrypoint, task
from langgraph.graph import StateGraph
from langgraph.types import RetryPolicy
from langgraph.warnings import LangGraphDeprecatedSinceV05
from langgraph.warnings import LangGraphDeprecatedSinceV05, LangGraphDeprecatedSinceV10
class PlainState(TypedDict): ...
@@ -66,3 +66,25 @@ def test_add_node_input_schema() -> None:
match="`input` is deprecated and will be removed. Please use `input_schema` instead.",
):
builder.add_node("test_node", lambda state: state, input=PlainState) # type: ignore[arg-type]
def test_constants_deprecation() -> None:
with pytest.warns(
LangGraphDeprecatedSinceV10,
match="Importing Send from langgraph.constants is deprecated. Please use 'from langgraph.types import Send' instead.",
):
from langgraph.constants import Send # noqa: F401
with pytest.warns(
LangGraphDeprecatedSinceV10,
match="Importing Interrupt from langgraph.constants is deprecated. Please use 'from langgraph.types import Interrupt' instead.",
):
from langgraph.constants import Interrupt # noqa: F401
def test_pregel_deprecation() -> None:
with pytest.warns(
LangGraphDeprecatedSinceV10,
match="Importing from langgraph.pregel.types is deprecated. Please use 'from langgraph.types import ...' instead.",
):
from langgraph.pregel.types import StateSnapshot # noqa: F401
+5 -6
View File
@@ -43,27 +43,26 @@ from langgraph.checkpoint.base import (
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.config import get_stream_writer
from langgraph.constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL, START
from langgraph.errors import InvalidUpdateError, ParentCommand
from langgraph.errors import GraphRecursionError, InvalidUpdateError, ParentCommand
from langgraph.func import entrypoint, task
from langgraph.graph import END, StateGraph
from langgraph.graph.message import MessageGraph, MessagesState, add_messages
from langgraph.prebuilt.tool_node import ToolNode
from langgraph.pregel import (
GraphRecursionError,
NodeBuilder,
Pregel,
StateSnapshot,
)
from langgraph.pregel.loop import SyncPregelLoop
from langgraph.pregel.retry import RetryPolicy
from langgraph.pregel.runner import PregelRunner
from langgraph.pregel._loop import SyncPregelLoop
from langgraph.pregel._runner import PregelRunner
from langgraph.store.base import BaseStore
from langgraph.types import (
CachePolicy,
Command,
Interrupt,
PregelTask,
RetryPolicy,
Send,
StateSnapshot,
StateUpdate,
StreamWriter,
interrupt,
+15 -9
View File
@@ -42,22 +42,28 @@ from langgraph.checkpoint.base import (
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
from langgraph.constants import CONFIG_KEY_NODE_FINISHED, ERROR, PULL, START
from langgraph.errors import InvalidUpdateError, NodeInterrupt, ParentCommand
from langgraph.errors import (
GraphRecursionError,
InvalidUpdateError,
NodeInterrupt,
ParentCommand,
)
from langgraph.func import entrypoint, task
from langgraph.graph import END, StateGraph
from langgraph.graph.message import MessagesState, add_messages
from langgraph.prebuilt.tool_node import ToolNode
from langgraph.pregel import GraphRecursionError, NodeBuilder, Pregel, StateSnapshot
from langgraph.pregel.loop import AsyncPregelLoop
from langgraph.pregel.retry import RetryPolicy
from langgraph.pregel.runner import PregelRunner
from langgraph.pregel import NodeBuilder, Pregel
from langgraph.pregel._loop import AsyncPregelLoop
from langgraph.pregel._runner import PregelRunner
from langgraph.store.base import BaseStore
from langgraph.types import (
CachePolicy,
Command,
Interrupt,
PregelTask,
RetryPolicy,
Send,
StateSnapshot,
StateUpdate,
StreamWriter,
interrupt,
@@ -8381,7 +8387,7 @@ async def test_draw_invalid():
"id": "__start__",
"type": "runnable",
"data": {
"id": ["langgraph", "utils", "runnable", "RunnableCallable"],
"id": ["langgraph", "_internal", "_runnable", "RunnableCallable"],
"name": "__start__",
},
},
@@ -8389,7 +8395,7 @@ async def test_draw_invalid():
"id": "agent",
"type": "runnable",
"data": {
"id": ["langgraph", "utils", "runnable", "RunnableCallable"],
"id": ["langgraph", "_internal", "_runnable", "RunnableCallable"],
"name": "agent",
},
},
@@ -8397,7 +8403,7 @@ async def test_draw_invalid():
"id": "tool",
"type": "runnable",
"data": {
"id": ["langgraph", "utils", "runnable", "RunnableCallable"],
"id": ["langgraph", "_internal", "_runnable", "RunnableCallable"],
"name": "tool",
},
},
@@ -8405,7 +8411,7 @@ async def test_draw_invalid():
"id": "nothing",
"type": "runnable",
"data": {
"id": ["langgraph", "utils", "runnable", "RunnableCallable"],
"id": ["langgraph", "_internal", "_runnable", "RunnableCallable"],
"name": "nothing",
},
},
+1 -1
View File
@@ -21,9 +21,9 @@ from pydantic import (
model_validator,
)
from langgraph._internal._pydantic import is_supported_by_pydantic
from langgraph.constants import END, START
from langgraph.graph.state import StateGraph
from langgraph.utils.pydantic import is_supported_by_pydantic
def test_is_supported_by_pydantic() -> None:
+1 -2
View File
@@ -15,8 +15,7 @@ from langgraph.errors import GraphInterrupt
from langgraph.graph import StateGraph, add_messages
from langgraph.pregel import Pregel
from langgraph.pregel.remote import RemoteGraph
from langgraph.pregel.types import StateSnapshot
from langgraph.types import Interrupt
from langgraph.types import Interrupt, StateSnapshot
from tests.conftest import NO_DOCKER
from tests.example_app.example_graph import app
+1 -1
View File
@@ -4,7 +4,7 @@ import pytest
from typing_extensions import TypedDict
from langgraph.graph import START, StateGraph
from langgraph.pregel.retry import _should_retry_on
from langgraph.pregel._retry import _should_retry_on
from langgraph.types import RetryPolicy
+3 -3
View File
@@ -4,9 +4,9 @@ from typing import Any, Optional
import pytest
from langgraph._internal._runnable import RunnableCallable
from langgraph.store.base import BaseStore
from langgraph.types import StreamWriter
from langgraph.utils.runnable import RunnableCallable
pytestmark = pytest.mark.anyio
@@ -85,7 +85,7 @@ def test_runnable_callable_injectable_arguments() -> None:
"""
# Test Optional[BaseStore] annotation.
def func_optional_store(inputs: Any, store: Optional[BaseStore]) -> str: # noqa: UP007
def func_optional_store(inputs: Any, store: Optional[BaseStore]) -> str: # noqa: UP045
"""Test function that accepts an optional store parameter."""
assert store is None
return "success"
@@ -159,7 +159,7 @@ async def test_runnable_callable_injectable_arguments_async() -> None:
"""
# Test Optional[BaseStore] annotation.
def func_optional_store(inputs: Any, store: Optional[BaseStore]) -> str: # noqa: UP007
def func_optional_store(inputs: Any, store: Optional[BaseStore]) -> str: # noqa: UP045
"""Test function that accepts an optional store parameter."""
assert store is None
return "success"
+5 -5
View File
@@ -17,18 +17,18 @@ import langsmith
import pytest
from typing_extensions import NotRequired, Required, TypedDict
from langgraph.graph import END, StateGraph
from langgraph.graph.state import CompiledStateGraph
from langgraph.utils.config import _is_not_empty
from langgraph.utils.fields import (
from langgraph._internal._config import _is_not_empty
from langgraph._internal._fields import (
_is_optional_type,
get_enhanced_type_hints,
get_field_default,
)
from langgraph.utils.runnable import (
from langgraph._internal._runnable import (
is_async_callable,
is_async_generator,
)
from langgraph.graph import END, StateGraph
from langgraph.graph.state import CompiledStateGraph
pytestmark = pytest.mark.anyio
+2
View File
@@ -0,0 +1,2 @@
# import for backwards compatibility
from langgraph._internal._runnable import RunnableCallable, RunnableSeq # noqa: F401
+414 -395
View File
File diff suppressed because it is too large Load Diff
@@ -34,6 +34,7 @@ from langchain_core.tools import BaseTool
from pydantic import BaseModel
from typing_extensions import Annotated, TypedDict
from langgraph._internal._runnable import RunnableCallable, RunnableLike
from langgraph.errors import ErrorCode, create_error_message
from langgraph.graph import END, StateGraph
from langgraph.graph.message import add_messages
@@ -42,7 +43,6 @@ from langgraph.managed import IsLastStep, RemainingSteps
from langgraph.prebuilt.tool_node import ToolNode
from langgraph.store.base import BaseStore
from langgraph.types import Checkpointer, Send
from langgraph.utils.runnable import RunnableCallable, RunnableLike
StructuredResponse = Union[dict, BaseModel]
StructuredResponseSchema = Union[dict, type[BaseModel]]
@@ -37,10 +37,10 @@ from langchain_core.tools.base import (
from pydantic import BaseModel
from typing_extensions import Annotated, get_args, get_origin
from langgraph._internal._runnable import RunnableCallable
from langgraph.errors import GraphBubbleUp
from langgraph.store.base import BaseStore
from langgraph.types import Command, Send
from langgraph.utils.runnable import RunnableCallable
INVALID_TOOL_NAME_ERROR_TEMPLATE = (
"Error: {requested_tool} is not a valid tool, try one of [{available_tools}]."
@@ -34,7 +34,7 @@ from pydantic import BaseModel, ValidationError
from pydantic.v1 import BaseModel as BaseModelV1
from pydantic.v1 import ValidationError as ValidationErrorV1
from langgraph.utils.runnable import RunnableCallable
from langgraph._internal._runnable import RunnableCallable
def _default_format_error(
+1 -1
View File
@@ -15,7 +15,7 @@ from langgraph.checkpoint.base import (
SerializerProtocol,
)
from langgraph.checkpoint.memory import InMemorySaver, PersistentDict
from langgraph.pregel.checkpoint import copy_checkpoint
from langgraph.pregel._checkpoint import copy_checkpoint
class NoopSerializer(SerializerProtocol):
+1 -1
View File
@@ -29,6 +29,7 @@ from pydantic.v1 import BaseModel as BaseModelV1
from typing_extensions import TypedDict
from langgraph.checkpoint.base import BaseCheckpointSaver
from langgraph.config import get_stream_writer
from langgraph.graph import START, MessagesState, StateGraph, add_messages
from langgraph.graph.message import REMOVE_ALL_MESSAGES
from langgraph.prebuilt import (
@@ -53,7 +54,6 @@ from langgraph.prebuilt.tool_node import (
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore
from langgraph.types import Command, Interrupt, interrupt
from langgraph.utils.config import get_stream_writer
from tests.any_str import AnyStr
from tests.messages import _AnyIdHumanMessage, _AnyIdToolMessage
from tests.model import FakeToolCallingModel