langchain-dev-utils 1.4.1__py3-none-any.whl → 1.4.2__py3-none-any.whl

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.
@@ -1 +1 @@
1
- __version__ = "1.4.1"
1
+ __version__ = "1.4.2"
@@ -1,11 +1,48 @@
1
1
  from importlib import util
2
2
  from typing import Literal, Optional, cast
3
3
 
4
+ from langchain_core.tools import BaseTool
4
5
  from langgraph.graph import StateGraph
5
6
  from langgraph.graph.state import StateNode
6
7
  from pydantic import BaseModel
7
8
 
8
9
 
10
+ def _duplicate_tools(tools: list[BaseTool]) -> list[BaseTool]:
11
+ """Duplicate tools with the same name.
12
+
13
+ Args:
14
+ tools (list[BaseTool]): The list of tools.
15
+
16
+ Returns:
17
+ list[BaseTool]: The duplicated tools.
18
+ """
19
+ tool_name_set = set()
20
+ duplicated_tools = []
21
+ for tool_obj in tools:
22
+ if tool_obj.name not in tool_name_set:
23
+ duplicated_tools.append(tool_obj)
24
+ tool_name_set.add(tool_obj.name)
25
+ return duplicated_tools
26
+
27
+
28
+ def _merge_tools(tools: list[BaseTool]) -> list[BaseTool]:
29
+ """Merge tools with the same name.
30
+
31
+ Args:
32
+ tools (list[BaseTool]): The list of tools.
33
+
34
+ Returns:
35
+ list[BaseTool]: The merged tools.
36
+ """
37
+ tool_name_set = set()
38
+ merged_tools = []
39
+ for tool_obj in tools:
40
+ if tool_obj.name not in tool_name_set:
41
+ merged_tools.append(tool_obj)
42
+ tool_name_set.add(tool_obj.name)
43
+ return merged_tools
44
+
45
+
9
46
  def _transform_node_to_tuple(
10
47
  node: StateNode | tuple[str, StateNode],
11
48
  ) -> tuple[str, StateNode]:
@@ -1,4 +1,4 @@
1
- from typing import Any, Awaitable, Callable, Literal, cast
1
+ from typing import Awaitable, Callable, Literal, cast
2
2
 
3
3
  from langchain.agents import AgentState
4
4
  from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
@@ -9,6 +9,7 @@ from langchain_core.messages import SystemMessage, ToolMessage
9
9
  from langgraph.types import Command
10
10
  from typing_extensions import NotRequired, Optional, TypedDict
11
11
 
12
+ from langchain_dev_utils._utils import _duplicate_tools, _merge_tools
12
13
  from langchain_dev_utils.chat_models import load_chat_model
13
14
 
14
15
 
@@ -19,7 +20,7 @@ class MultiAgentState(AgentState):
19
20
  class AgentConfig(TypedDict):
20
21
  model: NotRequired[str | BaseChatModel]
21
22
  prompt: str | SystemMessage
22
- tools: NotRequired[list[BaseTool | dict[str, Any]]]
23
+ tools: NotRequired[list[BaseTool]]
23
24
  default: NotRequired[bool]
24
25
  handoffs: list[str] | Literal["all"]
25
26
 
@@ -176,7 +177,16 @@ class HandoffAgentMiddleware(AgentMiddleware):
176
177
  agents_config,
177
178
  handoffs_tools,
178
179
  )
179
- self.tools = handoffs_tools
180
+
181
+ all_tools = [
182
+ *handoffs_tools,
183
+ ]
184
+
185
+ for agent_name in agents_config:
186
+ tools = agents_config.get(agent_name, {}).get("tools", [])
187
+ all_tools.extend(tools)
188
+
189
+ self.tools = _merge_tools(all_tools)
180
190
 
181
191
  def _get_override_request(self, request: ModelRequest) -> ModelRequest:
182
192
  active_agent_name = request.state.get("active_agent", self.default_agent_name)
@@ -184,15 +194,14 @@ class HandoffAgentMiddleware(AgentMiddleware):
184
194
  _config = self.agents_config[active_agent_name]
185
195
 
186
196
  params = {}
