mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-08-17 21:25:46 +02:00
langgraph: changes for compatibility pydantic v2 / langchain-core==0.3 (#1594)
This commit is contained in:
@@ -21,7 +21,11 @@ jobs:
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
name: "test #${{ matrix.python-version }}"
|
||||
core-version:
|
||||
- ">=0.3.0.dev1,<0.4.0"
|
||||
- "latest"
|
||||
|
||||
name: "test #${{ matrix.python-version }} (langchain-core: ${{ matrix.core-version }})"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python ${{ matrix.python-version }} + Poetry ${{ env.POETRY_VERSION }}
|
||||
@@ -35,7 +39,11 @@ jobs:
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: poetry install --with dev
|
||||
run: |
|
||||
poetry install --with dev
|
||||
if [ "${{ matrix.core-version }}" != "latest" ]; then
|
||||
poetry run pip install "langchain-core${{ matrix.core-version }}"
|
||||
fi
|
||||
|
||||
- name: Run core tests
|
||||
shell: bash
|
||||
|
||||
@@ -6,10 +6,10 @@ import numexpr
|
||||
from langchain.chains.openai_functions import create_structured_output_runnable
|
||||
from langchain_core.messages import SystemMessage
|
||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field
|
||||
from langchain_core.runnables import RunnableConfig
|
||||
from langchain_core.tools import StructuredTool
|
||||
from langchain_openai import ChatOpenAI
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
_MATH_DESCRIPTION = (
|
||||
"math(problem: str, context: Optional[list[str]]) -> float:\n"
|
||||
|
||||
Generated
+6
-5
@@ -1,4 +1,4 @@
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
@@ -227,13 +227,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "langchain-core"
|
||||
version = "0.2.24"
|
||||
version = "0.2.38"
|
||||
description = "Building applications with LLMs through composability"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8.1"
|
||||
files = [
|
||||
{file = "langchain_core-0.2.24-py3-none-any.whl", hash = "sha256:9444fc082d21ef075d925590a684a73fe1f9688a3d90087580ec929751be55e7"},
|
||||
{file = "langchain_core-0.2.24.tar.gz", hash = "sha256:f2e3fa200b124e8c45d270da9bf836bed9c09532612c96ff3225e59b9a232f5a"},
|
||||
{file = "langchain_core-0.2.38-py3-none-any.whl", hash = "sha256:8a5729bc7e68b4af089af20eff44fe4e7ca21d0e0c87ec21cef7621981fd1a4a"},
|
||||
{file = "langchain_core-0.2.38.tar.gz", hash = "sha256:eb69dbedd344f2ee1f15bcea6c71a05884b867588fadc42d04632e727c1238f3"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -246,6 +246,7 @@ pydantic = [
|
||||
]
|
||||
PyYAML = ">=5.3"
|
||||
tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
|
||||
typing-extensions = ">=4.7"
|
||||
|
||||
[[package]]
|
||||
name = "langsmith"
|
||||
@@ -850,4 +851,4 @@ watchmedo = ["PyYAML (>=3.10)"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9.0,<4.0"
|
||||
content-hash = "de79db3dc7701542739b3417e9d3f02c3b41167719603ee4d08b92e23b7443ee"
|
||||
content-hash = "d4c13800471766fa9e2d11d2f1092f02fbf28cc507189aeea8a3d5b297286068"
|
||||
|
||||
@@ -10,7 +10,7 @@ packages = [{ include = "langgraph" }]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9.0,<4.0"
|
||||
langchain-core = ">=0.2.22,<0.3"
|
||||
langchain-core = ">=0.2.38,<0.4"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
ruff = "^0.6.2"
|
||||
|
||||
@@ -10,9 +10,9 @@ from enum import Enum
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
import dataclasses_json
|
||||
from langchain_core.pydantic_v1 import BaseModel as LcBaseModel
|
||||
from langchain_core.runnables import RunnableMap
|
||||
from pydantic import BaseModel
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
|
||||
@@ -23,7 +23,7 @@ class MyPydantic(BaseModel):
|
||||
bar: int
|
||||
|
||||
|
||||
class MyFunnyPydantic(LcBaseModel):
|
||||
class MyFunnyPydantic(BaseModelV1):
|
||||
foo: str
|
||||
bar: int
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ from langchain_core.messages import (
|
||||
)
|
||||
from langchain_core.output_parsers import StrOutputParser
|
||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field
|
||||
from langchain_core.runnables import RunnableConfig, RunnableLambda
|
||||
from langchain_core.runnables import chain as as_runnable
|
||||
from langchain_core.tools import tool
|
||||
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
|
||||
from langgraph.graph import END, StateGraph
|
||||
from pydantic import BaseModel, Field
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
fast_llm = ChatOpenAI(model="gpt-3.5-turbo")
|
||||
|
||||
@@ -21,6 +21,7 @@ from langchain_core.runnables import Runnable, RunnableConfig
|
||||
from langchain_core.runnables.base import RunnableLike
|
||||
from langchain_core.runnables.utils import create_model
|
||||
from pydantic import BaseModel
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
|
||||
from langgraph.channels.base import BaseChannel
|
||||
from langgraph.channels.binop import BinaryOperatorAggregate
|
||||
@@ -473,10 +474,8 @@ class CompiledStateGraph(CompiledGraph):
|
||||
def get_input_schema(
|
||||
self, config: Optional[RunnableConfig] = None
|
||||
) -> type[BaseModel]:
|
||||
from pydantic import BaseModel as BaseModelP
|
||||
|
||||
if isclass(self.builder.input) and issubclass(
|
||||
self.builder.input, (BaseModel, BaseModelP)
|
||||
self.builder.input, (BaseModel, BaseModelV1)
|
||||
):
|
||||
return self.builder.input
|
||||
else:
|
||||
@@ -508,10 +507,8 @@ class CompiledStateGraph(CompiledGraph):
|
||||
def get_output_schema(
|
||||
self, config: Optional[RunnableConfig] = None
|
||||
) -> type[BaseModel]:
|
||||
from pydantic import BaseModel as BaseModelP
|
||||
|
||||
if isclass(self.builder.input) and issubclass(
|
||||
self.builder.output, (BaseModel, BaseModelP)
|
||||
if isclass(self.builder.output) and issubclass(
|
||||
self.builder.output, (BaseModel, BaseModelV1)
|
||||
):
|
||||
return self.builder.output
|
||||
|
||||
|
||||
@@ -24,20 +24,22 @@ from langchain_core.messages import (
|
||||
ToolCall,
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_core.pydantic_v1 import BaseModel, ValidationError
|
||||
from langchain_core.runnables import (
|
||||
RunnableConfig,
|
||||
)
|
||||
from langchain_core.runnables.config import get_executor_for_config
|
||||
from langchain_core.tools import BaseTool, create_schema_from_function
|
||||
from pydantic import BaseModel as BaseModelV2
|
||||
from pydantic import ValidationError as ValidationErrorV2
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
from pydantic.v1 import ValidationError as ValidationErrorV1
|
||||
|
||||
from langgraph.utils.runnable import RunnableCallable
|
||||
|
||||
|
||||
def _default_format_error(
|
||||
error: BaseException, call: ToolCall, schema: Type[BaseModel]
|
||||
error: BaseException,
|
||||
call: ToolCall,
|
||||
schema: Union[Type[BaseModel], Type[BaseModelV1]],
|
||||
) -> str:
|
||||
"""Default error formatting function."""
|
||||
return f"{repr(error)}\n\nRespond after fixing all validation errors."
|
||||
@@ -75,7 +77,7 @@ class ValidationNode(RunnableCallable):
|
||||
>>> from typing import Literal, Annotated, TypedDict
|
||||
...
|
||||
>>> from langchain_anthropic import ChatAnthropic
|
||||
>>> from langchain_core.pydantic_v1 import BaseModel, validator
|
||||
>>> from pydantic import BaseModel, validator
|
||||
...
|
||||
>>> from langgraph.graph import END, START, StateGraph
|
||||
>>> from langgraph.prebuilt import ValidationNode
|
||||
@@ -176,7 +178,7 @@ class ValidationNode(RunnableCallable):
|
||||
)
|
||||
self.schemas_by_name[schema.name] = schema.args_schema
|
||||
elif isinstance(schema, type) and issubclass(
|
||||
schema, (BaseModel, BaseModelV2)
|
||||
schema, (BaseModel, BaseModelV1)
|
||||
):
|
||||
self.schemas_by_name[schema.__name__] = cast(Type[BaseModel], schema)
|
||||
elif callable(schema):
|
||||
@@ -212,13 +214,22 @@ class ValidationNode(RunnableCallable):
|
||||
def run_one(call: ToolCall):
|
||||
schema = self.schemas_by_name[call["name"]]
|
||||
try:
|
||||
output = schema.validate(call["args"])
|
||||
if issubclass(schema, BaseModel):
|
||||
output = schema.model_validate(call["args"])
|
||||
content = output.model_dump_json()
|
||||
elif issubclass(schema, BaseModelV1):
|
||||
output = schema.validate(call["args"])
|
||||
content = output.json()
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unsupported schema type: {type(schema)}. Expected BaseModel or BaseModelV1."
|
||||
)
|
||||
return ToolMessage(
|
||||
content=output.json(),
|
||||
content=content,
|
||||
name=call["name"],
|
||||
tool_call_id=cast(str, call["id"]),
|
||||
)
|
||||
except (ValidationError, ValidationErrorV2) as e:
|
||||
except (ValidationError, ValidationErrorV1) as e:
|
||||
return ToolMessage(
|
||||
content=self._format_error(e, call, schema),
|
||||
name=call["name"],
|
||||
|
||||
@@ -59,19 +59,19 @@ class RunnableCallable(Runnable):
|
||||
recurse: bool = True,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
if name is not None:
|
||||
self.name = name
|
||||
elif func:
|
||||
try:
|
||||
if func.__name__ != "<lambda>":
|
||||
self.name = func.__name__
|
||||
except AttributeError:
|
||||
pass
|
||||
elif afunc:
|
||||
try:
|
||||
self.name = afunc.__name__
|
||||
except AttributeError:
|
||||
pass
|
||||
self.name = name
|
||||
if self.name is None:
|
||||
if func:
|
||||
try:
|
||||
if func.__name__ != "<lambda>":
|
||||
self.name = func.__name__
|
||||
except AttributeError:
|
||||
pass
|
||||
elif afunc:
|
||||
try:
|
||||
self.name = afunc.__name__
|
||||
except AttributeError:
|
||||
pass
|
||||
self.func = func
|
||||
if func is not None:
|
||||
self.func_accepts_config = accepts_config(func)
|
||||
|
||||
Generated
+11
-1236
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ repository = "https://www.github.com/langchain-ai/langgraph"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9.0,<4.0"
|
||||
langchain-core = ">=0.2.27,<0.3"
|
||||
langchain-core = ">=0.2.38,<0.4"
|
||||
langgraph-checkpoint = "^1.0.2"
|
||||
|
||||
|
||||
@@ -21,14 +21,10 @@ pytest-mock = "^3.10.0"
|
||||
syrupy = "^4.0.2"
|
||||
httpx = "^0.26.0"
|
||||
pytest-watcher = "^0.4.1"
|
||||
langchain = ">=0.1.0"
|
||||
grandalf = "^0.8"
|
||||
mypy = "^1.6.0"
|
||||
ruff = "^0.6.2"
|
||||
jupyter = "^1.0.0"
|
||||
langchainhub = "^0.1.14"
|
||||
langchain-openai = ">=0.1.2"
|
||||
langchain-anthropic = ">=0.1.8"
|
||||
pytest-xdist = {extras = ["psutil"], version = "^3.6.1"}
|
||||
pytest-repeat = "^0.9.3"
|
||||
langgraph-checkpoint = {path = "../checkpoint", develop = true}
|
||||
@@ -36,9 +32,6 @@ langgraph-checkpoint-sqlite = {path = "../checkpoint-sqlite", develop = true}
|
||||
langgraph-checkpoint-postgres = {path = "../checkpoint-postgres", develop = true}
|
||||
psycopg = {extras = ["binary"], version = ">=3.0.0"}
|
||||
|
||||
[tool.poetry.group.dev]
|
||||
optional = true
|
||||
|
||||
[tool.ruff]
|
||||
lint.select = [ "E", "F", "I" ]
|
||||
lint.ignore = [ "E501" ]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1204,15 +1204,15 @@
|
||||
weather_graph_model_node(model_node)
|
||||
weather_graph_weather_node(weather_node<hr/><small><em>__interrupt = before</em></small>)
|
||||
__end__([__end__]):::last
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
__start__ --> router_node;
|
||||
normal_llm_node --> __end__;
|
||||
weather_graph_weather_node --> __end__;
|
||||
router_node -.-> normal_llm_node;
|
||||
router_node -.-> weather_graph_model_node;
|
||||
router_node -.-> __end__;
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
@@ -1229,15 +1229,15 @@
|
||||
weather_graph_model_node(model_node)
|
||||
weather_graph_weather_node(weather_node<hr/><small><em>__interrupt = before</em></small>)
|
||||
__end__([__end__]):::last
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
__start__ --> router_node;
|
||||
normal_llm_node --> __end__;
|
||||
weather_graph_weather_node --> __end__;
|
||||
router_node -.-> normal_llm_node;
|
||||
router_node -.-> weather_graph_model_node;
|
||||
router_node -.-> __end__;
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
@@ -1254,15 +1254,15 @@
|
||||
weather_graph_model_node(model_node)
|
||||
weather_graph_weather_node(weather_node<hr/><small><em>__interrupt = before</em></small>)
|
||||
__end__([__end__]):::last
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
__start__ --> router_node;
|
||||
normal_llm_node --> __end__;
|
||||
weather_graph_weather_node --> __end__;
|
||||
router_node -.-> normal_llm_node;
|
||||
router_node -.-> weather_graph_model_node;
|
||||
router_node -.-> __end__;
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
@@ -1279,15 +1279,15 @@
|
||||
weather_graph_model_node(model_node)
|
||||
weather_graph_weather_node(weather_node<hr/><small><em>__interrupt = before</em></small>)
|
||||
__end__([__end__]):::last
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
__start__ --> router_node;
|
||||
normal_llm_node --> __end__;
|
||||
weather_graph_weather_node --> __end__;
|
||||
router_node -.-> normal_llm_node;
|
||||
router_node -.-> weather_graph_model_node;
|
||||
router_node -.-> __end__;
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
@@ -1304,15 +1304,15 @@
|
||||
weather_graph_model_node(model_node)
|
||||
weather_graph_weather_node(weather_node<hr/><small><em>__interrupt = before</em></small>)
|
||||
__end__([__end__]):::last
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
__start__ --> router_node;
|
||||
normal_llm_node --> __end__;
|
||||
weather_graph_weather_node --> __end__;
|
||||
router_node -.-> normal_llm_node;
|
||||
router_node -.-> weather_graph_model_node;
|
||||
router_node -.-> __end__;
|
||||
subgraph weather_graph
|
||||
weather_graph_model_node --> weather_graph_weather_node;
|
||||
end
|
||||
classDef default fill:#f2f0ff,line-height:1.2
|
||||
classDef first fill-opacity:0
|
||||
classDef last fill:#bfb6fc
|
||||
|
||||
@@ -4,6 +4,8 @@ from typing import AsyncIterator, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
import pytest
|
||||
from langchain_core import __version__ as core_version
|
||||
from packaging import version
|
||||
from psycopg import AsyncConnection, Connection
|
||||
from psycopg_pool import AsyncConnectionPool, ConnectionPool
|
||||
from pytest_mock import MockerFixture
|
||||
@@ -16,6 +18,8 @@ from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
|
||||
from tests.memory_assert import MemorySaverAssertImmutable
|
||||
|
||||
DEFAULT_POSTGRES_URI = "postgres://postgres:postgres@localhost:5442/"
|
||||
# TODO: fix this once core is released
|
||||
SHOULD_CHECK_SNAPSHOTS = version.parse(core_version) >= version.parse("0.3.0.dev0")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@@ -10,7 +10,7 @@ subclassed strings.
|
||||
from typing import Any
|
||||
|
||||
from langchain_core.documents import Document
|
||||
from langchain_core.messages import AIMessage, AIMessageChunk, HumanMessage
|
||||
from langchain_core.messages import AIMessage, AIMessageChunk, HumanMessage, ToolMessage
|
||||
|
||||
from tests.any_str import AnyStr
|
||||
|
||||
@@ -37,7 +37,14 @@ def _AnyIdAIMessageChunk(**kwargs: Any) -> AIMessageChunk:
|
||||
|
||||
|
||||
def _AnyIdHumanMessage(**kwargs: Any) -> HumanMessage:
|
||||
"""Create a human with an any id field."""
|
||||
"""Create a human message with an any id field."""
|
||||
message = HumanMessage(**kwargs)
|
||||
message.id = AnyStr()
|
||||
return message
|
||||
|
||||
|
||||
def _AnyIdToolMessage(**kwargs: Any) -> ToolMessage:
|
||||
"""Create a tool message with an any id field."""
|
||||
message = ToolMessage(**kwargs)
|
||||
message.id = AnyStr()
|
||||
return message
|
||||
|
||||
@@ -14,11 +14,11 @@ from langchain_core.messages import (
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_core.outputs import ChatGeneration, ChatResult
|
||||
from langchain_core.pydantic_v1 import BaseModel
|
||||
from langchain_core.runnables import Runnable, RunnableLambda
|
||||
from langchain_core.tools import BaseTool
|
||||
from langchain_core.tools import tool as dec_tool
|
||||
from pydantic import BaseModel as BaseModelV2
|
||||
from pydantic import BaseModel
|
||||
from pydantic.v1 import BaseModel as BaseModelV1
|
||||
|
||||
from langgraph.checkpoint.base import BaseCheckpointSaver
|
||||
from langgraph.prebuilt import ToolNode, ValidationNode, create_react_agent
|
||||
@@ -379,7 +379,7 @@ class MyModel(BaseModel):
|
||||
some_other_val: str
|
||||
|
||||
|
||||
class MyModelV2(BaseModelV2):
|
||||
class MyModelV1(BaseModelV1):
|
||||
some_val: int
|
||||
some_other_val: str
|
||||
|
||||
@@ -395,7 +395,7 @@ def my_tool(some_val: int, some_other_val: str) -> str:
|
||||
[
|
||||
my_function,
|
||||
MyModel,
|
||||
MyModelV2,
|
||||
MyModelV1,
|
||||
my_tool,
|
||||
],
|
||||
)
|
||||
|
||||
+217
-190
@@ -34,6 +34,7 @@ from langchain_core.runnables import (
|
||||
RunnablePick,
|
||||
)
|
||||
from langsmith import traceable
|
||||
from pydantic import BaseModel
|
||||
from pytest_mock import MockerFixture
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
@@ -72,10 +73,50 @@ from langgraph.pregel.retry import RetryPolicy
|
||||
from langgraph.pregel.types import PregelTask
|
||||
from langgraph.store.memory import MemoryStore
|
||||
from tests.any_str import AnyDict, AnyStr, AnyVersion, UnsortedSequence
|
||||
from tests.conftest import ALL_CHECKPOINTERS_SYNC
|
||||
from tests.conftest import ALL_CHECKPOINTERS_SYNC, SHOULD_CHECK_SNAPSHOTS
|
||||
from tests.fake_tracer import FakeTracer
|
||||
from tests.memory_assert import MemorySaverAssertCheckpointMetadata
|
||||
from tests.messages import _AnyIdAIMessage, _AnyIdHumanMessage
|
||||
from tests.messages import _AnyIdAIMessage, _AnyIdHumanMessage, _AnyIdToolMessage
|
||||
|
||||
|
||||
# define these objects to avoid importing langchain_core.agents
|
||||
# and therefore avoid relying on core Pydantic version
|
||||
class AgentAction(BaseModel):
|
||||
tool: str
|
||||
tool_input: Union[str, dict]
|
||||
log: str
|
||||
type: Literal["AgentAction"] = "AgentAction"
|
||||
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"description": (
|
||||
"""Represents a request to execute an action by an agent.
|
||||
|
||||
The action consists of the name of the tool to execute and the input to pass
|
||||
to the tool. The log is used to pass along extra information about the action."""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AgentFinish(BaseModel):
|
||||
"""Final return value of an ActionAgent.
|
||||
|
||||
Agents return an AgentFinish when they have reached a stopping condition.
|
||||
"""
|
||||
|
||||
return_values: dict
|
||||
log: str
|
||||
type: Literal["AgentFinish"] = "AgentFinish"
|
||||
model_config = {
|
||||
"json_schema_extra": {
|
||||
"description": (
|
||||
"""Final return value of an ActionAgent.
|
||||
|
||||
Agents return an AgentFinish when they have reached a stopping condition."""
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def test_graph_validation() -> None:
|
||||
@@ -440,15 +481,23 @@ def test_invoke_single_process_in_out(mocker: MockerFixture) -> None:
|
||||
graph.set_finish_point("add_one")
|
||||
gapp = graph.compile()
|
||||
|
||||
assert app.input_schema.schema() == {"title": "LangGraphInput", "type": "integer"}
|
||||
assert app.output_schema.schema() == {"title": "LangGraphOutput", "type": "integer"}
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error") # raise warnings as errors
|
||||
assert app.config_schema().schema() == {
|
||||
"properties": {},
|
||||
"title": "LangGraphConfig",
|
||||
"type": "object",
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.input_schema.model_json_schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "integer",
|
||||
}
|
||||
assert app.output_schema.model_json_schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "integer",
|
||||
}
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error") # raise warnings as errors
|
||||
assert app.config_schema().model_json_schema() == {
|
||||
"properties": {},
|
||||
"title": "LangGraphConfig",
|
||||
"type": "object",
|
||||
}
|
||||
|
||||
assert app.invoke(2) == 3
|
||||
assert app.invoke(2, output_keys=["output"]) == {"output": 3}
|
||||
assert repr(app), "does not raise recursion error"
|
||||
@@ -489,16 +538,24 @@ def test_invoke_single_process_in_write_kwargs(mocker: MockerFixture) -> None:
|
||||
input_channels="input",
|
||||
)
|
||||
|
||||
assert app.input_schema.schema() == {"title": "LangGraphInput", "type": "integer"}
|
||||
assert app.output_schema.schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"output": {"title": "Output", "type": "integer"},
|
||||
"fixed": {"title": "Fixed", "type": "integer"},
|
||||
"output_plus_one": {"title": "Output Plus One", "type": "integer"},
|
||||
},
|
||||
}
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.input_schema.model_json_schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "integer",
|
||||
}
|
||||
assert app.output_schema.model_json_schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"output": {"title": "Output", "type": "integer", "default": None},
|
||||
"fixed": {"title": "Fixed", "type": "integer", "default": None},
|
||||
"output_plus_one": {
|
||||
"title": "Output Plus One",
|
||||
"type": "integer",
|
||||
"default": None,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert app.invoke(2) == {"output": 3, "fixed": 5, "output_plus_one": 4}
|
||||
|
||||
|
||||
@@ -513,12 +570,18 @@ def test_invoke_single_process_in_out_dict(mocker: MockerFixture) -> None:
|
||||
output_channels=["output"],
|
||||
)
|
||||
|
||||
assert app.input_schema.schema() == {"title": "LangGraphInput", "type": "integer"}
|
||||
assert app.output_schema.schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {"output": {"title": "Output", "type": "integer"}},
|
||||
}
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.input_schema.model_json_schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "integer",
|
||||
}
|
||||
assert app.output_schema.model_json_schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"output": {"title": "Output", "type": "integer", "default": None}
|
||||
},
|
||||
}
|
||||
assert app.invoke(2) == {"output": 3}
|
||||
|
||||
|
||||
@@ -532,17 +595,21 @@ def test_invoke_single_process_in_dict_out_dict(mocker: MockerFixture) -> None:
|
||||
input_channels=["input"],
|
||||
output_channels=["output"],
|
||||
)
|
||||
|
||||
assert app.input_schema.schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "object",
|
||||
"properties": {"input": {"title": "Input", "type": "integer"}},
|
||||
}
|
||||
assert app.output_schema.schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {"output": {"title": "Output", "type": "integer"}},
|
||||
}
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.input_schema.model_json_schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {"title": "Input", "type": "integer", "default": None}
|
||||
},
|
||||
}
|
||||
assert app.output_schema.model_json_schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"output": {"title": "Output", "type": "integer", "default": None}
|
||||
},
|
||||
}
|
||||
assert app.invoke({"input": 2}) == {"output": 3}
|
||||
|
||||
|
||||
@@ -1955,7 +2022,6 @@ def test_channel_enter_exit_timing(mocker: MockerFixture) -> None:
|
||||
def test_conditional_graph(
|
||||
snapshot: SnapshotAssertion, request: pytest.FixtureRequest, checkpointer_name: str
|
||||
) -> None:
|
||||
from langchain_core.agents import AgentAction, AgentFinish
|
||||
from langchain_core.language_models.fake import FakeStreamingListLLM
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
from langchain_core.runnables import RunnablePassthrough
|
||||
@@ -2034,11 +2100,12 @@ def test_conditional_graph(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
assert app.get_graph().draw_mermaid() == snapshot
|
||||
assert json.dumps(app.get_graph(xray=True).to_json(), indent=2) == snapshot
|
||||
assert app.get_graph(xray=True).draw_mermaid(with_styles=False) == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
assert app.get_graph().draw_mermaid() == snapshot
|
||||
assert json.dumps(app.get_graph(xray=True).to_json(), indent=2) == snapshot
|
||||
assert app.get_graph(xray=True).draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
assert app.invoke({"input": "what is weather in sf"}) == {
|
||||
"input": "what is weather in sf",
|
||||
@@ -2168,8 +2235,9 @@ def test_conditional_graph(
|
||||
)
|
||||
config = {"configurable": {"thread_id": "1"}}
|
||||
|
||||
assert app_w_interrupt.get_graph().to_json() == snapshot
|
||||
assert app_w_interrupt.get_graph().draw_mermaid() == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app_w_interrupt.get_graph().to_json() == snapshot
|
||||
assert app_w_interrupt.get_graph().draw_mermaid() == snapshot
|
||||
|
||||
assert [
|
||||
c for c in app_w_interrupt.stream({"input": "what is weather in sf"}, config)
|
||||
@@ -2811,10 +2879,11 @@ def test_conditional_entrypoint_graph(snapshot: SnapshotAssertion) -> None:
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_input_schema().schema_json() == snapshot
|
||||
assert app.get_output_schema().schema_json() == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
assert (
|
||||
app.invoke("what is weather in sf", debug=True)
|
||||
@@ -2852,10 +2921,11 @@ def test_conditional_entrypoint_to_multiple_state_graph(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_input_schema().schema_json() == snapshot
|
||||
assert app.get_output_schema().schema_json() == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
assert app.invoke({"locations": ["sf", "nyc"]}, debug=True) == {
|
||||
"locations": ["sf", "nyc"],
|
||||
@@ -2875,7 +2945,6 @@ def test_conditional_state_graph(
|
||||
request: pytest.FixtureRequest,
|
||||
checkpointer_name: str,
|
||||
) -> None:
|
||||
from langchain_core.agents import AgentAction, AgentFinish
|
||||
from langchain_core.language_models.fake import FakeStreamingListLLM
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
from langchain_core.tools import tool
|
||||
@@ -2993,10 +3062,11 @@ def test_conditional_state_graph(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_input_schema().schema_json() == snapshot
|
||||
assert app.get_output_schema().schema_json() == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
with assert_ctx_once():
|
||||
assert app.invoke({"input": "what is weather in sf"}) == {
|
||||
@@ -3687,7 +3757,6 @@ def test_conditional_state_graph_with_list_edge_inputs(snapshot: SnapshotAsserti
|
||||
|
||||
|
||||
def test_state_graph_w_config_inherited_state_keys(snapshot: SnapshotAssertion) -> None:
|
||||
from langchain_core.agents import AgentAction, AgentFinish
|
||||
from langchain_core.language_models.fake import FakeStreamingListLLM
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
from langchain_core.tools import tool
|
||||
@@ -3777,9 +3846,10 @@ def test_state_graph_w_config_inherited_state_keys(snapshot: SnapshotAssertion)
|
||||
|
||||
app = builder.compile()
|
||||
|
||||
assert app.config_schema().schema_json() == snapshot
|
||||
assert app.get_input_schema().schema_json() == snapshot
|
||||
assert app.get_output_schema().schema_json() == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert json.dumps(app.config_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot
|
||||
|
||||
assert builder.channels.keys() == {"input", "agent_outcome", "intermediate_steps"}
|
||||
|
||||
@@ -3842,10 +3912,11 @@ def test_conditional_entrypoint_graph_state(snapshot: SnapshotAssertion) -> None
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_input_schema().schema_json() == snapshot
|
||||
assert app.get_output_schema().schema_json() == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
assert app.invoke({"input": "what is weather in sf"}) == {
|
||||
"input": "what is weather in sf",
|
||||
@@ -3862,7 +3933,7 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
from langchain_core.language_models.fake_chat_models import (
|
||||
FakeMessagesListChatModel,
|
||||
)
|
||||
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
|
||||
from langchain_core.messages import AIMessage, HumanMessage
|
||||
from langchain_core.tools import tool
|
||||
|
||||
class FakeFuntionChatModel(FakeMessagesListChatModel):
|
||||
@@ -3909,18 +3980,18 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
|
||||
app = create_tool_calling_executor(model, tools)
|
||||
|
||||
assert app.get_input_schema().schema_json() == snapshot
|
||||
assert app.get_output_schema().schema_json() == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
assert app.invoke(
|
||||
{"messages": [HumanMessage(content="what is weather in sf")]}
|
||||
) == {
|
||||
"messages": [
|
||||
_AnyIdHumanMessage(content="what is weather in sf"),
|
||||
AIMessage(
|
||||
id=AnyStr(),
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -3930,14 +4001,12 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
),
|
||||
AIMessage(
|
||||
id=AnyStr(),
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -3952,13 +4021,12 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call234",
|
||||
id=AnyStr(),
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call567",
|
||||
@@ -3988,7 +4056,7 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
{
|
||||
"agent": {
|
||||
"messages": [
|
||||
AIMessage(
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -3997,7 +4065,6 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
"args": {"query": "query"},
|
||||
},
|
||||
],
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -4005,11 +4072,10 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
{
|
||||
"tools": {
|
||||
"messages": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -4017,7 +4083,7 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
{
|
||||
"agent": {
|
||||
"messages": [
|
||||
AIMessage(
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -4031,7 +4097,6 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
"args": {"query": "a third one"},
|
||||
},
|
||||
],
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -4039,17 +4104,15 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
{
|
||||
"tools": {
|
||||
"messages": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call234",
|
||||
id=AnyStr(),
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call567",
|
||||
id=AnyStr(),
|
||||
),
|
||||
]
|
||||
}
|
||||
@@ -4063,8 +4126,7 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
{
|
||||
"agent": {
|
||||
"messages": [
|
||||
AIMessage(
|
||||
id=AnyStr(),
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -4080,11 +4142,10 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
{
|
||||
"tools": {
|
||||
"messages": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -4092,8 +4153,7 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
{
|
||||
"agent": {
|
||||
"messages": [
|
||||
AIMessage(
|
||||
id=AnyStr(),
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -4114,17 +4174,15 @@ def test_prebuilt_tool_chat(snapshot: SnapshotAssertion) -> None:
|
||||
{
|
||||
"tools": {
|
||||
"messages": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call234",
|
||||
id=AnyStr(),
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call567",
|
||||
id=AnyStr(),
|
||||
),
|
||||
]
|
||||
}
|
||||
@@ -4272,10 +4330,9 @@ def test_state_graph_packets(
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(
|
||||
@@ -4294,16 +4351,14 @@ def test_state_graph_packets(
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call234",
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call567",
|
||||
),
|
||||
AIMessage(content="answer", id="ai3"),
|
||||
@@ -4333,10 +4388,9 @@ def test_state_graph_packets(
|
||||
},
|
||||
{
|
||||
"tools": {
|
||||
"messages": ToolMessage(
|
||||
"messages": _AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
)
|
||||
}
|
||||
@@ -4363,20 +4417,18 @@ def test_state_graph_packets(
|
||||
},
|
||||
{
|
||||
"tools": {
|
||||
"messages": ToolMessage(
|
||||
"messages": _AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call234",
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
"tools": {
|
||||
"messages": ToolMessage(
|
||||
"messages": _AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call567",
|
||||
),
|
||||
},
|
||||
@@ -4513,10 +4565,9 @@ def test_state_graph_packets(
|
||||
assert [c for c in app_w_interrupt.stream(None, config)] == [
|
||||
{
|
||||
"tools": {
|
||||
"messages": ToolMessage(
|
||||
"messages": _AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
)
|
||||
}
|
||||
@@ -4558,10 +4609,9 @@ def test_state_graph_packets(
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(
|
||||
@@ -4638,10 +4688,9 @@ def test_state_graph_packets(
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(content="answer", id="ai2"),
|
||||
@@ -4683,7 +4732,6 @@ def test_message_graph(
|
||||
AIMessage,
|
||||
BaseMessage,
|
||||
HumanMessage,
|
||||
ToolMessage,
|
||||
)
|
||||
from langchain_core.outputs import ChatGeneration, ChatResult
|
||||
from langchain_core.tools import tool
|
||||
@@ -4797,15 +4845,15 @@ def test_message_graph(
|
||||
# meaning you can use it as you would any other runnable
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_input_schema().schema_json() == snapshot
|
||||
assert app.get_output_schema().schema_json() == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert json.dumps(app.get_input_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_output_schema().model_json_schema()) == snapshot
|
||||
assert json.dumps(app.get_graph().to_json(), indent=2) == snapshot
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
|
||||
assert app.invoke(HumanMessage(content="what is weather in sf")) == [
|
||||
HumanMessage(
|
||||
_AnyIdHumanMessage(
|
||||
content="what is weather in sf",
|
||||
id="00000000-0000-4000-8000-000000000002", # adds missing ids
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
@@ -4818,11 +4866,10 @@ def test_message_graph(
|
||||
],
|
||||
id="ai1", # respects ids passed in
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id="00000000-0000-4000-8000-000000000010",
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
@@ -4835,11 +4882,10 @@ def test_message_graph(
|
||||
],
|
||||
id="ai2",
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call456",
|
||||
id="00000000-0000-4000-8000-000000000018",
|
||||
),
|
||||
AIMessage(content="answer", id="ai3"),
|
||||
]
|
||||
@@ -4860,11 +4906,10 @@ def test_message_graph(
|
||||
},
|
||||
{
|
||||
"tools": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id="00000000-0000-4000-8000-000000000033",
|
||||
)
|
||||
]
|
||||
},
|
||||
@@ -4883,11 +4928,10 @@ def test_message_graph(
|
||||
},
|
||||
{
|
||||
"tools": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call456",
|
||||
id="00000000-0000-4000-8000-000000000041",
|
||||
)
|
||||
]
|
||||
},
|
||||
@@ -5007,11 +5051,10 @@ def test_message_graph(
|
||||
assert [c for c in app_w_interrupt.stream(None, config)] == [
|
||||
{
|
||||
"tools": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
},
|
||||
@@ -5044,11 +5087,10 @@ def test_message_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
@@ -5107,11 +5149,10 @@ def test_message_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
),
|
||||
AIMessage(content="answer", id="ai2"),
|
||||
],
|
||||
@@ -5240,11 +5281,10 @@ def test_message_graph(
|
||||
assert [c for c in app_w_interrupt.stream(None, config)] == [
|
||||
{
|
||||
"tools": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
},
|
||||
@@ -5277,11 +5317,10 @@ def test_message_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
@@ -5340,7 +5379,7 @@ def test_message_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
@@ -5380,7 +5419,7 @@ def test_message_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
@@ -5537,9 +5576,8 @@ def test_root_graph(
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.invoke(HumanMessage(content="what is weather in sf")) == [
|
||||
HumanMessage(
|
||||
_AnyIdHumanMessage(
|
||||
content="what is weather in sf",
|
||||
id="00000000-0000-4000-8000-000000000002", # adds missing ids
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
@@ -5552,11 +5590,10 @@ def test_root_graph(
|
||||
],
|
||||
id="ai1", # respects ids passed in
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id="00000000-0000-4000-8000-000000000010",
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
@@ -5569,11 +5606,10 @@ def test_root_graph(
|
||||
],
|
||||
id="ai2",
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call456",
|
||||
id="00000000-0000-4000-8000-000000000018",
|
||||
),
|
||||
AIMessage(content="answer", id="ai3"),
|
||||
]
|
||||
@@ -5741,11 +5777,10 @@ def test_root_graph(
|
||||
assert [c for c in app_w_interrupt.stream(None, config)] == [
|
||||
{
|
||||
"tools": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
},
|
||||
@@ -5778,7 +5813,7 @@ def test_root_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
@@ -5841,7 +5876,7 @@ def test_root_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
@@ -5974,11 +6009,10 @@ def test_root_graph(
|
||||
assert [c for c in app_w_interrupt.stream(None, config)] == [
|
||||
{
|
||||
"tools": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
},
|
||||
@@ -6011,7 +6045,7 @@ def test_root_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
@@ -6074,11 +6108,10 @@ def test_root_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
),
|
||||
AIMessage(content="answer", id="ai2"),
|
||||
],
|
||||
@@ -6114,7 +6147,7 @@ def test_root_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
@@ -6186,11 +6219,10 @@ def test_root_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
),
|
||||
AIMessage(content="answer", id="ai2"),
|
||||
_AnyIdAIMessage(content="an extra message"),
|
||||
@@ -6234,10 +6266,9 @@ def test_root_graph(
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id="00000000-0000-4000-8000-000000000082",
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(content="answer", id="ai2"),
|
||||
@@ -7531,7 +7562,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class_pydantic1(
|
||||
request: pytest.FixtureRequest,
|
||||
checkpointer_name: str,
|
||||
) -> None:
|
||||
from langchain_core.pydantic_v1 import BaseModel, ValidationError
|
||||
from pydantic.v1 import BaseModel, ValidationError
|
||||
|
||||
checkpointer = request.getfixturevalue(f"checkpointer_{checkpointer_name}")
|
||||
setup = mocker.Mock()
|
||||
@@ -7632,6 +7663,7 @@ def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class_pydantic1(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
# because it's a v1 pydantic, we're using .schema() here instead of the new methods
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
assert app.get_input_schema().schema() == snapshot
|
||||
assert app.get_output_schema().schema() == snapshot
|
||||
@@ -7799,9 +7831,10 @@ def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class_pydantic2(
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
assert app.get_input_schema().schema() == snapshot
|
||||
assert app.get_output_schema().schema() == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
assert app.get_input_schema().model_json_schema() == snapshot
|
||||
assert app.get_output_schema().model_json_schema() == snapshot
|
||||
|
||||
with pytest.raises(ValidationError), assert_ctx_once():
|
||||
app.invoke({"query": {}})
|
||||
@@ -10103,7 +10136,7 @@ def test_weather_subgraph(
|
||||
from langchain_core.language_models.fake_chat_models import (
|
||||
FakeMessagesListChatModel,
|
||||
)
|
||||
from langchain_core.messages import AIMessage, HumanMessage, ToolCall
|
||||
from langchain_core.messages import AIMessage, ToolCall
|
||||
from langchain_core.tools import tool
|
||||
|
||||
from langgraph.graph import MessagesState
|
||||
@@ -10222,7 +10255,7 @@ def test_weather_subgraph(
|
||||
state = graph.get_state(config)
|
||||
assert state == StateSnapshot(
|
||||
values={
|
||||
"messages": [HumanMessage(content="what's the weather in sf", id=AnyStr())],
|
||||
"messages": [_AnyIdHumanMessage(content="what's the weather in sf")],
|
||||
"route": "weather",
|
||||
},
|
||||
next=("weather_graph",),
|
||||
@@ -10284,8 +10317,8 @@ def test_weather_subgraph(
|
||||
{
|
||||
"weather_graph": {
|
||||
"messages": [
|
||||
HumanMessage(content="what's the weather in sf", id=AnyStr()),
|
||||
AIMessage(content="I'ts sunny in la!", id=AnyStr()),
|
||||
_AnyIdHumanMessage(content="what's the weather in sf"),
|
||||
_AnyIdAIMessage(content="I'ts sunny in la!"),
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -10307,7 +10340,7 @@ def test_weather_subgraph(
|
||||
state = graph.get_state(config, subgraphs=True)
|
||||
assert state == StateSnapshot(
|
||||
values={
|
||||
"messages": [HumanMessage(content="what's the weather in sf", id=AnyStr())],
|
||||
"messages": [_AnyIdHumanMessage(content="what's the weather in sf")],
|
||||
"route": "weather",
|
||||
},
|
||||
next=("weather_graph",),
|
||||
@@ -10339,9 +10372,7 @@ def test_weather_subgraph(
|
||||
state=StateSnapshot(
|
||||
values={
|
||||
"messages": [
|
||||
HumanMessage(
|
||||
content="what's the weather in sf", id=AnyStr()
|
||||
)
|
||||
_AnyIdHumanMessage(content="what's the weather in sf")
|
||||
],
|
||||
"city": "San Francisco",
|
||||
},
|
||||
@@ -10386,7 +10417,7 @@ def test_weather_subgraph(
|
||||
state = graph.get_state(config, subgraphs=True)
|
||||
assert state == StateSnapshot(
|
||||
values={
|
||||
"messages": [HumanMessage(content="what's the weather in sf", id=AnyStr())],
|
||||
"messages": [_AnyIdHumanMessage(content="what's the weather in sf")],
|
||||
"route": "weather",
|
||||
},
|
||||
next=("weather_graph",),
|
||||
@@ -10418,10 +10449,8 @@ def test_weather_subgraph(
|
||||
state=StateSnapshot(
|
||||
values={
|
||||
"messages": [
|
||||
HumanMessage(
|
||||
content="what's the weather in sf", id=AnyStr()
|
||||
),
|
||||
AIMessage(content="rainy", id=AnyStr()),
|
||||
_AnyIdHumanMessage(content="what's the weather in sf"),
|
||||
_AnyIdAIMessage(content="rainy"),
|
||||
],
|
||||
"city": "San Francisco",
|
||||
},
|
||||
@@ -10473,8 +10502,8 @@ def test_weather_subgraph(
|
||||
{
|
||||
"weather_graph": {
|
||||
"messages": [
|
||||
HumanMessage(content="what's the weather in sf", id=AnyStr()),
|
||||
AIMessage(content="rainy", id=AnyStr()),
|
||||
_AnyIdHumanMessage(content="what's the weather in sf"),
|
||||
_AnyIdAIMessage(content="rainy"),
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -10534,7 +10563,7 @@ def test_checkpoint_metadata() -> None:
|
||||
from langchain_core.language_models.fake_chat_models import (
|
||||
FakeMessagesListChatModel,
|
||||
)
|
||||
from langchain_core.messages import AIMessage, AnyMessage, ToolMessage
|
||||
from langchain_core.messages import AIMessage, AnyMessage
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.tools import tool
|
||||
|
||||
@@ -10621,9 +10650,8 @@ def test_checkpoint_metadata() -> None:
|
||||
) == {
|
||||
"messages": [
|
||||
_AnyIdHumanMessage(content="what is weather in sf"),
|
||||
AIMessage(
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
id=AnyStr(),
|
||||
tool_calls=[
|
||||
{
|
||||
"name": "search_api",
|
||||
@@ -10633,10 +10661,9 @@ def test_checkpoint_metadata() -> None:
|
||||
}
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
_AnyIdAIMessage(content="answer"),
|
||||
@@ -10767,7 +10794,7 @@ def test_remove_message_from_node():
|
||||
|
||||
def test_xray_lance(snapshot: SnapshotAssertion):
|
||||
from langchain_core.messages import AnyMessage, HumanMessage
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Analyst(BaseModel):
|
||||
affiliation: str = Field(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import asyncio
|
||||
import json
|
||||
import operator
|
||||
import re
|
||||
import sys
|
||||
@@ -60,7 +59,6 @@ from langgraph.managed.shared_value import SharedValue
|
||||
from langgraph.prebuilt.chat_agent_executor import (
|
||||
create_tool_calling_executor,
|
||||
)
|
||||
from langgraph.prebuilt.tool_executor import ToolExecutor
|
||||
from langgraph.prebuilt.tool_node import ToolNode
|
||||
from langgraph.pregel import (
|
||||
Channel,
|
||||
@@ -75,6 +73,7 @@ from tests.any_str import AnyDict, AnyStr, AnyVersion, UnsortedSequence
|
||||
from tests.conftest import (
|
||||
ALL_CHECKPOINTERS_ASYNC,
|
||||
ALL_CHECKPOINTERS_ASYNC_PLUS_NONE,
|
||||
SHOULD_CHECK_SNAPSHOTS,
|
||||
awith_checkpointer,
|
||||
)
|
||||
from tests.fake_tracer import FakeTracer
|
||||
@@ -82,7 +81,7 @@ from tests.memory_assert import (
|
||||
MemorySaverAssertCheckpointMetadata,
|
||||
MemorySaverNoPending,
|
||||
)
|
||||
from tests.messages import _AnyIdAIMessage, _AnyIdHumanMessage
|
||||
from tests.messages import _AnyIdAIMessage, _AnyIdHumanMessage, _AnyIdToolMessage
|
||||
|
||||
pytestmark = pytest.mark.anyio
|
||||
|
||||
@@ -657,8 +656,15 @@ async def test_invoke_single_process_in_out(mocker: MockerFixture) -> None:
|
||||
graph.set_finish_point("add_one")
|
||||
gapp = graph.compile()
|
||||
|
||||
assert app.input_schema.schema() == {"title": "LangGraphInput", "type": "integer"}
|
||||
assert app.output_schema.schema() == {"title": "LangGraphOutput", "type": "integer"}
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.input_schema.model_json_schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "integer",
|
||||
}
|
||||
assert app.output_schema.model_json_schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "integer",
|
||||
}
|
||||
assert await app.ainvoke(2) == 3
|
||||
assert await app.ainvoke(2, output_keys=["output"]) == {"output": 3}
|
||||
|
||||
@@ -698,16 +704,24 @@ async def test_invoke_single_process_in_write_kwargs(mocker: MockerFixture) -> N
|
||||
input_channels="input",
|
||||
)
|
||||
|
||||
assert app.input_schema.schema() == {"title": "LangGraphInput", "type": "integer"}
|
||||
assert app.output_schema.schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"output": {"title": "Output", "type": "integer"},
|
||||
"fixed": {"title": "Fixed", "type": "integer"},
|
||||
"output_plus_one": {"title": "Output Plus One", "type": "integer"},
|
||||
},
|
||||
}
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.input_schema.model_json_schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "integer",
|
||||
}
|
||||
assert app.output_schema.model_json_schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"output": {"title": "Output", "type": "integer", "default": None},
|
||||
"fixed": {"title": "Fixed", "type": "integer", "default": None},
|
||||
"output_plus_one": {
|
||||
"title": "Output Plus One",
|
||||
"type": "integer",
|
||||
"default": None,
|
||||
},
|
||||
},
|
||||
}
|
||||
assert await app.ainvoke(2) == {"output": 3, "fixed": 5, "output_plus_one": 4}
|
||||
|
||||
|
||||
@@ -722,12 +736,18 @@ async def test_invoke_single_process_in_out_dict(mocker: MockerFixture) -> None:
|
||||
output_channels=["output"],
|
||||
)
|
||||
|
||||
assert app.input_schema.schema() == {"title": "LangGraphInput", "type": "integer"}
|
||||
assert app.output_schema.schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {"output": {"title": "Output", "type": "integer"}},
|
||||
}
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.input_schema.model_json_schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "integer",
|
||||
}
|
||||
assert app.output_schema.model_json_schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"output": {"title": "Output", "type": "integer", "default": None}
|
||||
},
|
||||
}
|
||||
assert await app.ainvoke(2) == {"output": 3}
|
||||
|
||||
|
||||
@@ -742,16 +762,21 @@ async def test_invoke_single_process_in_dict_out_dict(mocker: MockerFixture) ->
|
||||
output_channels=["output"],
|
||||
)
|
||||
|
||||
assert app.input_schema.schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "object",
|
||||
"properties": {"input": {"title": "Input", "type": "integer"}},
|
||||
}
|
||||
assert app.output_schema.schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {"output": {"title": "Output", "type": "integer"}},
|
||||
}
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.input_schema.model_json_schema() == {
|
||||
"title": "LangGraphInput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"input": {"title": "Input", "type": "integer", "default": None}
|
||||
},
|
||||
}
|
||||
assert app.output_schema.model_json_schema() == {
|
||||
"title": "LangGraphOutput",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"output": {"title": "Output", "type": "integer", "default": None}
|
||||
},
|
||||
}
|
||||
assert await app.ainvoke({"input": 2}) == {"output": 3}
|
||||
|
||||
|
||||
@@ -3823,7 +3848,7 @@ async def test_prebuilt_tool_chat() -> None:
|
||||
from langchain_core.language_models.fake_chat_models import (
|
||||
FakeMessagesListChatModel,
|
||||
)
|
||||
from langchain_core.messages import AIMessage, HumanMessage, ToolMessage
|
||||
from langchain_core.messages import AIMessage, HumanMessage
|
||||
from langchain_core.tools import tool
|
||||
|
||||
class FakeFuntionChatModel(FakeMessagesListChatModel):
|
||||
@@ -3876,8 +3901,7 @@ async def test_prebuilt_tool_chat() -> None:
|
||||
) == {
|
||||
"messages": [
|
||||
_AnyIdHumanMessage(content="what is weather in sf"),
|
||||
AIMessage(
|
||||
id=AnyStr(),
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -3887,14 +3911,12 @@ async def test_prebuilt_tool_chat() -> None:
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
),
|
||||
AIMessage(
|
||||
id=AnyStr(),
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -3909,13 +3931,12 @@ async def test_prebuilt_tool_chat() -> None:
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call234",
|
||||
id=AnyStr(),
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call567",
|
||||
@@ -3934,8 +3955,7 @@ async def test_prebuilt_tool_chat() -> None:
|
||||
{
|
||||
"agent": {
|
||||
"messages": [
|
||||
AIMessage(
|
||||
id=AnyStr(),
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -3951,11 +3971,10 @@ async def test_prebuilt_tool_chat() -> None:
|
||||
{
|
||||
"tools": {
|
||||
"messages": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
id=AnyStr(),
|
||||
)
|
||||
]
|
||||
}
|
||||
@@ -3963,8 +3982,7 @@ async def test_prebuilt_tool_chat() -> None:
|
||||
{
|
||||
"agent": {
|
||||
"messages": [
|
||||
AIMessage(
|
||||
id=AnyStr(),
|
||||
_AnyIdAIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
@@ -3985,17 +4003,15 @@ async def test_prebuilt_tool_chat() -> None:
|
||||
{
|
||||
"tools": {
|
||||
"messages": [
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call234",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
tool_call_id="tool_call567",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call567",
|
||||
),
|
||||
]
|
||||
}
|
||||
@@ -4132,10 +4148,9 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(
|
||||
@@ -4154,16 +4169,14 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call234",
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call567",
|
||||
),
|
||||
AIMessage(content="answer", id="ai3"),
|
||||
@@ -4193,10 +4206,9 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
},
|
||||
{
|
||||
"tools": {
|
||||
"messages": ToolMessage(
|
||||
"messages": _AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
)
|
||||
}
|
||||
@@ -4223,20 +4235,18 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
},
|
||||
{
|
||||
"tools": {
|
||||
"messages": ToolMessage(
|
||||
"messages": _AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call234",
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
"tools": {
|
||||
"messages": ToolMessage(
|
||||
"messages": _AnyIdToolMessage(
|
||||
content="result for a third one",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call567",
|
||||
),
|
||||
},
|
||||
@@ -4378,10 +4388,9 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
assert [c async for c in app_w_interrupt.astream(None, config)] == [
|
||||
{
|
||||
"tools": {
|
||||
"messages": ToolMessage(
|
||||
"messages": _AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
)
|
||||
}
|
||||
@@ -4424,10 +4433,9 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(
|
||||
@@ -4504,10 +4512,9 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
},
|
||||
],
|
||||
),
|
||||
ToolMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(content="answer", id="ai2"),
|
||||
@@ -4522,7 +4529,9 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
"source": "update",
|
||||
"step": 5,
|
||||
"writes": {
|
||||
"agent": {"messages": AIMessage(content="answer", id="ai2")}
|
||||
"agent": {
|
||||
"messages": AIMessage(content="answer", id="ai2"),
|
||||
}
|
||||
},
|
||||
},
|
||||
parent_config=[
|
||||
@@ -4533,11 +4542,10 @@ async def test_state_graph_packets(checkpointer_name: str) -> None:
|
||||
|
||||
@pytest.mark.parametrize("checkpointer_name", ALL_CHECKPOINTERS_ASYNC)
|
||||
async def test_message_graph(checkpointer_name: str) -> None:
|
||||
from langchain_core.agents import AgentAction
|
||||
from langchain_core.language_models.fake_chat_models import (
|
||||
FakeMessagesListChatModel,
|
||||
)
|
||||
from langchain_core.messages import AIMessage, FunctionMessage, HumanMessage
|
||||
from langchain_core.messages import AIMessage, HumanMessage
|
||||
from langchain_core.tools import tool
|
||||
|
||||
class FakeFuntionChatModel(FakeMessagesListChatModel):
|
||||
@@ -4555,63 +4563,46 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
responses=[
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"arguments": json.dumps("query"),
|
||||
"args": {"query": "query"},
|
||||
}
|
||||
},
|
||||
],
|
||||
id="ai1",
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call456",
|
||||
"name": "search_api",
|
||||
"arguments": json.dumps("another"),
|
||||
"args": {"query": "another"},
|
||||
}
|
||||
},
|
||||
],
|
||||
id="ai2",
|
||||
),
|
||||
AIMessage(content="answer", id="ai3"),
|
||||
]
|
||||
)
|
||||
|
||||
tool_executor = ToolExecutor(tools)
|
||||
|
||||
# Define the function that determines whether to continue or not
|
||||
def should_continue(messages):
|
||||
last_message = messages[-1]
|
||||
# If there is no function call, then we finish
|
||||
if "function_call" not in last_message.additional_kwargs:
|
||||
if not last_message.tool_calls:
|
||||
return "end"
|
||||
# Otherwise if there is, we continue
|
||||
else:
|
||||
return "continue"
|
||||
|
||||
async def call_tool(messages):
|
||||
# Based on the continue condition
|
||||
# we know the last message involves a function call
|
||||
last_message = messages[-1]
|
||||
# We construct an AgentAction from the function_call
|
||||
action = AgentAction(
|
||||
tool=last_message.additional_kwargs["function_call"]["name"],
|
||||
tool_input=json.loads(
|
||||
last_message.additional_kwargs["function_call"]["arguments"]
|
||||
),
|
||||
log="",
|
||||
)
|
||||
# We call the tool_executor and get back a response
|
||||
response = await tool_executor.ainvoke(action)
|
||||
# We use the response to create a FunctionMessage
|
||||
return FunctionMessage(content=str(response), name=action.tool)
|
||||
|
||||
# Define a new graph
|
||||
workflow = MessageGraph()
|
||||
|
||||
# Define the two nodes we will cycle between
|
||||
workflow.add_node("agent", model)
|
||||
workflow.add_node("tools", call_tool)
|
||||
workflow.add_node("tools", ToolNode(tools))
|
||||
|
||||
# Set the entrypoint as `agent`
|
||||
# This means that this node is the first one called
|
||||
@@ -4648,23 +4639,41 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
app = workflow.compile()
|
||||
|
||||
assert await app.ainvoke(HumanMessage(content="what is weather in sf")) == [
|
||||
_AnyIdHumanMessage(content="what is weather in sf"),
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {"name": "search_api", "arguments": '"query"'}
|
||||
},
|
||||
id="ai1",
|
||||
_AnyIdHumanMessage(
|
||||
content="what is weather in sf",
|
||||
),
|
||||
FunctionMessage(content="result for query", name="search_api", id=AnyStr()),
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {"name": "search_api", "arguments": '"another"'}
|
||||
},
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"args": {"query": "query"},
|
||||
}
|
||||
],
|
||||
id="ai1", # respects ids passed in
|
||||
),
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call456",
|
||||
"name": "search_api",
|
||||
"args": {"query": "another"},
|
||||
}
|
||||
],
|
||||
id="ai2",
|
||||
),
|
||||
FunctionMessage(content="result for another", name="search_api", id=AnyStr()),
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call456",
|
||||
),
|
||||
AIMessage(content="answer", id="ai3"),
|
||||
]
|
||||
|
||||
@@ -4674,30 +4683,46 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
{
|
||||
"agent": AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {"name": "search_api", "arguments": '"query"'}
|
||||
},
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"args": {"query": "query"},
|
||||
}
|
||||
],
|
||||
id="ai1",
|
||||
)
|
||||
},
|
||||
{
|
||||
"tools": FunctionMessage(
|
||||
content="result for query", name="search_api", id=AnyStr()
|
||||
)
|
||||
"tools": [
|
||||
_AnyIdToolMessage(
|
||||
content="result for query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
)
|
||||
]
|
||||
},
|
||||
{
|
||||
"agent": AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {"name": "search_api", "arguments": '"another"'}
|
||||
},
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call456",
|
||||
"name": "search_api",
|
||||
"args": {"query": "another"},
|
||||
}
|
||||
],
|
||||
id="ai2",
|
||||
)
|
||||
},
|
||||
{
|
||||
"tools": FunctionMessage(
|
||||
content="result for another", name="search_api", id=AnyStr()
|
||||
)
|
||||
"tools": [
|
||||
_AnyIdToolMessage(
|
||||
content="result for another",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call456",
|
||||
)
|
||||
]
|
||||
},
|
||||
{"agent": AIMessage(content="answer", id="ai3")},
|
||||
]
|
||||
@@ -4718,9 +4743,13 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
{
|
||||
"agent": AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {"name": "search_api", "arguments": '"query"'}
|
||||
},
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"args": {"query": "query"},
|
||||
}
|
||||
],
|
||||
id="ai1",
|
||||
)
|
||||
},
|
||||
@@ -4732,9 +4761,13 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
_AnyIdHumanMessage(content="what is weather in sf"),
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {"name": "search_api", "arguments": '"query"'}
|
||||
},
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"args": {"query": "query"},
|
||||
}
|
||||
],
|
||||
id="ai1",
|
||||
),
|
||||
],
|
||||
@@ -4749,12 +4782,13 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
"writes": {
|
||||
"agent": AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"arguments": '"query"',
|
||||
"args": {"query": "query"},
|
||||
}
|
||||
},
|
||||
],
|
||||
id="ai1",
|
||||
)
|
||||
},
|
||||
@@ -4766,9 +4800,7 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
|
||||
# modify ai message
|
||||
last_message = (await app_w_interrupt.aget_state(config)).values[-1]
|
||||
last_message.additional_kwargs["function_call"]["arguments"] = (
|
||||
'"a different query"'
|
||||
)
|
||||
last_message.tool_calls[0]["args"] = {"query": "a different query"}
|
||||
await app_w_interrupt.aupdate_state(config, last_message)
|
||||
|
||||
# message was replaced instead of appended
|
||||
@@ -4778,13 +4810,14 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
_AnyIdHumanMessage(content="what is weather in sf"),
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
"name": "search_api",
|
||||
"arguments": '"a different query"',
|
||||
}
|
||||
},
|
||||
id="ai1",
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"args": {"query": "a different query"},
|
||||
}
|
||||
],
|
||||
),
|
||||
],
|
||||
tasks=(PregelTask(AnyStr(), "tools"),),
|
||||
@@ -4798,12 +4831,13 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
"writes": {
|
||||
"agent": AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"arguments": '"a different query"',
|
||||
"args": {"query": "a different query"},
|
||||
}
|
||||
},
|
||||
],
|
||||
id="ai1",
|
||||
)
|
||||
},
|
||||
@@ -4815,21 +4849,24 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
|
||||
assert [c async for c in app_w_interrupt.astream(None, config)] == [
|
||||
{
|
||||
"tools": FunctionMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
)
|
||||
"tools": [
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
tool_call_id="tool_call123",
|
||||
)
|
||||
]
|
||||
},
|
||||
{
|
||||
"agent": AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call456",
|
||||
"name": "search_api",
|
||||
"arguments": '"another"',
|
||||
"args": {"query": "another"},
|
||||
}
|
||||
},
|
||||
],
|
||||
id="ai2",
|
||||
)
|
||||
},
|
||||
@@ -4841,27 +4878,29 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
_AnyIdHumanMessage(content="what is weather in sf"),
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
"name": "search_api",
|
||||
"arguments": '"a different query"',
|
||||
}
|
||||
},
|
||||
id="ai1",
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"args": {"query": "a different query"},
|
||||
}
|
||||
],
|
||||
),
|
||||
FunctionMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call456",
|
||||
"name": "search_api",
|
||||
"arguments": '"another"',
|
||||
"args": {"query": "another"},
|
||||
}
|
||||
},
|
||||
],
|
||||
id="ai2",
|
||||
),
|
||||
],
|
||||
@@ -4876,12 +4915,13 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
"writes": {
|
||||
"agent": AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call456",
|
||||
"name": "search_api",
|
||||
"arguments": '"another"',
|
||||
"args": {"query": "another"},
|
||||
}
|
||||
},
|
||||
],
|
||||
id="ai2",
|
||||
)
|
||||
},
|
||||
@@ -4903,18 +4943,19 @@ async def test_message_graph(checkpointer_name: str) -> None:
|
||||
_AnyIdHumanMessage(content="what is weather in sf"),
|
||||
AIMessage(
|
||||
content="",
|
||||
additional_kwargs={
|
||||
"function_call": {
|
||||
"name": "search_api",
|
||||
"arguments": '"a different query"',
|
||||
}
|
||||
},
|
||||
id="ai1",
|
||||
tool_calls=[
|
||||
{
|
||||
"id": "tool_call123",
|
||||
"name": "search_api",
|
||||
"args": {"query": "a different query"},
|
||||
}
|
||||
],
|
||||
),
|
||||
FunctionMessage(
|
||||
_AnyIdToolMessage(
|
||||
content="result for a different query",
|
||||
name="search_api",
|
||||
id=AnyStr(),
|
||||
tool_call_id="tool_call123",
|
||||
),
|
||||
AIMessage(content="answer", id="ai2"),
|
||||
],
|
||||
@@ -6215,7 +6256,7 @@ async def test_in_one_fan_out_state_graph_waiting_edge_via_branch(
|
||||
async def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class(
|
||||
snapshot: SnapshotAssertion, mocker: MockerFixture, checkpointer_name: str
|
||||
) -> None:
|
||||
from langchain_core.pydantic_v1 import BaseModel, ValidationError
|
||||
from pydantic.v1 import BaseModel, ValidationError
|
||||
|
||||
setup = mocker.Mock()
|
||||
teardown = mocker.Mock()
|
||||
@@ -6467,9 +6508,10 @@ async def test_in_one_fan_out_state_graph_waiting_edge_custom_state_class_pydant
|
||||
|
||||
app = workflow.compile()
|
||||
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
assert app.get_input_schema().schema() == snapshot
|
||||
assert app.get_output_schema().schema() == snapshot
|
||||
if SHOULD_CHECK_SNAPSHOTS:
|
||||
assert app.get_graph().draw_mermaid(with_styles=False) == snapshot
|
||||
assert app.get_input_schema().model_json_schema() == snapshot
|
||||
assert app.get_output_schema().model_json_schema() == snapshot
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
await app.ainvoke({"query": {}})
|
||||
@@ -8688,7 +8730,7 @@ async def test_weather_subgraph(
|
||||
from langchain_core.language_models.fake_chat_models import (
|
||||
FakeMessagesListChatModel,
|
||||
)
|
||||
from langchain_core.messages import AIMessage, HumanMessage, ToolCall
|
||||
from langchain_core.messages import AIMessage, ToolCall
|
||||
from langchain_core.tools import tool
|
||||
|
||||
from langgraph.graph import MessagesState
|
||||
@@ -8812,9 +8854,7 @@ async def test_weather_subgraph(
|
||||
state = await graph.aget_state(config)
|
||||
assert state == StateSnapshot(
|
||||
values={
|
||||
"messages": [
|
||||
HumanMessage(content="what's the weather in sf", id=AnyStr())
|
||||
],
|
||||
"messages": [_AnyIdHumanMessage(content="what's the weather in sf")],
|
||||
"route": "weather",
|
||||
},
|
||||
next=("weather_graph",),
|
||||
@@ -8880,10 +8920,8 @@ async def test_weather_subgraph(
|
||||
{
|
||||
"weather_graph": {
|
||||
"messages": [
|
||||
HumanMessage(
|
||||
content="what's the weather in sf", id=AnyStr()
|
||||
),
|
||||
AIMessage(content="I'ts sunny in la!", id=AnyStr()),
|
||||
_AnyIdHumanMessage(content="what's the weather in sf"),
|
||||
_AnyIdAIMessage(content="I'ts sunny in la!"),
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -8905,9 +8943,7 @@ async def test_weather_subgraph(
|
||||
state = await graph.aget_state(config, subgraphs=True)
|
||||
assert state == StateSnapshot(
|
||||
values={
|
||||
"messages": [
|
||||
HumanMessage(content="what's the weather in sf", id=AnyStr())
|
||||
],
|
||||
"messages": [_AnyIdHumanMessage(content="what's the weather in sf")],
|
||||
"route": "weather",
|
||||
},
|
||||
next=("weather_graph",),
|
||||
@@ -8939,9 +8975,7 @@ async def test_weather_subgraph(
|
||||
state=StateSnapshot(
|
||||
values={
|
||||
"messages": [
|
||||
HumanMessage(
|
||||
content="what's the weather in sf", id=AnyStr()
|
||||
)
|
||||
_AnyIdHumanMessage(content="what's the weather in sf")
|
||||
],
|
||||
"city": "San Francisco",
|
||||
},
|
||||
@@ -8986,9 +9020,7 @@ async def test_weather_subgraph(
|
||||
state = await graph.aget_state(config, subgraphs=True)
|
||||
assert state == StateSnapshot(
|
||||
values={
|
||||
"messages": [
|
||||
HumanMessage(content="what's the weather in sf", id=AnyStr())
|
||||
],
|
||||
"messages": [_AnyIdHumanMessage(content="what's the weather in sf")],
|
||||
"route": "weather",
|
||||
},
|
||||
next=("weather_graph",),
|
||||
@@ -9020,10 +9052,8 @@ async def test_weather_subgraph(
|
||||
state=StateSnapshot(
|
||||
values={
|
||||
"messages": [
|
||||
HumanMessage(
|
||||
content="what's the weather in sf", id=AnyStr()
|
||||
),
|
||||
AIMessage(content="rainy", id=AnyStr()),
|
||||
_AnyIdHumanMessage(content="what's the weather in sf"),
|
||||
_AnyIdAIMessage(content="rainy"),
|
||||
],
|
||||
"city": "San Francisco",
|
||||
},
|
||||
@@ -9077,10 +9107,8 @@ async def test_weather_subgraph(
|
||||
{
|
||||
"weather_graph": {
|
||||
"messages": [
|
||||
HumanMessage(
|
||||
content="what's the weather in sf", id=AnyStr()
|
||||
),
|
||||
AIMessage(content="rainy", id=AnyStr()),
|
||||
_AnyIdHumanMessage(content="what's the weather in sf"),
|
||||
_AnyIdAIMessage(content="rainy"),
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user