langchain-dev-utils 1.2.16__py3-none-any.whl → 1.3.1__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.2.16"
1
+ __version__ = "1.3.1"
@@ -5,9 +5,9 @@ from langchain.agents.middleware.types import (
5
5
  AgentMiddleware,
6
6
  AgentState,
7
7
  ResponseT,
8
+ StateT_co,
8
9
  _InputAgentState,
9
10
  _OutputAgentState,
10
- StateT_co,
11
11
  )
12
12
  from langchain.agents.structured_output import ResponseFormat
13
13
  from langchain_core.messages import SystemMessage
@@ -1,4 +1,5 @@
1
1
  from .format_prompt import format_prompt
2
+ from .handoffs import HandoffsAgentMiddleware, create_handoffs_tool
2
3
  from .model_fallback import ModelFallbackMiddleware
3
4
  from .model_router import ModelRouterMiddleware
4
5
  from .plan import (
@@ -24,4 +25,6 @@ __all__ = [
24
25
  "ModelRouterMiddleware",
25
26
  "ToolCallRepairMiddleware",
26
27
  "format_prompt",
28
+ "create_handoffs_tool",
29
+ "HandoffsAgentMiddleware",
27
30
  ]
@@ -0,0 +1,138 @@
1
+ from typing import Any, Awaitable, Callable
2
+
3
+ from langchain.agents import AgentState
4
+ from langchain.agents.middleware import AgentMiddleware, ModelRequest, ModelResponse
5
+ from langchain.agents.middleware.types import ModelCallResult
6
+ from langchain.tools import BaseTool, ToolRuntime, tool
7
+ from langchain_core.language_models.chat_models import BaseChatModel
8
+ from langchain_core.messages import SystemMessage, ToolMessage
9
+ from langgraph.types import Command
10
+ from typing_extensions import NotRequired, Optional, TypedDict
11
+
12
+ from langchain_dev_utils.chat_models import load_chat_model
13
+
14
+
15
+ class MultiAgentState(AgentState):
16
+ active_agent: NotRequired[str]
17
+
18
+
19
+ class AgentConfig(TypedDict):
20
+ model: NotRequired[str | BaseChatModel]
21
+ prompt: str | SystemMessage
22
+ tools: list[BaseTool | dict[str, Any]]
23
+ default: NotRequired[bool]
24
+
25
+
26
+ def create_handoffs_tool(
27
+ agent_name: str,
28
+ tool_name: Optional[str] = None,
29
+ tool_description: Optional[str] = None,
30
+ ):
31
+ """Create a tool for handoffs to a specified agent.
32
+
33
+ Args:
34
+ agent_name (str): The name of the agent to transfer to.
35
+ tool_name (Optional[str], optional): The name of the tool. Defaults to None.
36
+ tool_description (Optional[str], optional): The description of the tool. Defaults to None.
37
+
38
+ Returns:
39
+ BaseTool: A tool instance for handoffs to the specified agent.
40
+
41
+ Example:
42
+ Basic usage
43
+ >>> from langchain_dev_utils.agents.middleware import create_handoffs_tool
44
+ >>> handoffs_tool = create_handoffs_tool("time_agent")
45
+ """
46
+ if tool_name is None:
47
+ tool_name = f"transfer_to_{agent_name}"
48
+ if not tool_name.endswith("_agent"):
49
+ tool_name += "_agent"
50
+
51
+ if tool_description is None:
52
+ tool_description = f"Transfer to the {agent_name}"
53
+
54
+ @tool(name_or_callable=tool_name, description=tool_description)
55
+ def handoffs_tool(runtime: ToolRuntime) -> Command:
56
+ return Command(
57
+ update={
58
+ "messages": [
59
+ ToolMessage(
60
+ content=f"Transferred to {agent_name}",
61
+ tool_call_id=runtime.tool_call_id,
62
+ )
63
+ ],
64
+ "active_agent": agent_name,
65
+ }
66
+ )
67
+
68
+ return handoffs_tool
69
+
70
+
71
+ def _get_default_active_agent(state: dict[str, AgentConfig]) -> Optional[str]:
72
+ for agent_name, config in state.items():
73
+ if config.get("default", False):
74
+ return agent_name
75
+ return None
76
+
77
+
78
+ class HandoffsAgentMiddleware(AgentMiddleware):
79
+ """Agent middleware for switching between multiple agents.
80
+ This middleware dynamically replaces model call parameters based on the currently active agent configuration, enabling seamless switching between different agents.
81
+
82
+ Args:
83
+ agents_config (dict[str, AgentConfig]): A dictionary of agent configurations.
84
+
85
+ Examples:
86
+ ```python
87
+ from langchain_dev_utils.agents.middleware import HandoffsAgentMiddleware
88
+ middleware = HandoffsAgentMiddleware(agents_config)
89
+ ```
90
+ """
91
+
92
+ state_schema = MultiAgentState
93
+
94
+ def __init__(self, agents_config: dict[str, AgentConfig]):
95
+ default_agent_name = _get_default_active_agent(agents_config)
96
+ if default_agent_name is None:
97
+ raise ValueError(
98
+ "No default agent found, you must set one by set default=True"
99
+ )
100
+ self.default_agent_name = default_agent_name
101
+ self.agents_config = agents_config
102
+
103
+ def _get_active_agent_config(self, request: ModelRequest) -> dict[str, Any]:
104
+ active_agent_name = request.state.get("active_agent", self.default_agent_name)
105
+
106
+ _config = self.agents_config[active_agent_name]
107
+
108
+ params = {}
109
+ if _config.get("model"):
110
+ model = _config.get("model")
111
+ if isinstance(model, str):
112
+ model = load_chat_model(model)
113
+ params["model"] = model
114
+ if _config.get("prompt"):
115
+ params["system_prompt"] = _config.get("prompt")
116
+ if _config.get("tools"):
117
+ params["tools"] = _config.get("tools")
118
+ return params
119
+
120
+ def wrap_model_call(
121
+ self, request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse]
122
+ ) -> ModelCallResult:
123
+ override_kwargs = self._get_active_agent_config(request)
124
+ if override_kwargs:
125
+ return handler(request.override(**override_kwargs))
126
+ else:
127
+ return handler(request)
128
+
129
+ async def awrap_model_call(
130
+ self,
131
+ request: ModelRequest,
132
+ handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
133
+ ) -> ModelCallResult:
134
+ override_kwargs = self._get_active_agent_config(request)
135
+ if override_kwargs:
136
+ return await handler(request.override(**override_kwargs))
137
+ else:
138
+ return await handler(request)
@@ -22,7 +22,7 @@ class ModelFallbackMiddleware(_ModelFallbackMiddleware):
22
22
 
