mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-27 01:52:25 +02:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2920a9dd19 | ||
|
|
db8ed4e9e4 | ||
|
|
b16fcc8468 | ||
|
|
a2fe4df89b | ||
|
|
69dd20e523 | ||
|
|
5152a96fce | ||
|
|
220314b53a | ||
|
|
38bbd92e01 | ||
|
|
e3cb2dd23b | ||
|
|
2f23d1a30d |
@@ -18,6 +18,9 @@ build-prebuilt:
|
||||
build-docs: build-prebuilt
|
||||
TARGET_LANGUAGE=python uv run python -m mkdocs build --clean -f mkdocs.yml --strict
|
||||
|
||||
build-docs-js: build-prebuilt
|
||||
TARGET_LANGUAGE=js uv run python -m mkdocs build --clean -f mkdocs.yml --strict
|
||||
|
||||
llms-text:
|
||||
uv run python -m _scripts.generate_llms_text docs/llms-full.txt
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ pip install -U langgraph "langchain[anthropic]"
|
||||
|
||||
!!! info
|
||||
|
||||
LangChain is installed so the agent can call the [model](https://python.langchain.com/docs/integrations/chat/).
|
||||
`langchain[anthropic]` is installed so the agent can call the [model](https://python.langchain.com/docs/integrations/chat/).
|
||||
|
||||
:::
|
||||
|
||||
@@ -41,7 +41,7 @@ npm install @langchain/langgraph @langchain/core @langchain/anthropic
|
||||
|
||||
!!! info
|
||||
|
||||
LangChain is installed so the agent can call the [model](https://js.langchain.com/docs/integrations/chat/).
|
||||
`@langchain/core` `@langchain/anthropic` are installed so the agent can call the [model](https://js.langchain.com/docs/integrations/chat/).
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ LangGraph Platform provides different security defaults:
|
||||
- Can be customized with your auth handler
|
||||
|
||||
!!! note "Custom auth"
|
||||
Custom auth **is supported** for all plans in LangGraph Platform.
|
||||
|
||||
Custom auth **is supported** for all plans in LangGraph Platform.
|
||||
|
||||
### Self-Hosted
|
||||
|
||||
|
||||
@@ -51,6 +51,51 @@ For some examples of pitfalls to avoid, see the [Common Pitfalls](./functional_a
|
||||
how to structure your code using **tasks** to avoid these issues. The same principles apply to the @[StateGraph (Graph API)][StateGraph].
|
||||
:::
|
||||
|
||||
## Durability modes
|
||||
|
||||
LangGraph supports three durability modes that allow you to balance performance and data consistency based on your application's requirements. The durability modes, from least to most durable, are as follows:
|
||||
|
||||
- [`"exit"`](#exit)
|
||||
- [`"async"`](#async)
|
||||
- [`"sync"`](#sync)
|
||||
|
||||
A higher durability mode add more overhead to the workflow execution.
|
||||
|
||||
!!! version-added "Added in v0.6.0"
|
||||
|
||||
Use the `durability` parameter instead of `checkpoint_during` (deprecated in v0.6.0) for persistence policy management:
|
||||
|
||||
* `durability="async"` replaces `checkpoint_during=True`
|
||||
* `durability="exit"` replaces `checkpoint_during=False`
|
||||
|
||||
for persistence policy management, with the following mapping:
|
||||
|
||||
* `checkpoint_during=True` -> `durability="async"`
|
||||
* `checkpoint_during=False` -> `durability="exit"`
|
||||
|
||||
|
||||
### `"exit"`
|
||||
Changes are persisted only when graph execution completes (either successfully or with an error). This provides the best performance for long-running graphs but means intermediate state is not saved, so you cannot recover from mid-execution failures or interrupt the graph execution.
|
||||
|
||||
### `"async"`
|
||||
Changes are persisted asynchronously while the next step executes. This provides good performance and durability, but there's a small risk that checkpoints might not be written if the process crashes during execution.
|
||||
|
||||
### `"sync"`
|
||||
Changes are persisted synchronously before the next step starts. This ensures that every checkpoint is written before continuing execution, providing high durability at the cost of some performance overhead.
|
||||
|
||||
You can specify the durability mode when calling any graph execution method:
|
||||
|
||||
:::python
|
||||
|
||||
```python
|
||||
graph.stream(
|
||||
{"input": "test"},
|
||||
durability="sync"
|
||||
)
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Using tasks in nodes
|
||||
|
||||
If a [node](./low_level.md#nodes) contains multiple operations, you may find it easier to convert each operation into a **task** rather than refactor the operations into individual nodes.
|
||||
|
||||
@@ -88,8 +88,6 @@ Typically, all graph nodes communicate with a single schema. This means that the
|
||||
|
||||
It is possible to have nodes write to private state channels inside the graph for internal node communication. We can simply define a private schema, `PrivateState`.
|
||||
|
||||
See [this guide](../how-tos/graph-api.ipynb#pass-private-state-between-nodes) for more detail.
|
||||
|
||||
It is also possible to define explicit input and output schemas for a graph. In these cases, we define an "internal" schema that contains _all_ keys relevant to graph operations. But, we also define `input` and `output` schemas that are sub-sets of the "internal" schema to constrain the input and output of the graph. See [this guide](../how-tos/graph-api.md#define-input-and-output-schemas) for more detail.
|
||||
|
||||
Let's look at an example:
|
||||
@@ -473,7 +471,7 @@ const builder = new StateGraph(State);
|
||||
|
||||
:::
|
||||
|
||||
Behind the scenes, functions are converted to [RunnableLambda](https://api.python.langchain.com/en/latest/runnables/langchain_core.runnables.base.RunnableLambda.html#langchain_core.runnables.base.RunnableLambda)s, which add batch and async support to your function, along with native tracing and debugging.
|
||||
Behind the scenes, functions are converted to [RunnableLambda](https://python.langchain.com/api_reference/core/runnables/langchain_core.runnables.base.RunnableLambda.html)s, which add batch and async support to your function, along with native tracing and debugging.
|
||||
|
||||
If you add a node to a graph without specifying a name, it will be given a default name equivalent to the function name.
|
||||
|
||||
@@ -701,7 +699,8 @@ graph.addConditionalEdges("nodeA", routingFunction, {
|
||||
:::
|
||||
|
||||
!!! tip
|
||||
Use [`Command`](#command) instead of conditional edges if you want to combine state updates and routing in a single function.
|
||||
|
||||
Use [`Command`](#command) instead of conditional edges if you want to combine state updates and routing in a single function.
|
||||
|
||||
### Entry Point
|
||||
|
||||
@@ -820,7 +819,6 @@ def my_node(state: State) -> Command[Literal["my_other_node"]]:
|
||||
return Command(update={"foo": "baz"}, goto="my_other_node")
|
||||
```
|
||||
|
||||
Check out this [how-to guide](../how-tos/graph-api.ipynb#combine-control-flow-and-state-updates-with-command) for an end-to-end example of how to use `Command`.
|
||||
:::
|
||||
|
||||
:::js
|
||||
@@ -860,7 +858,6 @@ builder.addNode("myNode", myNode, {
|
||||
});
|
||||
```
|
||||
|
||||
Check out this [how-to guide](../how-tos/graph-api.ipynb#combine-control-flow-and-state-updates-with-command) for an end-to-end example of how to use `Command`.
|
||||
:::
|
||||
|
||||
!!! important
|
||||
|
||||
@@ -145,7 +145,7 @@ def my_node(state, config):
|
||||
By default, if you add custom authorization on your resources, this will also apply to interactions made from the Studio. If you want, you can handle logged-in Studio users differently by checking [is_studio_user()](../../reference/functions/sdk_auth.isStudioUser.html).
|
||||
|
||||
!!! note
|
||||
`is_studio_user` was added in version 0.1.73 of the langgraph-sdk. If you're on an older version, you can still check whether `isinstance(ctx.user, StudioUser)`.
|
||||
`is_studio_user` was added in version 0.1.73 of the langgraph-sdk. If you're on an older version, you can still check whether `isinstance(ctx.user, StudioUser)`.
|
||||
|
||||
```python
|
||||
from langgraph_sdk.auth import is_studio_user, Auth
|
||||
|
||||
@@ -57,7 +57,7 @@ def create_handoff_tool(*, agent_name: str, description: str | None = None):
|
||||
return handoff_tool
|
||||
```
|
||||
|
||||
1. Access the [state](../concepts/low_level.md#state) of the agent that is calling the handoff tool using the @[InjectedState][InjectedState] annotation.
|
||||
1. Access the [state](../concepts/low_level.md#state) of the agent that is calling the handoff tool using the @[InjectedState] annotation.
|
||||
2. The `Command` primitive allows specifying a state update and a node transition as a single operation, making it useful for implementing handoffs.
|
||||
3. Name of the agent or node to hand off to.
|
||||
4. Take the agent's messages and **add** them to the parent's **state** as part of the handoff. The next agent will see the parent state.
|
||||
|
||||
@@ -17,7 +17,7 @@ Create a `MemorySaver` checkpointer:
|
||||
:::python
|
||||
|
||||
```python
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
|
||||
memory = InMemorySaver()
|
||||
```
|
||||
@@ -447,3 +447,4 @@ const graph = new StateGraph(State)
|
||||
## Next steps
|
||||
|
||||
In the next tutorial, you will [add human-in-the-loop to the chatbot](./4-human-in-the-loop.md) to handle situations where it may need guidance or verification before proceeding.
|
||||
|
||||
|
||||
Generated
+16
-2
@@ -2337,7 +2337,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = { editable = "../libs/langgraph" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -2524,6 +2524,7 @@ docs = [
|
||||
{ name = "markdown-include" },
|
||||
{ name = "mkdocs" },
|
||||
{ name = "mkdocs-exclude" },
|
||||
{ name = "mkdocs-exclude-search" },
|
||||
{ name = "mkdocs-git-committers-plugin-2" },
|
||||
{ name = "mkdocs-include-markdown-plugin" },
|
||||
{ name = "mkdocs-material", extra = ["imaging"] },
|
||||
@@ -2595,6 +2596,7 @@ docs = [
|
||||
{ name = "markdown-include" },
|
||||
{ name = "mkdocs" },
|
||||
{ name = "mkdocs-exclude" },
|
||||
{ name = "mkdocs-exclude-search" },
|
||||
{ name = "mkdocs-git-committers-plugin-2" },
|
||||
{ name = "mkdocs-include-markdown-plugin", specifier = ">=7.1.6" },
|
||||
{ name = "mkdocs-material", extras = ["imaging"] },
|
||||
@@ -2641,7 +2643,7 @@ test = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = { editable = "../libs/prebuilt" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -3030,6 +3032,18 @@ dependencies = [
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/54/b5/3a8e289282c9e8d7003f8a2f53d673d4fdaa81d493dc6966092d9985b6fc/mkdocs-exclude-1.0.2.tar.gz", hash = "sha256:ba6fab3c80ddbe3fd31d3e579861fd3124513708271180a5f81846da8c7e2a51", size = 6751, upload-time = "2019-02-20T23:34:12.81Z" }
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-exclude-search"
|
||||
version = "0.6.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "mkdocs" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/1d/52/8243589d294cf6091c1145896915fe50feea0e91d64d843942d0175770c2/mkdocs-exclude-search-0.6.6.tar.gz", hash = "sha256:3cdff1b9afdc1b227019cd1e124f401453235b92153d60c0e5e651a76be4f044", size = 9501, upload-time = "2023-12-03T22:58:21.259Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/ef/9af45ffb1bdba684a0694922abae0bb771e9777aba005933f838b7f1bcea/mkdocs_exclude_search-0.6.6-py3-none-any.whl", hash = "sha256:2b4b941d1689808db533fe4a6afba75ce76c9bab8b21d4e31efc05fd8c4e0a4f", size = 7821, upload-time = "2023-12-03T22:58:19.355Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mkdocs-get-deps"
|
||||
version = "0.2.0"
|
||||
|
||||
@@ -4,6 +4,7 @@ import asyncio
|
||||
import enum
|
||||
import inspect
|
||||
import sys
|
||||
import warnings
|
||||
from collections.abc import (
|
||||
AsyncIterator,
|
||||
Awaitable,
|
||||
@@ -303,6 +304,16 @@ class RunnableCallable(Runnable):
|
||||
if typ != (ANY_TYPE,) and p.annotation not in typ:
|
||||
# A specific type is required, but the function annotation does
|
||||
# not match the expected type.
|
||||
|
||||
# If this is a config parameter with incorrect typing, emit a warning
|
||||
# because we used to support any type but are moving towards more correct typing
|
||||
if kw == "config" and p.annotation != inspect.Parameter.empty:
|
||||
warnings.warn(
|
||||
f"The 'config' parameter should be typed as 'RunnableConfig' or "
|
||||
f"'RunnableConfig | None', not '{p.annotation}'. ",
|
||||
UserWarning,
|
||||
stacklevel=4,
|
||||
)
|
||||
continue
|
||||
|
||||
# If the kwarg is accepted by the function, store the key / runtime attribute to inject
|
||||
|
||||
@@ -91,7 +91,7 @@ class GraphInterrupt(GraphBubbleUp):
|
||||
|
||||
@deprecated(
|
||||
"NodeInterrupt is deprecated. Please use `langgraph.types.interrupt` instead.",
|
||||
stacklevel=2,
|
||||
category=None,
|
||||
)
|
||||
class NodeInterrupt(GraphInterrupt):
|
||||
"""Raised by a node to interrupt execution.
|
||||
|
||||
@@ -128,7 +128,6 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
Use this to expose immutable context data to your nodes, like user_id, db_conn, etc.
|
||||
input_schema: The schema class that defines the input to the graph.
|
||||
output_schema: The schema class that defines the output from the graph.
|
||||
name: The default name to use when compiling the graph.
|
||||
|
||||
!!! warning "`config_schema` Deprecated"
|
||||
The `config_schema` parameter is deprecated in v0.6.0 and support will be removed in v2.0.0.
|
||||
@@ -178,7 +177,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
managed: dict[str, ManagedValueSpec]
|
||||
schemas: dict[type[Any], dict[str, BaseChannel | ManagedValueSpec]]
|
||||
waiting_edges: set[tuple[tuple[str, ...], str]]
|
||||
name: str = "LangGraph"
|
||||
|
||||
compiled: bool
|
||||
state_schema: type[StateT]
|
||||
context_schema: type[ContextT] | None
|
||||
@@ -875,7 +874,7 @@ class StateGraph(Generic[StateT, ContextT, InputT, OutputT]):
|
||||
debug=debug,
|
||||
store=store,
|
||||
cache=cache,
|
||||
name=name or self.name,
|
||||
name=name or "LangGraph",
|
||||
)
|
||||
|
||||
compiled.attach_node(START, None)
|
||||
@@ -1391,6 +1390,14 @@ def _is_field_managed_value(name: str, typ: type[Any]) -> ManagedValueSpec | Non
|
||||
if is_managed_value(decoration):
|
||||
return decoration
|
||||
|
||||
# Handle Required, NotRequired, etc wrapped types by extracting the inner type
|
||||
if (
|
||||
get_origin(typ) is not None
|
||||
and (args := get_args(typ))
|
||||
and (inner_type := args[0])
|
||||
):
|
||||
return _is_field_managed_value(name, inner_type)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from typing import (
|
||||
|
||||
from typing_extensions import TypeGuard
|
||||
|
||||
from langgraph.pregel._scratchpad import PregelScratchpad
|
||||
from langgraph._internal._scratchpad import PregelScratchpad
|
||||
|
||||
V = TypeVar("V")
|
||||
U = TypeVar("U")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Annotated
|
||||
|
||||
from langgraph._internal._scratchpad import PregelScratchpad
|
||||
from langgraph.managed.base import ManagedValue
|
||||
from langgraph.pregel._scratchpad import PregelScratchpad
|
||||
|
||||
__all__ = ("IsLastStep", "RemainingStepsManager")
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ from langgraph._internal._constants import (
|
||||
RETURN,
|
||||
TASKS,
|
||||
)
|
||||
from langgraph._internal._scratchpad import PregelScratchpad
|
||||
from langgraph._internal._typing import EMPTY_SEQ, MISSING
|
||||
from langgraph.channels.base import BaseChannel
|
||||
from langgraph.channels.topic import Topic
|
||||
@@ -69,7 +70,6 @@ from langgraph.pregel._call import get_runnable_for_task, identifier
|
||||
from langgraph.pregel._io import read_channels
|
||||
from langgraph.pregel._log import logger
|
||||
from langgraph.pregel._read import INPUT_CACHE_KEY_TYPE, PregelNode
|
||||
from langgraph.pregel._scratchpad import PregelScratchpad
|
||||
from langgraph.runtime import DEFAULT_RUNTIME, Runtime
|
||||
from langgraph.store.base import BaseStore
|
||||
from langgraph.types import (
|
||||
|
||||
@@ -48,6 +48,7 @@ from langgraph._internal._constants import (
|
||||
PUSH,
|
||||
RESUME,
|
||||
)
|
||||
from langgraph._internal._scratchpad import PregelScratchpad
|
||||
from langgraph._internal._typing import EMPTY_SEQ, MISSING
|
||||
from langgraph.cache.base import BaseCache
|
||||
from langgraph.channels.base import BaseChannel
|
||||
@@ -100,7 +101,6 @@ from langgraph.pregel._io import (
|
||||
read_channels,
|
||||
)
|
||||
from langgraph.pregel._read import PregelNode
|
||||
from langgraph.pregel._scratchpad import PregelScratchpad
|
||||
from langgraph.pregel._utils import get_new_channel_versions, is_xxh3_128_hexdigest
|
||||
from langgraph.pregel.debug import (
|
||||
map_debug_checkpoint,
|
||||
|
||||
@@ -30,13 +30,13 @@ from langgraph._internal._constants import (
|
||||
RETURN,
|
||||
)
|
||||
from langgraph._internal._future import chain_future, run_coroutine_threadsafe
|
||||
from langgraph._internal._scratchpad import PregelScratchpad
|
||||
from langgraph._internal._typing import MISSING
|
||||
from langgraph.constants import TAG_HIDDEN
|
||||
from langgraph.errors import GraphBubbleUp, GraphInterrupt
|
||||
from langgraph.pregel._algo import Call
|
||||
from langgraph.pregel._executor import Submit
|
||||
from langgraph.pregel._retry import arun_with_retry, run_with_retry
|
||||
from langgraph.pregel._scratchpad import PregelScratchpad
|
||||
from langgraph.types import (
|
||||
CachePolicy,
|
||||
PregelExecutableTask,
|
||||
|
||||
@@ -11,7 +11,7 @@ from collections.abc import AsyncIterator, Iterator, Mapping, Sequence
|
||||
from dataclasses import is_dataclass
|
||||
from functools import partial
|
||||
from inspect import isclass
|
||||
from typing import Any, Callable, Generic, Optional, Union, cast, get_type_hints
|
||||
from typing import Any, Callable, Generic, Union, cast, get_type_hints
|
||||
from uuid import UUID, uuid5
|
||||
|
||||
from langchain_core.globals import get_debug
|
||||
@@ -637,8 +637,7 @@ class Pregel(
|
||||
**deprecated_kwargs: Unpack[DeprecatedKwargs],
|
||||
) -> None:
|
||||
if (
|
||||
config_type := deprecated_kwargs.get("config_type"),
|
||||
MISSING,
|
||||
config_type := deprecated_kwargs.get("config_type", MISSING)
|
||||
) is not MISSING:
|
||||
warnings.warn(
|
||||
"`config_type` is deprecated and will be removed. Please use `context_schema` instead.",
|
||||
@@ -785,7 +784,8 @@ class Pregel(
|
||||
return self
|
||||
|
||||
@deprecated(
|
||||
"`config_schema` is deprecated. Use `get_context_jsonschema` for the relevant schema instead."
|
||||
"`config_schema` is deprecated. Use `get_context_jsonschema` for the relevant schema instead.",
|
||||
category=None,
|
||||
)
|
||||
def config_schema(self, *, include: Sequence[str] | None = None) -> type[BaseModel]:
|
||||
warnings.warn(
|
||||
@@ -810,7 +810,8 @@ class Pregel(
|
||||
return create_model(self.get_name("Config"), field_definitions=fields)
|
||||
|
||||
@deprecated(
|
||||
"`get_config_jsonschema` is deprecated. Use `get_context_jsonschema` instead."
|
||||
"`get_config_jsonschema` is deprecated. Use `get_context_jsonschema` instead.",
|
||||
category=None,
|
||||
)
|
||||
def get_config_jsonschema(
|
||||
self, *, include: Sequence[str] | None = None
|
||||
@@ -2351,7 +2352,6 @@ class Pregel(
|
||||
interrupt_before: All | Sequence[str] | None,
|
||||
interrupt_after: All | Sequence[str] | None,
|
||||
durability: Durability | None = None,
|
||||
checkpoint_during: bool | None = None,
|
||||
) -> tuple[
|
||||
set[StreamMode],
|
||||
str | Sequence[str],
|
||||
@@ -2399,15 +2399,6 @@ class Pregel(
|
||||
cache: BaseCache | None = config[CONF][CONFIG_KEY_CACHE]
|
||||
else:
|
||||
cache = self.cache
|
||||
if checkpoint_during is not None:
|
||||
if durability is not None:
|
||||
raise ValueError(
|
||||
"Cannot use both `checkpoint_during` and `durability` parameters."
|
||||
)
|
||||
elif checkpoint_during:
|
||||
durability = "async"
|
||||
else:
|
||||
durability = "exit"
|
||||
if durability is None:
|
||||
durability = config.get(CONF, {}).get(CONFIG_KEY_DURABILITY, "async")
|
||||
return (
|
||||
@@ -2480,6 +2471,17 @@ class Pregel(
|
||||
Yields:
|
||||
The output of each step in the graph. The output shape depends on the stream_mode.
|
||||
"""
|
||||
if (checkpoint_during := kwargs.get("checkpoint_during")) is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.",
|
||||
category=LangGraphDeprecatedSinceV10,
|
||||
stacklevel=2,
|
||||
)
|
||||
if durability is not None:
|
||||
raise ValueError(
|
||||
"Cannot use both `checkpoint_during` and `durability` parameters. Please use `durability` instead."
|
||||
)
|
||||
durability = "async" if checkpoint_during else "exit"
|
||||
|
||||
if stream_mode is None:
|
||||
# if being called as a node in another graph, default to values mode
|
||||
@@ -2503,14 +2505,6 @@ class Pregel(
|
||||
run_id=config.get("run_id"),
|
||||
)
|
||||
try:
|
||||
deprecated_checkpoint_during = cast(
|
||||
Optional[bool], kwargs.get("checkpoint_during")
|
||||
)
|
||||
if deprecated_checkpoint_during is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.",
|
||||
category=LangGraphDeprecatedSinceV10,
|
||||
)
|
||||
# assign defaults
|
||||
(
|
||||
stream_modes,
|
||||
@@ -2529,11 +2523,8 @@ class Pregel(
|
||||
interrupt_before=interrupt_before,
|
||||
interrupt_after=interrupt_after,
|
||||
durability=durability,
|
||||
checkpoint_during=deprecated_checkpoint_during,
|
||||
)
|
||||
if checkpointer is None and (
|
||||
durability is not None or deprecated_checkpoint_during is not None
|
||||
):
|
||||
if checkpointer is None and durability is not None:
|
||||
warnings.warn(
|
||||
"`durability` has no effect when no checkpointer is present.",
|
||||
)
|
||||
@@ -2570,7 +2561,7 @@ class Pregel(
|
||||
pass
|
||||
|
||||
# set durability mode for subgraphs
|
||||
if durability is not None or deprecated_checkpoint_during is not None:
|
||||
if durability is not None:
|
||||
config[CONF][CONFIG_KEY_DURABILITY] = durability_
|
||||
|
||||
runtime = Runtime(
|
||||
@@ -2741,6 +2732,17 @@ class Pregel(
|
||||
Yields:
|
||||
The output of each step in the graph. The output shape depends on the stream_mode.
|
||||
"""
|
||||
if (checkpoint_during := kwargs.get("checkpoint_during")) is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.",
|
||||
category=LangGraphDeprecatedSinceV10,
|
||||
stacklevel=2,
|
||||
)
|
||||
if durability is not None:
|
||||
raise ValueError(
|
||||
"Cannot use both `checkpoint_during` and `durability` parameters. Please use `durability` instead."
|
||||
)
|
||||
durability = "async" if checkpoint_during else "exit"
|
||||
|
||||
if stream_mode is None:
|
||||
# if being called as a node in another graph, default to values mode
|
||||
@@ -2783,14 +2785,6 @@ class Pregel(
|
||||
else False
|
||||
)
|
||||
try:
|
||||
deprecated_checkpoint_during = cast(
|
||||
Optional[bool], kwargs.get("checkpoint_during")
|
||||
)
|
||||
if deprecated_checkpoint_during is not None:
|
||||
warnings.warn(
|
||||
"`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.",
|
||||
category=LangGraphDeprecatedSinceV10,
|
||||
)
|
||||
# assign defaults
|
||||
(
|
||||
stream_modes,
|
||||
@@ -2809,11 +2803,8 @@ class Pregel(
|
||||
interrupt_before=interrupt_before,
|
||||
interrupt_after=interrupt_after,
|
||||
durability=durability,
|
||||
checkpoint_during=deprecated_checkpoint_during,
|
||||
)
|
||||
if checkpointer is None and (
|
||||
durability is not None or deprecated_checkpoint_during is not None
|
||||
):
|
||||
if checkpointer is None and durability is not None:
|
||||
warnings.warn(
|
||||
"`durability` has no effect when no checkpointer is present.",
|
||||
)
|
||||
@@ -2865,7 +2856,7 @@ class Pregel(
|
||||
pass
|
||||
|
||||
# set durability mode for subgraphs
|
||||
if durability is not None or deprecated_checkpoint_during is not None:
|
||||
if durability is not None:
|
||||
config[CONF][CONFIG_KEY_DURABILITY] = durability_
|
||||
|
||||
runtime = Runtime(
|
||||
@@ -2990,6 +2981,7 @@ class Pregel(
|
||||
output_keys: str | Sequence[str] | None = None,
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
durability: Durability | None = None,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any] | Any:
|
||||
"""Run the graph with a single input and config.
|
||||
@@ -3004,6 +2996,10 @@ class Pregel(
|
||||
output_keys: Optional. The output keys to retrieve from the graph run.
|
||||
interrupt_before: Optional. The nodes to interrupt the graph run before.
|
||||
interrupt_after: Optional. The nodes to interrupt the graph run after.
|
||||
durability: The durability mode for the graph execution, defaults to "async". Options are:
|
||||
- `"sync"`: Changes are persisted synchronously before the next step starts.
|
||||
- `"async"`: Changes are persisted asynchronously while the next step executes.
|
||||
- `"exit"`: Changes are persisted only when the graph exits.
|
||||
**kwargs: Additional keyword arguments to pass to the graph run.
|
||||
|
||||
Returns:
|
||||
@@ -3027,6 +3023,7 @@ class Pregel(
|
||||
output_keys=output_keys,
|
||||
interrupt_before=interrupt_before,
|
||||
interrupt_after=interrupt_after,
|
||||
durability=durability,
|
||||
**kwargs,
|
||||
):
|
||||
if stream_mode == "values":
|
||||
@@ -3069,6 +3066,7 @@ class Pregel(
|
||||
output_keys: str | Sequence[str] | None = None,
|
||||
interrupt_before: All | Sequence[str] | None = None,
|
||||
interrupt_after: All | Sequence[str] | None = None,
|
||||
durability: Durability | None = None,
|
||||
**kwargs: Any,
|
||||
) -> dict[str, Any] | Any:
|
||||
"""Asynchronously invoke the graph on a single input.
|
||||
@@ -3083,6 +3081,10 @@ class Pregel(
|
||||
output_keys: Optional. The output keys to include in the result. Default is None.
|
||||
interrupt_before: Optional. The nodes to interrupt before. Default is None.
|
||||
interrupt_after: Optional. The nodes to interrupt after. Default is None.
|
||||
durability: The durability mode for the graph execution, defaults to "async". Options are:
|
||||
- `"sync"`: Changes are persisted synchronously before the next step starts.
|
||||
- `"async"`: Changes are persisted asynchronously while the next step executes.
|
||||
- `"exit"`: Changes are persisted only when the graph exits.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
Returns:
|
||||
@@ -3107,6 +3109,7 @@ class Pregel(
|
||||
output_keys=output_keys,
|
||||
interrupt_before=interrupt_before,
|
||||
interrupt_after=interrupt_after,
|
||||
durability=durability,
|
||||
**kwargs,
|
||||
):
|
||||
if stream_mode == "values":
|
||||
|
||||
@@ -191,10 +191,7 @@ class Interrupt:
|
||||
return cls(value=value, id=xxh3_128_hexdigest(ns.encode()))
|
||||
|
||||
@property
|
||||
@deprecated(
|
||||
"`interrupt_id` is deprecated. Use `id` instead.",
|
||||
stacklevel=2,
|
||||
)
|
||||
@deprecated("`interrupt_id` is deprecated. Use `id` instead.", category=None)
|
||||
def interrupt_id(self) -> str:
|
||||
warn(
|
||||
"`interrupt_id` is deprecated. Use `id` instead.",
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
description = "Building stateful, multi-actor applications with LLMs"
|
||||
authors = []
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -175,10 +175,10 @@
|
||||
'''
|
||||
# ---
|
||||
# name: test_prebuilt_tool_chat
|
||||
'{"$defs": {"BaseMessage": {"additionalProperties": true, "description": "Base abstract message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "type"], "title": "BaseMessage", "type": "object"}}, "description": "The state of the agent.", "properties": {"messages": {"items": {"$ref": "#/$defs/BaseMessage"}, "title": "Messages", "type": "array"}, "is_last_step": {"title": "Is Last Step", "type": "boolean"}, "remaining_steps": {"title": "Remaining Steps", "type": "integer"}}, "required": ["messages", "is_last_step", "remaining_steps"], "title": "AgentState", "type": "object"}'
|
||||
'{"$defs": {"BaseMessage": {"additionalProperties": true, "description": "Base abstract message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "type"], "title": "BaseMessage", "type": "object"}}, "description": "The state of the agent.", "properties": {"messages": {"items": {"$ref": "#/$defs/BaseMessage"}, "title": "Messages", "type": "array"}, "remaining_steps": {"title": "Remaining Steps", "type": "integer"}}, "required": ["messages"], "title": "AgentState", "type": "object"}'
|
||||
# ---
|
||||
# name: test_prebuilt_tool_chat.1
|
||||
'{"$defs": {"BaseMessage": {"additionalProperties": true, "description": "Base abstract message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "type"], "title": "BaseMessage", "type": "object"}}, "description": "The state of the agent.", "properties": {"messages": {"items": {"$ref": "#/$defs/BaseMessage"}, "title": "Messages", "type": "array"}, "is_last_step": {"title": "Is Last Step", "type": "boolean"}, "remaining_steps": {"title": "Remaining Steps", "type": "integer"}}, "required": ["messages", "is_last_step", "remaining_steps"], "title": "AgentState", "type": "object"}'
|
||||
'{"$defs": {"BaseMessage": {"additionalProperties": true, "description": "Base abstract message class.\\n\\nMessages are the inputs and outputs of ChatModels.", "properties": {"content": {"anyOf": [{"type": "string"}, {"items": {"anyOf": [{"type": "string"}, {"additionalProperties": true, "type": "object"}]}, "type": "array"}], "title": "Content"}, "additional_kwargs": {"additionalProperties": true, "title": "Additional Kwargs", "type": "object"}, "response_metadata": {"additionalProperties": true, "title": "Response Metadata", "type": "object"}, "type": {"title": "Type", "type": "string"}, "name": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Name"}, "id": {"anyOf": [{"type": "string"}, {"type": "null"}], "default": null, "title": "Id"}}, "required": ["content", "type"], "title": "BaseMessage", "type": "object"}}, "description": "The state of the agent.", "properties": {"messages": {"items": {"$ref": "#/$defs/BaseMessage"}, "title": "Messages", "type": "array"}, "remaining_steps": {"title": "Remaining Steps", "type": "integer"}}, "required": ["messages"], "title": "AgentState", "type": "object"}'
|
||||
# ---
|
||||
# name: test_prebuilt_tool_chat.2
|
||||
'''
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
from typing import Any, Optional
|
||||
|
||||
import pytest
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from pytest_mock import MockerFixture
|
||||
from typing_extensions import TypedDict
|
||||
from typing_extensions import NotRequired, TypedDict
|
||||
|
||||
from langgraph.channels.last_value import LastValue
|
||||
from langgraph.errors import NodeInterrupt
|
||||
@@ -94,8 +100,6 @@ def test_pregel_types_deprecation() -> None:
|
||||
from langgraph.pregel.types import StateSnapshot # noqa: F401
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:`config_schema` is deprecated")
|
||||
@pytest.mark.filterwarnings("ignore:`get_config_jsonschema` is deprecated")
|
||||
def test_config_schema_deprecation() -> None:
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
@@ -121,7 +125,6 @@ def test_config_schema_deprecation() -> None:
|
||||
graph.get_config_jsonschema()
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:`config_schema` is deprecated")
|
||||
def test_config_schema_deprecation_on_entrypoint() -> None:
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
@@ -132,10 +135,15 @@ def test_config_schema_deprecation_on_entrypoint() -> None:
|
||||
def my_entrypoint(state: PlainState) -> PlainState:
|
||||
return state
|
||||
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`config_schema` is deprecated. Use `get_context_jsonschema` for the relevant schema instead.",
|
||||
):
|
||||
assert my_entrypoint.context_schema == PlainState
|
||||
assert my_entrypoint.config_schema() is not None
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:`config_type` is deprecated")
|
||||
def test_config_type_deprecation_pregel(mocker: MockerFixture) -> None:
|
||||
add_one = mocker.Mock(side_effect=lambda x: x + 1)
|
||||
chain = NodeBuilder().subscribe_only("input").do(add_one).write_to("output")
|
||||
@@ -159,7 +167,6 @@ def test_config_type_deprecation_pregel(mocker: MockerFixture) -> None:
|
||||
assert instance.context_schema == PlainState
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:`interrupt_id` is deprecated. Use `id` instead.")
|
||||
def test_interrupt_attributes_deprecation() -> None:
|
||||
interrupt = Interrupt(value="question", id="abc")
|
||||
|
||||
@@ -170,7 +177,6 @@ def test_interrupt_attributes_deprecation() -> None:
|
||||
interrupt.interrupt_id
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:NodeInterrupt is deprecated.")
|
||||
def test_node_interrupt_deprecation() -> None:
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
@@ -185,3 +191,144 @@ def test_deprecated_import() -> None:
|
||||
match="Importing PREVIOUS from langgraph.constants is deprecated. This constant is now private and should not be used directly.",
|
||||
):
|
||||
from langgraph.constants import PREVIOUS # noqa: F401
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings(
|
||||
"ignore:`durability` has no effect when no checkpointer is present"
|
||||
)
|
||||
def test_checkpoint_during_deprecation_state_graph() -> None:
|
||||
class CheckDurability(TypedDict):
|
||||
durability: NotRequired[str]
|
||||
|
||||
def plain_node(state: CheckDurability, config: RunnableConfig) -> CheckDurability:
|
||||
return {"durability": config["configurable"]["__pregel_durability"]}
|
||||
|
||||
builder = StateGraph(CheckDurability)
|
||||
builder.add_node("plain_node", plain_node)
|
||||
builder.set_entry_point("plain_node")
|
||||
graph = builder.compile()
|
||||
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.",
|
||||
):
|
||||
result = graph.invoke({}, checkpoint_during=True)
|
||||
assert result["durability"] == "async"
|
||||
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.",
|
||||
):
|
||||
result = graph.invoke({}, checkpoint_during=False)
|
||||
assert result["durability"] == "exit"
|
||||
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.",
|
||||
):
|
||||
for chunk in graph.stream({}, checkpoint_during=True): # type: ignore[arg-type]
|
||||
assert chunk["plain_node"]["durability"] == "async"
|
||||
|
||||
with pytest.warns(
|
||||
LangGraphDeprecatedSinceV10,
|
||||
match="`checkpoint_during` is deprecated and will be removed. Please use `durability` instead.",
|
||||
):
|
||||
for chunk in graph.stream({}, checkpoint_during=False): # type: ignore[arg-type]
|
||||
assert chunk["plain_node"]["durability"] == "exit"
|
||||
|
||||
|
||||
def test_config_parameter_incorrect_typing() -> None:
|
||||
"""Test that a warning is raised when config parameter is typed incorrectly."""
|
||||
builder = StateGraph(PlainState)
|
||||
|
||||
# Test sync function with config: dict
|
||||
with pytest.warns(
|
||||
UserWarning,
|
||||
match="The 'config' parameter should be typed as 'RunnableConfig' or 'RunnableConfig | None', not '.*dict.*'. ",
|
||||
):
|
||||
|
||||
def sync_node_with_dict_config(state: PlainState, config: dict) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(sync_node_with_dict_config)
|
||||
|
||||
# Test async function with config: dict
|
||||
with pytest.warns(
|
||||
UserWarning,
|
||||
match="The 'config' parameter should be typed as 'RunnableConfig' or 'RunnableConfig | None', not '.*dict.*'. ",
|
||||
):
|
||||
|
||||
async def async_node_with_dict_config(
|
||||
state: PlainState, config: dict
|
||||
) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(async_node_with_dict_config)
|
||||
|
||||
# Test with other incorrect types
|
||||
with pytest.warns(
|
||||
UserWarning,
|
||||
match="The 'config' parameter should be typed as 'RunnableConfig' or 'RunnableConfig | None', not '.*Any.*'. ",
|
||||
):
|
||||
|
||||
def sync_node_with_any_config(state: PlainState, config: Any) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(sync_node_with_any_config)
|
||||
|
||||
with pytest.warns(
|
||||
UserWarning,
|
||||
match="The 'config' parameter should be typed as 'RunnableConfig' or 'RunnableConfig | None', not '.*Any.*'. ",
|
||||
):
|
||||
|
||||
async def async_node_with_any_config(
|
||||
state: PlainState, config: Any
|
||||
) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(async_node_with_any_config)
|
||||
|
||||
with warnings.catch_warnings(record=True) as w:
|
||||
|
||||
def node_with_correct_config(
|
||||
state: PlainState, config: RunnableConfig
|
||||
) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(node_with_correct_config)
|
||||
|
||||
def node_with_optional_config(
|
||||
state: PlainState,
|
||||
config: Optional[RunnableConfig], # noqa: UP045
|
||||
) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(node_with_optional_config)
|
||||
|
||||
def node_with_untyped_config(state: PlainState, config) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(node_with_untyped_config)
|
||||
|
||||
async def async_node_with_correct_config(
|
||||
state: PlainState, config: RunnableConfig
|
||||
) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(async_node_with_correct_config)
|
||||
|
||||
async def async_node_with_optional_config(
|
||||
state: PlainState,
|
||||
config: Optional[RunnableConfig], # noqa: UP045
|
||||
) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(async_node_with_optional_config)
|
||||
|
||||
async def async_node_with_untyped_config(
|
||||
state: PlainState, config
|
||||
) -> PlainState:
|
||||
return state
|
||||
|
||||
builder.add_node(async_node_with_untyped_config)
|
||||
assert len(w) == 0
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
from typing_extensions import NotRequired, Required, TypedDict
|
||||
|
||||
from langgraph.graph import StateGraph
|
||||
from langgraph.managed import RemainingSteps
|
||||
|
||||
|
||||
class StatePlain(TypedDict):
|
||||
remaining_steps: RemainingSteps
|
||||
|
||||
|
||||
class StateNotRequired(TypedDict):
|
||||
remaining_steps: NotRequired[RemainingSteps]
|
||||
|
||||
|
||||
class StateRequired(TypedDict):
|
||||
remaining_steps: Required[RemainingSteps]
|
||||
|
||||
|
||||
def test_managed_values_recognized() -> None:
|
||||
graph = StateGraph(StatePlain)
|
||||
assert "remaining_steps" in graph.managed
|
||||
|
||||
graph = StateGraph(StateNotRequired)
|
||||
assert "remaining_steps" in graph.managed
|
||||
|
||||
graph = StateGraph(StateRequired)
|
||||
assert "remaining_steps" in graph.managed
|
||||
Generated
+2
-2
@@ -1192,7 +1192,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -1433,7 +1433,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = { editable = "../prebuilt" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
@@ -34,7 +34,7 @@ from langchain_core.runnables import (
|
||||
)
|
||||
from langchain_core.tools import BaseTool
|
||||
from pydantic import BaseModel
|
||||
from typing_extensions import Annotated, TypedDict
|
||||
from typing_extensions import Annotated, NotRequired, TypedDict
|
||||
|
||||
from langgraph._internal._runnable import RunnableCallable, RunnableLike
|
||||
from langgraph._internal._typing import MISSING
|
||||
@@ -42,7 +42,7 @@ from langgraph.errors import ErrorCode, create_error_message
|
||||
from langgraph.graph import END, StateGraph
|
||||
from langgraph.graph.message import add_messages
|
||||
from langgraph.graph.state import CompiledStateGraph
|
||||
from langgraph.managed import IsLastStep, RemainingSteps
|
||||
from langgraph.managed import RemainingSteps
|
||||
from langgraph.prebuilt._internal import ToolCallWithContext
|
||||
from langgraph.prebuilt.tool_node import ToolNode
|
||||
from langgraph.runtime import Runtime
|
||||
@@ -65,9 +65,7 @@ class AgentState(TypedDict):
|
||||
|
||||
messages: Annotated[Sequence[BaseMessage], add_messages]
|
||||
|
||||
is_last_step: IsLastStep
|
||||
|
||||
remaining_steps: RemainingSteps
|
||||
remaining_steps: NotRequired[RemainingSteps]
|
||||
|
||||
|
||||
class AgentStatePydantic(BaseModel):
|
||||
@@ -571,16 +569,13 @@ def create_react_agent(
|
||||
else False
|
||||
)
|
||||
remaining_steps = _get_state_value(state, "remaining_steps", None)
|
||||
is_last_step = _get_state_value(state, "is_last_step", False)
|
||||
return (
|
||||
(remaining_steps is None and is_last_step and has_tool_calls)
|
||||
or (
|
||||
remaining_steps is not None
|
||||
and remaining_steps < 1
|
||||
and all_tools_return_direct
|
||||
)
|
||||
or (remaining_steps is not None and remaining_steps < 2 and has_tool_calls)
|
||||
)
|
||||
if remaining_steps is not None:
|
||||
if remaining_steps < 1 and all_tools_return_direct:
|
||||
return True
|
||||
elif remaining_steps < 2 and has_tool_calls:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _get_model_input_state(state: StateSchema) -> StateSchema:
|
||||
if pre_model_hook is not None:
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
description = "Library with high-level APIs for creating and executing LangGraph agents and tools."
|
||||
authors = []
|
||||
requires-python = ">=3.9"
|
||||
|
||||
Generated
+2
-2
@@ -316,7 +316,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = { editable = "../langgraph" }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
@@ -460,7 +460,7 @@ dev = [
|
||||
|
||||
[[package]]
|
||||
name = "langgraph-prebuilt"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "langchain-core" },
|
||||
|
||||
Reference in New Issue
Block a user