187
- if _config.get("model"):
188
- model = _config.get("model")
197
+ if model := _config.get("model"):
189
198
  if isinstance(model, str):
190
199
  model = load_chat_model(model)
191
200
  params["model"] = model
192
- if _config.get("prompt"):
193
- params["system_prompt"] = _config.get("prompt")
194
- if _config.get("tools"):
195
- params["tools"] = _config.get("tools")
201
+ if prompt := _config.get("prompt"):
202
+ params["system_prompt"] = prompt
203
+ if tools := _config.get("tools"):
204
+ params["tools"] = _duplicate_tools(tools)
196
205
 
197
206
  if params:
198
207
  return request.override(**params)
@@ -10,6 +10,7 @@ from langgraph.runtime import Runtime
10
10
  from pydantic import BaseModel, Field
11
11
  from typing_extensions import TypedDict
12
12
 
13
+ from langchain_dev_utils._utils import _duplicate_tools, _merge_tools
13
14
  from langchain_dev_utils.chat_models import load_chat_model
14
15
  from langchain_dev_utils.message_convert import format_sequence
15
16
 
@@ -120,6 +121,13 @@ class ModelRouterMiddleware(AgentMiddleware):
120
121
 
121
122
  self.router_prompt = router_prompt
122
123
 
124
+ all_tools = []
125
+ for model in model_list:
126
+ if tools := model.get("tools"):
127
+ all_tools.extend(tools)
128
+
129
+ self.tools = _merge_tools(all_tools)
130
+
123
131
  def _select_model(self, messages: list[AnyMessage]):
