langgraph-prebuilt 0.2.2__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/.gitignore +1 -0
  2. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/PKG-INFO +2 -2
  3. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/langgraph/prebuilt/chat_agent_executor.py +20 -14
  4. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/langgraph/prebuilt/tool_node.py +1 -1
  5. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/pyproject.toml +2 -2
  6. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/conftest.py +0 -7
  7. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/memory_assert.py +1 -1
  8. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/model.py +4 -1
  9. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/test_react_agent.py +26 -26
  10. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/test_tool_node.py +0 -9
  11. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/uv.lock +750 -747
  12. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/LICENSE +0 -0
  13. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/Makefile +0 -0
  14. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/README.md +0 -0
  15. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/langgraph/prebuilt/__init__.py +0 -0
  16. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/langgraph/prebuilt/interrupt.py +0 -0
  17. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/langgraph/prebuilt/py.typed +0 -0
  18. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/langgraph/prebuilt/tool_validator.py +0 -0
  19. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/__init__.py +0 -0
  20. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/__snapshots__/test_react_agent_graph.ambr +0 -0
  21. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/any_str.py +0 -0
  22. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/compose-postgres.yml +0 -0
  23. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/conftest_checkpointer.py +0 -0
  24. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/conftest_store.py +0 -0
  25. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/messages.py +0 -0
  26. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/test_react_agent_graph.py +0 -0
  27. {langgraph_prebuilt-0.2.2 → langgraph_prebuilt-0.5.0}/tests/test_validation_node.py +0 -0
@@ -181,3 +181,4 @@ Chinook.db
181
181
  .vercel
182
182
  .turbo
183
183
  .editorconfig
184
+ .scratch
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-prebuilt
3
- Version: 0.2.2
3
+ Version: 0.5.0
4
4
  Summary: Library with high-level APIs for creating and executing LangGraph agents and tools.
5
5
  Project-URL: Repository, https://www.github.com/langchain-ai/langgraph
6
6
  License-Expression: MIT
7
7
  License-File: LICENSE
8
8
  Requires-Python: >=3.9
9
9
  Requires-Dist: langchain-core>=0.3.22
10
- Requires-Dist: langgraph-checkpoint>=2.0.10
10
+ Requires-Dist: langgraph-checkpoint>=2.1.0
11
11
  Description-Content-Type: text/markdown
12
12
 
13
13
  # LangGraph Prebuilt
@@ -36,8 +36,8 @@ from typing_extensions import Annotated, TypedDict
36
36
 
37
37
  from langgraph.errors import ErrorCode, create_error_message
38
38
  from langgraph.graph import END, StateGraph
39
- from langgraph.graph.graph import CompiledGraph
40
39
  from langgraph.graph.message import add_messages
40
+ from langgraph.graph.state import CompiledStateGraph
41
41
  from langgraph.managed import IsLastStep, RemainingSteps
42
42
  from langgraph.prebuilt.tool_node import ToolNode
43
43
  from langgraph.store.base import BaseStore
@@ -140,7 +140,9 @@ def _get_prompt_runnable(prompt: Optional[Prompt]) -> Runnable:
140
140
  return prompt_runnable
141
141
 
142
142
 
143
- def _should_bind_tools(model: LanguageModelLike, tools: Sequence[BaseTool]) -> bool:
143
+ def _should_bind_tools(
144
+ model: LanguageModelLike, tools: Sequence[BaseTool], num_builtin: int = 0
145
+ ) -> bool:
144
146
  if isinstance(model, RunnableSequence):