23
23
  fallback = ModelFallbackMiddleware(
24
24
  "vllm:qwen3-8b", ## Try first on error
25
- "openrouter:meta-llama/llama-3.3-8b-instruct:free" #Then this
25
+ "vllm:gpt-oss-20b", #Then this
26
26
  )
27
27
 
28
28
  agent = create_agent(
@@ -30,7 +30,7 @@ class ModelFallbackMiddleware(_ModelFallbackMiddleware):
30
30
  middleware=[fallback],
31
31
  )
32
32
 
33
- # If primary fails: tries qwen3-8b, then llama-3.3-8b-instruct
33
+ # If primary fails: tries qwen3-8b, then gpt-oss-20b
34
34
  result = await agent.invoke({"messages": [HumanMessage("Hello")]})
35
35
  ```
36
36
  """
@@ -126,13 +126,12 @@ class _BaseChatOpenAICompatible(BaseChatOpenAI):
126
126
  Base template class for OpenAI-compatible chat model implementations.
127
127
 
128
128
  This class provides a foundation for integrating various LLM providers that
129
- offer OpenAI-compatible APIs (such as vLLM, OpenRouter, ZAI, Moonshot,
130
- and many others). It enhances the base OpenAI functionality by:
129
+ offer OpenAI-compatible APIs. It enhances the base OpenAI functionality by:
131
130
 
132
131
  **1. Supports output of more types of reasoning content (reasoning_content)**
133
132
  ChatOpenAI can only output reasoning content natively supported by official
134
133
  OpenAI models, while OpenAICompatibleChatModel can output reasoning content
135
- from other model providers (e.g., OpenRouter, vLLM).
134
+ from other model providers.
136
135
 
137
136
  **2. Dynamically adapts to choose the most suitable structured-output method**
138
137
  OpenAICompatibleChatModel adds method="auto" (default), which selects the best
@@ -593,7 +592,7 @@ def _create_openai_compatible_model(
593
592
  configuring environment variable mappings and default base URLs specific to each provider.
594
593
 
595
594
  Args:
596
- provider: Provider identifier (e.g., `vllm`,`openrouter`)
595
+ provider: Provider identifier (e.g.`vllm`)
597
596
  base_url: Default API base URL for the provider
598
597
  compatibility_options: Optional configuration for the provider
599
598
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langchain-dev-utils
3
- Version: 1.2.16
3
+ Version: 1.3.1
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
@@ -8,7 +8,8 @@ Project-URL: documentation, https://tbice123123.github.io/langchain-dev-utils
8
8
  Author-email: tiebingice <tiebingice123@outlook.com>
9
9
  License-File: LICENSE
10
10
  Requires-Python: >=3.11
11
- Requires-Dist: langchain>=1.1.0
11
+ Requires-Dist: langchain-core>=1.2.5
12
+ Requires-Dist: langchain>=1.2.0
12
13
  Requires-Dist: langgraph>=1.0.0
13
14
  Provides-Extra: standard
14
15
  Requires-Dist: json-repair>=0.53.1; extra == 'standard'
@@ -1,14 +1,15 @@
1
- langchain_dev_utils/__init__.py,sha256=7RIJ-Nh9kHJf5O5NvahUeP8DNXq6oIzbYcIt_yKv0lQ,23
1
+ langchain_dev_utils/__init__.py,sha256=-ypEJktJToAL9by62JJKWEzDo_KPCQtmE5kwFgX24z4,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
4
  langchain_dev_utils/agents/__init__.py,sha256=PJ-lSDZv_AXMYA3H4fx-HzJa14tPbkGmq1HX8LNfaPo,125
5
- langchain_dev_utils/agents/factory.py,sha256=mr-Q3fxxV_yEN3l8p-Rk0uxXvKjDUGEZDtrg9HRwr-8,3732
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
8
  langchain_dev_utils/agents/wrap.py,sha256=RuchoH_VotPmKFuYEn2SXoSgNxZhSA9jKM0Iv_8oHLk,4718
9
- langchain_dev_utils/agents/middleware/__init__.py,sha256=EECbcYcHXQAMA-guJNRGwCVi9jG957d0nOaoIuyIKC0,832
9
+ langchain_dev_utils/agents/middleware/__init__.py,sha256=n3PsbL8LSmariEe94rUMoT0GnUQMrZUNdSj-z4om2cY,962
10
10
  langchain_dev_utils/agents/middleware/format_prompt.py,sha256=LzYiQXCRvkpfDhGPxhZwdepeU3j-HUWPJqcx3FWsfT4,2357
11
- langchain_dev_utils/agents/middleware/model_fallback.py,sha256=nivtXXF4cwyOBv6p7RW12nXtNg87wjTWxO3BKIYiroI,1674
11
+ langchain_dev_utils/agents/middleware/handoffs.py,sha256=aCD90yTiAPyG8pC_DAr0eDi-iYW57W0wt6LzIoKLT_Y,5024
12
+ langchain_dev_utils/agents/middleware/model_fallback.py,sha256=8xiNjTJ0yiRkPLCRfAGNnqY1TLstj1Anmiqyv5w2mA8,1633
12
13
  langchain_dev_utils/agents/middleware/model_router.py,sha256=qBspvj9ZoKfmC1pHWTO0EHHfxjgCUd-TuSbqvZl0kmg,7977
13
14
  langchain_dev_utils/agents/middleware/plan.py,sha256=0qDCmenxgY_zrwMfOyYlgLfhYNw-HszNLeeOkfj14NA,16002
14
15
  langchain_dev_utils/agents/middleware/summarization.py,sha256=IoZ2PM1OC3AXwf0DWpfreuPOAipeiYu0KPmAABWXuY0,3087
@@ -19,7 +20,7 @@ langchain_dev_utils/chat_models/__init__.py,sha256=YSLUyHrWEEj4y4DtGFCOnDW02VIYZ
19
20
  langchain_dev_utils/chat_models/base.py,sha256=BzaoCIv145eE8b5wNDsbZDHn4EAxe4vdlptp7qXPWKk,11625
20
21
  langchain_dev_utils/chat_models/types.py,sha256=MD3cv_ZIe9fCdgwisNfuxAOhy-j4YSs1ZOQYyCjlNKs,927
21
22
  langchain_dev_utils/chat_models/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
- langchain_dev_utils/chat_models/adapters/openai_compatible.py,sha256=4iJgMGAReiJ668O9ZGAZbduxNY2PhEJJonDhQDBge44,24596
23
+ langchain_dev_utils/chat_models/adapters/openai_compatible.py,sha256=PkOoYNwp2kEhXalUHXCCAnL7LXfeiNoeiQ678suEse4,24492
23
24
  langchain_dev_utils/embeddings/__init__.py,sha256=zbEOaV86TUi9Zrg_dH9dpdgacWg31HMJTlTQknA9EKk,244
24
25
  langchain_dev_utils/embeddings/base.py,sha256=BGoWY0L7nG9iRV3d4sSagXhECXrwvS1xA-A_OVltn3k,9406
25
26
  langchain_dev_utils/message_convert/__init__.py,sha256=ZGrHGXPKMrZ_p9MqfIVZ4jgbEyb7aC4Q7X-muuThIYU,457
@@ -32,7 +33,7 @@ langchain_dev_utils/pipeline/types.py,sha256=T3aROKKXeWvd0jcH5XkgMDQfEkLfPaiOhhV
32
33
  langchain_dev_utils/tool_calling/__init__.py,sha256=mu_WxKMcu6RoTf4vkTPbA1WSBSNc6YIqyBtOQ6iVQj4,322
33
34
  langchain_dev_utils/tool_calling/human_in_the_loop.py,sha256=7Z_QO5OZUR6K8nLoIcafc6osnvX2IYNorOJcbx6bVso,9672
34
35
  langchain_dev_utils/tool_calling/utils.py,sha256=S4-KXQ8jWmpGTXYZitovF8rxKpaSSUkFruM8LDwvcvE,2765
35
- langchain_dev_utils-1.2.16.dist-info/METADATA,sha256=2lobuT6h5sJqxw25CaeDtdEQrk2EvROnPLhuF-ny4mI,4516
36
- langchain_dev_utils-1.2.16.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
37
- langchain_dev_utils-1.2.16.dist-info/licenses/LICENSE,sha256=AWAOzNEcsvCEzHOF0qby5OKxviVH_eT9Yce1sgJTico,1084
38
- langchain_dev_utils-1.2.16.dist-info/RECORD,,
36
+ langchain_dev_utils-1.3.1.dist-info/METADATA,sha256=6UJeWibp3qpwITK0THhTdFpM8tq7XyLyoUvbkMnRhLc,4552
37
+ langchain_dev_utils-1.3.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
38
+ langchain_dev_utils-1.3.1.dist-info/licenses/LICENSE,sha256=AWAOzNEcsvCEzHOF0qby5OKxviVH_eT9Yce1sgJTico,1084
39
+ langchain_dev_utils-1.3.1.dist-info/RECORD,,