langchain 1.0.2__tar.gz → 1.0.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.
Potentially problematic release.
This version of langchain might be problematic. Click here for more details.
- {langchain-1.0.2 → langchain-1.0.3}/.gitignore +2 -0
- {langchain-1.0.2 → langchain-1.0.3}/Makefile +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/PKG-INFO +3 -3
- {langchain-1.0.2 → langchain-1.0.3}/README.md +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/__init__.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/factory.py +62 -31
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/context_editing.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/model_call_limit.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/model_fallback.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/shell_tool.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/summarization.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/tool_call_limit.py +2 -49
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/tool_emulator.py +5 -7
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/tool_retry.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/types.py +3 -2
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/structured_output.py +8 -2
- {langchain-1.0.2 → langchain-1.0.3}/langchain/chat_models/base.py +60 -33
- {langchain-1.0.2 → langchain-1.0.3}/langchain/embeddings/__init__.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/langchain/embeddings/base.py +21 -15
- {langchain-1.0.2 → langchain-1.0.3}/langchain/messages/__init__.py +7 -1
- langchain-1.0.3/langchain/tools/tool_node.py +20 -0
- {langchain-1.0.2 → langchain-1.0.3}/pyproject.toml +3 -2
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/agents/middleware/test_shell_tool_integration.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_override_methods.py +1 -1
- langchain-1.0.3/tests/unit_tests/agents/middleware/test_structured_output_retry.py +369 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_tool_emulator.py +1 -3
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_wrap_model_call_middleware.py +1 -2
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_wrap_tool_call_decorator.py +1 -1
- langchain-1.0.3/tests/unit_tests/agents/test_create_agent_tool_validation.py +370 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_middleware_tools.py +3 -3
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_on_tool_call_middleware.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_response_format.py +51 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_sync_async_tool_wrapper_composition.py +1 -1
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_tool_call_limit.py +0 -78
- {langchain-1.0.2 → langchain-1.0.3}/uv.lock +12 -12
- langchain-1.0.2/langchain/tools/tool_node.py +0 -1786
- langchain-1.0.2/tests/unit_tests/agents/test_tool_node.py +0 -1716
- langchain-1.0.2/tests/unit_tests/agents/test_tool_node_interceptor_unregistered.py +0 -571
- langchain-1.0.2/tests/unit_tests/agents/test_tool_node_validation_error_filtering.py +0 -678
- langchain-1.0.2/tests/unit_tests/tools/test_on_tool_call.py +0 -1287
- {langchain-1.0.2 → langchain-1.0.3}/LICENSE +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/extended_testing_deps.txt +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/_execution.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/_redaction.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/file_search.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/human_in_the_loop.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/pii.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/todo.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/agents/middleware/tool_selection.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/chat_models/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/py.typed +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/rate_limiters/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/langchain/tools/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/scripts/check_imports.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/agents/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/agents/middleware/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/agents/test_response_format.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/cache/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/cache/fake_embeddings.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/chat_models/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/chat_models/test_base.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/conftest.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/embeddings/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/embeddings/test_base.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/integration_tests/test_compile.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/__snapshots__/test_middleware_agent.ambr +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/__snapshots__/test_middleware_decorators.ambr +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/__snapshots__/test_return_direct_graph.ambr +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/any_str.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/compose-postgres.yml +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/compose-redis.yml +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/conftest.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/conftest_checkpointer.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/conftest_store.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/memory_assert.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/messages.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_before_after_agent.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_file_search.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_llm_tool_selection.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_shell_execution_policies.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_shell_tool.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_tool_retry.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/middleware/test_wrap_model_call_decorator.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/model.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/specifications/responses.json +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/specifications/return_direct.json +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_context_editing_middleware.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_handler_composition.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_injected_runtime_create_agent.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_middleware_agent.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_middleware_decorators.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_model_fallback_middleware.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_pii_middleware.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_react_agent.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_responses.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_responses_spec.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_return_direct_graph.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_return_direct_spec.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_state_schema.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/test_todo_middleware.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/agents/utils.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/chat_models/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/chat_models/test_chat_models.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/conftest.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/embeddings/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/embeddings/test_base.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/embeddings/test_imports.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/stubs.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/test_dependencies.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/test_imports.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/test_pytest_config.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/tools/__init__.py +0 -0
- {langchain-1.0.2 → langchain-1.0.3}/tests/unit_tests/tools/test_imports.py +0 -0
|
@@ -28,7 +28,7 @@ coverage:
|
|
|
28
28
|
$(TEST_FILE)
|
|
29
29
|
|
|
30
30
|
test:
|
|
31
|
-
make start_services && LANGGRAPH_TEST_FAST=0 uv run --group test pytest -n auto --disable-socket --allow-unix-socket $(TEST_FILE) --cov-report term-missing:skip-covered; \
|
|
31
|
+
make start_services && LANGGRAPH_TEST_FAST=0 uv run --no-sync --active --group test pytest -n auto --disable-socket --allow-unix-socket $(TEST_FILE) --cov-report term-missing:skip-covered; \
|
|
32
32
|
EXIT_CODE=$$?; \
|
|
33
33
|
make stop_services; \
|
|
34
34
|
exit $$EXIT_CODE
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langchain
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: Building applications with LLMs through composability
|
|
5
5
|
Project-URL: Homepage, https://docs.langchain.com/
|
|
6
6
|
Project-URL: Documentation, https://reference.langchain.com/python/langchain/langchain/
|
|
@@ -13,7 +13,7 @@ License: MIT
|
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Python: <4.0.0,>=3.10.0
|
|
15
15
|
Requires-Dist: langchain-core<2.0.0,>=1.0.0
|
|
16
|
-
Requires-Dist: langgraph<1.1.0,>=1.0.
|
|
16
|
+
Requires-Dist: langgraph<1.1.0,>=1.0.2
|
|
17
17
|
Requires-Dist: pydantic<3.0.0,>=2.7.4
|
|
18
18
|
Provides-Extra: anthropic
|
|
19
19
|
Requires-Dist: langchain-anthropic; extra == 'anthropic'
|
|
@@ -75,7 +75,7 @@ LangChain [agents](https://docs.langchain.com/oss/python/langchain/agents) are b
|
|
|
75
75
|
|
|
76
76
|
## 📖 Documentation
|
|
77
77
|
|
|
78
|
-
For full documentation, see the [API reference](https://reference.langchain.com/python/
|
|
78
|
+
For full documentation, see the [API reference](https://reference.langchain.com/python/langchain/langchain/).
|
|
79
79
|
|
|
80
80
|
## 📕 Releases & Versioning
|
|
81
81
|
|
|
@@ -26,7 +26,7 @@ LangChain [agents](https://docs.langchain.com/oss/python/langchain/agents) are b
|
|
|
26
26
|
|
|
27
27
|
## 📖 Documentation
|
|
28
28
|
|
|
29
|
-
For full documentation, see the [API reference](https://reference.langchain.com/python/
|
|
29
|
+
For full documentation, see the [API reference](https://reference.langchain.com/python/langchain/langchain/).
|
|
30
30
|
|
|
31
31
|
## 📕 Releases & Versioning
|
|
32
32
|
|
|
@@ -11,6 +11,7 @@ from langchain_core.tools import BaseTool
|
|
|
11
11
|
from langgraph._internal._runnable import RunnableCallable
|
|
12
12
|
from langgraph.constants import END, START
|
|
13
13
|
from langgraph.graph.state import StateGraph
|
|
14
|
+
from langgraph.prebuilt.tool_node import ToolCallWithContext, ToolNode
|
|
14
15
|
from langgraph.runtime import Runtime # noqa: TC002
|
|
15
16
|
from langgraph.types import Command, Send
|
|
16
17
|
from langgraph.typing import ContextT # noqa: TC002
|
|
@@ -33,11 +34,11 @@ from langchain.agents.structured_output import (
|
|
|
33
34
|
ProviderStrategy,
|
|
34
35
|
ProviderStrategyBinding,
|
|
35
36
|
ResponseFormat,
|
|
37
|
+
StructuredOutputError,
|
|
36
38
|
StructuredOutputValidationError,
|
|
37
39
|
ToolStrategy,
|
|
38
40
|
)
|
|
39
41
|
from langchain.chat_models import init_chat_model
|
|
40
|
-
from langchain.tools.tool_node import ToolCallWithContext, _ToolNode
|
|
41
42
|
|
|
42
43
|
if TYPE_CHECKING:
|
|
43
44
|
from collections.abc import Awaitable, Callable, Sequence
|
|
@@ -48,7 +49,7 @@ if TYPE_CHECKING:
|
|
|
48
49
|
from langgraph.store.base import BaseStore
|
|
49
50
|
from langgraph.types import Checkpointer
|
|
50
51
|
|
|
51
|
-
from langchain.
|
|
52
|
+
from langchain.agents.middleware.types import ToolCallRequest, ToolCallWrapper
|
|
52
53
|
|
|
53
54
|
STRUCTURED_OUTPUT_ERROR_TEMPLATE = "Error: {error}\n Please fix your mistakes."
|
|
54
55
|
|
|
@@ -529,44 +530,65 @@ def create_agent( # noqa: PLR0915
|
|
|
529
530
|
|
|
530
531
|
Args:
|
|
531
532
|
model: The language model for the agent. Can be a string identifier
|
|
532
|
-
(e.g., `"openai:gpt-4"`) or a chat model instance (e.g.,
|
|
533
|
+
(e.g., `"openai:gpt-4"`) or a direct chat model instance (e.g.,
|
|
534
|
+
[`ChatOpenAI`][langchain_openai.ChatOpenAI] or other another
|
|
535
|
+
[chat model](https://docs.langchain.com/oss/python/integrations/chat)).
|
|
536
|
+
|
|
533
537
|
For a full list of supported model strings, see
|
|
534
538
|
[`init_chat_model`][langchain.chat_models.init_chat_model(model_provider)].
|
|
535
|
-
tools: A list of tools, `dicts`, or `Callable`.
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
+
tools: A list of tools, `dicts`, or `Callable`.
|
|
540
|
+
|
|
541
|
+
If `None` or an empty list, the agent will consist of a model node without a
|
|
542
|
+
tool calling loop.
|
|
543
|
+
system_prompt: An optional system prompt for the LLM.
|
|
544
|
+
|
|
545
|
+
Prompts are converted to a
|
|
546
|
+
[`SystemMessage`][langchain.messages.SystemMessage] and added to the
|
|
547
|
+
beginning of the message list.
|
|
539
548
|
middleware: A sequence of middleware instances to apply to the agent.
|
|
540
|
-
|
|
549
|
+
|
|
550
|
+
Middleware can intercept and modify agent behavior at various stages. See
|
|
551
|
+
the [full guide](https://docs.langchain.com/oss/python/langchain/middleware).
|
|
541
552
|
response_format: An optional configuration for structured responses.
|
|
553
|
+
|
|
542
554
|
Can be a `ToolStrategy`, `ProviderStrategy`, or a Pydantic model class.
|
|
555
|
+
|
|
543
556
|
If provided, the agent will handle structured output during the
|
|
544
557
|
conversation flow. Raw schemas will be wrapped in an appropriate strategy
|
|
545
558
|
based on model capabilities.
|
|
546
559
|
state_schema: An optional `TypedDict` schema that extends `AgentState`.
|
|
560
|
+
|
|
547
561
|
When provided, this schema is used instead of `AgentState` as the base
|
|
548
562
|
schema for merging with middleware state schemas. This allows users to
|
|
549
563
|
add custom state fields without needing to create custom middleware.
|
|
550
|
-
Generally, it's recommended to use state_schema extensions via middleware
|
|
564
|
+
Generally, it's recommended to use `state_schema` extensions via middleware
|
|
551
565
|
to keep relevant extensions scoped to corresponding hooks / tools.
|
|
566
|
+
|
|
552
567
|
The schema must be a subclass of `AgentState[ResponseT]`.
|
|
553
568
|
context_schema: An optional schema for runtime context.
|
|
554
|
-
checkpointer: An optional checkpoint saver object.
|
|
555
|
-
|
|
556
|
-
(e.g.,
|
|
557
|
-
|
|
558
|
-
|
|
569
|
+
checkpointer: An optional checkpoint saver object.
|
|
570
|
+
|
|
571
|
+
Used for persisting the state of the graph (e.g., as chat memory) for a
|
|
572
|
+
single thread (e.g., a single conversation).
|
|
573
|
+
store: An optional store object.
|
|
574
|
+
|
|
575
|
+
Used for persisting data across multiple threads (e.g., multiple
|
|
576
|
+
conversations / users).
|
|
559
577
|
interrupt_before: An optional list of node names to interrupt before.
|
|
578
|
+
|
|
560
579
|
Useful if you want to add a user confirmation or other interrupt
|
|
561
580
|
before taking an action.
|
|
562
581
|
interrupt_after: An optional list of node names to interrupt after.
|
|
582
|
+
|
|
563
583
|
Useful if you want to return directly or run additional processing
|
|
564
584
|
on an output.
|
|
565
|
-
debug: Whether to enable verbose logging for graph execution.
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
585
|
+
debug: Whether to enable verbose logging for graph execution.
|
|
586
|
+
|
|
587
|
+
When enabled, prints detailed information about each node execution, state
|
|
588
|
+
updates, and transitions during agent runtime. Useful for debugging
|
|
589
|
+
middleware behavior and understanding agent execution flow.
|
|
569
590
|
name: An optional name for the `CompiledStateGraph`.
|
|
591
|
+
|
|
570
592
|
This name will be automatically used when adding the agent graph to
|
|
571
593
|
another graph as a subgraph node - particularly useful for building
|
|
572
594
|
multi-agent systems.
|
|
@@ -576,11 +598,12 @@ def create_agent( # noqa: PLR0915
|
|
|
576
598
|
A compiled `StateGraph` that can be used for chat interactions.
|
|
577
599
|
|
|
578
600
|
The agent node calls the language model with the messages list (after applying
|
|
579
|
-
the system prompt). If the resulting `AIMessage`
|
|
580
|
-
will then call the tools. The tools node executes
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
601
|
+
the system prompt). If the resulting [`AIMessage`][langchain.messages.AIMessage]
|
|
602
|
+
contains `tool_calls`, the graph will then call the tools. The tools node executes
|
|
603
|
+
the tools and adds the responses to the messages list as
|
|
604
|
+
[`ToolMessage`][langchain.messages.ToolMessage] objects. The agent node then calls
|
|
605
|
+
the language model again. The process repeats until no more `tool_calls` are present
|
|
606
|
+
in the response. The agent then returns the full list of messages.
|
|
584
607
|
|
|
585
608
|
Example:
|
|
586
609
|
```python
|
|
@@ -675,7 +698,7 @@ def create_agent( # noqa: PLR0915
|
|
|
675
698
|
awrap_tool_call_wrapper = _chain_async_tool_call_wrappers(async_wrappers)
|
|
676
699
|
|
|
677
700
|
# Setup tools
|
|
678
|
-
tool_node:
|
|
701
|
+
tool_node: ToolNode | None = None
|
|
679
702
|
# Extract built-in provider tools (dict format) and regular tools (BaseTool/callables)
|
|
680
703
|
built_in_tools = [t for t in tools if isinstance(t, dict)]
|
|
681
704
|
regular_tools = [t for t in tools if not isinstance(t, dict)]
|
|
@@ -685,7 +708,7 @@ def create_agent( # noqa: PLR0915
|
|
|
685
708
|
|
|
686
709
|
# Only create ToolNode if we have client-side tools
|
|
687
710
|
tool_node = (
|
|
688
|
-
|
|
711
|
+
ToolNode(
|
|
689
712
|
tools=available_tools,
|
|
690
713
|
wrap_tool_call=wrap_tool_call_wrapper,
|
|
691
714
|
awrap_tool_call=awrap_tool_call_wrapper,
|
|
@@ -797,8 +820,16 @@ def create_agent( # noqa: PLR0915
|
|
|
797
820
|
provider_strategy_binding = ProviderStrategyBinding.from_schema_spec(
|
|
798
821
|
effective_response_format.schema_spec
|
|
799
822
|
)
|
|
800
|
-
|
|
801
|
-
|
|
823
|
+
try:
|
|
824
|
+
structured_response = provider_strategy_binding.parse(output)
|
|
825
|
+
except Exception as exc: # noqa: BLE001
|
|
826
|
+
schema_name = getattr(
|
|
827
|
+
effective_response_format.schema_spec.schema, "__name__", "response_format"
|
|
828
|
+
)
|
|
829
|
+
validation_error = StructuredOutputValidationError(schema_name, exc, output)
|
|
830
|
+
raise validation_error
|
|
831
|
+
else:
|
|
832
|
+
return {"messages": [output], "structured_response": structured_response}
|
|
802
833
|
return {"messages": [output]}
|
|
803
834
|
|
|
804
835
|
# Handle structured output with tool strategy
|
|
@@ -812,11 +843,11 @@ def create_agent( # noqa: PLR0915
|
|
|
812
843
|
]
|
|
813
844
|
|
|
814
845
|
if structured_tool_calls:
|
|
815
|
-
exception:
|
|
846
|
+
exception: StructuredOutputError | None = None
|
|
816
847
|
if len(structured_tool_calls) > 1:
|
|
817
848
|
# Handle multiple structured outputs error
|
|
818
849
|
tool_names = [tc["name"] for tc in structured_tool_calls]
|
|
819
|
-
exception = MultipleStructuredOutputsError(tool_names)
|
|
850
|
+
exception = MultipleStructuredOutputsError(tool_names, output)
|
|
820
851
|
should_retry, error_message = _handle_structured_output_error(
|
|
821
852
|
exception, effective_response_format
|
|
822
853
|
)
|
|
@@ -858,7 +889,7 @@ def create_agent( # noqa: PLR0915
|
|
|
858
889
|
"structured_response": structured_response,
|
|
859
890
|
}
|
|
860
891
|
except Exception as exc: # noqa: BLE001
|
|
861
|
-
exception = StructuredOutputValidationError(tool_call["name"], exc)
|
|
892
|
+
exception = StructuredOutputValidationError(tool_call["name"], exc, output)
|
|
862
893
|
should_retry, error_message = _handle_structured_output_error(
|
|
863
894
|
exception, effective_response_format
|
|
864
895
|
)
|
|
@@ -1491,7 +1522,7 @@ def _make_model_to_model_edge(
|
|
|
1491
1522
|
|
|
1492
1523
|
def _make_tools_to_model_edge(
|
|
1493
1524
|
*,
|
|
1494
|
-
tool_node:
|
|
1525
|
+
tool_node: ToolNode,
|
|
1495
1526
|
model_destination: str,
|
|
1496
1527
|
structured_output_tools: dict[str, OutputToolBinding],
|
|
1497
1528
|
end_destination: str,
|
|
@@ -182,7 +182,7 @@ class ClearToolUsesEdit(ContextEdit):
|
|
|
182
182
|
|
|
183
183
|
|
|
184
184
|
class ContextEditingMiddleware(AgentMiddleware):
|
|
185
|
-
"""
|
|
185
|
+
"""Automatically prunes tool results to manage context size.
|
|
186
186
|
|
|
187
187
|
The middleware applies a sequence of edits when the total input token count
|
|
188
188
|
exceeds configured thresholds. Currently the `ClearToolUsesEdit` strategy is
|
|
@@ -87,7 +87,7 @@ class ModelCallLimitExceededError(Exception):
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
class ModelCallLimitMiddleware(AgentMiddleware[ModelCallLimitState, Any]):
|
|
90
|
-
"""
|
|
90
|
+
"""Tracks model call counts and enforces limits.
|
|
91
91
|
|
|
92
92
|
This middleware monitors the number of model calls made during agent execution
|
|
93
93
|
and can terminate the agent when specified limits are reached. It supports
|
|
@@ -31,7 +31,7 @@ class ModelFallbackMiddleware(AgentMiddleware):
|
|
|
31
31
|
|
|
32
32
|
fallback = ModelFallbackMiddleware(
|
|
33
33
|
"openai:gpt-4o-mini", # Try first on error
|
|
34
|
-
"anthropic:claude-
|
|
34
|
+
"anthropic:claude-sonnet-4-5-20250929", # Then this
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
agent = create_agent(
|
|
@@ -45,7 +45,7 @@ if TYPE_CHECKING:
|
|
|
45
45
|
from langgraph.runtime import Runtime
|
|
46
46
|
from langgraph.types import Command
|
|
47
47
|
|
|
48
|
-
from langchain.
|
|
48
|
+
from langchain.agents.middleware.types import ToolCallRequest
|
|
49
49
|
|
|
50
50
|
LOGGER = logging.getLogger(__name__)
|
|
51
51
|
_DONE_MARKER_PREFIX = "__LC_SHELL_DONE__"
|
|
@@ -60,7 +60,7 @@ _SEARCH_RANGE_FOR_TOOL_PAIRS = 5
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
class SummarizationMiddleware(AgentMiddleware):
|
|
63
|
-
"""
|
|
63
|
+
"""Summarizes conversation history when token limits are approached.
|
|
64
64
|
|
|
65
65
|
This middleware monitors message token counts and automatically summarizes older
|
|
66
66
|
messages when a threshold is reached, preserving recent messages and maintaining
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import TYPE_CHECKING, Annotated, Any, Literal
|
|
6
6
|
|
|
7
|
-
from langchain_core.messages import AIMessage
|
|
7
|
+
from langchain_core.messages import AIMessage
|
|
8
8
|
from langgraph.channels.untracked_value import UntrackedValue
|
|
9
9
|
from typing_extensions import NotRequired
|
|
10
10
|
|
|
@@ -33,53 +33,6 @@ class ToolCallLimitState(AgentState):
|
|
|
33
33
|
run_tool_call_count: NotRequired[Annotated[dict[str, int], UntrackedValue, PrivateStateAttr]]
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
def _count_tool_calls_in_messages(messages: list[AnyMessage], tool_name: str | None = None) -> int:
|
|
37
|
-
"""Count tool calls in a list of messages.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
messages: List of messages to count tool calls in.
|
|
41
|
-
tool_name: If specified, only count calls to this specific tool.
|
|
42
|
-
If `None`, count all tool calls.
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
The total number of tool calls (optionally filtered by tool_name).
|
|
46
|
-
"""
|
|
47
|
-
count = 0
|
|
48
|
-
for message in messages:
|
|
49
|
-
if isinstance(message, AIMessage) and message.tool_calls:
|
|
50
|
-
if tool_name is None:
|
|
51
|
-
# Count all tool calls
|
|
52
|
-
count += len(message.tool_calls)
|
|
53
|
-
else:
|
|
54
|
-
# Count only calls to the specified tool
|
|
55
|
-
count += sum(1 for tc in message.tool_calls if tc["name"] == tool_name)
|
|
56
|
-
return count
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _get_run_messages(messages: list[AnyMessage]) -> list[AnyMessage]:
|
|
60
|
-
"""Get messages from the current run (after the last HumanMessage).
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
messages: Full list of messages.
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
Messages from the current run (after last HumanMessage).
|
|
67
|
-
"""
|
|
68
|
-
# Find the last HumanMessage
|
|
69
|
-
last_human_index = -1
|
|
70
|
-
for i in range(len(messages) - 1, -1, -1):
|
|
71
|
-
if isinstance(messages[i], HumanMessage):
|
|
72
|
-
last_human_index = i
|
|
73
|
-
break
|
|
74
|
-
|
|
75
|
-
# If no HumanMessage found, return all messages
|
|
76
|
-
if last_human_index == -1:
|
|
77
|
-
return messages
|
|
78
|
-
|
|
79
|
-
# Return messages after the last HumanMessage
|
|
80
|
-
return messages[last_human_index + 1 :]
|
|
81
|
-
|
|
82
|
-
|
|
83
36
|
def _build_tool_limit_exceeded_message(
|
|
84
37
|
thread_count: int,
|
|
85
38
|
run_count: int,
|
|
@@ -146,7 +99,7 @@ class ToolCallLimitExceededError(Exception):
|
|
|
146
99
|
|
|
147
100
|
|
|
148
101
|
class ToolCallLimitMiddleware(AgentMiddleware[ToolCallLimitState, Any]):
|
|
149
|
-
"""
|
|
102
|
+
"""Tracks tool call counts and enforces limits.
|
|
150
103
|
|
|
151
104
|
This middleware monitors the number of tool calls made during agent execution
|
|
152
105
|
and can terminate the agent when specified limits are reached. It supports
|
|
@@ -15,12 +15,12 @@ if TYPE_CHECKING:
|
|
|
15
15
|
|
|
16
16
|
from langgraph.types import Command
|
|
17
17
|
|
|
18
|
+
from langchain.agents.middleware.types import ToolCallRequest
|
|
18
19
|
from langchain.tools import BaseTool
|
|
19
|
-
from langchain.tools.tool_node import ToolCallRequest
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class LLMToolEmulator(AgentMiddleware):
|
|
23
|
-
"""
|
|
23
|
+
"""Emulates specified tools using an LLM instead of executing them.
|
|
24
24
|
|
|
25
25
|
This middleware allows selective emulation of tools for testing purposes.
|
|
26
26
|
By default (when tools=None), all tools are emulated. You can specify which
|
|
@@ -47,9 +47,7 @@ class LLMToolEmulator(AgentMiddleware):
|
|
|
47
47
|
|
|
48
48
|
Use a custom model for emulation:
|
|
49
49
|
```python
|
|
50
|
-
middleware = LLMToolEmulator(
|
|
51
|
-
tools=["get_weather"], model="anthropic:claude-3-5-sonnet-latest"
|
|
52
|
-
)
|
|
50
|
+
middleware = LLMToolEmulator(tools=["get_weather"], model="anthropic:claude-sonnet-4-5")
|
|
53
51
|
```
|
|
54
52
|
|
|
55
53
|
Emulate specific tools by passing tool instances:
|
|
@@ -71,7 +69,7 @@ class LLMToolEmulator(AgentMiddleware):
|
|
|
71
69
|
If None (default), ALL tools will be emulated.
|
|
72
70
|
If empty list, no tools will be emulated.
|
|
73
71
|
model: Model to use for emulation.
|
|
74
|
-
Defaults to "anthropic:claude-
|
|
72
|
+
Defaults to "anthropic:claude-sonnet-4-5".
|
|
75
73
|
Can be a model identifier string or BaseChatModel instance.
|
|
76
74
|
"""
|
|
77
75
|
super().__init__()
|
|
@@ -91,7 +89,7 @@ class LLMToolEmulator(AgentMiddleware):
|
|
|
91
89
|
|
|
92
90
|
# Initialize emulator model
|
|
93
91
|
if model is None:
|
|
94
|
-
self.model = init_chat_model("anthropic:claude-
|
|
92
|
+
self.model = init_chat_model("anthropic:claude-sonnet-4-5", temperature=1)
|
|
95
93
|
elif isinstance(model, BaseChatModel):
|
|
96
94
|
self.model = model
|
|
97
95
|
else:
|
|
@@ -16,8 +16,8 @@ if TYPE_CHECKING:
|
|
|
16
16
|
|
|
17
17
|
from langgraph.types import Command
|
|
18
18
|
|
|
19
|
+
from langchain.agents.middleware.types import ToolCallRequest
|
|
19
20
|
from langchain.tools import BaseTool
|
|
20
|
-
from langchain.tools.tool_node import ToolCallRequest
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class ToolRetryMiddleware(AgentMiddleware):
|
|
@@ -19,14 +19,13 @@ from typing import (
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
20
20
|
from collections.abc import Awaitable
|
|
21
21
|
|
|
22
|
-
from langchain.tools.tool_node import ToolCallRequest
|
|
23
|
-
|
|
24
22
|
# Needed as top level import for Pydantic schema generation on AgentState
|
|
25
23
|
from typing import TypeAlias
|
|
26
24
|
|
|
27
25
|
from langchain_core.messages import AIMessage, AnyMessage, BaseMessage, ToolMessage # noqa: TC002
|
|
28
26
|
from langgraph.channels.ephemeral_value import EphemeralValue
|
|
29
27
|
from langgraph.graph.message import add_messages
|
|
28
|
+
from langgraph.prebuilt.tool_node import ToolCallRequest, ToolCallWrapper
|
|
30
29
|
from langgraph.types import Command # noqa: TC002
|
|
31
30
|
from langgraph.typing import ContextT
|
|
32
31
|
from typing_extensions import NotRequired, Required, TypedDict, TypeVar, Unpack
|
|
@@ -45,6 +44,8 @@ __all__ = [
|
|
|
45
44
|
"ModelRequest",
|
|
46
45
|
"ModelResponse",
|
|
47
46
|
"OmitFromSchema",
|
|
47
|
+
"ToolCallRequest",
|
|
48
|
+
"ToolCallWrapper",
|
|
48
49
|
"after_agent",
|
|
49
50
|
"after_model",
|
|
50
51
|
"before_agent",
|
|
@@ -34,17 +34,21 @@ SchemaKind = Literal["pydantic", "dataclass", "typeddict", "json_schema"]
|
|
|
34
34
|
class StructuredOutputError(Exception):
|
|
35
35
|
"""Base class for structured output errors."""
|
|
36
36
|
|
|
37
|
+
ai_message: AIMessage
|
|
38
|
+
|
|
37
39
|
|
|
38
40
|
class MultipleStructuredOutputsError(StructuredOutputError):
|
|
39
41
|
"""Raised when model returns multiple structured output tool calls when only one is expected."""
|
|
40
42
|
|
|
41
|
-
def __init__(self, tool_names: list[str]) -> None:
|
|
43
|
+
def __init__(self, tool_names: list[str], ai_message: AIMessage) -> None:
|
|
42
44
|
"""Initialize `MultipleStructuredOutputsError`.
|
|
43
45
|
|
|
44
46
|
Args:
|
|
45
47
|
tool_names: The names of the tools called for structured output.
|
|
48
|
+
ai_message: The AI message that contained the invalid multiple tool calls.
|
|
46
49
|
"""
|
|
47
50
|
self.tool_names = tool_names
|
|
51
|
+
self.ai_message = ai_message
|
|
48
52
|
|
|
49
53
|
super().__init__(
|
|
50
54
|
"Model incorrectly returned multiple structured responses "
|
|
@@ -55,15 +59,17 @@ class MultipleStructuredOutputsError(StructuredOutputError):
|
|
|
55
59
|
class StructuredOutputValidationError(StructuredOutputError):
|
|
56
60
|
"""Raised when structured output tool call arguments fail to parse according to the schema."""
|
|
57
61
|
|
|
58
|
-
def __init__(self, tool_name: str, source: Exception) -> None:
|
|
62
|
+
def __init__(self, tool_name: str, source: Exception, ai_message: AIMessage) -> None:
|
|
59
63
|
"""Initialize `StructuredOutputValidationError`.
|
|
60
64
|
|
|
61
65
|
Args:
|
|
62
66
|
tool_name: The name of the tool that failed.
|
|
63
67
|
source: The exception that occurred.
|
|
68
|
+
ai_message: The AI message that contained the invalid structured output.
|
|
64
69
|
"""
|
|
65
70
|
self.tool_name = tool_name
|
|
66
71
|
self.source = source
|
|
72
|
+
self.ai_message = ai_message
|
|
67
73
|
super().__init__(f"Failed to parse structured output for tool '{tool_name}': {source}.")
|
|
68
74
|
|
|
69
75
|
|