langgraph-prebuilt 0.2.1__tar.gz → 0.2.3__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.1 → langgraph_prebuilt-0.2.3}/PKG-INFO +1 -1
  2. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/langgraph/prebuilt/chat_agent_executor.py +10 -4
  3. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/pyproject.toml +1 -1
  4. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/model.py +4 -1
  5. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/test_react_agent.py +81 -3
  6. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/uv.lock +743 -743
  7. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/.gitignore +0 -0
  8. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/LICENSE +0 -0
  9. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/Makefile +0 -0
  10. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/README.md +0 -0
  11. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/langgraph/prebuilt/__init__.py +0 -0
  12. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/langgraph/prebuilt/interrupt.py +0 -0
  13. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/langgraph/prebuilt/py.typed +0 -0
  14. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/langgraph/prebuilt/tool_node.py +0 -0
  15. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/langgraph/prebuilt/tool_validator.py +0 -0
  16. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/__init__.py +0 -0
  17. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/__snapshots__/test_react_agent_graph.ambr +0 -0
  18. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/any_str.py +0 -0
  19. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/compose-postgres.yml +0 -0
  20. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/conftest.py +0 -0
  21. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/conftest_checkpointer.py +0 -0
  22. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/conftest_store.py +0 -0
  23. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/memory_assert.py +0 -0
  24. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/messages.py +0 -0
  25. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/test_react_agent_graph.py +0 -0
  26. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/test_tool_node.py +0 -0
  27. {langgraph_prebuilt-0.2.1 → langgraph_prebuilt-0.2.3}/tests/test_validation_node.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-prebuilt
3
- Version: 0.2.1
3
+ Version: 0.2.3
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
@@ -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)
@@ -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
 
@@ -727,6 +729,10 @@ def create_react_agent(
727
729
  ]
728
730
 
729
731
  if pending_tool_calls:
732
+ pending_tool_calls = [
733
+ tool_node.inject_tool_args(call, state, store) # type: ignore[arg-type]
734
+ for call in pending_tool_calls
735
+ ]
730
736
  return [Send("tools", [tool_call]) for tool_call in pending_tool_calls]
731
737
  elif isinstance(messages[-1], ToolMessage):
732
738
  return entrypoint
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "langgraph-prebuilt"
7
- version = "0.2.1"
7
+ version = "0.2.3"
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"
@@ -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
@@ -275,7 +275,8 @@ async def test_prompt_with_store_async():
275
275
 
276
276
  @pytest.mark.parametrize("tool_style", ["openai", "anthropic"])
277
277
  @pytest.mark.parametrize("version", REACT_TOOL_CALL_VERSIONS)
278
- def test_model_with_tools(tool_style: str, version: str):
278
+ @pytest.mark.parametrize("include_builtin", [True, False])
279
+ def test_model_with_tools(tool_style: str, version: str, include_builtin: bool):
279
280
  model = FakeToolCallingModel(tool_style=tool_style)
280
281
 
281
282
  @dec_tool
@@ -288,10 +289,27 @@ def test_model_with_tools(tool_style: str, version: str):
288
289
  """Tool 2 docstring."""
289
290
  return f"Tool 2: {some_val}"
290
291
 
292
+ tools = [tool1, tool2]
293
+ if include_builtin:
294
+ tools.append(
295
+ {
296
+ "type": "mcp",
297
+ "server_label": "atest_sever",
298
+ "server_url": "https://some.mcp.somewhere.com/sse",
299
+ "headers": {"foo": "bar"},
300
+ "allowed_tools": [
301
+ "mcp_tool_1",
302
+ "set_active_account",
303
+ "get_url_markdown",
304
+ "get_url_screenshot",
305
+ ],
306
+ "require_approval": "never",
307
+ }
308
+ )
291
309
  # check valid agent constructor
292
310
  agent = create_react_agent(
293
- model.bind_tools([tool1, tool2]),
294
- [tool1, tool2],
311
+ model.bind_tools(tools),
312
+ tools,
295
313
  version=version,
296
314
  )
297
315
  result = agent.nodes["tools"].invoke(
@@ -1540,3 +1558,63 @@ def test_post_model_hook_with_structured_output() -> None:
1540
1558
  }
1541
1559
  },
1542
1560
  ]
1561
+
1562
+
1563
+ @pytest.mark.parametrize(
1564
+ "state_schema", [AgentStateExtraKey, AgentStateExtraKeyPydantic]
1565
+ )
1566
+ def test_create_react_agent_inject_vars_with_post_model_hook(
1567
+ state_schema: StateSchemaType,
1568
+ ) -> None:
1569
+ store = InMemoryStore()
1570
+ namespace = ("test",)
1571
+ store.put(namespace, "test_key", {"bar": 3})
1572
+
1573
+ if issubclass(state_schema, AgentStatePydantic):
1574
+
1575
+ def tool1(
1576
+ some_val: int,
1577
+ state: Annotated[AgentStateExtraKeyPydantic, InjectedState],
1578
+ store: Annotated[BaseStore, InjectedStore()],
1579
+ ) -> str:
1580
+ """Tool 1 docstring."""
1581
+ store_val = store.get(namespace, "test_key").value["bar"]
1582
+ return some_val + state.foo + store_val
1583
+ else:
1584
+
1585
+ def tool1(
1586
+ some_val: int,
1587
+ state: Annotated[dict, InjectedState],
1588
+ store: Annotated[BaseStore, InjectedStore()],
1589
+ ) -> str:
1590
+ """Tool 1 docstring."""
1591
+ store_val = store.get(namespace, "test_key").value["bar"]
1592
+ return some_val + state["foo"] + store_val
1593
+
1594
+ tool_call = {
1595
+ "name": "tool1",
1596
+ "args": {"some_val": 1},
1597
+ "id": "some 0",
1598
+ "type": "tool_call",
1599
+ }
1600
+
1601
+ def post_model_hook(state: dict) -> None:
1602
+ return
1603
+
1604
+ model = FakeToolCallingModel(tool_calls=[[tool_call], []])
1605
+ agent = create_react_agent(
1606
+ model,
1607
+ [tool1],
1608
+ state_schema=state_schema,
1609
+ store=store,
1610
+ post_model_hook=post_model_hook,
1611
+ )
1612
+ input_message = HumanMessage("hi")
1613
+ result = agent.invoke({"messages": [input_message], "foo": 2})
1614
+ assert result["messages"] == [
1615
+ input_message,
1616
+ AIMessage(content="hi", tool_calls=[tool_call], id="0"),
1617
+ _AnyIdToolMessage(content="6", name="tool1", tool_call_id="some 0"),
1618
+ AIMessage("hi-hi-6", id="1"),
1619
+ ]
1620
+ assert result["foo"] == 2