mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-31 12:19:58 +02:00
Merge branch 'main' into sr/better-interrupts
This commit is contained in:
@@ -78,7 +78,7 @@ test_watch_all:
|
||||
PYTHON_FILES=.
|
||||
MYPY_CACHE=.mypy_cache
|
||||
lint format: PYTHON_FILES=.
|
||||
lint_diff format_diff: PYTHON_FILES=$(shell git diff --name-only --relative --diff-filter=d main . | grep -E '\.py$$|\.ipynb$$')
|
||||
lint_diff format_diff: PYTHON_FILES=$(shell git diff --name-only --relative --diff-filter=d main . | grep -E r'\.py$$|\.ipynb$$')
|
||||
lint_package: PYTHON_FILES=langgraph
|
||||
lint_tests: PYTHON_FILES=tests
|
||||
lint_tests: MYPY_CACHE=.mypy_cache_test
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import operator
|
||||
from collections.abc import Sequence
|
||||
from functools import partial
|
||||
from random import choice
|
||||
from typing import Annotated, Optional, Sequence
|
||||
from typing import Annotated, Optional
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import operator
|
||||
from collections.abc import Sequence
|
||||
from functools import partial
|
||||
from random import choice
|
||||
from typing import Annotated, Optional, Sequence
|
||||
from typing import Annotated, Optional
|
||||
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import operator
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import dataclass, field
|
||||
from functools import partial
|
||||
from random import choice
|
||||
from typing import Annotated, Optional, Sequence
|
||||
from typing import Annotated, Optional
|
||||
|
||||
from langgraph.constants import END, START
|
||||
from langgraph.graph.state import StateGraph
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import functools
|
||||
import warnings
|
||||
from typing import Any, Callable, Type, TypeVar, Union, cast
|
||||
from typing import Any, Callable, TypeVar, Union, cast
|
||||
|
||||
|
||||
class LangGraphDeprecationWarning(DeprecationWarning):
|
||||
@@ -8,7 +8,7 @@ class LangGraphDeprecationWarning(DeprecationWarning):
|
||||
|
||||
|
||||
F = TypeVar("F", bound=Callable[..., Any])
|
||||
C = TypeVar("C", bound=Type[Any])
|
||||
C = TypeVar("C", bound=type[Any])
|
||||
|
||||
|
||||
def deprecated(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Generic, Sequence, Type
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, Generic
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
@@ -21,12 +22,12 @@ class AnyValue(Generic[Value], BaseChannel[Value, Value, Value]):
|
||||
return isinstance(value, AnyValue)
|
||||
|
||||
@property
|
||||
def ValueType(self) -> Type[Value]:
|
||||
def ValueType(self) -> type[Value]:
|
||||
"""The type of the value stored in the channel."""
|
||||
return self.typ
|
||||
|
||||
@property
|
||||
def UpdateType(self) -> Type[Value]:
|
||||
def UpdateType(self) -> type[Value]:
|
||||
"""The type of the update received by the channel."""
|
||||
return self.typ
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Generic, Sequence, TypeVar
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, Generic, TypeVar
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import collections.abc
|
||||
from typing import Callable, Generic, Sequence, Type
|
||||
from collections.abc import Sequence
|
||||
from typing import Callable, Generic
|
||||
|
||||
from typing_extensions import NotRequired, Required, Self
|
||||
|
||||
@@ -31,7 +32,7 @@ class BinaryOperatorAggregate(Generic[Value], BaseChannel[Value, Value, Value]):
|
||||
|
||||
__slots__ = ("value", "operator")
|
||||
|
||||
def __init__(self, typ: Type[Value], operator: Callable[[Value, Value], Value]):
|
||||
def __init__(self, typ: type[Value], operator: Callable[[Value, Value], Value]):
|
||||
super().__init__(typ)
|
||||
self.operator = operator
|
||||
# special forms from typing or collections.abc are not instantiable
|
||||
@@ -57,12 +58,12 @@ class BinaryOperatorAggregate(Generic[Value], BaseChannel[Value, Value, Value]):
|
||||
)
|
||||
|
||||
@property
|
||||
def ValueType(self) -> Type[Value]:
|
||||
def ValueType(self) -> type[Value]:
|
||||
"""The type of the value stored in the channel."""
|
||||
return self.typ
|
||||
|
||||
@property
|
||||
def UpdateType(self) -> Type[Value]:
|
||||
def UpdateType(self) -> type[Value]:
|
||||
"""The type of the update received by the channel."""
|
||||
return self.typ
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from collections.abc import Set
|
||||
from typing import Any, Generic, NamedTuple, Optional, Sequence, Type, Union
|
||||
from collections.abc import Sequence, Set
|
||||
from typing import Any, Generic, NamedTuple, Optional, Union
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
@@ -13,7 +13,7 @@ class WaitForNames(NamedTuple):
|
||||
|
||||
|
||||
class DynamicBarrierValue(
|
||||
Generic[Value], BaseChannel[Value, Union[Value, WaitForNames], set[Value]]
|
||||
Generic[Value], BaseChannel[Value, Union[Value, WaitForNames], Set[Value]]
|
||||
):
|
||||
"""A channel that switches between two states
|
||||
|
||||
@@ -29,7 +29,7 @@ class DynamicBarrierValue(
|
||||
names: Optional[Set[Value]]
|
||||
seen: set[Value]
|
||||
|
||||
def __init__(self, typ: Type[Value]) -> None:
|
||||
def __init__(self, typ: type[Value]) -> None:
|
||||
super().__init__(typ)
|
||||
self.names = None
|
||||
self.seen = set()
|
||||
@@ -38,12 +38,12 @@ class DynamicBarrierValue(
|
||||
return isinstance(value, DynamicBarrierValue) and value.names == self.names
|
||||
|
||||
@property
|
||||
def ValueType(self) -> Type[Value]:
|
||||
def ValueType(self) -> type[Value]:
|
||||
"""The type of the value stored in the channel."""
|
||||
return self.typ
|
||||
|
||||
@property
|
||||
def UpdateType(self) -> Type[Value]:
|
||||
def UpdateType(self) -> type[Value]:
|
||||
"""The type of the update received by the channel."""
|
||||
return self.typ
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Generic, Sequence, Type
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, Generic
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
@@ -21,12 +22,12 @@ class EphemeralValue(Generic[Value], BaseChannel[Value, Value, Value]):
|
||||
return isinstance(value, EphemeralValue) and value.guard == self.guard
|
||||
|
||||
@property
|
||||
def ValueType(self) -> Type[Value]:
|
||||
def ValueType(self) -> type[Value]:
|
||||
"""The type of the value stored in the channel."""
|
||||
return self.typ
|
||||
|
||||
@property
|
||||
def UpdateType(self) -> Type[Value]:
|
||||
def UpdateType(self) -> type[Value]:
|
||||
"""The type of the update received by the channel."""
|
||||
return self.typ
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Generic, Sequence, Type
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, Generic
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
@@ -25,12 +26,12 @@ class LastValue(Generic[Value], BaseChannel[Value, Value, Value]):
|
||||
return isinstance(value, LastValue)
|
||||
|
||||
@property
|
||||
def ValueType(self) -> Type[Value]:
|
||||
def ValueType(self) -> type[Value]:
|
||||
"""The type of the value stored in the channel."""
|
||||
return self.typ
|
||||
|
||||
@property
|
||||
def UpdateType(self) -> Type[Value]:
|
||||
def UpdateType(self) -> type[Value]:
|
||||
"""The type of the update received by the channel."""
|
||||
return self.typ
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Generic, Sequence, Type
|
||||
from collections.abc import Sequence
|
||||
from typing import Generic
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
@@ -15,7 +16,7 @@ class NamedBarrierValue(Generic[Value], BaseChannel[Value, Value, set[Value]]):
|
||||
names: set[Value]
|
||||
seen: set[Value]
|
||||
|
||||
def __init__(self, typ: Type[Value], names: set[Value]) -> None:
|
||||
def __init__(self, typ: type[Value], names: set[Value]) -> None:
|
||||
super().__init__(typ)
|
||||
self.names = names
|
||||
self.seen: set[str] = set()
|
||||
@@ -24,12 +25,12 @@ class NamedBarrierValue(Generic[Value], BaseChannel[Value, Value, set[Value]]):
|
||||
return isinstance(value, NamedBarrierValue) and value.names == self.names
|
||||
|
||||
@property
|
||||
def ValueType(self) -> Type[Value]:
|
||||
def ValueType(self) -> type[Value]:
|
||||
"""The type of the value stored in the channel."""
|
||||
return self.typ
|
||||
|
||||
@property
|
||||
def UpdateType(self) -> Type[Value]:
|
||||
def UpdateType(self) -> type[Value]:
|
||||
"""The type of the update received by the channel."""
|
||||
return self.typ
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Generic, Iterator, Sequence, Type, Union
|
||||
from collections.abc import Iterator, Sequence
|
||||
from typing import Any, Generic, Union
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
@@ -28,7 +29,7 @@ class Topic(
|
||||
|
||||
__slots__ = ("values", "accumulate")
|
||||
|
||||
def __init__(self, typ: Type[Value], accumulate: bool = False) -> None:
|
||||
def __init__(self, typ: type[Value], accumulate: bool = False) -> None:
|
||||
super().__init__(typ)
|
||||
# attrs
|
||||
self.accumulate = accumulate
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Generic, Sequence, Type
|
||||
from collections.abc import Sequence
|
||||
from typing import Generic
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
@@ -12,7 +13,7 @@ class UntrackedValue(Generic[Value], BaseChannel[Value, Value, Value]):
|
||||
|
||||
__slots__ = ("value", "guard")
|
||||
|
||||
def __init__(self, typ: Type[Value], guard: bool = True) -> None:
|
||||
def __init__(self, typ: type[Value], guard: bool = True) -> None:
|
||||
super().__init__(typ)
|
||||
self.guard = guard
|
||||
self.value = MISSING
|
||||
@@ -21,12 +22,12 @@ class UntrackedValue(Generic[Value], BaseChannel[Value, Value, Value]):
|
||||
return isinstance(value, UntrackedValue) and value.guard == self.guard
|
||||
|
||||
@property
|
||||
def ValueType(self) -> Type[Value]:
|
||||
def ValueType(self) -> type[Value]:
|
||||
"""The type of the value stored in the channel."""
|
||||
return self.typ
|
||||
|
||||
@property
|
||||
def UpdateType(self) -> Type[Value]:
|
||||
def UpdateType(self) -> type[Value]:
|
||||
"""The type of the update received by the channel."""
|
||||
return self.typ
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from collections.abc import Mapping
|
||||
from types import MappingProxyType
|
||||
from typing import Any, Literal, Mapping, cast
|
||||
from typing import Any, Literal, cast
|
||||
|
||||
from langgraph.types import Interrupt, Send # noqa: F401
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from collections.abc import Sequence
|
||||
from enum import Enum
|
||||
from typing import Any, Sequence
|
||||
from typing import Any
|
||||
|
||||
from langgraph.checkpoint.base import EmptyChannelError # noqa: F401
|
||||
from langgraph.types import Command, Interrupt
|
||||
|
||||
@@ -2,14 +2,13 @@ import asyncio
|
||||
import concurrent.futures
|
||||
import functools
|
||||
import inspect
|
||||
from collections.abc import Awaitable, Sequence
|
||||
from dataclasses import dataclass
|
||||
from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Generic,
|
||||
Optional,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
Union,
|
||||
get_args,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from collections.abc import Awaitable, Hashable, Sequence
|
||||
from inspect import (
|
||||
isfunction,
|
||||
ismethod,
|
||||
@@ -7,14 +8,10 @@ from itertools import zip_longest
|
||||
from types import FunctionType
|
||||
from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Hashable,
|
||||
Literal,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
Union,
|
||||
cast,
|
||||
get_args,
|
||||
@@ -48,7 +45,7 @@ def _get_branch_path_input_schema(
|
||||
Callable[..., Awaitable[Union[Hashable, list[Hashable]]]],
|
||||
Runnable[Any, Union[Hashable, list[Hashable]]],
|
||||
],
|
||||
) -> Optional[Type[Any]]:
|
||||
) -> Optional[type[Any]]:
|
||||
input = None
|
||||
# detect input schema annotation in the branch callable
|
||||
try:
|
||||
@@ -91,7 +88,7 @@ class Branch(NamedTuple):
|
||||
path: Runnable[Any, Union[Hashable, list[Hashable]]]
|
||||
ends: Optional[dict[Hashable, str]]
|
||||
then: Optional[str] = None
|
||||
input_schema: Optional[Type[Any]] = None
|
||||
input_schema: Optional[type[Any]] = None
|
||||
|
||||
@classmethod
|
||||
def from_path(
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from collections.abc import Awaitable, Hashable, Sequence
|
||||
from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Hashable,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Sequence,
|
||||
Union,
|
||||
cast,
|
||||
overload,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import uuid
|
||||
import warnings
|
||||
from collections.abc import Sequence
|
||||
from functools import partial
|
||||
from typing import (
|
||||
Annotated,
|
||||
@@ -7,7 +8,6 @@ from typing import (
|
||||
Callable,
|
||||
Literal,
|
||||
Optional,
|
||||
Sequence,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
@@ -3,10 +3,10 @@ import logging
|
||||
import weakref
|
||||
from inspect import isclass
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
Callable,
|
||||
Optional,
|
||||
Type,
|
||||
Union,
|
||||
get_args,
|
||||
get_origin,
|
||||
@@ -15,14 +15,13 @@ from typing import (
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
from typing_extensions import Annotated
|
||||
|
||||
__all__ = ["SchemaCoercionMapper"]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
_cache: weakref.WeakKeyDictionary[Type[Any], dict[int, "SchemaCoercionMapper"]] = (
|
||||
_cache: weakref.WeakKeyDictionary[type[Any], dict[int, "SchemaCoercionMapper"]] = (
|
||||
weakref.WeakKeyDictionary()
|
||||
)
|
||||
|
||||
@@ -32,7 +31,7 @@ class SchemaCoercionMapper:
|
||||
|
||||
def __new__(
|
||||
cls,
|
||||
schema: Type[Any],
|
||||
schema: type[Any],
|
||||
type_hints: Optional[dict[str, Any]] = None,
|
||||
*,
|
||||
max_depth: int = 12,
|
||||
@@ -46,7 +45,7 @@ class SchemaCoercionMapper:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
schema: Type[Any],
|
||||
schema: type[Any],
|
||||
type_hints: Optional[dict[str, Any]] = None,
|
||||
*,
|
||||
max_depth: int = 12,
|
||||
@@ -187,7 +186,7 @@ class SchemaCoercionMapper:
|
||||
def dict_coercer(v: Any, d: Any) -> Any:
|
||||
if not isinstance(v, dict):
|
||||
if throw:
|
||||
raise TypeError("Expected dict, got %s" % type(v))
|
||||
raise TypeError(f"Expected dict, got {type(v)}")
|
||||
return v
|
||||
|
||||
return dict_coercer
|
||||
@@ -197,7 +196,7 @@ class SchemaCoercionMapper:
|
||||
def dict_coercer(v: Any, d: Any) -> Any:
|
||||
if not isinstance(v, dict):
|
||||
if throw:
|
||||
raise TypeError("Expected dict, got %s" % type(v))
|
||||
raise TypeError(f"Expected dict, got {type(v)}")
|
||||
return v
|
||||
return {k_sub(k, d - 1): v_sub(val, d - 1) for k, val in v.items()}
|
||||
|
||||
|
||||
@@ -3,19 +3,16 @@ import logging
|
||||
import typing
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
from collections.abc import Awaitable, Hashable, Sequence
|
||||
from functools import partial
|
||||
from inspect import isclass, isfunction, ismethod, signature
|
||||
from types import FunctionType
|
||||
from typing import (
|
||||
Any,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Hashable,
|
||||
Literal,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
Union,
|
||||
cast,
|
||||
get_args,
|
||||
@@ -84,7 +81,7 @@ from langgraph.utils.runnable import RunnableLike, coerce_to_runnable
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _warn_invalid_state_schema(schema: Union[Type[Any], Any]) -> None:
|
||||
def _warn_invalid_state_schema(schema: Union[type[Any], Any]) -> None:
|
||||
if isinstance(schema, type):
|
||||
return
|
||||
if typing.get_args(schema):
|
||||
@@ -108,7 +105,7 @@ def _get_node_name(node: RunnableLike) -> str:
|
||||
class StateNodeSpec(NamedTuple):
|
||||
runnable: Runnable
|
||||
metadata: Optional[dict[str, Any]]
|
||||
input: Type[Any]
|
||||
input: type[Any]
|
||||
retry_policy: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]]
|
||||
ends: Optional[Union[tuple[str, ...], dict[str, str]]] = EMPTY_SEQ
|
||||
|
||||
@@ -166,15 +163,15 @@ class StateGraph(Graph):
|
||||
nodes: dict[str, StateNodeSpec] # type: ignore[assignment]
|
||||
channels: dict[str, BaseChannel]
|
||||
managed: dict[str, ManagedValueSpec]
|
||||
schemas: dict[Type[Any], dict[str, Union[BaseChannel, ManagedValueSpec]]]
|
||||
schemas: dict[type[Any], dict[str, Union[BaseChannel, ManagedValueSpec]]]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
state_schema: Optional[Type[Any]] = None,
|
||||
config_schema: Optional[Type[Any]] = None,
|
||||
state_schema: Optional[type[Any]] = None,
|
||||
config_schema: Optional[type[Any]] = None,
|
||||
*,
|
||||
input: Optional[Type[Any]] = None,
|
||||
output: Optional[Type[Any]] = None,
|
||||
input: Optional[type[Any]] = None,
|
||||
output: Optional[type[Any]] = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
if state_schema is None:
|
||||
@@ -195,7 +192,7 @@ class StateGraph(Graph):
|
||||
self.schemas = {}
|
||||
self.channels = {}
|
||||
self.managed = {}
|
||||
self.type_hints: dict[Type[Any], dict[str, Any]] = {}
|
||||
self.type_hints: dict[type[Any], dict[str, Any]] = {}
|
||||
self.schema = state_schema
|
||||
self.input = input
|
||||
self.output = output
|
||||
@@ -211,7 +208,7 @@ class StateGraph(Graph):
|
||||
(start, end) for starts, end in self.waiting_edges for start in starts
|
||||
}
|
||||
|
||||
def _add_schema(self, schema: Type[Any], /, allow_managed: bool = True) -> None:
|
||||
def _add_schema(self, schema: type[Any], /, allow_managed: bool = True) -> None:
|
||||
if schema not in self.schemas:
|
||||
_warn_invalid_state_schema(schema)
|
||||
channels, managed, type_hints = _get_channels(schema)
|
||||
@@ -250,7 +247,7 @@ class StateGraph(Graph):
|
||||
node: RunnableLike,
|
||||
*,
|
||||
metadata: Optional[dict[str, Any]] = None,
|
||||
input: Optional[Type[Any]] = None,
|
||||
input: Optional[type[Any]] = None,
|
||||
retry: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]] = None,
|
||||
destinations: Optional[Union[dict[str, str], tuple[str, ...]]] = None,
|
||||
) -> Self:
|
||||
@@ -275,7 +272,7 @@ class StateGraph(Graph):
|
||||
action: RunnableLike,
|
||||
*,
|
||||
metadata: Optional[dict[str, Any]] = None,
|
||||
input: Optional[Type[Any]] = None,
|
||||
input: Optional[type[Any]] = None,
|
||||
retry: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]] = None,
|
||||
destinations: Optional[Union[dict[str, str], tuple[str, ...]]] = None,
|
||||
) -> Self:
|
||||
@@ -299,7 +296,7 @@ class StateGraph(Graph):
|
||||
action: Optional[RunnableLike] = None,
|
||||
*,
|
||||
metadata: Optional[dict[str, Any]] = None,
|
||||
input: Optional[Type[Any]] = None,
|
||||
input: Optional[type[Any]] = None,
|
||||
retry: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]] = None,
|
||||
destinations: Optional[Union[dict[str, str], tuple[str, ...]]] = None,
|
||||
) -> Self:
|
||||
@@ -686,12 +683,12 @@ class StateGraph(Graph):
|
||||
|
||||
class CompiledStateGraph(CompiledGraph):
|
||||
builder: StateGraph
|
||||
schema_to_mapper: dict[Type[Any], Optional[Callable[[Any], Any]]]
|
||||
schema_to_mapper: dict[type[Any], Optional[Callable[[Any], Any]]]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
schema_to_mapper: dict[Type[Any], Optional[Callable[[Any], Any]]],
|
||||
schema_to_mapper: dict[type[Any], Optional[Callable[[Any], Any]]],
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
@@ -1022,7 +1019,7 @@ class CompiledStateGraph(CompiledGraph):
|
||||
|
||||
|
||||
def _pick_mapper(
|
||||
state_keys: Sequence[str], schema: Type[Any], type_hints: Optional[dict[str, Any]]
|
||||
state_keys: Sequence[str], schema: type[Any], type_hints: Optional[dict[str, Any]]
|
||||
) -> Optional[Callable[[Any], Any]]:
|
||||
if state_keys == ["__root__"]:
|
||||
return None
|
||||
@@ -1034,7 +1031,7 @@ def _pick_mapper(
|
||||
return partial(_coerce_state, schema)
|
||||
|
||||
|
||||
def _coerce_state(schema: Type[Any], input: dict[str, Any]) -> dict[str, Any]:
|
||||
def _coerce_state(schema: type[Any], input: dict[str, Any]) -> dict[str, Any]:
|
||||
return schema(**input)
|
||||
|
||||
|
||||
@@ -1103,7 +1100,7 @@ def _get_root(input: Any) -> Optional[Sequence[tuple[str, Any]]]:
|
||||
|
||||
|
||||
def _get_channels(
|
||||
schema: Type[dict],
|
||||
schema: type[dict],
|
||||
) -> tuple[dict[str, BaseChannel], dict[str, ManagedValueSpec], dict[str, Any]]:
|
||||
if not hasattr(schema, "__annotations__"):
|
||||
return (
|
||||
@@ -1157,7 +1154,7 @@ def _get_channel(
|
||||
return fallback
|
||||
|
||||
|
||||
def _is_field_channel(typ: Type[Any]) -> Optional[BaseChannel]:
|
||||
def _is_field_channel(typ: type[Any]) -> Optional[BaseChannel]:
|
||||
if hasattr(typ, "__metadata__"):
|
||||
meta = typ.__metadata__
|
||||
if len(meta) >= 1 and isinstance(meta[-1], BaseChannel):
|
||||
@@ -1167,7 +1164,7 @@ def _is_field_channel(typ: Type[Any]) -> Optional[BaseChannel]:
|
||||
return None
|
||||
|
||||
|
||||
def _is_field_binop(typ: Type[Any]) -> Optional[BinaryOperatorAggregate]:
|
||||
def _is_field_binop(typ: type[Any]) -> Optional[BinaryOperatorAggregate]:
|
||||
if hasattr(typ, "__metadata__"):
|
||||
meta = typ.__metadata__
|
||||
if len(meta) >= 1 and callable(meta[-1]):
|
||||
@@ -1188,7 +1185,7 @@ def _is_field_binop(typ: Type[Any]) -> Optional[BinaryOperatorAggregate]:
|
||||
return None
|
||||
|
||||
|
||||
def _is_field_managed_value(name: str, typ: Type[Any]) -> Optional[ManagedValueSpec]:
|
||||
def _is_field_managed_value(name: str, typ: type[Any]) -> Optional[ManagedValueSpec]:
|
||||
if hasattr(typ, "__metadata__"):
|
||||
meta = typ.__metadata__
|
||||
if len(meta) >= 1:
|
||||
@@ -1206,7 +1203,7 @@ def _is_field_managed_value(name: str, typ: Type[Any]) -> Optional[ManagedValueS
|
||||
|
||||
|
||||
def _get_schema(
|
||||
typ: Type,
|
||||
typ: type,
|
||||
schemas: dict,
|
||||
channels: dict,
|
||||
name: str,
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from contextlib import asynccontextmanager, contextmanager
|
||||
from inspect import isclass
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Generic,
|
||||
Iterator,
|
||||
NamedTuple,
|
||||
Sequence,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
)
|
||||
@@ -66,11 +63,11 @@ class WritableManagedValue(Generic[V, U], ManagedValue[V], ABC):
|
||||
|
||||
|
||||
class ConfiguredManagedValue(NamedTuple):
|
||||
cls: Type[ManagedValue]
|
||||
cls: type[ManagedValue]
|
||||
kwargs: dict[str, Any]
|
||||
|
||||
|
||||
ManagedValueSpec = Union[Type[ManagedValue], ConfiguredManagedValue]
|
||||
ManagedValueSpec = Union[type[ManagedValue], ConfiguredManagedValue]
|
||||
|
||||
|
||||
def is_managed_value(value: Any) -> TypeGuard[ManagedValueSpec]:
|
||||
@@ -79,7 +76,7 @@ def is_managed_value(value: Any) -> TypeGuard[ManagedValueSpec]:
|
||||
)
|
||||
|
||||
|
||||
def is_readonly_managed_value(value: Any) -> TypeGuard[Type[ManagedValue]]:
|
||||
def is_readonly_managed_value(value: Any) -> TypeGuard[type[ManagedValue]]:
|
||||
return (
|
||||
isclass(value)
|
||||
and issubclass(value, ManagedValue)
|
||||
@@ -90,7 +87,7 @@ def is_readonly_managed_value(value: Any) -> TypeGuard[Type[ManagedValue]]:
|
||||
)
|
||||
|
||||
|
||||
def is_writable_managed_value(value: Any) -> TypeGuard[Type[WritableManagedValue]]:
|
||||
def is_writable_managed_value(value: Any) -> TypeGuard[type[WritableManagedValue]]:
|
||||
return (isclass(value) and issubclass(value, WritableManagedValue)) or (
|
||||
isinstance(value, ConfiguredManagedValue)
|
||||
and issubclass(value.cls, WritableManagedValue)
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
from contextlib import asynccontextmanager, contextmanager
|
||||
from collections.abc import AsyncIterator, Iterator
|
||||
from contextlib import (
|
||||
AbstractAsyncContextManager,
|
||||
AbstractContextManager,
|
||||
asynccontextmanager,
|
||||
contextmanager,
|
||||
)
|
||||
from inspect import signature
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncContextManager,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
ContextManager,
|
||||
Generic,
|
||||
Iterator,
|
||||
Optional,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
|
||||
@@ -28,15 +29,15 @@ class Context(ManagedValue[V], Generic[V]):
|
||||
def of(
|
||||
ctx: Union[
|
||||
None,
|
||||
Callable[..., ContextManager[V]],
|
||||
Type[ContextManager[V]],
|
||||
Callable[..., AsyncContextManager[V]],
|
||||
Type[AsyncContextManager[V]],
|
||||
Callable[..., AbstractContextManager[V]],
|
||||
type[AbstractContextManager[V]],
|
||||
Callable[..., AbstractAsyncContextManager[V]],
|
||||
type[AbstractAsyncContextManager[V]],
|
||||
] = None,
|
||||
actx: Optional[
|
||||
Union[
|
||||
Callable[..., AsyncContextManager[V]],
|
||||
Type[AsyncContextManager[V]],
|
||||
Callable[..., AbstractAsyncContextManager[V]],
|
||||
type[AbstractAsyncContextManager[V]],
|
||||
]
|
||||
] = None,
|
||||
) -> ConfiguredManagedValue:
|
||||
@@ -98,8 +99,10 @@ class Context(ManagedValue[V], Generic[V]):
|
||||
self,
|
||||
loop: LoopProtocol,
|
||||
*,
|
||||
ctx: Union[None, Type[ContextManager[V]], Type[AsyncContextManager[V]]] = None,
|
||||
actx: Optional[Type[AsyncContextManager[V]]] = None,
|
||||
ctx: Union[
|
||||
None, type[AbstractContextManager[V]], type[AbstractAsyncContextManager[V]]
|
||||
] = None,
|
||||
actx: Optional[type[AbstractAsyncContextManager[V]]] = None,
|
||||
) -> None:
|
||||
self.ctx = ctx
|
||||
self.actx = actx
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import collections.abc
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from contextlib import asynccontextmanager, contextmanager
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Iterator,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
)
|
||||
|
||||
from typing_extensions import NotRequired, Required, Self
|
||||
@@ -71,7 +68,7 @@ class SharedValue(WritableManagedValue[Value, Update]):
|
||||
yield value
|
||||
|
||||
def __init__(
|
||||
self, loop: LoopProtocol, *, typ: Type[Any], scope: str, key: str
|
||||
self, loop: LoopProtocol, *, typ: type[Any], scope: str, key: str
|
||||
) -> None:
|
||||
super().__init__(loop)
|
||||
if typ := _strip_extras(typ):
|
||||
|
||||
@@ -6,17 +6,11 @@ import concurrent.futures
|
||||
import queue
|
||||
import weakref
|
||||
from collections import defaultdict, deque
|
||||
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
|
||||
from functools import partial
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
Union,
|
||||
cast,
|
||||
get_type_hints,
|
||||
@@ -143,8 +137,8 @@ class Channel:
|
||||
cls,
|
||||
channels: str,
|
||||
*,
|
||||
key: Optional[str] = None,
|
||||
tags: Optional[list[str]] = None,
|
||||
key: str | None = None,
|
||||
tags: list[str] | None = None,
|
||||
) -> PregelNode: ...
|
||||
|
||||
@overload
|
||||
@@ -154,16 +148,16 @@ class Channel:
|
||||
channels: Sequence[str],
|
||||
*,
|
||||
key: None = None,
|
||||
tags: Optional[list[str]] = None,
|
||||
tags: list[str] | None = None,
|
||||
) -> PregelNode: ...
|
||||
|
||||
@classmethod
|
||||
def subscribe_to(
|
||||
cls,
|
||||
channels: Union[str, Sequence[str]],
|
||||
channels: str | Sequence[str],
|
||||
*,
|
||||
key: Optional[str] = None,
|
||||
tags: Optional[list[str]] = None,
|
||||
key: str | None = None,
|
||||
tags: list[str] | None = None,
|
||||
) -> PregelNode:
|
||||
"""Runs process.invoke() each time channels are updated,
|
||||
with a dict of the channel values as input."""
|
||||
@@ -469,7 +463,7 @@ class Pregel(PregelProtocol):
|
||||
|
||||
nodes: dict[str, PregelNode]
|
||||
|
||||
channels: dict[str, Union[BaseChannel, ManagedValueSpec]]
|
||||
channels: dict[str, BaseChannel | ManagedValueSpec]
|
||||
|
||||
stream_mode: StreamMode = "values"
|
||||
"""Mode to stream output, defaults to 'values'."""
|
||||
@@ -478,18 +472,18 @@ class Pregel(PregelProtocol):
|
||||
"""Whether to force emitting stream events eagerly, automatically turned on
|
||||
for stream_mode "messages" and "custom"."""
|
||||
|
||||
output_channels: Union[str, Sequence[str]]
|
||||
output_channels: str | Sequence[str]
|
||||
|
||||
stream_channels: Optional[Union[str, Sequence[str]]] = None
|
||||
stream_channels: str | Sequence[str] | None = None
|
||||
"""Channels to stream, defaults to all channels not in reserved channels"""
|
||||
|
||||
interrupt_after_nodes: Union[All, Sequence[str]]
|
||||
interrupt_after_nodes: All | Sequence[str]
|
||||
|
||||
interrupt_before_nodes: Union[All, Sequence[str]]
|
||||
interrupt_before_nodes: All | Sequence[str]
|
||||
|
||||
input_channels: Union[str, Sequence[str]]
|
||||
input_channels: str | Sequence[str]
|
||||
|
||||
step_timeout: Optional[float] = None
|
||||
step_timeout: float | None = None
|
||||
"""Maximum time to wait for a step to complete, in seconds. Defaults to None."""
|
||||
|
||||
debug: bool
|
||||
@@ -498,44 +492,44 @@ class Pregel(PregelProtocol):
|
||||
checkpointer: Checkpointer = None
|
||||
"""Checkpointer used to save and load graph state. Defaults to None."""
|
||||
|
||||
store: Optional[BaseStore] = None
|
||||
store: BaseStore | None = None
|
||||
"""Memory store to use for SharedValues. Defaults to None."""
|
||||
|
||||
retry_policy: Optional[Sequence[RetryPolicy]] = None
|
||||
retry_policy: Sequence[RetryPolicy] | None = None
|
||||
"""Retry policies to use when running tasks. Set to None to disable."""
|
||||
|
||||
config_type: Optional[Type[Any]] = None
|
||||
config_type: type[Any] | None = None
|
||||
|
||||
input_model: Optional[Type[BaseModel]] = None
|
||||
input_model: type[BaseModel] | None = None
|
||||
|
||||
config: Optional[RunnableConfig] = None
|
||||
config: RunnableConfig | None = None
|
||||
|
||||
name: str = "LangGraph"
|
||||
|
||||
trigger_to_nodes: Optional[Mapping[str, Sequence[str]]] = None
|
||||
trigger_to_nodes: Mapping[str, Sequence[str]] | None = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
nodes: dict[str, PregelNode],
|
||||
channels: Optional[dict[str, Union[BaseChannel, ManagedValueSpec]]],
|
||||
channels: dict[str, BaseChannel | ManagedValueSpec] | None,
|
||||
auto_validate: bool = True,
|
||||
stream_mode: StreamMode = "values",
|
||||
stream_eager: bool = False,
|
||||
output_channels: Union[str, Sequence[str]],
|
||||
stream_channels: Optional[Union[str, Sequence[str]]] = None,
|
||||
interrupt_after_nodes: Union[All, Sequence[str]] = (),
|
||||
interrupt_before_nodes: Union[All, Sequence[str]] = (),
|
||||
input_channels: Union[str, Sequence[str]],
|
||||
step_timeout: Optional[float] = None,
|
||||
debug: Optional[bool] = None,
|
||||
checkpointer: Optional[BaseCheckpointSaver] = None,
|
||||
store: Optional[BaseStore] = None,
|
||||
retry_policy: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]] = None,
|
||||
config_type: Optional[Type[Any]] = None,
|
||||
input_model: Optional[Type[BaseModel]] = None,
|
||||
config: Optional[RunnableConfig] = None,
|
||||
trigger_to_nodes: Optional[Mapping[str, Sequence[str]]] = None,
|
||||
output_channels: str | Sequence[str],
|
||||
stream_channels: str | Sequence[str] | None = None,
|
||||
interrupt_after_nodes: All | Sequence[str] = (),
|
||||
interrupt_before_nodes: All | Sequence[str] = (),
|
||||
input_channels: str | Sequence[str],
|
||||
step_timeout: float | None = None,
|
||||
debug: bool | None = None,
|
||||
checkpointer: BaseCheckpointSaver | None = None,
|
||||
store: BaseStore | None = None,
|
||||
retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None,
|
||||
config_type: type[Any] | None = None,
|
||||
input_model: type[BaseModel] | None = None,
|
||||
config: RunnableConfig | None = None,
|
||||
trigger_to_nodes: Mapping[str, Sequence[str]] | None = None,
|
||||
name: str = "LangGraph",
|
||||
) -> None:
|
||||
self.nodes = nodes
|
||||
@@ -564,10 +558,7 @@ class Pregel(PregelProtocol):
|
||||
self.validate()
|
||||
|
||||
def get_graph(
|
||||
self,
|
||||
config: Optional[RunnableConfig] = None,
|
||||
*,
|
||||
xray: Union[int, bool] = False,
|
||||
self, config: RunnableConfig | None = None, *, xray: int | bool = False
|
||||
) -> Graph:
|
||||
"""Returns a drawable representation of the computation graph."""
|
||||
# gather subgraphs
|
||||
@@ -595,10 +586,7 @@ class Pregel(PregelProtocol):
|
||||
)
|
||||
|
||||
async def aget_graph(
|
||||
self,
|
||||
config: Optional[RunnableConfig] = None,
|
||||
*,
|
||||
xray: Union[int, bool] = False,
|
||||
self, config: RunnableConfig | None = None, *, xray: int | bool = False
|
||||
) -> Graph:
|
||||
"""Returns a drawable representation of the computation graph."""
|
||||
|
||||
@@ -646,13 +634,11 @@ class Pregel(PregelProtocol):
|
||||
"image/png": self.get_graph().draw_mermaid_png(),
|
||||
}
|
||||
|
||||
def copy(self, update: Optional[dict[str, Any]] = None) -> Self:
|
||||
def copy(self, update: dict[str, Any] | None = None) -> Self:
|
||||
attrs = {**self.__dict__, **(update or {})}
|
||||
return self.__class__(**attrs)
|
||||
|
||||
def with_config(
|
||||
self, config: Optional[RunnableConfig] = None, **kwargs: Any
|
||||
) -> Self:
|
||||
def with_config(self, config: RunnableConfig | None = None, **kwargs: Any) -> Self:
|
||||
return self.copy(
|
||||
{"config": merge_configs(self.config, config, cast(RunnableConfig, kwargs))}
|
||||
)
|
||||
@@ -707,9 +693,7 @@ class Pregel(PregelProtocol):
|
||||
]
|
||||
]
|
||||
|
||||
def config_schema(
|
||||
self, *, include: Optional[Sequence[str]] = None
|
||||
) -> Type[BaseModel]:
|
||||
def config_schema(self, *, include: Sequence[str] | None = None) -> type[BaseModel]:
|
||||
# If the config type is not set explicitly, we will try to infer it.
|
||||
# If the config type is provided, but isn't directly supported by pydantic
|
||||
# (e.g., vanilla python class), we will also delegate to the parent class,
|
||||
@@ -729,8 +713,8 @@ class Pregel(PregelProtocol):
|
||||
return create_model(self.get_name("Config"), field_definitions=fields)
|
||||
|
||||
def get_config_jsonschema(
|
||||
self, *, include: Optional[Sequence[str]] = None
|
||||
) -> Dict[str, Any]:
|
||||
self, *, include: Sequence[str] | None = None
|
||||
) -> dict[str, Any]:
|
||||
schema = self.config_schema(include=include)
|
||||
if hasattr(schema, "model_json_schema"):
|
||||
return schema.model_json_schema()
|
||||
@@ -744,9 +728,7 @@ class Pregel(PregelProtocol):
|
||||
if isinstance(channel, BaseChannel):
|
||||
return channel.UpdateType
|
||||
|
||||
def get_input_schema(
|
||||
self, config: Optional[RunnableConfig] = None
|
||||
) -> Type[BaseModel]:
|
||||
def get_input_schema(self, config: RunnableConfig | None = None) -> type[BaseModel]:
|
||||
if self.input_model is not None:
|
||||
return self.input_model
|
||||
config = merge_configs(self.config, config)
|
||||
@@ -763,8 +745,8 @@ class Pregel(PregelProtocol):
|
||||
)
|
||||
|
||||
def get_input_jsonschema(
|
||||
self, config: Optional[RunnableConfig] = None
|
||||
) -> Dict[str, Any]:
|
||||
self, config: RunnableConfig | None = None
|
||||
) -> dict[str, Any]:
|
||||
schema = self.get_input_schema(config)
|
||||
if hasattr(schema, "model_json_schema"):
|
||||
return schema.model_json_schema()
|
||||
@@ -779,8 +761,8 @@ class Pregel(PregelProtocol):
|
||||
return channel.ValueType
|
||||
|
||||
def get_output_schema(
|
||||
self, config: Optional[RunnableConfig] = None
|
||||
) -> Type[BaseModel]:
|
||||
self, config: RunnableConfig | None = None
|
||||
) -> type[BaseModel]:
|
||||
config = merge_configs(self.config, config)
|
||||
if isinstance(self.output_channels, str):
|
||||
return super().get_output_schema(config)
|
||||
@@ -795,8 +777,8 @@ class Pregel(PregelProtocol):
|
||||
)
|
||||
|
||||
def get_output_jsonschema(
|
||||
self, config: Optional[RunnableConfig] = None
|
||||
) -> Dict[str, Any]:
|
||||
self, config: RunnableConfig | None = None
|
||||
) -> dict[str, Any]:
|
||||
schema = self.get_output_schema(config)
|
||||
if hasattr(schema, "model_json_schema"):
|
||||
return schema.model_json_schema()
|
||||
@@ -811,13 +793,13 @@ class Pregel(PregelProtocol):
|
||||
)
|
||||
|
||||
@property
|
||||
def stream_channels_asis(self) -> Union[str, Sequence[str]]:
|
||||
def stream_channels_asis(self) -> str | Sequence[str]:
|
||||
return self.stream_channels or [
|
||||
k for k in self.channels if isinstance(self.channels[k], BaseChannel)
|
||||
]
|
||||
|
||||
def get_subgraphs(
|
||||
self, *, namespace: Optional[str] = None, recurse: bool = False
|
||||
self, *, namespace: str | None = None, recurse: bool = False
|
||||
) -> Iterator[tuple[str, PregelProtocol]]:
|
||||
for name, node in self.nodes.items():
|
||||
# filter by prefix
|
||||
@@ -846,7 +828,7 @@ class Pregel(PregelProtocol):
|
||||
)
|
||||
|
||||
async def aget_subgraphs(
|
||||
self, *, namespace: Optional[str] = None, recurse: bool = False
|
||||
self, *, namespace: str | None = None, recurse: bool = False
|
||||
) -> AsyncIterator[tuple[str, PregelProtocol]]:
|
||||
for name, node in self.get_subgraphs(namespace=namespace, recurse=recurse):
|
||||
yield name, node
|
||||
@@ -858,8 +840,8 @@ class Pregel(PregelProtocol):
|
||||
def _prepare_state_snapshot(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
saved: Optional[CheckpointTuple],
|
||||
recurse: Optional[BaseCheckpointSaver] = None,
|
||||
saved: CheckpointTuple | None,
|
||||
recurse: BaseCheckpointSaver | None = None,
|
||||
apply_pending_writes: bool = False,
|
||||
) -> StateSnapshot:
|
||||
if not saved:
|
||||
@@ -907,7 +889,7 @@ class Pregel(PregelProtocol):
|
||||
# get the subgraphs
|
||||
subgraphs = dict(self.get_subgraphs())
|
||||
parent_ns = saved.config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "")
|
||||
task_states: dict[str, Union[RunnableConfig, StateSnapshot]] = {}
|
||||
task_states: dict[str, RunnableConfig | StateSnapshot] = {}
|
||||
for task in next_tasks.values():
|
||||
if task.name not in subgraphs:
|
||||
continue
|
||||
@@ -974,8 +956,8 @@ class Pregel(PregelProtocol):
|
||||
async def _aprepare_state_snapshot(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
saved: Optional[CheckpointTuple],
|
||||
recurse: Optional[BaseCheckpointSaver] = None,
|
||||
saved: CheckpointTuple | None,
|
||||
recurse: BaseCheckpointSaver | None = None,
|
||||
apply_pending_writes: bool = False,
|
||||
) -> StateSnapshot:
|
||||
if not saved:
|
||||
@@ -1026,7 +1008,7 @@ class Pregel(PregelProtocol):
|
||||
# get the subgraphs
|
||||
subgraphs = {n: g async for n, g in self.aget_subgraphs()}
|
||||
parent_ns = saved.config[CONF].get(CONFIG_KEY_CHECKPOINT_NS, "")
|
||||
task_states: dict[str, Union[RunnableConfig, StateSnapshot]] = {}
|
||||
task_states: dict[str, RunnableConfig | StateSnapshot] = {}
|
||||
for task in next_tasks.values():
|
||||
if task.name not in subgraphs:
|
||||
continue
|
||||
@@ -1094,7 +1076,7 @@ class Pregel(PregelProtocol):
|
||||
self, config: RunnableConfig, *, subgraphs: bool = False
|
||||
) -> StateSnapshot:
|
||||
"""Get the current state of the graph."""
|
||||
checkpointer: Optional[BaseCheckpointSaver] = ensure_config(config)[CONF].get(
|
||||
checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get(
|
||||
CONFIG_KEY_CHECKPOINTER, self.checkpointer
|
||||
)
|
||||
if not checkpointer:
|
||||
@@ -1136,7 +1118,7 @@ class Pregel(PregelProtocol):
|
||||
self, config: RunnableConfig, *, subgraphs: bool = False
|
||||
) -> StateSnapshot:
|
||||
"""Get the current state of the graph."""
|
||||
checkpointer: Optional[BaseCheckpointSaver] = ensure_config(config)[CONF].get(
|
||||
checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get(
|
||||
CONFIG_KEY_CHECKPOINTER, self.checkpointer
|
||||
)
|
||||
if not checkpointer:
|
||||
@@ -1178,13 +1160,13 @@ class Pregel(PregelProtocol):
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
*,
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
before: Optional[RunnableConfig] = None,
|
||||
limit: Optional[int] = None,
|
||||
filter: dict[str, Any] | None = None,
|
||||
before: RunnableConfig | None = None,
|
||||
limit: int | None = None,
|
||||
) -> Iterator[StateSnapshot]:
|
||||
config = ensure_config(config)
|
||||
"""Get the history of the state of the graph."""
|
||||
checkpointer: Optional[BaseCheckpointSaver] = ensure_config(config)[CONF].get(
|
||||
checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get(
|
||||
CONFIG_KEY_CHECKPOINTER, self.checkpointer
|
||||
)
|
||||
if not checkpointer:
|
||||
@@ -1229,13 +1211,13 @@ class Pregel(PregelProtocol):
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
*,
|
||||
filter: Optional[Dict[str, Any]] = None,
|
||||
before: Optional[RunnableConfig] = None,
|
||||
limit: Optional[int] = None,
|
||||
filter: dict[str, Any] | None = None,
|
||||
before: RunnableConfig | None = None,
|
||||
limit: int | None = None,
|
||||
) -> AsyncIterator[StateSnapshot]:
|
||||
config = ensure_config(config)
|
||||
"""Get the history of the state of the graph."""
|
||||
checkpointer: Optional[BaseCheckpointSaver] = ensure_config(config)[CONF].get(
|
||||
checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get(
|
||||
CONFIG_KEY_CHECKPOINTER, self.checkpointer
|
||||
)
|
||||
if not checkpointer:
|
||||
@@ -1300,7 +1282,7 @@ class Pregel(PregelProtocol):
|
||||
RunnableConfig: The updated config.
|
||||
"""
|
||||
|
||||
checkpointer: Optional[BaseCheckpointSaver] = ensure_config(config)[CONF].get(
|
||||
checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get(
|
||||
CONFIG_KEY_CHECKPOINTER, self.checkpointer
|
||||
)
|
||||
if not checkpointer:
|
||||
@@ -1575,7 +1557,7 @@ class Pregel(PregelProtocol):
|
||||
next_tasks[tid].writes.append((k, v))
|
||||
if tasks := [t for t in next_tasks.values() if t.writes]:
|
||||
apply_writes(checkpoint, channels, tasks, None)
|
||||
valid_updates: list[tuple[str, Optional[dict[str, Any]]]] = []
|
||||
valid_updates: list[tuple[str, dict[str, Any] | None]] = []
|
||||
if len(updates) == 1:
|
||||
values, as_node = updates[0]
|
||||
# find last node that updated the state, if not provided
|
||||
@@ -1714,7 +1696,7 @@ class Pregel(PregelProtocol):
|
||||
RunnableConfig: The updated config.
|
||||
"""
|
||||
|
||||
checkpointer: Optional[BaseCheckpointSaver] = ensure_config(config)[CONF].get(
|
||||
checkpointer: BaseCheckpointSaver | None = ensure_config(config)[CONF].get(
|
||||
CONFIG_KEY_CHECKPOINTER, self.checkpointer
|
||||
)
|
||||
if not checkpointer:
|
||||
@@ -1989,7 +1971,7 @@ class Pregel(PregelProtocol):
|
||||
next_tasks[tid].writes.append((k, v))
|
||||
if tasks := [t for t in next_tasks.values() if t.writes]:
|
||||
apply_writes(checkpoint, channels, tasks, None)
|
||||
valid_updates: list[tuple[str, Optional[dict[str, Any]]]] = []
|
||||
valid_updates: list[tuple[str, dict[str, Any] | None]] = []
|
||||
if len(updates) == 1:
|
||||
values, as_node = updates[0]
|
||||
# find last node that updated the state, if not provided
|
||||
@@ -2109,8 +2091,8 @@ class Pregel(PregelProtocol):
|
||||
def update_state(
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
values: Optional[Union[dict[str, Any], Any]],
|
||||
as_node: Optional[str] = None,
|
||||
values: dict[str, Any] | Any | None,
|
||||
as_node: str | None = None,
|
||||
) -> RunnableConfig:
|
||||
"""Update the state of the graph with the given values, as if they came from
|
||||
node `as_node`. If `as_node` is not provided, it will be set to the last node
|
||||
@@ -2122,7 +2104,7 @@ class Pregel(PregelProtocol):
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
values: dict[str, Any] | Any,
|
||||
as_node: Optional[str] = None,
|
||||
as_node: str | None = None,
|
||||
) -> RunnableConfig:
|
||||
"""Update the state of the graph asynchronously with the given values, as if they came from
|
||||
node `as_node`. If `as_node` is not provided, it will be set to the last node
|
||||
@@ -2134,19 +2116,19 @@ class Pregel(PregelProtocol):
|
||||
self,
|
||||
config: RunnableConfig,
|
||||
*,
|
||||
stream_mode: Optional[Union[StreamMode, list[StreamMode]]],
|
||||
output_keys: Optional[Union[str, Sequence[str]]],
|
||||
interrupt_before: Optional[Union[All, Sequence[str]]],
|
||||
interrupt_after: Optional[Union[All, Sequence[str]]],
|
||||
debug: Optional[bool],
|
||||
stream_mode: StreamMode | list[StreamMode] | None,
|
||||
output_keys: str | Sequence[str] | None,
|
||||
interrupt_before: All | Sequence[str] | None,
|
||||
interrupt_after: All | Sequence[str] | None,
|
||||
debug: bool | None,
|
||||
) -> tuple[
|
||||
bool,
|
||||
set[StreamMode],
|
||||
Union[str, Sequence[str]],
|
||||
Union[All, Sequence[str]],
|
||||
Union[All, Sequence[str]],
|
||||
Optional[BaseCheckpointSaver],
|
||||
Optional[BaseStore],
|
||||
str | Sequence[str],
|
||||
All | Sequence[str],
|
||||
All | Sequence[str],
|
||||
BaseCheckpointSaver | None,
|
||||
BaseStore | None,
|
||||
]:
|
||||
if config["recursion_limit"] < 1:
|
||||
raise ValueError("recursion_limit must be at least 1")
|
||||
@@ -2164,7 +2146,7 @@ class Pregel(PregelProtocol):
|
||||
# if being called as a node in another graph, always use values mode
|
||||
stream_mode = ["values"]
|
||||
if self.checkpointer is False:
|
||||
checkpointer: Optional[BaseCheckpointSaver] = None
|
||||
checkpointer: BaseCheckpointSaver | None = None
|
||||
elif CONFIG_KEY_CHECKPOINTER in config.get(CONF, {}):
|
||||
checkpointer = config[CONF][CONFIG_KEY_CHECKPOINTER]
|
||||
elif self.checkpointer is True:
|
||||
@@ -2176,7 +2158,7 @@ class Pregel(PregelProtocol):
|
||||
f"Checkpointer requires one or more of the following 'configurable' keys: {[s.id for s in checkpointer.config_specs]}"
|
||||
)
|
||||
if CONFIG_KEY_STORE in config.get(CONF, {}):
|
||||
store: Optional[BaseStore] = config[CONF][CONFIG_KEY_STORE]
|
||||
store: BaseStore | None = config[CONF][CONFIG_KEY_STORE]
|
||||
else:
|
||||
store = self.store
|
||||
return (
|
||||
@@ -2191,17 +2173,17 @@ class Pregel(PregelProtocol):
|
||||
|
||||
def stream(
|
||||
self,
|
||||
input: Union[dict[str, Any], Any],
|
||||
config: Optional[RunnableConfig] = None,
|
||||
input: dict[str, Any] | Any,
|
||||
config: RunnableConfig | None = None,
|
||||
*,
|
||||
stream_mode: Optional[Union[StreamMode, list[StreamMode]]] = None,
|
||||
output_keys: Optional[Union[str, Sequence[str]]] = None,
|
||||
interrupt_before: Optional[Union[All, Sequence[str]]] = None,
|
||||
interrupt_after: Optional[Union[All, Sequence[str]]] = None,
|
||||
checkpoint_during: Optional[bool] = None,
|
||||
debug: Optional[bool] = None,
|
||||
stream_mode: StreamMode | list[StreamMode] | None = None,
|
||||
output_keys: str | Sequence[str] | None = None,
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
debug: bool | None = None,
|
||||
subgraphs: bool = False,
|
||||
) -> Iterator[Union[dict[str, Any], Any]]:
|
||||
) -> Iterator[dict[str, Any] | Any]:
|
||||
"""Stream graph steps for a single input.
|
||||
|
||||
Args:
|
||||
@@ -2427,7 +2409,7 @@ class Pregel(PregelProtocol):
|
||||
):
|
||||
# we are careful to have a single waiter live at any one time
|
||||
# because on exit we increment semaphore count by exactly 1
|
||||
waiter: Optional[concurrent.futures.Future] = None
|
||||
waiter: concurrent.futures.Future | None = None
|
||||
# because sync futures cannot be cancelled, we instead
|
||||
# release the stream semaphore on exit, which will cause
|
||||
# a pending waiter to return immediately
|
||||
@@ -2478,17 +2460,17 @@ class Pregel(PregelProtocol):
|
||||
|
||||
async def astream(
|
||||
self,
|
||||
input: Union[dict[str, Any], Any],
|
||||
config: Optional[RunnableConfig] = None,
|
||||
input: dict[str, Any] | Any,
|
||||
config: RunnableConfig | None = None,
|
||||
*,
|
||||
stream_mode: Optional[Union[StreamMode, list[StreamMode]]] = None,
|
||||
output_keys: Optional[Union[str, Sequence[str]]] = None,
|
||||
interrupt_before: Optional[Union[All, Sequence[str]]] = None,
|
||||
interrupt_after: Optional[Union[All, Sequence[str]]] = None,
|
||||
checkpoint_during: Optional[bool] = None,
|
||||
debug: Optional[bool] = None,
|
||||
stream_mode: StreamMode | list[StreamMode] | None = None,
|
||||
output_keys: str | Sequence[str] | None = None,
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
debug: bool | None = None,
|
||||
subgraphs: bool = False,
|
||||
) -> AsyncIterator[Union[dict[str, Any], Any]]:
|
||||
) -> AsyncIterator[dict[str, Any] | Any]:
|
||||
"""Stream graph steps for a single input.
|
||||
|
||||
Args:
|
||||
@@ -2779,17 +2761,17 @@ class Pregel(PregelProtocol):
|
||||
|
||||
def invoke(
|
||||
self,
|
||||
input: Union[dict[str, Any], Any],
|
||||
config: Optional[RunnableConfig] = None,
|
||||
input: dict[str, Any] | Any,
|
||||
config: RunnableConfig | None = None,
|
||||
*,
|
||||
stream_mode: StreamMode = "values",
|
||||
output_keys: Optional[Union[str, Sequence[str]]] = None,
|
||||
interrupt_before: Optional[Union[All, Sequence[str]]] = None,
|
||||
interrupt_after: Optional[Union[All, Sequence[str]]] = None,
|
||||
checkpoint_during: Optional[bool] = None,
|
||||
debug: Optional[bool] = None,
|
||||
output_keys: str | Sequence[str] | None = None,
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
debug: bool | None = None,
|
||||
**kwargs: Any,
|
||||
) -> Union[dict[str, Any], Any]:
|
||||
) -> dict[str, Any] | Any:
|
||||
"""Run the graph with a single input and config.
|
||||
|
||||
Args:
|
||||
@@ -2843,17 +2825,17 @@ class Pregel(PregelProtocol):
|
||||
|
||||
async def ainvoke(
|
||||
self,
|
||||
input: Union[dict[str, Any], Any],
|
||||
config: Optional[RunnableConfig] = None,
|
||||
input: dict[str, Any] | Any,
|
||||
config: RunnableConfig | None = None,
|
||||
*,
|
||||
stream_mode: StreamMode = "values",
|
||||
output_keys: Optional[Union[str, Sequence[str]]] = None,
|
||||
interrupt_before: Optional[Union[All, Sequence[str]]] = None,
|
||||
interrupt_after: Optional[Union[All, Sequence[str]]] = None,
|
||||
checkpoint_during: Optional[bool] = None,
|
||||
debug: Optional[bool] = None,
|
||||
output_keys: str | Sequence[str] | None = None,
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
debug: bool | None = None,
|
||||
**kwargs: Any,
|
||||
) -> Union[dict[str, Any], Any]:
|
||||
) -> dict[str, Any] | Any:
|
||||
"""Asynchronously invoke the graph on a single input.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -3,19 +3,17 @@ import itertools
|
||||
import sys
|
||||
import threading
|
||||
from collections import defaultdict, deque
|
||||
from collections.abc import Iterable, Mapping, Sequence
|
||||
from copy import copy
|
||||
from functools import partial
|
||||
from hashlib import sha1
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Iterable,
|
||||
Literal,
|
||||
Mapping,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Protocol,
|
||||
Sequence,
|
||||
Union,
|
||||
cast,
|
||||
overload,
|
||||
|
||||
@@ -5,7 +5,8 @@ import functools
|
||||
import inspect
|
||||
import sys
|
||||
import types
|
||||
from typing import Any, Callable, Generator, Generic, Optional, Sequence, TypeVar, cast
|
||||
from collections.abc import Generator, Sequence
|
||||
from typing import Any, Callable, Generic, Optional, TypeVar, cast
|
||||
|
||||
from langchain_core.runnables import Runnable
|
||||
from typing_extensions import ParamSpec
|
||||
@@ -29,16 +30,12 @@ from langgraph.utils.runnable import (
|
||||
def _getattribute(obj: Any, name: str) -> Any:
|
||||
for subpath in name.split("."):
|
||||
if subpath == "<locals>":
|
||||
raise AttributeError(
|
||||
"Can't get local attribute {!r} on {!r}".format(name, obj)
|
||||
)
|
||||
raise AttributeError(f"Can't get local attribute {name!r} on {obj!r}")
|
||||
try:
|
||||
parent = obj
|
||||
obj = getattr(obj, subpath)
|
||||
except AttributeError:
|
||||
raise AttributeError(
|
||||
"Can't get attribute {!r} on {!r}".format(name, obj)
|
||||
) from None
|
||||
raise AttributeError(f"Can't get attribute {name!r} on {obj!r}") from None
|
||||
return obj, parent
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from collections.abc import Mapping
|
||||
from datetime import datetime, timezone
|
||||
from typing import Mapping, Optional
|
||||
from typing import Optional
|
||||
|
||||
from langgraph.channels.base import BaseChannel
|
||||
from langgraph.checkpoint.base import Checkpoint
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
from collections import defaultdict
|
||||
from collections.abc import Iterable, Iterator, Mapping, Sequence
|
||||
from dataclasses import asdict
|
||||
from datetime import datetime, timezone
|
||||
from pprint import pformat
|
||||
from typing import (
|
||||
Any,
|
||||
Iterable,
|
||||
Iterator,
|
||||
Literal,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Union,
|
||||
)
|
||||
from uuid import UUID
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from collections import defaultdict
|
||||
from typing import Any, Mapping, Optional, Sequence, Union, cast
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import Any, Optional, Union, cast
|
||||
|
||||
from langchain_core.runnables.config import RunnableConfig
|
||||
from langchain_core.runnables.graph import Graph, Node
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import asyncio
|
||||
import concurrent.futures
|
||||
import time
|
||||
from contextlib import ExitStack
|
||||
from collections.abc import Awaitable, Coroutine
|
||||
from contextlib import AbstractAsyncContextManager, AbstractContextManager, ExitStack
|
||||
from contextvars import copy_context
|
||||
from types import TracebackType
|
||||
from typing import (
|
||||
AsyncContextManager,
|
||||
Awaitable,
|
||||
Callable,
|
||||
ContextManager,
|
||||
Coroutine,
|
||||
Optional,
|
||||
Protocol,
|
||||
TypeVar,
|
||||
@@ -40,7 +37,7 @@ class Submit(Protocol[P, T]):
|
||||
) -> concurrent.futures.Future[T]: ...
|
||||
|
||||
|
||||
class BackgroundExecutor(ContextManager):
|
||||
class BackgroundExecutor(AbstractContextManager):
|
||||
"""A context manager that runs sync tasks in the background.
|
||||
Uses a thread pool executor to delegate tasks to separate threads.
|
||||
On exit,
|
||||
@@ -122,7 +119,7 @@ class BackgroundExecutor(ContextManager):
|
||||
pass
|
||||
|
||||
|
||||
class AsyncBackgroundExecutor(AsyncContextManager):
|
||||
class AsyncBackgroundExecutor(AbstractAsyncContextManager):
|
||||
"""A context manager that runs async tasks in the background.
|
||||
Uses the current event loop to delegate tasks to asyncio tasks.
|
||||
On exit,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from collections import Counter
|
||||
from typing import Any, Iterator, Literal, Mapping, Optional, Sequence, TypeVar, Union
|
||||
from collections.abc import Iterator, Mapping, Sequence
|
||||
from typing import Any, Literal, Optional, TypeVar, Union
|
||||
from uuid import UUID
|
||||
|
||||
from langchain_core.runnables.utils import AddableDict
|
||||
|
||||
@@ -3,21 +3,20 @@ import binascii
|
||||
import concurrent.futures
|
||||
import dataclasses
|
||||
from collections import defaultdict, deque
|
||||
from contextlib import AsyncExitStack, ExitStack
|
||||
from collections.abc import Iterator, Mapping, Sequence
|
||||
from contextlib import (
|
||||
AbstractAsyncContextManager,
|
||||
AbstractContextManager,
|
||||
AsyncExitStack,
|
||||
ExitStack,
|
||||
)
|
||||
from inspect import signature
|
||||
from types import TracebackType
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncContextManager,
|
||||
Callable,
|
||||
ContextManager,
|
||||
Iterator,
|
||||
List,
|
||||
Literal,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
@@ -146,7 +145,7 @@ def DuplexStream(*streams: StreamProtocol) -> StreamProtocol:
|
||||
|
||||
class PregelLoop(LoopProtocol):
|
||||
input: Optional[Any]
|
||||
input_model: Optional[Type[BaseModel]]
|
||||
input_model: Optional[type[BaseModel]]
|
||||
checkpointer: Optional[BaseCheckpointSaver]
|
||||
nodes: Mapping[str, PregelNode]
|
||||
specs: Mapping[str, Union[BaseChannel, ManagedValueSpec]]
|
||||
@@ -186,7 +185,7 @@ class PregelLoop(LoopProtocol):
|
||||
checkpoint_ns: tuple[str, ...]
|
||||
checkpoint_config: RunnableConfig
|
||||
checkpoint_metadata: CheckpointMetadata
|
||||
checkpoint_pending_writes: List[PendingWrite]
|
||||
checkpoint_pending_writes: list[PendingWrite]
|
||||
checkpoint_previous_versions: dict[str, Union[str, float, int]]
|
||||
prev_checkpoint_config: Optional[RunnableConfig]
|
||||
|
||||
@@ -214,7 +213,7 @@ class PregelLoop(LoopProtocol):
|
||||
interrupt_after: Union[All, Sequence[str]] = EMPTY_SEQ,
|
||||
interrupt_before: Union[All, Sequence[str]] = EMPTY_SEQ,
|
||||
manager: Union[None, AsyncParentRunManager, ParentRunManager] = None,
|
||||
input_model: Optional[Type[BaseModel]] = None,
|
||||
input_model: Optional[type[BaseModel]] = None,
|
||||
debug: bool = False,
|
||||
migrate_checkpoint: Optional[Callable[[Checkpoint], None]] = None,
|
||||
trigger_to_nodes: Optional[Mapping[str, Sequence[str]]] = None,
|
||||
@@ -491,7 +490,7 @@ class PregelLoop(LoopProtocol):
|
||||
if self.input is INPUT_SHOULD_VALIDATE:
|
||||
self.input = INPUT_DONE
|
||||
# validate
|
||||
cast(Type[BaseModel], self.input_model)(
|
||||
cast(type[BaseModel], self.input_model)(
|
||||
**read_channels(self.channels, self.stream_keys)
|
||||
)
|
||||
# produce values output
|
||||
@@ -839,7 +838,7 @@ class PregelLoop(LoopProtocol):
|
||||
|
||||
def _suppress_interrupt(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_type: Optional[type[BaseException]],
|
||||
exc_value: Optional[BaseException],
|
||||
traceback: Optional[TracebackType],
|
||||
) -> Optional[bool]:
|
||||
@@ -943,7 +942,7 @@ class PregelLoop(LoopProtocol):
|
||||
)
|
||||
|
||||
|
||||
class SyncPregelLoop(PregelLoop, ContextManager):
|
||||
class SyncPregelLoop(PregelLoop, AbstractContextManager):
|
||||
def __init__(
|
||||
self,
|
||||
input: Optional[Any],
|
||||
@@ -959,7 +958,7 @@ class SyncPregelLoop(PregelLoop, ContextManager):
|
||||
interrupt_before: Union[All, Sequence[str]] = EMPTY_SEQ,
|
||||
output_keys: Union[str, Sequence[str]] = EMPTY_SEQ,
|
||||
stream_keys: Union[str, Sequence[str]] = EMPTY_SEQ,
|
||||
input_model: Optional[Type[BaseModel]] = None,
|
||||
input_model: Optional[type[BaseModel]] = None,
|
||||
debug: bool = False,
|
||||
migrate_checkpoint: Optional[Callable[[Checkpoint], None]] = None,
|
||||
trigger_to_nodes: Optional[Mapping[str, Sequence[str]]] = None,
|
||||
@@ -1085,7 +1084,7 @@ class SyncPregelLoop(PregelLoop, ContextManager):
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_type: Optional[type[BaseException]],
|
||||
exc_value: Optional[BaseException],
|
||||
traceback: Optional[TracebackType],
|
||||
) -> Optional[bool]:
|
||||
@@ -1093,7 +1092,7 @@ class SyncPregelLoop(PregelLoop, ContextManager):
|
||||
return self.stack.__exit__(exc_type, exc_value, traceback)
|
||||
|
||||
|
||||
class AsyncPregelLoop(PregelLoop, AsyncContextManager):
|
||||
class AsyncPregelLoop(PregelLoop, AbstractAsyncContextManager):
|
||||
def __init__(
|
||||
self,
|
||||
input: Optional[Any],
|
||||
@@ -1109,7 +1108,7 @@ class AsyncPregelLoop(PregelLoop, AsyncContextManager):
|
||||
manager: Union[None, AsyncParentRunManager, ParentRunManager] = None,
|
||||
output_keys: Union[str, Sequence[str]] = EMPTY_SEQ,
|
||||
stream_keys: Union[str, Sequence[str]] = EMPTY_SEQ,
|
||||
input_model: Optional[Type[BaseModel]] = None,
|
||||
input_model: Optional[type[BaseModel]] = None,
|
||||
debug: bool = False,
|
||||
migrate_checkpoint: Optional[Callable[[Checkpoint], None]] = None,
|
||||
trigger_to_nodes: Optional[Mapping[str, Sequence[str]]] = None,
|
||||
@@ -1238,7 +1237,7 @@ class AsyncPregelLoop(PregelLoop, AsyncContextManager):
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_type: Optional[type[BaseException]],
|
||||
exc_value: Optional[BaseException],
|
||||
traceback: Optional[TracebackType],
|
||||
) -> Optional[bool]:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import asyncio
|
||||
from collections.abc import AsyncIterator, Iterator, Mapping
|
||||
from contextlib import AsyncExitStack, ExitStack, asynccontextmanager, contextmanager
|
||||
from typing import AsyncIterator, Iterator, Mapping, Union
|
||||
from typing import Union
|
||||
|
||||
from langgraph.channels.base import BaseChannel
|
||||
from langgraph.checkpoint.base import Checkpoint
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
@@ -115,13 +111,13 @@ class StreamMessagesHandler(BaseCallbackHandler, _StreamingCallbackHandler):
|
||||
|
||||
def on_chain_start(
|
||||
self,
|
||||
serialized: Dict[str, Any],
|
||||
inputs: Dict[str, Any],
|
||||
serialized: dict[str, Any],
|
||||
inputs: dict[str, Any],
|
||||
*,
|
||||
run_id: UUID,
|
||||
parent_run_id: Optional[UUID] = None,
|
||||
tags: Optional[List[str]] = None,
|
||||
metadata: Optional[Dict[str, Any]] = None,
|
||||
tags: Optional[list[str]] = None,
|
||||
metadata: Optional[dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
if (
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Iterator,
|
||||
Optional,
|
||||
Sequence,
|
||||
Union,
|
||||
)
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
|
||||
from functools import cached_property
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Callable,
|
||||
Iterator,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Union,
|
||||
)
|
||||
|
||||
@@ -37,11 +33,11 @@ class ChannelRead(RunnableCallable):
|
||||
"""Implements the logic for reading state from CONFIG_KEY_READ.
|
||||
Usable both as a runnable as well as a static method to call imperatively."""
|
||||
|
||||
channel: Union[str, list[str]]
|
||||
channel: str | list[str]
|
||||
|
||||
fresh: bool = False
|
||||
|
||||
mapper: Optional[Callable[[Any], Any]] = None
|
||||
mapper: Callable[[Any], Any] | None = None
|
||||
|
||||
@property
|
||||
def config_specs(self) -> list[ConfigurableFieldSpec]:
|
||||
@@ -57,11 +53,11 @@ class ChannelRead(RunnableCallable):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
channel: Union[str, list[str]],
|
||||
channel: str | list[str],
|
||||
*,
|
||||
fresh: bool = False,
|
||||
mapper: Optional[Callable[[Any], Any]] = None,
|
||||
tags: Optional[list[str]] = None,
|
||||
mapper: Callable[[Any], Any] | None = None,
|
||||
tags: list[str] | None = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
func=self._read,
|
||||
@@ -75,9 +71,7 @@ class ChannelRead(RunnableCallable):
|
||||
self.mapper = mapper
|
||||
self.channel = channel
|
||||
|
||||
def get_name(
|
||||
self, suffix: Optional[str] = None, *, name: Optional[str] = None
|
||||
) -> str:
|
||||
def get_name(self, suffix: str | None = None, *, name: str | None = None) -> str:
|
||||
if name:
|
||||
pass
|
||||
elif isinstance(self.channel, str):
|
||||
@@ -100,9 +94,9 @@ class ChannelRead(RunnableCallable):
|
||||
def do_read(
|
||||
config: RunnableConfig,
|
||||
*,
|
||||
select: Union[str, list[str]],
|
||||
select: str | list[str],
|
||||
fresh: bool = False,
|
||||
mapper: Optional[Callable[[Any], Any]] = None,
|
||||
mapper: Callable[[Any], Any] | None = None,
|
||||
) -> Any:
|
||||
try:
|
||||
read: READ_TYPE = config[CONF][CONFIG_KEY_READ]
|
||||
@@ -125,7 +119,7 @@ class PregelNode(Runnable):
|
||||
itself, but instead acts as a container for the components necessary to make
|
||||
a PregelExecutableTask for a node."""
|
||||
|
||||
channels: Union[list[str], Mapping[str, str]]
|
||||
channels: list[str] | Mapping[str, str]
|
||||
"""The channels that will be passed as input to `bound`.
|
||||
If a list, the node will be invoked with the first of that isn't empty.
|
||||
If a dict, the keys are the names of the channels, and the values are the keys
|
||||
@@ -135,7 +129,7 @@ class PregelNode(Runnable):
|
||||
"""If any of these channels is written to, this node will be triggered in
|
||||
the next step."""
|
||||
|
||||
mapper: Optional[Callable[[Any], Any]]
|
||||
mapper: Callable[[Any], Any] | None
|
||||
"""A function to transform the input before passing it to `bound`."""
|
||||
|
||||
writers: list[Runnable]
|
||||
@@ -146,13 +140,13 @@ class PregelNode(Runnable):
|
||||
"""The main logic of the node. This will be invoked with the input from
|
||||
`channels`."""
|
||||
|
||||
retry_policy: Optional[Sequence[RetryPolicy]]
|
||||
retry_policy: Sequence[RetryPolicy] | None
|
||||
"""The retry policies to use when invoking the node."""
|
||||
|
||||
tags: Optional[Sequence[str]]
|
||||
tags: Sequence[str] | None
|
||||
"""Tags to attach to the node for tracing."""
|
||||
|
||||
metadata: Optional[Mapping[str, Any]]
|
||||
metadata: Mapping[str, Any] | None
|
||||
"""Metadata to attach to the node for tracing."""
|
||||
|
||||
subgraphs: Sequence[PregelProtocol]
|
||||
@@ -161,15 +155,15 @@ class PregelNode(Runnable):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
channels: Union[list[str], Mapping[str, str]],
|
||||
channels: list[str] | Mapping[str, str],
|
||||
triggers: Sequence[str],
|
||||
mapper: Optional[Callable[[Any], Any]] = None,
|
||||
writers: Optional[list[Runnable]] = None,
|
||||
tags: Optional[list[str]] = None,
|
||||
metadata: Optional[Mapping[str, Any]] = None,
|
||||
bound: Optional[Runnable[Any, Any]] = None,
|
||||
retry_policy: Optional[Union[RetryPolicy, Sequence[RetryPolicy]]] = None,
|
||||
subgraphs: Optional[Sequence[PregelProtocol]] = None,
|
||||
mapper: Callable[[Any], Any] | None = None,
|
||||
writers: list[Runnable] | None = None,
|
||||
tags: list[str] | None = None,
|
||||
metadata: Mapping[str, Any] | None = None,
|
||||
bound: Runnable[Any, Any] | None = None,
|
||||
retry_policy: RetryPolicy | Sequence[RetryPolicy] | None = None,
|
||||
subgraphs: Sequence[PregelProtocol] | None = None,
|
||||
) -> None:
|
||||
self.channels = channels
|
||||
self.triggers = list(triggers)
|
||||
@@ -219,7 +213,7 @@ class PregelNode(Runnable):
|
||||
return writers
|
||||
|
||||
@cached_property
|
||||
def node(self) -> Optional[Runnable[Any, Any]]:
|
||||
def node(self) -> Runnable[Any, Any] | None:
|
||||
"""Get a runnable that combines `bound` and `writers`."""
|
||||
writers = self.flat_writers
|
||||
if self.bound is DEFAULT_BOUND and not writers:
|
||||
@@ -262,11 +256,9 @@ class PregelNode(Runnable):
|
||||
|
||||
def __or__(
|
||||
self,
|
||||
other: Union[
|
||||
Runnable[Any, Other],
|
||||
Callable[[Any], Other],
|
||||
Mapping[str, Runnable[Any, Other] | Callable[[Any], Other]],
|
||||
],
|
||||
other: Runnable[Any, Other]
|
||||
| Callable[[Any], Other]
|
||||
| Mapping[str, Runnable[Any, Other] | Callable[[Any], Other]],
|
||||
) -> PregelNode:
|
||||
if isinstance(other, Runnable) and ChannelWrite.is_writer(other):
|
||||
return self.copy(update=dict(writers=[*self.writers, other]))
|
||||
@@ -278,7 +270,7 @@ class PregelNode(Runnable):
|
||||
def pipe(
|
||||
self,
|
||||
*others: Runnable[Any, Other] | Callable[[Any], Other],
|
||||
name: Optional[str] = None,
|
||||
name: str | None = None,
|
||||
) -> RunnableSerializable[Any, Other]:
|
||||
for other in others:
|
||||
self = self | other
|
||||
@@ -286,19 +278,17 @@ class PregelNode(Runnable):
|
||||
|
||||
def __ror__(
|
||||
self,
|
||||
other: Union[
|
||||
Runnable[Other, Any],
|
||||
Callable[[Any], Other],
|
||||
Mapping[str, Union[Runnable[Other, Any], Callable[[Other], Any]]],
|
||||
],
|
||||
other: Runnable[Other, Any]
|
||||
| Callable[[Any], Other]
|
||||
| Mapping[str, Runnable[Other, Any] | Callable[[Other], Any]],
|
||||
) -> RunnableSerializable:
|
||||
raise NotImplementedError()
|
||||
|
||||
def invoke(
|
||||
self,
|
||||
input: Input,
|
||||
config: Optional[RunnableConfig] = None,
|
||||
**kwargs: Optional[Any],
|
||||
config: RunnableConfig | None = None,
|
||||
**kwargs: Any | None,
|
||||
) -> Any:
|
||||
return self.bound.invoke(
|
||||
input,
|
||||
@@ -309,8 +299,8 @@ class PregelNode(Runnable):
|
||||
async def ainvoke(
|
||||
self,
|
||||
input: Input,
|
||||
config: Optional[RunnableConfig] = None,
|
||||
**kwargs: Optional[Any],
|
||||
config: RunnableConfig | None = None,
|
||||
**kwargs: Any | None,
|
||||
) -> Any:
|
||||
return await self.bound.ainvoke(
|
||||
input,
|
||||
@@ -321,8 +311,8 @@ class PregelNode(Runnable):
|
||||
def stream(
|
||||
self,
|
||||
input: Input,
|
||||
config: Optional[RunnableConfig] = None,
|
||||
**kwargs: Optional[Any],
|
||||
config: RunnableConfig | None = None,
|
||||
**kwargs: Any | None,
|
||||
) -> Iterator[Any]:
|
||||
yield from self.bound.stream(
|
||||
input,
|
||||
@@ -333,8 +323,8 @@ class PregelNode(Runnable):
|
||||
async def astream(
|
||||
self,
|
||||
input: Input,
|
||||
config: Optional[RunnableConfig] = None,
|
||||
**kwargs: Optional[Any],
|
||||
config: RunnableConfig | None = None,
|
||||
**kwargs: Any | None,
|
||||
) -> AsyncIterator[Any]:
|
||||
async for item in self.bound.astream(
|
||||
input,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
from collections.abc import AsyncIterator, Iterator, Sequence
|
||||
from dataclasses import asdict
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Iterator,
|
||||
Literal,
|
||||
Optional,
|
||||
Sequence,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
@@ -3,8 +3,9 @@ import logging
|
||||
import random
|
||||
import sys
|
||||
import time
|
||||
from collections.abc import Sequence
|
||||
from dataclasses import replace
|
||||
from typing import Any, Optional, Sequence
|
||||
from typing import Any, Optional
|
||||
|
||||
from langgraph.constants import (
|
||||
CONF,
|
||||
|
||||
@@ -3,18 +3,13 @@ import concurrent.futures
|
||||
import threading
|
||||
import time
|
||||
import weakref
|
||||
from collections.abc import AsyncIterator, Awaitable, Iterable, Iterator, Sequence
|
||||
from functools import partial
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Generic,
|
||||
Iterable,
|
||||
Iterator,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
@@ -72,7 +67,7 @@ class FuturesDict(Generic[F, E], dict[F, Optional[PregelExecutableTask]]):
|
||||
callback: weakref.ref[
|
||||
Callable[[PregelExecutableTask, Optional[BaseException]], None]
|
||||
],
|
||||
future_type: Type[F],
|
||||
future_type: type[F],
|
||||
# used for generic typing, newer py supports FutureDict[...](...)
|
||||
) -> None:
|
||||
super().__init__()
|
||||
@@ -475,7 +470,7 @@ def _exception(
|
||||
def _panic_or_proceed(
|
||||
futs: Union[set[concurrent.futures.Future], set[asyncio.Future]],
|
||||
*,
|
||||
timeout_exc_cls: Type[Exception] = TimeoutError,
|
||||
timeout_exc_cls: type[Exception] = TimeoutError,
|
||||
panic: bool = True,
|
||||
) -> None:
|
||||
"""Cancel remaining tasks if any failed, re-raise exception if panic is True."""
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from typing import Any, Mapping, Optional, Sequence, Union
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
from langgraph.channels.base import BaseChannel
|
||||
from langgraph.constants import RESERVED
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
@@ -32,12 +32,12 @@ class ChannelWriteEntry(NamedTuple):
|
||||
"""Value to write, or PASSTHROUGH to use the input."""
|
||||
skip_none: bool = False
|
||||
"""Whether to skip writing if the value is None."""
|
||||
mapper: Optional[Callable] = None
|
||||
mapper: Callable | None = None
|
||||
"""Function to transform the value before writing."""
|
||||
|
||||
|
||||
class ChannelWriteTupleEntry(NamedTuple):
|
||||
mapper: Callable[[Any], Optional[Sequence[tuple[str, Any]]]]
|
||||
mapper: Callable[[Any], Sequence[tuple[str, Any]] | None]
|
||||
"""Function to extract tuples from value."""
|
||||
value: Any = PASSTHROUGH
|
||||
"""Value to write, or PASSTHROUGH to use the input."""
|
||||
@@ -49,15 +49,15 @@ class ChannelWrite(RunnableCallable):
|
||||
"""Implements the logic for sending writes to CONFIG_KEY_SEND.
|
||||
Can be used as a runnable or as a static method to call imperatively."""
|
||||
|
||||
writes: list[Union[ChannelWriteEntry, ChannelWriteTupleEntry, Send]]
|
||||
writes: list[ChannelWriteEntry | ChannelWriteTupleEntry | Send]
|
||||
"""Sequence of write entries or Send objects to write."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
writes: Sequence[Union[ChannelWriteEntry, ChannelWriteTupleEntry, Send]],
|
||||
writes: Sequence[ChannelWriteEntry | ChannelWriteTupleEntry | Send],
|
||||
*,
|
||||
tags: Optional[Sequence[str]] = None, # ignored
|
||||
require_at_least_one_of: Optional[Sequence[str]] = None, # ignored
|
||||
tags: Sequence[str] | None = None, # ignored
|
||||
require_at_least_one_of: Sequence[str] | None = None, # ignored
|
||||
):
|
||||
super().__init__(
|
||||
func=self._write,
|
||||
@@ -70,9 +70,7 @@ class ChannelWrite(RunnableCallable):
|
||||
list[Union[ChannelWriteEntry, ChannelWriteTupleEntry, Send]], writes
|
||||
)
|
||||
|
||||
def get_name(
|
||||
self, suffix: Optional[str] = None, *, name: Optional[str] = None
|
||||
) -> str:
|
||||
def get_name(self, suffix: str | None = None, *, name: str | None = None) -> str:
|
||||
if not name:
|
||||
name = f"ChannelWrite<{','.join(w.channel if isinstance(w, ChannelWriteEntry) else '...' if isinstance(w, ChannelWriteTupleEntry) else w.node for w in self.writes)}>"
|
||||
return super().get_name(suffix, name=name)
|
||||
@@ -122,9 +120,9 @@ class ChannelWrite(RunnableCallable):
|
||||
@staticmethod
|
||||
def do_write(
|
||||
config: RunnableConfig,
|
||||
writes: Sequence[Union[ChannelWriteEntry, ChannelWriteTupleEntry, Send]],
|
||||
writes: Sequence[ChannelWriteEntry | ChannelWriteTupleEntry | Send],
|
||||
allow_passthrough: bool = True,
|
||||
require_at_least_one_of: Optional[Sequence[str]] = None, # ignored
|
||||
require_at_least_one_of: Sequence[str] | None = None, # ignored
|
||||
) -> None:
|
||||
# validate
|
||||
for w in writes:
|
||||
|
||||
@@ -3,18 +3,16 @@ import hashlib
|
||||
import sys
|
||||
import uuid
|
||||
from collections import deque
|
||||
from collections.abc import Hashable, Sequence
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
ClassVar,
|
||||
Generic,
|
||||
Hashable,
|
||||
Literal,
|
||||
NamedTuple,
|
||||
Optional,
|
||||
Sequence,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
@@ -120,7 +118,7 @@ class RetryPolicy(NamedTuple):
|
||||
jitter: bool = True
|
||||
"""Whether to add random jitter to the interval between retries."""
|
||||
retry_on: Union[
|
||||
Type[Exception], Sequence[Type[Exception]], Callable[[Exception], bool]
|
||||
type[Exception], Sequence[type[Exception]], Callable[[Exception], bool]
|
||||
] = default_retry_on
|
||||
"""List of exception classes that should trigger a retry, or a callable that returns True for exceptions that should trigger a retry."""
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from collections import ChainMap
|
||||
from collections.abc import Sequence
|
||||
from os import getenv
|
||||
from typing import Any, Optional, Sequence, cast
|
||||
from typing import Any, Optional, cast
|
||||
|
||||
from langchain_core.callbacks import (
|
||||
AsyncCallbackManager,
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import dataclasses
|
||||
from typing import Any, Generator, Optional, Sequence, Type, Union, get_type_hints
|
||||
from collections.abc import Generator, Sequence
|
||||
from typing import Annotated, Any, Optional, Union, get_type_hints
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
from typing_extensions import Annotated, NotRequired, ReadOnly, Required, get_origin
|
||||
from typing_extensions import NotRequired, ReadOnly, Required, get_origin
|
||||
|
||||
# NOTE: this is redefined here separately from langgraph.constants
|
||||
# to avoid a circular import
|
||||
@@ -68,7 +69,7 @@ def _is_readonly_type(type_: Any) -> bool:
|
||||
_DEFAULT_KEYS: frozenset[str] = frozenset()
|
||||
|
||||
|
||||
def get_field_default(name: str, type_: Any, schema: Type[Any]) -> Any:
|
||||
def get_field_default(name: str, type_: Any, schema: type[Any]) -> Any:
|
||||
"""Determine the default value for a field in a state schema.
|
||||
|
||||
This is based on:
|
||||
@@ -115,7 +116,7 @@ def get_field_default(name: str, type_: Any, schema: Type[Any]) -> Any:
|
||||
|
||||
|
||||
def get_enhanced_type_hints(
|
||||
type: Type[Any],
|
||||
type: type[Any],
|
||||
) -> Generator[tuple[str, Any, Any, Optional[str]], None, None]:
|
||||
"""Attempt to extract default values and descriptions from provided type, used for config schema."""
|
||||
for name, typ in get_type_hints(type).items():
|
||||
|
||||
@@ -4,7 +4,8 @@ import contextvars
|
||||
import inspect
|
||||
import sys
|
||||
import types
|
||||
from typing import Awaitable, Coroutine, Generator, Optional, TypeVar, Union, cast
|
||||
from collections.abc import Awaitable, Coroutine, Generator
|
||||
from typing import Optional, TypeVar, Union, cast
|
||||
|
||||
T = TypeVar("T")
|
||||
AnyFuture = Union[asyncio.Future, concurrent.futures.Future]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import sys
|
||||
import typing
|
||||
from dataclasses import is_dataclass
|
||||
from typing import Any, Dict, Optional, Union
|
||||
from typing import Any, Optional, Union
|
||||
|
||||
import typing_extensions
|
||||
from pydantic import BaseModel
|
||||
@@ -11,7 +11,7 @@ from pydantic.v1 import BaseModel as BaseModelV1
|
||||
def create_model(
|
||||
model_name: str,
|
||||
*,
|
||||
field_definitions: Optional[Dict[str, Any]] = None,
|
||||
field_definitions: Optional[dict[str, Any]] = None,
|
||||
root: Optional[Any] = None,
|
||||
) -> Union[BaseModel, BaseModelV1]:
|
||||
"""Create a pydantic model with the given field definitions.
|
||||
|
||||
@@ -2,21 +2,22 @@ import asyncio
|
||||
import enum
|
||||
import inspect
|
||||
import sys
|
||||
from collections.abc import (
|
||||
AsyncIterator,
|
||||
Awaitable,
|
||||
Coroutine,
|
||||
Generator,
|
||||
Iterator,
|
||||
Sequence,
|
||||
)
|
||||
from contextlib import AsyncExitStack, contextmanager
|
||||
from contextvars import Context, Token, copy_context
|
||||
from functools import partial, wraps
|
||||
from typing import (
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Generator,
|
||||
Iterator,
|
||||
Optional,
|
||||
Protocol,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
@@ -278,7 +279,7 @@ class RunnableCallable(Runnable):
|
||||
|
||||
if func_accepts_config is not None:
|
||||
self.func_accepts_config = func_accepts_config
|
||||
self.func_accepts: dict[str, Tuple[str, Any]] = {}
|
||||
self.func_accepts: dict[str, tuple[str, Any]] = {}
|
||||
else:
|
||||
params = inspect.signature(cast(Callable, func or afunc)).parameters
|
||||
|
||||
|
||||
@@ -41,11 +41,12 @@ types-requests = "^2.32.0.20240914"
|
||||
pycryptodome = "^3.21.0"
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [ "E", "F", "I", "TID251" ]
|
||||
lint.ignore = [ "E501" ]
|
||||
lint.select = [ "E", "F", "I", "TID251", "UP" ]
|
||||
lint.ignore = [ "E501", "UP007" ]
|
||||
line-length = 88
|
||||
indent-width = 4
|
||||
extend-include = ["*.ipynb"]
|
||||
target-version = "py39"
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import re
|
||||
from typing import Any, Sequence, Union
|
||||
from collections.abc import Sequence
|
||||
from typing import Any, Union
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncIterator, Optional
|
||||
from typing import Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import re
|
||||
from typing import Any, AsyncIterator, Iterator, List, Optional, cast
|
||||
from collections.abc import AsyncIterator, Iterator
|
||||
from typing import Any, Optional, cast
|
||||
|
||||
from langchain_core.callbacks import (
|
||||
AsyncCallbackManagerForLLMRun,
|
||||
@@ -20,8 +21,8 @@ class FakeChatModel(GenericFakeChatModel):
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
messages: list[BaseMessage],
|
||||
stop: Optional[list[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> ChatResult:
|
||||
@@ -42,8 +43,8 @@ class FakeChatModel(GenericFakeChatModel):
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
messages: list[BaseMessage],
|
||||
stop: Optional[list[str]] = None,
|
||||
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> Iterator[ChatGenerationChunk]:
|
||||
@@ -90,8 +91,8 @@ class FakeChatModel(GenericFakeChatModel):
|
||||
|
||||
async def _astream(
|
||||
self,
|
||||
messages: List[BaseMessage],
|
||||
stop: Optional[List[str]] = None,
|
||||
messages: list[BaseMessage],
|
||||
stop: Optional[list[str]] = None,
|
||||
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
|
||||
**kwargs: Any,
|
||||
) -> AsyncIterator[ChatGenerationChunk]:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import operator
|
||||
from typing import Sequence, Union
|
||||
from collections.abc import Sequence
|
||||
from typing import Union
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -33,7 +34,7 @@ def test_last_value() -> None:
|
||||
|
||||
def test_topic() -> None:
|
||||
channel = Topic(str).from_checkpoint(MISSING)
|
||||
assert channel.ValueType is Sequence[str]
|
||||
assert channel.ValueType == Sequence[str]
|
||||
assert channel.UpdateType is Union[str, list[str]]
|
||||
|
||||
assert channel.update(["a", "b"])
|
||||
@@ -57,7 +58,7 @@ def test_topic() -> None:
|
||||
|
||||
def test_topic_accumulate() -> None:
|
||||
channel = Topic(str, accumulate=True).from_checkpoint(MISSING)
|
||||
assert channel.ValueType is Sequence[str]
|
||||
assert channel.ValueType == Sequence[str]
|
||||
assert channel.UpdateType is Union[str, list[str]]
|
||||
|
||||
assert channel.update(["a", "b"])
|
||||
|
||||
@@ -1575,7 +1575,7 @@ def test_migrate_checkpoints(source: str, target: str) -> None:
|
||||
# check that the migrated checkpoint matches the target checkpoint
|
||||
assert (
|
||||
migrated == target_checkpoint.checkpoint
|
||||
), "Checkpoint mismatch at index {}".format(idx)
|
||||
), f"Checkpoint mismatch at index {idx}"
|
||||
|
||||
|
||||
@NEEDS_CONTEXTVARS
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Iterator
|
||||
from collections.abc import Iterator
|
||||
|
||||
from langgraph.pregel.io import single
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ import json
|
||||
import operator
|
||||
import re
|
||||
import time
|
||||
from collections.abc import Iterator
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import replace
|
||||
from typing import Annotated, Any, Iterator, Literal, Optional, Union, cast
|
||||
from typing import Annotated, Any, Literal, Optional, Union, cast
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
@@ -2,11 +2,11 @@ import asyncio
|
||||
import operator
|
||||
import re
|
||||
import sys
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
AsyncIterator,
|
||||
Literal,
|
||||
Optional,
|
||||
Union,
|
||||
|
||||
@@ -9,6 +9,7 @@ import time
|
||||
import uuid
|
||||
import warnings
|
||||
from collections import Counter, deque
|
||||
from collections.abc import Generator, Iterator, Sequence
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass, field
|
||||
@@ -16,14 +17,8 @@ from random import randrange
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
Dict,
|
||||
Generator,
|
||||
Iterator,
|
||||
List,
|
||||
Literal,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Union,
|
||||
get_type_hints,
|
||||
)
|
||||
@@ -243,7 +238,7 @@ def test_checkpoint_errors() -> None:
|
||||
|
||||
class FaultyPutWritesCheckpointer(InMemorySaver):
|
||||
def put_writes(
|
||||
self, config: RunnableConfig, writes: List[Tuple[str, Any]], task_id: str
|
||||
self, config: RunnableConfig, writes: list[tuple[str, Any]], task_id: str
|
||||
) -> RunnableConfig:
|
||||
raise ValueError("Faulty put_writes")
|
||||
|
||||
@@ -448,7 +443,7 @@ def test_reducer_before_first_node() -> None:
|
||||
|
||||
class State(TypedDict):
|
||||
hello: str
|
||||
messages: Annotated[List[str], add_messages]
|
||||
messages: Annotated[list[str], add_messages]
|
||||
|
||||
def node_a(state: State) -> State:
|
||||
assert state == {
|
||||
@@ -1131,7 +1126,7 @@ def test_pending_writes_resume(
|
||||
value: Annotated[int, operator.add]
|
||||
|
||||
class AwhileMaker:
|
||||
def __init__(self, sleep: float, rtn: Union[Dict, Exception]) -> None:
|
||||
def __init__(self, sleep: float, rtn: Union[dict, Exception]) -> None:
|
||||
self.sleep = sleep
|
||||
self.rtn = rtn
|
||||
self.reset()
|
||||
@@ -4429,7 +4424,7 @@ def test_xray_lance(snapshot: SnapshotAssertion):
|
||||
return f"Name: {self.name}\nRole: {self.role}\nAffiliation: {self.affiliation}\nDescription: {self.description}\n"
|
||||
|
||||
class Perspectives(BaseModel):
|
||||
analysts: List[Analyst] = Field(
|
||||
analysts: list[Analyst] = Field(
|
||||
description="Comprehensive list of investment analysts with their roles and affiliations.",
|
||||
)
|
||||
|
||||
@@ -4448,15 +4443,15 @@ def test_xray_lance(snapshot: SnapshotAssertion):
|
||||
)
|
||||
|
||||
class InterviewState(TypedDict):
|
||||
messages: Annotated[List[AnyMessage], add_messages]
|
||||
messages: Annotated[list[AnyMessage], add_messages]
|
||||
analyst: Analyst
|
||||
section: Section
|
||||
|
||||
class ResearchGraphState(TypedDict):
|
||||
analysts: List[Analyst]
|
||||
analysts: list[Analyst]
|
||||
topic: str
|
||||
max_analysts: int
|
||||
sections: List[Section]
|
||||
sections: list[Section]
|
||||
interviews: Annotated[list, operator.add]
|
||||
|
||||
# Conditional edge
|
||||
@@ -7435,7 +7430,7 @@ def test_parallel_interrupts(
|
||||
class ChildState(BaseModel):
|
||||
prompt: str = Field(..., description="What is going to be asked to the user?")
|
||||
human_input: Optional[str] = Field(None, description="What the human said")
|
||||
human_inputs: Annotated[List[str], operator.add] = Field(
|
||||
human_inputs: Annotated[list[str], operator.add] = Field(
|
||||
default_factory=list, description="All of my messages"
|
||||
)
|
||||
|
||||
@@ -7456,10 +7451,10 @@ def test_parallel_interrupts(
|
||||
# --- PARENT GRAPH ---
|
||||
|
||||
class ParentState(BaseModel):
|
||||
prompts: List[str] = Field(
|
||||
prompts: list[str] = Field(
|
||||
..., description="What is going to be asked to the user?"
|
||||
)
|
||||
human_inputs: Annotated[List[str], operator.add] = Field(
|
||||
human_inputs: Annotated[list[str], operator.add] = Field(
|
||||
default_factory=list, description="All of my messages"
|
||||
)
|
||||
|
||||
@@ -7612,7 +7607,7 @@ def test_parallel_interrupts_double(
|
||||
class ChildState(BaseModel):
|
||||
prompt: str = Field(..., description="What is going to be asked to the user?")
|
||||
human_input: Optional[str] = Field(None, description="What the human said")
|
||||
human_inputs: Annotated[List[str], operator.add] = Field(
|
||||
human_inputs: Annotated[list[str], operator.add] = Field(
|
||||
default_factory=list, description="All of my messages"
|
||||
)
|
||||
|
||||
@@ -7640,10 +7635,10 @@ def test_parallel_interrupts_double(
|
||||
# --- PARENT GRAPH ---
|
||||
|
||||
class ParentState(BaseModel):
|
||||
prompts: List[str] = Field(
|
||||
prompts: list[str] = Field(
|
||||
..., description="What is going to be asked to the user?"
|
||||
)
|
||||
human_inputs: Annotated[List[str], operator.add] = Field(
|
||||
human_inputs: Annotated[list[str], operator.add] = Field(
|
||||
default_factory=list, description="All of my messages"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,20 +8,15 @@ import random
|
||||
import sys
|
||||
import uuid
|
||||
from collections import Counter, deque
|
||||
from collections.abc import AsyncGenerator, AsyncIterator, Generator
|
||||
from contextlib import asynccontextmanager, contextmanager
|
||||
from dataclasses import replace
|
||||
from time import perf_counter
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
AsyncGenerator,
|
||||
AsyncIterator,
|
||||
Dict,
|
||||
Generator,
|
||||
List,
|
||||
Literal,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
from uuid import UUID
|
||||
@@ -113,7 +108,7 @@ async def test_checkpoint_errors() -> None:
|
||||
|
||||
class FaultyPutWritesCheckpointer(InMemorySaver):
|
||||
async def aput_writes(
|
||||
self, config: RunnableConfig, writes: List[Tuple[str, Any]], task_id: str
|
||||
self, config: RunnableConfig, writes: list[tuple[str, Any]], task_id: str
|
||||
) -> RunnableConfig:
|
||||
raise ValueError("Faulty put_writes")
|
||||
|
||||
@@ -1959,7 +1954,7 @@ async def test_pending_writes_resume(
|
||||
value: Annotated[int, operator.add]
|
||||
|
||||
class AwhileMaker:
|
||||
def __init__(self, sleep: float, rtn: Union[Dict, Exception]) -> None:
|
||||
def __init__(self, sleep: float, rtn: Union[dict, Exception]) -> None:
|
||||
self.sleep = sleep
|
||||
self.rtn = rtn
|
||||
self.reset()
|
||||
@@ -6249,7 +6244,7 @@ async def test_store_injected_async(checkpointer_name: str, store_name: str) ->
|
||||
assert result == {"count": N + 1}
|
||||
returned_doc = (await the_store.aget(namespace, doc_id)).value
|
||||
assert returned_doc == {**doc, "from_thread": thread_1, "some_val": 0}
|
||||
assert len((await the_store.asearch(namespace))) == 1
|
||||
assert len(await the_store.asearch(namespace)) == 1
|
||||
|
||||
# Check results after another turn of the same thread
|
||||
result = await graph.ainvoke(
|
||||
@@ -6258,7 +6253,7 @@ async def test_store_injected_async(checkpointer_name: str, store_name: str) ->
|
||||
assert result == {"count": (N + 1) * 2}
|
||||
returned_doc = (await the_store.aget(namespace, doc_id)).value
|
||||
assert returned_doc == {**doc, "from_thread": thread_1, "some_val": N + 1}
|
||||
assert len((await the_store.asearch(namespace))) == 1
|
||||
assert len(await the_store.asearch(namespace)) == 1
|
||||
|
||||
# Test with a different thread
|
||||
result = await graph.ainvoke(
|
||||
@@ -6272,7 +6267,7 @@ async def test_store_injected_async(checkpointer_name: str, store_name: str) ->
|
||||
"some_val": 0,
|
||||
} # Overwrites the whole doc
|
||||
assert (
|
||||
len((await the_store.asearch(namespace))) == 1
|
||||
len(await the_store.asearch(namespace)) == 1
|
||||
) # still overwriting the same one
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import re
|
||||
import sys
|
||||
import uuid
|
||||
from enum import Enum
|
||||
from typing import Annotated, List, Literal, Optional, Union
|
||||
from typing import Annotated, Literal, Optional, Union
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -183,7 +183,7 @@ def test_nested_pydantic_models(version: str) -> None:
|
||||
validated_age: Annotated[int, Field(gt=0, lt=120)]
|
||||
|
||||
# Generic containers with validators
|
||||
decimal_list: List[decimal.Decimal]
|
||||
decimal_list: list[decimal.Decimal]
|
||||
id_tuple: tuple[uuid.UUID, uuid.UUID]
|
||||
|
||||
inputs = {
|
||||
|
||||
@@ -2,13 +2,13 @@ import inspect
|
||||
import operator
|
||||
import warnings
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Annotated, Any, Optional
|
||||
from typing import Annotated as Annotated2
|
||||
from typing import Any, Optional
|
||||
|
||||
import pytest
|
||||
from langchain_core.runnables import RunnableConfig, RunnableLambda
|
||||
from pydantic.v1 import BaseModel
|
||||
from typing_extensions import Annotated, NotRequired, Required, TypedDict
|
||||
from typing_extensions import NotRequired, Required, TypedDict
|
||||
|
||||
from langgraph.graph.state import StateGraph, _get_node_name, _warn_invalid_state_schema
|
||||
from langgraph.managed.shared_value import SharedValue
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
from typing import Any, Callable, Tuple, TypeVar
|
||||
from typing import Any, Callable, TypeVar
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import langsmith as ls
|
||||
@@ -35,7 +35,7 @@ T = TypeVar("T")
|
||||
|
||||
|
||||
def wait_for(
|
||||
condition: Callable[[], Tuple[T, bool]],
|
||||
condition: Callable[[], tuple[T, bool]],
|
||||
max_sleep_time: int = 10,
|
||||
sleep_time: int = 3,
|
||||
) -> T:
|
||||
|
||||
@@ -2,11 +2,10 @@ import functools
|
||||
import sys
|
||||
import uuid
|
||||
from typing import (
|
||||
Annotated,
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
ForwardRef,
|
||||
List,
|
||||
Literal,
|
||||
Optional,
|
||||
TypeVar,
|
||||
@@ -16,7 +15,7 @@ from unittest.mock import patch
|
||||
|
||||
import langsmith
|
||||
import pytest
|
||||
from typing_extensions import Annotated, NotRequired, Required, TypedDict
|
||||
from typing_extensions import NotRequired, Required, TypedDict
|
||||
|
||||
from langgraph.graph import END, StateGraph
|
||||
from langgraph.graph.graph import CompiledGraph
|
||||
@@ -150,9 +149,9 @@ def test_is_optional_type():
|
||||
assert not _is_optional_type(int)
|
||||
assert _is_optional_type(Optional[Literal[1, 2, 3]])
|
||||
assert not _is_optional_type(Literal[1, 2, 3])
|
||||
assert _is_optional_type(Optional[List[int]])
|
||||
assert _is_optional_type(Optional[Dict[str, int]])
|
||||
assert not _is_optional_type(List[Optional[int]])
|
||||
assert _is_optional_type(Optional[list[int]])
|
||||
assert _is_optional_type(Optional[dict[str, int]])
|
||||
assert not _is_optional_type(list[Optional[int]])
|
||||
assert _is_optional_type(Union[Optional[str], Optional[int]])
|
||||
assert _is_optional_type(
|
||||
Union[
|
||||
@@ -177,8 +176,8 @@ def test_is_optional_type():
|
||||
assert _is_optional_type(Optional[ForwardRef("MyClass")])
|
||||
assert not _is_optional_type(ForwardRef("MyClass"))
|
||||
|
||||
assert _is_optional_type(Optional[Union[List[int], Dict[str, Optional[int]]]])
|
||||
assert not _is_optional_type(Union[List[int], Dict[str, Optional[int]]])
|
||||
assert _is_optional_type(Optional[Union[list[int], dict[str, Optional[int]]]])
|
||||
assert not _is_optional_type(Union[list[int], dict[str, Optional[int]]])
|
||||
|
||||
assert _is_optional_type(Optional[Callable[[int], str]])
|
||||
assert not _is_optional_type(Callable[[int], Optional[str]])
|
||||
|
||||
Reference in New Issue
Block a user