124
132
  response = cast(
125
133
  SelectModel,
@@ -174,7 +182,7 @@ class ModelRouterMiddleware(AgentMiddleware):
174
182
  model = load_chat_model(select_model_name)
175
183
  override_kwargs["model"] = model
176
184
  if model_values["tools"] is not None:
177
- override_kwargs["tools"] = model_values["tools"]
185
+ override_kwargs["tools"] = _duplicate_tools(model_values["tools"])
178
186
  if model_values["system_prompt"] is not None:
179
187
  override_kwargs["system_message"] = SystemMessage(
180
188
  content=model_values["system_prompt"]
@@ -17,7 +17,7 @@ def _process_input(request: str, runtime: ToolRuntime) -> str:
17
17
  def _process_output(
18
18
  request: str, response: dict[str, Any], runtime: ToolRuntime
19
19
  ) -> Any:
20
- return response["messages"][-1].content
20
+ return response["messages"][-1].text
21
21
 
22
22
 
23
23
  def get_subagent_name(runtime: ToolRuntime) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langchain-dev-utils
3
- Version: 1.4.1
3
+ Version: 1.4.2
4
4
  Summary: A practical utility library for LangChain and LangGraph development
5
5
  Project-URL: Source Code, https://github.com/TBice123123/langchain-dev-utils
6
6
  Project-URL: repository, https://github.com/TBice123123/langchain-dev-utils
@@ -1,14 +1,14 @@
1
- langchain_dev_utils/__init__.py,sha256=nxjQ6mMSJJKqkpX2IY-1BmdpErEa1Lx5hYzk9ULB09w,23
2
- langchain_dev_utils/_utils.py,sha256=EC1mlQggFvso1csYxCrk4XbXkJuK_8zrrjsD19ELzgk,4806
1
+ langchain_dev_utils/__init__.py,sha256=BG12pmEO_YOxB_xUz7sNv20lzgrwQ60u0paDn7SG_9Q,23
2
+ langchain_dev_utils/_utils.py,sha256=tDC-Hp8yRgqGi8KQbUgjnFLqE5who8mv5SREbZWPshQ,5827
3
3
  langchain_dev_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  langchain_dev_utils/agents/__init__.py,sha256=69_biZzyJvW9OBT1g8TX_77mp9-I_TvWo9QtlvHq83E,177
5
5
  langchain_dev_utils/agents/factory.py,sha256=8XB6y_ddf58vXlTLHBL6KCirFqkD2GjtzsuOt98sS7U,3732
6
- langchain_dev_utils/agents/wrap.py,sha256=ufxcOCLDE4Aw2TbgcyFYor0BXXaJDzLTTJJyyD4x7bQ,12011
6
+ langchain_dev_utils/agents/wrap.py,sha256=kPyxm4CP6-Q-iBV_NBkVkCBB5tg9rKI0B6qzEyK_JEA,12008
7
7
  langchain_dev_utils/agents/middleware/__init__.py,sha256=SiTibbzv2QC0FvGKN_1A10yOO62F7wtdScbGARjkyjA,808
8
8
  langchain_dev_utils/agents/middleware/format_prompt.py,sha256=HrgHsMCnfHQ-Eh_RY-h56otAJ62-2zJJHbOi2kRyEiw,5303
9
- langchain_dev_utils/agents/middleware/handoffs.py,sha256=rSkNXxqtjB8_xT0HUdxnKbchgY76BuTPX-Zc69H-_wI,7687
9
+ langchain_dev_utils/agents/middleware/handoffs.py,sha256=Q99X8QnaPr6yr9OF-IHZPmYL_RsLnaAMZkPLDF7BRPQ,7940
10
10
  langchain_dev_utils/agents/middleware/model_fallback.py,sha256=8xiNjTJ0yiRkPLCRfAGNnqY1TLstj1Anmiqyv5w2mA8,1633
11
- langchain_dev_utils/agents/middleware/model_router.py,sha256=IidYq72tPLa053gEg5IQpPzDzyCxYYEvpgT1K4qBwXw,7862
11
+ langchain_dev_utils/agents/middleware/model_router.py,sha256=wsrC_Yg-i_mLlMM0SG8lLOFoOCNpmen6WI2PAj73anQ,8145
12
12
  langchain_dev_utils/agents/middleware/plan.py,sha256=Zz0dh1BRbsVgROmhjH2IIqylSsuKHZXJx0iztMBm8EU,14719
13
13
  langchain_dev_utils/agents/middleware/summarization.py,sha256=IoZ2PM1OC3AXwf0DWpfreuPOAipeiYu0KPmAABWXuY0,3087
14
14
  langchain_dev_utils/agents/middleware/tool_call_repair.py,sha256=mO21g9nzukdjNcERTg63jS7UOEF47Wx8yd7CqFS_VVQ,3580
@@ -40,7 +40,7 @@ langchain_dev_utils/pipeline/types.py,sha256=T3aROKKXeWvd0jcH5XkgMDQfEkLfPaiOhhV
40
40
  langchain_dev_utils/tool_calling/__init__.py,sha256=mu_WxKMcu6RoTf4vkTPbA1WSBSNc6YIqyBtOQ6iVQj4,322
41
41
  langchain_dev_utils/tool_calling/human_in_the_loop.py,sha256=7Z_QO5OZUR6K8nLoIcafc6osnvX2IYNorOJcbx6bVso,9672
42
42
  langchain_dev_utils/tool_calling/utils.py,sha256=S4-KXQ8jWmpGTXYZitovF8rxKpaSSUkFruM8LDwvcvE,2765
43
- langchain_dev_utils-1.4.1.dist-info/METADATA,sha256=QqpJZwTwFUIzwIXZRjfSVGHMrtZG7pJ6salBq_zYEL0,4808
44
- langchain_dev_utils-1.4.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
45
- langchain_dev_utils-1.4.1.dist-info/licenses/LICENSE,sha256=AWAOzNEcsvCEzHOF0qby5OKxviVH_eT9Yce1sgJTico,1084
46
- langchain_dev_utils-1.4.1.dist-info/RECORD,,
43
+ langchain_dev_utils-1.4.2.dist-info/METADATA,sha256=VEE7lWcW1nMGXgx6QX_c7C565NXXDufHAnAqDqCfkAY,4808
44
+ langchain_dev_utils-1.4.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
45
+ langchain_dev_utils-1.4.2.dist-info/licenses/LICENSE,sha256=AWAOzNEcsvCEzHOF0qby5OKxviVH_eT9Yce1sgJTico,1084
46
+ langchain_dev_utils-1.4.2.dist-info/RECORD,,