langchain 1.0.0a7__tar.gz → 1.0.0a8__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.0a7 → langchain-1.0.0a8}/PKG-INFO +1 -1
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/middleware/__init__.py +0 -2
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/middleware/human_in_the_loop.py +23 -19
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/middleware/prompt_caching.py +22 -5
- langchain-1.0.0a8/langchain/agents/middleware/types.py +543 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/middleware_agent.py +26 -22
- {langchain-1.0.0a7 → langchain-1.0.0a8}/pyproject.toml +1 -1
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/__snapshots__/test_middleware_agent.ambr +40 -119
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/test_middleware_agent.py +149 -88
- langchain-1.0.0a8/tests/unit_tests/agents/test_middleware_decorators.py +152 -0
- langchain-1.0.0a7/langchain/agents/middleware/dynamic_system_prompt.py +0 -105
- langchain-1.0.0a7/langchain/agents/middleware/types.py +0 -119
- {langchain-1.0.0a7 → langchain-1.0.0a8}/LICENSE +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/README.md +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/_internal/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/_internal/_documents.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/_internal/_lazy_import.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/_internal/_prompts.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/_internal/_typing.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/_internal/_utils.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/_internal/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/_internal/_typing.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/middleware/summarization.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/react_agent.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/structured_output.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/agents/tool_node.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/chat_models/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/chat_models/base.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/documents/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/embeddings/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/embeddings/base.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/embeddings/cache.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/globals.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/py.typed +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/storage/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/storage/encoder_backed.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/storage/exceptions.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/storage/in_memory.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/text_splitter.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/langchain/tools/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/agents/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/agents/test_response_format.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/cache/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/cache/fake_embeddings.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/chat_models/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/chat_models/test_base.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/conftest.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/embeddings/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/embeddings/test_base.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/integration_tests/test_compile.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/__snapshots__/test_react_agent_graph.ambr +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/any_str.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/compose-postgres.yml +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/compose-redis.yml +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/conftest.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/conftest_checkpointer.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/conftest_store.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/memory_assert.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/messages.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/model.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/specifications/responses.json +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/specifications/return_direct.json +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/test_react_agent.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/test_react_agent_graph.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/test_response_format.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/test_responses.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/test_responses_spec.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/test_return_direct_spec.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/test_tool_node.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/agents/utils.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/chat_models/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/chat_models/test_chat_models.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/conftest.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/embeddings/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/embeddings/test_base.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/embeddings/test_caching.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/embeddings/test_imports.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/storage/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/storage/test_imports.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/stubs.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/test_dependencies.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/test_imports.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/test_pytest_config.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/tools/__init__.py +0 -0
- {langchain-1.0.0a7 → langchain-1.0.0a8}/tests/unit_tests/tools/test_imports.py +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""Middleware plugins for agents."""
|
|
2
2
|
|
|
3
|
-
from .dynamic_system_prompt import DynamicSystemPromptMiddleware
|
|
4
3
|
from .human_in_the_loop import HumanInTheLoopMiddleware
|
|
5
4
|
from .prompt_caching import AnthropicPromptCachingMiddleware
|
|
6
5
|
from .summarization import SummarizationMiddleware
|
|
@@ -11,7 +10,6 @@ __all__ = [
|
|
|
11
10
|
"AgentState",
|
|
12
11
|
# should move to langchain-anthropic if we decide to keep it
|
|
13
12
|
"AnthropicPromptCachingMiddleware",
|
|
14
|
-
"DynamicSystemPromptMiddleware",
|
|
15
13
|
"HumanInTheLoopMiddleware",
|
|
16
14
|
"ModelRequest",
|
|
17
15
|
"SummarizationMiddleware",
|
|
@@ -112,14 +112,14 @@ class HumanInTheLoopMiddleware(AgentMiddleware):
|
|
|
112
112
|
|
|
113
113
|
def __init__(
|
|
114
114
|
self,
|
|
115
|
-
|
|
115
|
+
interrupt_on: dict[str, bool | ToolConfig],
|
|
116
116
|
*,
|
|
117
117
|
description_prefix: str = "Tool execution requires approval",
|
|
118
118
|
) -> None:
|
|
119
119
|
"""Initialize the human in the loop middleware.
|
|
120
120
|
|
|
121
121
|
Args:
|
|
122
|
-
|
|
122
|
+
interrupt_on: Mapping of tool name to allowed actions.
|
|
123
123
|
If a tool doesn't have an entry, it's auto-approved by default.
|
|
124
124
|
* `True` indicates all actions are allowed: accept, edit, and respond.
|
|
125
125
|
* `False` indicates that the tool is auto-approved.
|
|
@@ -130,7 +130,7 @@ class HumanInTheLoopMiddleware(AgentMiddleware):
|
|
|
130
130
|
"""
|
|
131
131
|
super().__init__()
|
|
132
132
|
resolved_tool_configs: dict[str, ToolConfig] = {}
|
|
133
|
-
for tool_name, tool_config in
|
|
133
|
+
for tool_name, tool_config in interrupt_on.items():
|
|
134
134
|
if isinstance(tool_config, bool):
|
|
135
135
|
if tool_config is True:
|
|
136
136
|
resolved_tool_configs[tool_name] = ToolConfig(
|
|
@@ -138,13 +138,15 @@ class HumanInTheLoopMiddleware(AgentMiddleware):
|
|
|
138
138
|
allow_edit=True,
|
|
139
139
|
allow_respond=True,
|
|
140
140
|
)
|
|
141
|
-
|
|
141
|
+
elif any(
|
|
142
|
+
tool_config.get(x, False) for x in ["allow_accept", "allow_edit", "allow_respond"]
|
|
143
|
+
):
|
|
142
144
|
resolved_tool_configs[tool_name] = tool_config
|
|
143
|
-
self.
|
|
145
|
+
self.interrupt_on = resolved_tool_configs
|
|
144
146
|
self.description_prefix = description_prefix
|
|
145
147
|
|
|
146
148
|
def after_model(self, state: AgentState) -> dict[str, Any] | None: # type: ignore[override]
|
|
147
|
-
"""Trigger
|
|
149
|
+
"""Trigger interrupt flows for relevant tool calls after an AIMessage."""
|
|
148
150
|
messages = state["messages"]
|
|
149
151
|
if not messages:
|
|
150
152
|
return None
|
|
@@ -154,16 +156,16 @@ class HumanInTheLoopMiddleware(AgentMiddleware):
|
|
|
154
156
|
return None
|
|
155
157
|
|
|
156
158
|
# Separate tool calls that need interrupts from those that don't
|
|
157
|
-
|
|
159
|
+
interrupt_tool_calls: list[ToolCall] = []
|
|
158
160
|
auto_approved_tool_calls = []
|
|
159
161
|
|
|
160
162
|
for tool_call in last_ai_msg.tool_calls:
|
|
161
|
-
|
|
163
|
+
interrupt_tool_calls.append(tool_call) if tool_call[
|
|
162
164
|
"name"
|
|
163
|
-
] in self.
|
|
165
|
+
] in self.interrupt_on else auto_approved_tool_calls.append(tool_call)
|
|
164
166
|
|
|
165
167
|
# If no interrupts needed, return early
|
|
166
|
-
if not
|
|
168
|
+
if not interrupt_tool_calls:
|
|
167
169
|
return None
|
|
168
170
|
|
|
169
171
|
# Process all tool calls that require interrupts
|
|
@@ -171,11 +173,11 @@ class HumanInTheLoopMiddleware(AgentMiddleware):
|
|
|
171
173
|
artificial_tool_messages: list[ToolMessage] = []
|
|
172
174
|
|
|
173
175
|
# Create interrupt requests for all tools that need approval
|
|
174
|
-
|
|
175
|
-
for tool_call in
|
|
176
|
+
interrupt_requests: list[HumanInTheLoopRequest] = []
|
|
177
|
+
for tool_call in interrupt_tool_calls:
|
|
176
178
|
tool_name = tool_call["name"]
|
|
177
179
|
tool_args = tool_call["args"]
|
|
178
|
-
config = self.
|
|
180
|
+
config = self.interrupt_on[tool_name]
|
|
179
181
|
description = (
|
|
180
182
|
config.get("description")
|
|
181
183
|
or f"{self.description_prefix}\n\nTool: {tool_name}\nArgs: {tool_args}"
|
|
@@ -189,21 +191,23 @@ class HumanInTheLoopMiddleware(AgentMiddleware):
|
|
|
189
191
|
"config": config,
|
|
190
192
|
"description": description,
|
|
191
193
|
}
|
|
192
|
-
|
|
194
|
+
interrupt_requests.append(request)
|
|
193
195
|
|
|
194
|
-
responses: list[HumanInTheLoopResponse] = interrupt(
|
|
196
|
+
responses: list[HumanInTheLoopResponse] = interrupt(interrupt_requests)
|
|
195
197
|
|
|
196
198
|
# Validate that the number of responses matches the number of interrupt tool calls
|
|
197
|
-
if (responses_len := len(responses)) != (
|
|
199
|
+
if (responses_len := len(responses)) != (
|
|
200
|
+
interrupt_tool_calls_len := len(interrupt_tool_calls)
|
|
201
|
+
):
|
|
198
202
|
msg = (
|
|
199
203
|
f"Number of human responses ({responses_len}) does not match "
|
|
200
|
-
f"number of hanging tool calls ({
|
|
204
|
+
f"number of hanging tool calls ({interrupt_tool_calls_len})."
|
|
201
205
|
)
|
|
202
206
|
raise ValueError(msg)
|
|
203
207
|
|
|
204
208
|
for i, response in enumerate(responses):
|
|
205
|
-
tool_call =
|
|
206
|
-
config = self.
|
|
209
|
+
tool_call = interrupt_tool_calls[i]
|
|
210
|
+
config = self.interrupt_on[tool_call["name"]]
|
|
207
211
|
|
|
208
212
|
if response["type"] == "accept" and config.get("allow_accept"):
|
|
209
213
|
approved_tool_calls.append(tool_call)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Anthropic prompt caching middleware."""
|
|
2
2
|
|
|
3
3
|
from typing import Literal
|
|
4
|
+
from warnings import warn
|
|
4
5
|
|
|
5
6
|
from langchain.agents.middleware.types import AgentMiddleware, ModelRequest
|
|
6
7
|
|
|
@@ -19,6 +20,7 @@ class AnthropicPromptCachingMiddleware(AgentMiddleware):
|
|
|
19
20
|
type: Literal["ephemeral"] = "ephemeral",
|
|
20
21
|
ttl: Literal["5m", "1h"] = "5m",
|
|
21
22
|
min_messages_to_cache: int = 0,
|
|
23
|
+
unsupported_model_behavior: Literal["ignore", "warn", "raise"] = "warn",
|
|
22
24
|
) -> None:
|
|
23
25
|
"""Initialize the middleware with cache control settings.
|
|
24
26
|
|
|
@@ -27,10 +29,15 @@ class AnthropicPromptCachingMiddleware(AgentMiddleware):
|
|
|
27
29
|
ttl: The time to live for the cache, only "5m" and "1h" are supported.
|
|
28
30
|
min_messages_to_cache: The minimum number of messages until the cache is used,
|
|
29
31
|
default is 0.
|
|
32
|
+
unsupported_model_behavior: The behavior to take when an unsupported model is used.
|
|
33
|
+
"ignore" will ignore the unsupported model and continue without caching.
|
|
34
|
+
"warn" will warn the user and continue without caching.
|
|
35
|
+
"raise" will raise an error and stop the agent.
|
|
30
36
|
"""
|
|
31
37
|
self.type = type
|
|
32
38
|
self.ttl = ttl
|
|
33
39
|
self.min_messages_to_cache = min_messages_to_cache
|
|
40
|
+
self.unsupported_model_behavior = unsupported_model_behavior
|
|
34
41
|
|
|
35
42
|
def modify_model_request( # type: ignore[override]
|
|
36
43
|
self,
|
|
@@ -40,19 +47,29 @@ class AnthropicPromptCachingMiddleware(AgentMiddleware):
|
|
|
40
47
|
try:
|
|
41
48
|
from langchain_anthropic import ChatAnthropic
|
|
42
49
|
except ImportError:
|
|
50
|
+
ChatAnthropic = None # noqa: N806
|
|
51
|
+
|
|
52
|
+
msg: str | None = None
|
|
53
|
+
|
|
54
|
+
if ChatAnthropic is None:
|
|
43
55
|
msg = (
|
|
44
56
|
"AnthropicPromptCachingMiddleware caching middleware only supports "
|
|
45
|
-
"Anthropic models."
|
|
57
|
+
"Anthropic models. "
|
|
46
58
|
"Please install langchain-anthropic."
|
|
47
59
|
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if not isinstance(request.model, ChatAnthropic):
|
|
60
|
+
elif not isinstance(request.model, ChatAnthropic):
|
|
51
61
|
msg = (
|
|
52
62
|
"AnthropicPromptCachingMiddleware caching middleware only supports "
|
|
53
63
|
f"Anthropic models, not instances of {type(request.model)}"
|
|
54
64
|
)
|
|
55
|
-
|
|
65
|
+
|
|
66
|
+
if msg is not None:
|
|
67
|
+
if self.unsupported_model_behavior == "raise":
|
|
68
|
+
raise ValueError(msg)
|
|
69
|
+
if self.unsupported_model_behavior == "warn":
|
|
70
|
+
warn(msg, stacklevel=3)
|
|
71
|
+
else:
|
|
72
|
+
return request
|
|
56
73
|
|
|
57
74
|
messages_count = (
|
|
58
75
|
len(request.messages) + 1 if request.system_prompt else len(request.messages)
|