langchain-dev-utils 1.3.2__py3-none-any.whl → 1.3.3__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.3.2"
1
+ __version__ = "1.3.3"
@@ -1,4 +1,4 @@
1
1
  from .factory import create_agent
2
- from .wrap import wrap_agent_as_tool
2
+ from .wrap import wrap_agent_as_tool, wrap_all_agents_as_tool
3
3
 
4
- __all__ = ["create_agent", "wrap_agent_as_tool"]
4
+ __all__ = ["create_agent", "wrap_agent_as_tool", "wrap_all_agents_as_tool"]
@@ -78,7 +78,19 @@ def _transform_agent_config(
78
78
  dict[str, AgentConfig]: The transformed agent config.
79
79
  """
80
80
 
81
+ new_config = {}
81
82
  for agent_name, _cfg in config.items():
83
+ new_config[agent_name] = {}
84
+
85
+ if "model" in _cfg:
86
+ new_config[agent_name]["model"] = _cfg["model"]
87
+ if "prompt" in _cfg:
88
+ new_config[agent_name]["prompt"] = _cfg["prompt"]
89
+ if "default" in _cfg:
90
+ new_config[agent_name]["default"] = _cfg["default"]
91
+ if "tools" in _cfg:
92
+ new_config[agent_name]["tools"] = _cfg["tools"]
93
+
82
94
  handoffs = _cfg.get("handoffs", [])
83
95
  if handoffs == "all":
84
96
  handoff_tools = [
@@ -102,8 +114,11 @@ def _transform_agent_config(
102
114
  ]
103
115
  ]
104
116
 
105
- _cfg["tools"] = [*_cfg.get("tools", []), *handoff_tools]
106
- return config
117
+ new_config[agent_name]["tools"] = [
118
+ *new_config[agent_name].get("tools", []),
119
+ *handoff_tools,
120
+ ]
121
+ return new_config
107
122
 
108
123
 
109
124
  class HandoffAgentMiddleware(AgentMiddleware):
@@ -5,11 +5,8 @@ from langchain.tools import ToolRuntime
5
5
  from langchain_core.messages import AnyMessage, HumanMessage
6
6
  from langchain_core.tools import BaseTool, StructuredTool
7
7
  from langgraph.graph.state import CompiledStateGraph
8
- from pydantic import BaseModel, Field
9
8
 
10
-
11
- class AgentToolInput(BaseModel):
12
- request: str = Field(description="The input to the agent")
9
+ from langchain_dev_utils.message_convert import format_sequence
13
10
 
14
11
 
15
12
  def _process_input(request: str, runtime: ToolRuntime) -> str:
@@ -138,3 +135,140 @@ def wrap_agent_as_tool(
138
135
  name=tool_name,
139
136
  description=tool_description,
140
137
  )
138
+
139
+
140
+ def wrap_all_agents_as_tool(
141
+ agents: list[CompiledStateGraph],
142
+ tool_name: Optional[str] = None,
143
+ tool_description: Optional[str] = None,
144
+ pre_input_hooks: Optional[
145
+ tuple[
146
+ Callable[[str, ToolRuntime], str],
147
+ Callable[[str, ToolRuntime], Awaitable[str]],
148
+ ]
149
+ | Callable[[str, ToolRuntime], str]
150
+ ] = None,
151
+ post_output_hooks: Optional[
152
+ tuple[
153
+ Callable[[str, list[AnyMessage], ToolRuntime], Any],
154
+ Callable[[str, list[AnyMessage], ToolRuntime], Awaitable[Any]],
155
+ ]
156
+ | Callable[[str, list[AnyMessage], ToolRuntime], Any]
157
+ ] = None,
158
+ ) -> BaseTool:
159
+ """Wraps all agents as single tool
160
+
161
+ Args:
162
+ agents: The agents to wrap
163
+ tool_name: The name of the tool, default to "task"
164
+ tool_description: The description of the tool
165
+ pre_input_hooks: Hooks to run before the input is processed
166
+ post_output_hooks: Hooks to run after the output is processed
167
+
168
+ Returns:
169
+ BaseTool: The wrapped agents as single tool
170
+
171
+ Example:
172
+ >>> from langchain_dev_utils.agents import wrap_all_agents_as_tool, create_agent
173
+ >>>
174
+ >>> call_time_agent_tool = wrap_all_agents_as_tool(
175
+ ... [time_agent,weather_agent],
176
+ ... tool_name="call_sub_agents",
177
+ ... tool_description="Used to invoke the sub-agents to perform tasks"
178
+ ... )
179
+ >>>
180
+ >>> agent = create_agent("vllm:qwen3-4b", tools=[call_sub_agents_tool], name="agent")
181
+
182
+ >>> response = agent.invoke({"messages": [HumanMessage(content="What time is it now?")]})
183
+ >>> response
184
+ """
185
+ if len(agents) <= 1:
186
+ raise ValueError("At least more than one agent must be provided")
187
+
188
+ agents_map = {}
189
+
190
+ for agent in agents:
191
+ if agent.name is None:
192
+ raise ValueError("Agent name must not be provided")
193
+ if agent.name in agents_map:
194
+ raise ValueError("Agent name must be unique")
195
+ agents_map[agent.name] = agent
196
+
197
+ process_input = _process_input
198
+ process_input_async = _process_input
199
+ process_output = _process_output
200
+ process_output_async = _process_output
201
+
202
+ if pre_input_hooks:
203
+ if isinstance(pre_input_hooks, tuple):
204
+ process_input = pre_input_hooks[0]
205
+ process_input_async = pre_input_hooks[1]
206
+ else:
207
+ process_input = pre_input_hooks
208
+ process_input_async = pre_input_hooks
209
+
210
+ if post_output_hooks:
211
+ if isinstance(post_output_hooks, tuple):
212
+ process_output = post_output_hooks[0]
213
+ process_output_async = post_output_hooks[1]
214
+ else:
215
+ process_output = post_output_hooks
216
+ process_output_async = post_output_hooks
217
+
218
+ def call_agent(
219
+ agent_name: str,
220
+ description: str,
221
+ runtime: ToolRuntime,
222
+ ) -> str:
223
+ task_description = (
224
+ process_input(description, runtime) if process_input else description
225
+ )
226
+
227
+ if agent_name not in agents_map:
228
+ raise ValueError(f"Agent {agent_name} not found")
229
+
230
+ messages = [HumanMessage(content=task_description)]
231
+ response = agents_map[agent_name].invoke({"messages": messages})
232
+
233
+ response = process_output(task_description, response["messages"], runtime)
234
+ return response
235
+
236
+ async def acall_agent(
237
+ agent_name: str,
238
+ description: str,
239
+ runtime: ToolRuntime,
240
+ ) -> str:
241
+ if asyncio.iscoroutinefunction(process_input_async):
242
+ task_description = await process_input_async(description, runtime)
243
+ else:
244
+ task_description = cast(str, process_input_async(description, runtime))
245
+
246
+ if agent_name not in agents_map:
247
+ raise ValueError(f"Agent {agent_name} not found")
248
+
249
+ messages = [HumanMessage(content=task_description)]
250
+ response = await agents_map[agent_name].ainvoke({"messages": messages})
251
+
252
+ if asyncio.iscoroutinefunction(process_output_async):
253
+ response = await process_output_async(
254
+ task_description, response["messages"], runtime
255
+ )
256
+ else:
257
+ response = process_output(task_description, response["messages"], runtime)
258
+
259
+ return response
260
+
261
+ if tool_name is None:
262
+ tool_name = "task"
263
+
264
+ if tool_description is None:
265
+ tool_description = (
266
+ "Launch an ephemeral subagent for a task.\nAvailable agents:\n "
267
+ + format_sequence(list(agents_map.keys()), with_num=True)
268
+ )
269
+ return StructuredTool.from_function(
270
+ func=call_agent,
271
+ coroutine=acall_agent,
272
+ name=tool_name,
273
+ description=tool_description,
274
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langchain-dev-utils
3
- Version: 1.3.2
3
+ Version: 1.3.3
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=HgKA3RqZvC7slo8MgLyffCGwJbQ3cY6I7oUMFvGLWps,22
1
+ langchain_dev_utils/__init__.py,sha256=Vi6om3KImlKsS_Wg5CjUgYffoi2zx7T-SRPnnGL0G7M,22
2
2
  langchain_dev_utils/_utils.py,sha256=MFEzR1BjXMj6HEVwt2x2omttFuDJ_rYAEbNqe99r9pM,1338
3
3
  langchain_dev_utils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- langchain_dev_utils/agents/__init__.py,sha256=PJ-lSDZv_AXMYA3H4fx-HzJa14tPbkGmq1HX8LNfaPo,125
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
6
  langchain_dev_utils/agents/file_system.py,sha256=Yk3eetREE26WNrnTWLoiDUpOyCJ-rhjlfFDk6foLa1E,8468
7
7
  langchain_dev_utils/agents/plan.py,sha256=WwhoiJBmVYVI9bT8HfjCzTJ_SIp9WFil0gOeznv2omQ,6497
8
- langchain_dev_utils/agents/wrap.py,sha256=RuchoH_VotPmKFuYEn2SXoSgNxZhSA9jKM0Iv_8oHLk,4718
8
+ langchain_dev_utils/agents/wrap.py,sha256=O8LSieRhlqL4cwf_BPNIiJD3lhTyL6civBHcQPWCqSU,9490
9
9
  langchain_dev_utils/agents/middleware/__init__.py,sha256=QVQibaNHvHPyNTZ2UNFfYL153ZboaCHcoioTHK0FsiY,710
10
10
  langchain_dev_utils/agents/middleware/format_prompt.py,sha256=LzYiQXCRvkpfDhGPxhZwdepeU3j-HUWPJqcx3FWsfT4,2357
11
- langchain_dev_utils/agents/middleware/handoffs.py,sha256=hYtm6NKUvfJQ_fxtTBuvEay185_RCB0yXAEdgSL7-Ls,6721
11
+ langchain_dev_utils/agents/middleware/handoffs.py,sha256=r196Xk0Jws1Tz6JQuvy5HEc3HAAQejCxFmJpB6KrvLU,7230
12
12
  langchain_dev_utils/agents/middleware/model_fallback.py,sha256=8xiNjTJ0yiRkPLCRfAGNnqY1TLstj1Anmiqyv5w2mA8,1633
13
13
  langchain_dev_utils/agents/middleware/model_router.py,sha256=qBspvj9ZoKfmC1pHWTO0EHHfxjgCUd-TuSbqvZl0kmg,7977
14
14
  langchain_dev_utils/agents/middleware/plan.py,sha256=-ZLkp85QQTSCX9thMblacJ1N86h0BYPoTwCfJlJ_jzQ,14981
@@ -33,7 +33,7 @@ langchain_dev_utils/pipeline/types.py,sha256=T3aROKKXeWvd0jcH5XkgMDQfEkLfPaiOhhV
33
33
  langchain_dev_utils/tool_calling/__init__.py,sha256=mu_WxKMcu6RoTf4vkTPbA1WSBSNc6YIqyBtOQ6iVQj4,322
34
34
  langchain_dev_utils/tool_calling/human_in_the_loop.py,sha256=7Z_QO5OZUR6K8nLoIcafc6osnvX2IYNorOJcbx6bVso,9672
35
35
  langchain_dev_utils/tool_calling/utils.py,sha256=S4-KXQ8jWmpGTXYZitovF8rxKpaSSUkFruM8LDwvcvE,2765
36
- langchain_dev_utils-1.3.2.dist-info/METADATA,sha256=PLQKHjjqwlL9lvaW3Et1q0gYbZTtRdmTN6w5A4gJiYM,4552
37
- langchain_dev_utils-1.3.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
38
- langchain_dev_utils-1.3.2.dist-info/licenses/LICENSE,sha256=AWAOzNEcsvCEzHOF0qby5OKxviVH_eT9Yce1sgJTico,1084
39
- langchain_dev_utils-1.3.2.dist-info/RECORD,,
36
+ langchain_dev_utils-1.3.3.dist-info/METADATA,sha256=PEt8qHRYBK2l-A2djg8eJEuz6uZdxZq-fPa6U6yR8Cc,4552
37
+ langchain_dev_utils-1.3.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
38
+ langchain_dev_utils-1.3.3.dist-info/licenses/LICENSE,sha256=AWAOzNEcsvCEzHOF0qby5OKxviVH_eT9Yce1sgJTico,1084
39
+ langchain_dev_utils-1.3.3.dist-info/RECORD,,