145
147
  model = next(
146
148
  (
@@ -158,9 +160,10 @@ def _should_bind_tools(model: LanguageModelLike, tools: Sequence[BaseTool]) -> b
158
160
  return True
159
161
 
160
162
  bound_tools = model.kwargs["tools"]
161
- if len(tools) != len(bound_tools):
163
+ if len(tools) != len(bound_tools) - num_builtin:
162
164
  raise ValueError(
163
165
  "Number of tools in the model.bind_tools() and tools passed to create_react_agent must match"
166
+ f" Got {len(tools)} tools, expected {len(bound_tools) - num_builtin}"
164
167
  )
165
168
 
166
169
  tool_names = set(tool.name for tool in tools)
@@ -257,7 +260,7 @@ def create_react_agent(
257
260
  debug: bool = False,
258
261
  version: Literal["v1", "v2"] = "v2",
259
262
  name: Optional[str] = None,
260
- ) -> CompiledGraph:
263
+ ) -> CompiledStateGraph:
261
264
  """Creates an agent graph that calls tools in a loop until a stopping condition is met.
262
265
 
263
266
  For more details on using `create_react_agent`, visit [Agents](https://langchain-ai.github.io/langgraph/agents/overview/) documentation.
@@ -444,9 +447,8 @@ def create_react_agent(
444
447
  tool_calling_enabled = len(tool_classes) > 0
445
448
 
446
449
  if (
447
- _should_bind_tools(model, tool_classes)
450
+ _should_bind_tools(model, tool_classes, num_builtin=len(llm_builtin_tools))
448
451
  and len(tool_classes) > 0
449
- or (len(llm_builtin_tools) > 0)
450
452
  ):
451
453
  model = cast(BaseChatModel, model).bind_tools(tool_classes + llm_builtin_tools) # type: ignore[operator]
452
454
 
@@ -592,10 +594,10 @@ def create_react_agent(
592
594
  workflow.add_node(
593
595
  "agent",
594
596
  RunnableCallable(call_model, acall_model),
595
- input=input_schema,
597
+ input_schema=input_schema,
596
598
  )
597
599
  if pre_model_hook is not None:
598
- workflow.add_node("pre_model_hook", pre_model_hook)
600
+ workflow.add_node("pre_model_hook", pre_model_hook) # type: ignore[arg-type]
599
601
  workflow.add_edge("pre_model_hook", "agent")
600
602
  entrypoint = "pre_model_hook"
601
603
  else:
@@ -604,14 +606,15 @@ def create_react_agent(
604
606
  workflow.set_entry_point(entrypoint)
605
607
 
606
608
  if post_model_hook is not None:
607
- workflow.add_node("post_model_hook", post_model_hook)
609
+ workflow.add_node("post_model_hook", post_model_hook) # type: ignore[arg-type]
608
610
  workflow.add_edge("agent", "post_model_hook")
609
611
 
610
612
  if response_format is not None:
611
613
  workflow.add_node(
612
614
  "generate_structured_response",
613
615
  RunnableCallable(
614
- generate_structured_response, agenerate_structured_response
616
+ generate_structured_response,
617
+ agenerate_structured_response,
615
618
  ),
616
619
  )
617
620
  if post_model_hook is not None:
@@ -658,14 +661,16 @@ def create_react_agent(
658
661
 
659
662
  # Define the two nodes we will cycle between
660
663
  workflow.add_node(
661
- "agent", RunnableCallable(call_model, acall_model), input=input_schema
664
+ "agent",
665
+ RunnableCallable(call_model, acall_model),
666
+ input_schema=input_schema,
662
667
  )
663
668
  workflow.add_node("tools", tool_node)
664
669
 
665
670
  # Optionally add a pre-model hook node that will be called
666
671
  # every time before the "agent" (LLM-calling node)
667
672
  if pre_model_hook is not None:
668
- workflow.add_node("pre_model_hook", pre_model_hook)
673
+ workflow.add_node("pre_model_hook", pre_model_hook) # type: ignore[arg-type]
669
674
  workflow.add_edge("pre_model_hook", "agent")
670
675
  entrypoint = "pre_model_hook"
671
676
  else:
@@ -680,7 +685,7 @@ def create_react_agent(
680
685
 
681
686
  # Add a post model hook node if post_model_hook is provided
682
687
  if post_model_hook is not None:
683
- workflow.add_node("post_model_hook", post_model_hook)
688
+ workflow.add_node("post_model_hook", post_model_hook) # type: ignore[arg-type]
684
689
  agent_paths.append("post_model_hook")
685
690
  workflow.add_edge("agent", "post_model_hook")
686
691
  else:
@@ -691,7 +696,8 @@ def create_react_agent(
691
696
  workflow.add_node(
692
697
  "generate_structured_response",
693
698
  RunnableCallable(
694
- generate_structured_response, agenerate_structured_response
699
+ generate_structured_response,
700
+ agenerate_structured_response,
695
701
  ),
696
702
  )
697
703
  if post_model_hook is not None:
@@ -850,7 +850,7 @@ def _get_store_arg(tool: BaseTool) -> Optional[str]:
850
850
  if _is_injection(type_arg, InjectedStore)
851
851
  ]
852
852
  if len(injections) > 1:
853
- ValueError(
853
+ raise ValueError(
854
854
  "A tool argument should not be annotated with InjectedStore more than "
855
855
  f"once. Received arg {name} with annotations {injections}."
856
856
  )
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "langgraph-prebuilt"
7
- version = "0.2.2"
7
+ version = "0.5.0"
8
8
  description = "Library with high-level APIs for creating and executing LangGraph agents and tools."
9
9
  authors = []
10
10
  requires-python = ">=3.9"
@@ -12,7 +12,7 @@ readme = "README.md"
12
12
  license = "MIT"
13
13
  license-files = ['LICENSE']
14
14
  dependencies = [
15
- "langgraph-checkpoint>=2.0.10",
15
+ "langgraph-checkpoint>=2.1.0",
16
16
  "langchain-core>=0.3.22",
17
17
  ]
18
18
 
@@ -2,8 +2,6 @@ from collections.abc import AsyncIterator, Iterator
2
2
  from uuid import UUID
3
3
 
4
4
  import pytest
5
- from langchain_core import __version__ as core_version
6
- from packaging import version
7
5
  from pytest_mock import MockerFixture
8
6
 
9
7
  from langgraph.checkpoint.base import BaseCheckpointSaver
@@ -31,11 +29,6 @@ from tests.conftest_store import (
31
29
 
32
30
  pytest.register_assert_rewrite("tests.memory_assert")
33
31
 
34
- # TODO: fix this once core is released
35
- IS_LANGCHAIN_CORE_030_OR_GREATER = version.parse(core_version) >= version.parse(
36
- "0.3.0.dev0"
37
- )
38
-
39
32
 
40
33
  @pytest.fixture
41
34
  def anyio_backend():
@@ -13,9 +13,9 @@ from langgraph.checkpoint.base import (
13
13
  CheckpointMetadata,
14
14
  CheckpointTuple,
15
15
  SerializerProtocol,
16
- copy_checkpoint,
17
16
  )
18
17
  from langgraph.checkpoint.memory import InMemorySaver, PersistentDict
18
+ from langgraph.pregel.checkpoint import copy_checkpoint
19
19
 
20
20
 
21
21
  class NoopSerializer(SerializerProtocol):
@@ -73,9 +73,12 @@ class FakeToolCallingModel(BaseChatModel):
73
73
 
74
74
  tool_dicts = []
75
75
  for tool in tools:
76
+ if isinstance(tool, dict):
77
+ tool_dicts.append(tool)
78
+ continue
76
79
  if not isinstance(tool, BaseTool):
77
80
  raise TypeError(
78
- "Only BaseTool is supported by FakeToolCallingModel.bind_tools"
81
+ "Only BaseTool and dict is supported by FakeToolCallingModel.bind_tools"
79
82
  )
80
83
 
81
84
  # NOTE: this is a simplified tool spec for testing purposes only
@@ -55,7 +55,6 @@ from langgraph.store.memory import InMemoryStore
55
55
  from langgraph.types import Command, Interrupt, interrupt
56
56
  from langgraph.utils.config import get_stream_writer
57
57
  from tests.any_str import AnyStr
58
- from tests.conftest import IS_LANGCHAIN_CORE_030_OR_GREATER
59
58
  from tests.messages import _AnyIdHumanMessage, _AnyIdToolMessage
60
59
  from tests.model import FakeToolCallingModel
61
60
 
@@ -91,9 +90,7 @@ def test_no_prompt(sync_checkpointer: BaseCheckpointSaver, version: str) -> None
91
90
  assert saved.metadata == {
92
91
  "parents": {},
93
92
  "source": "loop",
94
- "writes": {"agent": {"messages": [AIMessage(content="hi?", id="0")]}},
95
93
  "step": 1,
96
- "thread_id": "123",
97
94
  }
98
95
  assert saved.pending_writes == []
99
96
 
@@ -119,9 +116,7 @@ async def test_no_prompt_async(async_checkpointer: BaseCheckpointSaver) -> None:
119
116
  assert saved.metadata == {
120
117
  "parents": {},
121
118
  "source": "loop",
122
- "writes": {"agent": {"messages": [AIMessage(content="hi?", id="0")]}},
123
119
  "step": 1,
124
- "thread_id": "123",
125
120
  }
126
121
  assert saved.pending_writes == []
127
122
 
@@ -275,7 +270,8 @@ async def test_prompt_with_store_async():
275
270
 
276
271
  @pytest.mark.parametrize("tool_style", ["openai", "anthropic"])
277
272
  @pytest.mark.parametrize("version", REACT_TOOL_CALL_VERSIONS)
278
- def test_model_with_tools(tool_style: str, version: str):
273
+ @pytest.mark.parametrize("include_builtin", [True, False])
274
+ def test_model_with_tools(tool_style: str, version: str, include_builtin: bool):
279
275
  model = FakeToolCallingModel(tool_style=tool_style)
280
276
 
281
277
  @dec_tool
@@ -288,10 +284,27 @@ def test_model_with_tools(tool_style: str, version: str):
288
284
  """Tool 2 docstring."""
289
285
  return f"Tool 2: {some_val}"
290
286
 
287
+ tools = [tool1, tool2]
288
+ if include_builtin:
289
+ tools.append(
290
+ {
291
+ "type": "mcp",
292
+ "server_label": "atest_sever",
293
+ "server_url": "https://some.mcp.somewhere.com/sse",
294
+ "headers": {"foo": "bar"},
295
+ "allowed_tools": [
296
+ "mcp_tool_1",
297
+ "set_active_account",
298
+ "get_url_markdown",
299
+ "get_url_screenshot",
300
+ ],
301
+ "require_approval": "never",
302
+ }
303
+ )
291
304
  # check valid agent constructor
292
305
  agent = create_react_agent(
293
- model.bind_tools([tool1, tool2]),
294
- [tool1, tool2],
306
+ model.bind_tools(tools),
307
+ tools,
295
308
  version=version,
296
309
  )
297
310
  result = agent.nodes["tools"].invoke(
@@ -456,10 +469,6 @@ def test__infer_handled_types() -> None:
456
469
  _infer_handled_types(handler)
457
470
 
458
471
 
459
- @pytest.mark.skipif(
460
- not IS_LANGCHAIN_CORE_030_OR_GREATER,
461
- reason="Pydantic v1 is required for this test to pass in langchain-core < 0.3",
462
- )
463
472
  @pytest.mark.parametrize("version", REACT_TOOL_CALL_VERSIONS)
464
473
  def test_react_agent_with_structured_response(version: str) -> None:
465
474
  class WeatherResponse(BaseModel):
@@ -496,10 +505,6 @@ class CustomStatePydantic(AgentStatePydantic):
496
505
  user_name: Optional[str] = None
497
506
 
498
507
 
499
- @pytest.mark.skipif(
500
- not IS_LANGCHAIN_CORE_030_OR_GREATER,
501
- reason="Langchain core 0.3.0 or greater is required",
502
- )
503
508
  @pytest.mark.parametrize("version", REACT_TOOL_CALL_VERSIONS)
504
509
  @pytest.mark.parametrize("state_schema", [CustomState, CustomStatePydantic])
505
510
  def test_react_agent_update_state(
@@ -565,10 +570,6 @@ def test_react_agent_update_state(
565
570
  assert tool_message.name == "get_user_name"
566
571
 
567
572
 
568
- @pytest.mark.skipif(
569
- not IS_LANGCHAIN_CORE_030_OR_GREATER,
570
- reason="Langchain core 0.3.0 or greater is required",
571
- )
572
573
  @pytest.mark.parametrize("version", REACT_TOOL_CALL_VERSIONS)
573
574
  def test_react_agent_parallel_tool_calls(
574
575
  sync_checkpointer: BaseCheckpointSaver, version: str
@@ -832,10 +833,6 @@ def test_create_react_agent_inject_vars(
832
833
  assert result["foo"] == 2
833
834
 
834
835
 
835
- @pytest.mark.skipif(
836
- not IS_LANGCHAIN_CORE_030_OR_GREATER,
837
- reason="Langchain core 0.3.0 or greater is required",
838
- )
839
836
  def test_tool_node_inject_store() -> None:
840
837
  store = InMemoryStore()
841
838
  namespace = ("test",)
@@ -1108,14 +1105,17 @@ def test_react_with_subgraph_tools(
1108
1105
  return {"result": state["a"] + state["b"]}
1109
1106
 
1110
1107
  add_subgraph = (
1111
- StateGraph(State, output=Output).add_node(add).add_edge(START, "add").compile()
1108
+ StateGraph(State, output_schema=Output)
1109
+ .add_node(add)
1110
+ .add_edge(START, "add")
1111
+ .compile()
1112
1112
  )
1113
1113
 
1114
1114
  def multiply(state):
1115
1115
  return {"result": state["a"] * state["b"]}
1116
1116
 
1117
1117
  multiply_subgraph = (
1118
- StateGraph(State, output=Output)
1118
+ StateGraph(State, output_schema=Output)
1119
1119
  .add_node(multiply)
1120
1120
  .add_edge(START, "multiply")
1121
1121
  .compile()
@@ -18,7 +18,6 @@ from langgraph.errors import NodeInterrupt
18
18
  from langgraph.prebuilt import ToolNode
19
19
  from langgraph.prebuilt.tool_node import TOOL_CALL_ERROR_TEMPLATE
20
20
  from langgraph.types import Command, Send
21
- from tests.conftest import IS_LANGCHAIN_CORE_030_OR_GREATER
22
21
 
23
22
  pytestmark = pytest.mark.anyio
24
23
 
@@ -492,10 +491,6 @@ def test_tool_node_node_interrupt():
492
491
  assert exc_info.value == "foo"
493
492
 
494
493
 
495
- @pytest.mark.skipif(
496
- not IS_LANGCHAIN_CORE_030_OR_GREATER,
497
- reason="Langchain core 0.3.0 or greater is required",
498
- )
499
494
  @pytest.mark.parametrize("input_type", ["dict", "tool_calls"])
500
495
  async def test_tool_node_command(input_type: str):
501
496
  from langchain_core.tools.base import InjectedToolCallId
@@ -797,10 +792,6 @@ async def test_tool_node_command(input_type: str):
797
792
  ) == [Command(update={"messages": []}, graph=Command.PARENT)]
798
793
 
799
794
 
800
- @pytest.mark.skipif(
801
- not IS_LANGCHAIN_CORE_030_OR_GREATER,
802
- reason="Langchain core 0.3.0 or greater is required",
803
- )
804
795
  async def test_tool_node_command_list_input():
805
796
  from langchain_core.tools.base import InjectedToolCallId
806
797