langchain-dev-utils 1.3.2__tar.gz → 1.3.4__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.
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/.gitignore +15 -15
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/.python-version +1 -1
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/PKG-INFO +1 -1
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/README_cn.md +85 -85
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/pyproject.toml +47 -47
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/__init__.py +1 -0
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/agents/__init__.py +4 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/handoffs.py +17 -2
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/agents/wrap.py +274 -0
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/chat_models/adapters/__init__.py +3 -0
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/chat_models/adapters/create_utils.py +53 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/chat_models/adapters/openai_compatible.py +23 -4
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/chat_models/adapters/register_profiles.py +15 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/chat_models/base.py +4 -11
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/embeddings/adapters/__init__.py +3 -0
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/embeddings/adapters/create_utils.py +45 -0
- langchain_dev_utils-1.3.4/src/langchain_dev_utils/embeddings/adapters/openai_compatible.py +75 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/embeddings/base.py +11 -25
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/message_convert/__init__.py +15 -15
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/message_convert/format.py +69 -69
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_chat_models.py +191 -185
- langchain_dev_utils-1.3.4/tests/test_embedding.py +22 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_handoffs_middleware.py +34 -39
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_load_model.py +104 -104
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_messages.py +164 -164
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_plan_middleware.py +68 -68
- langchain_dev_utils-1.3.4/tests/test_wrap_agent.py +186 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/uv.lock +2883 -2883
- langchain_dev_utils-1.3.2/src/langchain_dev_utils/__init__.py +0 -1
- langchain_dev_utils-1.3.2/src/langchain_dev_utils/agents/__init__.py +0 -4
- langchain_dev_utils-1.3.2/src/langchain_dev_utils/agents/wrap.py +0 -140
- langchain_dev_utils-1.3.2/tests/test_wrap_agent.py +0 -102
- langchain_dev_utils-1.3.2/tests/utils/__init__.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/.vscode/settings.json +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/LICENSE +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/README.md +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/_utils.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/factory.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/file_system.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/__init__.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/format_prompt.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/model_fallback.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/model_router.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/plan.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/summarization.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/tool_call_repair.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/tool_emulator.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/tool_selection.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/plan.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/chat_models/__init__.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/chat_models/types.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/embeddings/__init__.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/message_convert/content.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/pipeline/__init__.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/pipeline/parallel.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/pipeline/sequential.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/pipeline/types.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/py.typed +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/tool_calling/__init__.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/tool_calling/human_in_the_loop.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/tool_calling/utils.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/__init__.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_agent.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_human_in_the_loop.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_load_embbeding.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_model_tool_emulator.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_pipline.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_router_model.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_tool_call_repair.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_tool_calling.py +0 -0
- {langchain_dev_utils-1.3.2/src/langchain_dev_utils/chat_models/adapters → langchain_dev_utils-1.3.4/tests/utils}/__init__.py +0 -0
- {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/utils/register.py +0 -0
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# Python-generated files
|
|
2
|
-
__pycache__/
|
|
3
|
-
*.py[oc]
|
|
4
|
-
build/
|
|
5
|
-
dist/
|
|
6
|
-
wheels/
|
|
7
|
-
*.egg-info
|
|
8
|
-
|
|
9
|
-
# Virtual environments
|
|
10
|
-
.venv
|
|
11
|
-
.env
|
|
12
|
-
.benchmarks
|
|
13
|
-
data/
|
|
14
|
-
node_modules
|
|
15
|
-
dist
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
.env
|
|
12
|
+
.benchmarks
|
|
13
|
+
data/
|
|
14
|
+
node_modules
|
|
15
|
+
dist
|
|
16
16
|
site/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
3.11
|
|
1
|
+
3.11
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langchain-dev-utils
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
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,86 +1,86 @@
|
|
|
1
|
-
# 🦜️🧰 langchain-dev-utils
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
<em>🚀 专为 LangChain 和 LangGraph 开发者打造的高效工具库</em>
|
|
5
|
-
</p>
|
|
6
|
-
|
|
7
|
-
<p align="center">
|
|
8
|
-
📚 <a href="https://tbice123123.github.io/langchain-dev-utils/">English</a> •
|
|
9
|
-
<a href="https://tbice123123.github.io/langchain-dev-utils/zh/">中文</a>
|
|
10
|
-
</p>
|
|
11
|
-
|
|
12
|
-
[](https://pypi.org/project/langchain-dev-utils/)
|
|
13
|
-
[](https://opensource.org/licenses/MIT)
|
|
14
|
-
[](https://www.python.org/downloads)
|
|
15
|
-
[](https://pepy.tech/project/langchain-dev-utils)
|
|
16
|
-
[](https://tbice123123.github.io/langchain-dev-utils/zh/)
|
|
17
|
-
|
|
18
|
-
> 当前为中文版,英文版请访问[English Version](https://github.com/TBice123123/langchain-dev-utils/blob/master/README.md)
|
|
19
|
-
|
|
20
|
-
## ✨ 为什么选择 langchain-dev-utils?
|
|
21
|
-
|
|
22
|
-
厌倦了在 LangChain 开发中编写重复代码?`langchain-dev-utils` 正是您需要的解决方案!这个轻量但功能强大的工具库专为提升 LangChain 和 LangGraph 开发体验而设计,帮助您:
|
|
23
|
-
|
|
24
|
-
- ⚡ **提升开发效率** - 减少样板代码,让您专注于核心功能
|
|
25
|
-
- 🧩 **简化复杂流程** - 轻松管理多模型、多工具和多智能体应用
|
|
26
|
-
- 🔧 **增强代码质量** - 提高一致性和可读性,减少维护成本
|
|
27
|
-
- 🎯 **加速原型开发** - 快速实现想法,更快迭代验证
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## 🎯 核心功能
|
|
31
|
-
|
|
32
|
-
- **🔌 统一的模型管理** - 通过字符串指定模型提供商,轻松切换和组合不同模型
|
|
33
|
-
- **💬 灵活的消息处理** - 支持思维链拼接、流式处理和消息格式化
|
|
34
|
-
- **🛠️ 强大的工具调用** - 内置工具调用检测、参数解析和人工审核功能
|
|
35
|
-
- **🤖 高效的 Agent 开发** - 简化智能体创建流程,扩充更多的常用中间件
|
|
36
|
-
- **📊 灵活的状态图组合** - 支持串行和并行方式组合多个 StateGraph
|
|
37
|
-
|
|
38
|
-
## ⚡ 快速开始
|
|
39
|
-
|
|
40
|
-
**1. 安装 `langchain-dev-utils`**
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
pip install -U "langchain-dev-utils[standard]"
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**2. 开始使用**
|
|
47
|
-
|
|
48
|
-
```python
|
|
49
|
-
from langchain.tools import tool
|
|
50
|
-
from langchain_core.messages import HumanMessage
|
|
51
|
-
from langchain_dev_utils.chat_models import register_model_provider, load_chat_model
|
|
52
|
-
from langchain_dev_utils.agents import create_agent
|
|
53
|
-
|
|
54
|
-
# 注册模型提供商
|
|
55
|
-
register_model_provider("vllm", "openai-compatible", base_url="http://localhost:8000/v1")
|
|
56
|
-
|
|
57
|
-
@tool
|
|
58
|
-
def get_current_weather(location: str) -> str:
|
|
59
|
-
"""获取指定地点的当前天气"""
|
|
60
|
-
return f"25度,{location}"
|
|
61
|
-
|
|
62
|
-
# 使用字符串动态加载模型
|
|
63
|
-
model = load_chat_model("vllm:qwen3-4b")
|
|
64
|
-
response = model.invoke("你好")
|
|
65
|
-
print(response)
|
|
66
|
-
|
|
67
|
-
# 创建智能体
|
|
68
|
-
agent = create_agent("vllm:qwen3-4b", tools=[get_current_weather])
|
|
69
|
-
response = agent.invoke({"messages": [HumanMessage(content="今天纽约的天气如何?")]})
|
|
70
|
-
print(response)
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
**获取更多的本库功能,请访问[完整文档](https://tbice123123.github.io/langchain-dev-utils/zh/)**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
## 🛠️ GitHub 仓库
|
|
77
|
-
|
|
78
|
-
访问 [GitHub 仓库](https://github.com/TBice123123/langchain-dev-utils) 查看源代码和问题。
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
<div align="center">
|
|
84
|
-
<p>由 ❤️ 和 ☕ 驱动开发</p>
|
|
85
|
-
<p>如果这个项目对您有帮助,请给我们一个 ⭐️</p>
|
|
1
|
+
# 🦜️🧰 langchain-dev-utils
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<em>🚀 专为 LangChain 和 LangGraph 开发者打造的高效工具库</em>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
📚 <a href="https://tbice123123.github.io/langchain-dev-utils/">English</a> •
|
|
9
|
+
<a href="https://tbice123123.github.io/langchain-dev-utils/zh/">中文</a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
12
|
+
[](https://pypi.org/project/langchain-dev-utils/)
|
|
13
|
+
[](https://opensource.org/licenses/MIT)
|
|
14
|
+
[](https://www.python.org/downloads)
|
|
15
|
+
[](https://pepy.tech/project/langchain-dev-utils)
|
|
16
|
+
[](https://tbice123123.github.io/langchain-dev-utils/zh/)
|
|
17
|
+
|
|
18
|
+
> 当前为中文版,英文版请访问[English Version](https://github.com/TBice123123/langchain-dev-utils/blob/master/README.md)
|
|
19
|
+
|
|
20
|
+
## ✨ 为什么选择 langchain-dev-utils?
|
|
21
|
+
|
|
22
|
+
厌倦了在 LangChain 开发中编写重复代码?`langchain-dev-utils` 正是您需要的解决方案!这个轻量但功能强大的工具库专为提升 LangChain 和 LangGraph 开发体验而设计,帮助您:
|
|
23
|
+
|
|
24
|
+
- ⚡ **提升开发效率** - 减少样板代码,让您专注于核心功能
|
|
25
|
+
- 🧩 **简化复杂流程** - 轻松管理多模型、多工具和多智能体应用
|
|
26
|
+
- 🔧 **增强代码质量** - 提高一致性和可读性,减少维护成本
|
|
27
|
+
- 🎯 **加速原型开发** - 快速实现想法,更快迭代验证
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## 🎯 核心功能
|
|
31
|
+
|
|
32
|
+
- **🔌 统一的模型管理** - 通过字符串指定模型提供商,轻松切换和组合不同模型
|
|
33
|
+
- **💬 灵活的消息处理** - 支持思维链拼接、流式处理和消息格式化
|
|
34
|
+
- **🛠️ 强大的工具调用** - 内置工具调用检测、参数解析和人工审核功能
|
|
35
|
+
- **🤖 高效的 Agent 开发** - 简化智能体创建流程,扩充更多的常用中间件
|
|
36
|
+
- **📊 灵活的状态图组合** - 支持串行和并行方式组合多个 StateGraph
|
|
37
|
+
|
|
38
|
+
## ⚡ 快速开始
|
|
39
|
+
|
|
40
|
+
**1. 安装 `langchain-dev-utils`**
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install -U "langchain-dev-utils[standard]"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**2. 开始使用**
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from langchain.tools import tool
|
|
50
|
+
from langchain_core.messages import HumanMessage
|
|
51
|
+
from langchain_dev_utils.chat_models import register_model_provider, load_chat_model
|
|
52
|
+
from langchain_dev_utils.agents import create_agent
|
|
53
|
+
|
|
54
|
+
# 注册模型提供商
|
|
55
|
+
register_model_provider("vllm", "openai-compatible", base_url="http://localhost:8000/v1")
|
|
56
|
+
|
|
57
|
+
@tool
|
|
58
|
+
def get_current_weather(location: str) -> str:
|
|
59
|
+
"""获取指定地点的当前天气"""
|
|
60
|
+
return f"25度,{location}"
|
|
61
|
+
|
|
62
|
+
# 使用字符串动态加载模型
|
|
63
|
+
model = load_chat_model("vllm:qwen3-4b")
|
|
64
|
+
response = model.invoke("你好")
|
|
65
|
+
print(response)
|
|
66
|
+
|
|
67
|
+
# 创建智能体
|
|
68
|
+
agent = create_agent("vllm:qwen3-4b", tools=[get_current_weather])
|
|
69
|
+
response = agent.invoke({"messages": [HumanMessage(content="今天纽约的天气如何?")]})
|
|
70
|
+
print(response)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**获取更多的本库功能,请访问[完整文档](https://tbice123123.github.io/langchain-dev-utils/zh/)**
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
## 🛠️ GitHub 仓库
|
|
77
|
+
|
|
78
|
+
访问 [GitHub 仓库](https://github.com/TBice123123/langchain-dev-utils) 查看源代码和问题。
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
<div align="center">
|
|
84
|
+
<p>由 ❤️ 和 ☕ 驱动开发</p>
|
|
85
|
+
<p>如果这个项目对您有帮助,请给我们一个 ⭐️</p>
|
|
86
86
|
</div>
|
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "langchain-dev-utils"
|
|
3
|
-
version = "1.3.
|
|
4
|
-
description = "A practical utility library for LangChain and LangGraph development"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
authors = [{ name = "tiebingice", email = "tiebingice123@outlook.com" }]
|
|
7
|
-
requires-python = ">=3.11"
|
|
8
|
-
dependencies = ["langchain>=1.2.0", "langchain-core>=1.2.5", "langgraph>=1.0.0"]
|
|
9
|
-
|
|
10
|
-
[project.urls]
|
|
11
|
-
"Source Code" = "https://github.com/TBice123123/langchain-dev-utils"
|
|
12
|
-
repository = "https://github.com/TBice123123/langchain-dev-utils"
|
|
13
|
-
documentation = "https://tbice123123.github.io/langchain-dev-utils"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
[project.optional-dependencies]
|
|
17
|
-
standard = ["json-repair>=0.53.1", "langchain-openai"]
|
|
18
|
-
|
|
19
|
-
[build-system]
|
|
20
|
-
requires = ["hatchling"]
|
|
21
|
-
build-backend = "hatchling.build"
|
|
22
|
-
|
|
23
|
-
[tool.hatch.build]
|
|
24
|
-
exclude = ["/data", "/docs", "mkdocs.yml"]
|
|
25
|
-
|
|
26
|
-
[tool.pytest.ini_options]
|
|
27
|
-
asyncio_mode = "auto"
|
|
28
|
-
testpaths = ["tests"]
|
|
29
|
-
python_files = ["test_*.py"]
|
|
30
|
-
python_functions = ["test_*"]
|
|
31
|
-
|
|
32
|
-
[dependency-groups]
|
|
33
|
-
dev = ["langchain-model-profiles>=0.0.5", "ruff>=0.14.5"]
|
|
34
|
-
docs = ["mkdocs-material>=9.7.0", "mkdocs-static-i18n>=1.3.0"]
|
|
35
|
-
tests = [
|
|
36
|
-
"python-dotenv>=1.1.1",
|
|
37
|
-
"langchain-tests>=1.0.0",
|
|
38
|
-
"langchain-deepseek>=1.0.0",
|
|
39
|
-
"langchain-qwq>=0.3.0",
|
|
40
|
-
"langchain-ollama>=1.0.0",
|
|
41
|
-
"langchain-community>=0.4.1",
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
[tool.ruff.lint]
|
|
46
|
-
select = ["E", "F", "I", "PGH003", "T201"]
|
|
47
|
-
ignore = ["E501"]
|
|
1
|
+
[project]
|
|
2
|
+
name = "langchain-dev-utils"
|
|
3
|
+
version = "1.3.4"
|
|
4
|
+
description = "A practical utility library for LangChain and LangGraph development"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [{ name = "tiebingice", email = "tiebingice123@outlook.com" }]
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
dependencies = ["langchain>=1.2.0", "langchain-core>=1.2.5", "langgraph>=1.0.0"]
|
|
9
|
+
|
|
10
|
+
[project.urls]
|
|
11
|
+
"Source Code" = "https://github.com/TBice123123/langchain-dev-utils"
|
|
12
|
+
repository = "https://github.com/TBice123123/langchain-dev-utils"
|
|
13
|
+
documentation = "https://tbice123123.github.io/langchain-dev-utils"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
standard = ["json-repair>=0.53.1", "langchain-openai"]
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["hatchling"]
|
|
21
|
+
build-backend = "hatchling.build"
|
|
22
|
+
|
|
23
|
+
[tool.hatch.build]
|
|
24
|
+
exclude = ["/data", "/docs", "mkdocs.yml"]
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
asyncio_mode = "auto"
|
|
28
|
+
testpaths = ["tests"]
|
|
29
|
+
python_files = ["test_*.py"]
|
|
30
|
+
python_functions = ["test_*"]
|
|
31
|
+
|
|
32
|
+
[dependency-groups]
|
|
33
|
+
dev = ["langchain-model-profiles>=0.0.5", "ruff>=0.14.5"]
|
|
34
|
+
docs = ["mkdocs-material>=9.7.0", "mkdocs-static-i18n>=1.3.0"]
|
|
35
|
+
tests = [
|
|
36
|
+
"python-dotenv>=1.1.1",
|
|
37
|
+
"langchain-tests>=1.0.0",
|
|
38
|
+
"langchain-deepseek>=1.0.0",
|
|
39
|
+
"langchain-qwq>=0.3.0",
|
|
40
|
+
"langchain-ollama>=1.0.0",
|
|
41
|
+
"langchain-community>=0.4.1",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint]
|
|
46
|
+
select = ["E", "F", "I", "PGH003", "T201"]
|
|
47
|
+
ignore = ["E501"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.3.4"
|
|
@@ -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
|
-
|
|
106
|
-
|
|
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):
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any, Awaitable, Callable, Optional, cast
|
|
3
|
+
|
|
4
|
+
from langchain.tools import ToolRuntime
|
|
5
|
+
from langchain_core.messages import AnyMessage, HumanMessage
|
|
6
|
+
from langchain_core.tools import BaseTool, StructuredTool
|
|
7
|
+
from langgraph.graph.state import CompiledStateGraph
|
|
8
|
+
|
|
9
|
+
from langchain_dev_utils.message_convert import format_sequence
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _process_input(request: str, runtime: ToolRuntime) -> str:
|
|
13
|
+
return request
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _process_output(
|
|
17
|
+
request: str, response: list[AnyMessage], runtime: ToolRuntime
|
|
18
|
+
) -> Any:
|
|
19
|
+
return response[-1].content
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def wrap_agent_as_tool(
|
|
23
|
+
agent: CompiledStateGraph,
|
|
24
|
+
tool_name: Optional[str] = None,
|
|
25
|
+
tool_description: Optional[str] = None,
|
|
26
|
+
pre_input_hooks: Optional[
|
|
27
|
+
tuple[
|
|
28
|
+
Callable[[str, ToolRuntime], str],
|
|
29
|
+
Callable[[str, ToolRuntime], Awaitable[str]],
|
|
30
|
+
]
|
|
31
|
+
| Callable[[str, ToolRuntime], str]
|
|
32
|
+
] = None,
|
|
33
|
+
post_output_hooks: Optional[
|
|
34
|
+
tuple[
|
|
35
|
+
Callable[[str, list[AnyMessage], ToolRuntime], Any],
|
|
36
|
+
Callable[[str, list[AnyMessage], ToolRuntime], Awaitable[Any]],
|
|
37
|
+
]
|
|
38
|
+
| Callable[[str, list[AnyMessage], ToolRuntime], Any]
|
|
39
|
+
] = None,
|
|
40
|
+
) -> BaseTool:
|
|
41
|
+
"""Wraps an agent as a tool
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
agent: The agent to wrap
|
|
45
|
+
tool_name: The name of the tool
|
|
46
|
+
tool_description: The description of the tool
|
|
47
|
+
pre_input_hooks: Hooks to run before the input is processed
|
|
48
|
+
post_output_hooks: Hooks to run after the output is processed
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
BaseTool: The wrapped agent as a tool
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
>>> from langchain_dev_utils.agents import wrap_agent_as_tool, create_agent
|
|
55
|
+
>>>
|
|
56
|
+
>>> call_time_agent_tool = wrap_agent_as_tool(
|
|
57
|
+
... time_agent,
|
|
58
|
+
... tool_name="call_time_agent",
|
|
59
|
+
... tool_description="Used to invoke the time sub-agent to perform time-related tasks"
|
|
60
|
+
... )
|
|
61
|
+
>>>
|
|
62
|
+
>>> agent = create_agent("vllm:qwen3-4b", tools=[call_time_agent_tool], name="agent")
|
|
63
|
+
|
|
64
|
+
>>> response = agent.invoke({"messages": [HumanMessage(content="What time is it now?")]})
|
|
65
|
+
>>> response
|
|
66
|
+
"""
|
|
67
|
+
if agent.name is None:
|
|
68
|
+
raise ValueError("Agent name must not be None")
|
|
69
|
+
|
|
70
|
+
process_input = _process_input
|
|
71
|
+
process_input_async = _process_input
|
|
72
|
+
process_output = _process_output
|
|
73
|
+
process_output_async = _process_output
|
|
74
|
+
|
|
75
|
+
if pre_input_hooks:
|
|
76
|
+
if isinstance(pre_input_hooks, tuple):
|
|
77
|
+
process_input = pre_input_hooks[0]
|
|
78
|
+
process_input_async = pre_input_hooks[1]
|
|
79
|
+
else:
|
|
80
|
+
process_input = pre_input_hooks
|
|
81
|
+
process_input_async = pre_input_hooks
|
|
82
|
+
|
|
83
|
+
if post_output_hooks:
|
|
84
|
+
if isinstance(post_output_hooks, tuple):
|
|
85
|
+
process_output = post_output_hooks[0]
|
|
86
|
+
process_output_async = post_output_hooks[1]
|
|
87
|
+
else:
|
|
88
|
+
process_output = post_output_hooks
|
|
89
|
+
process_output_async = post_output_hooks
|
|
90
|
+
|
|
91
|
+
def call_agent(
|
|
92
|
+
request: str,
|
|
93
|
+
runtime: ToolRuntime,
|
|
94
|
+
) -> str:
|
|
95
|
+
request = process_input(request, runtime) if process_input else request
|
|
96
|
+
|
|
97
|
+
messages = [HumanMessage(content=request)]
|
|
98
|
+
response = agent.invoke({"messages": messages})
|
|
99
|
+
|
|
100
|
+
response = process_output(request, response["messages"], runtime)
|
|
101
|
+
return response
|
|
102
|
+
|
|
103
|
+
async def acall_agent(
|
|
104
|
+
request: str,
|
|
105
|
+
runtime: ToolRuntime,
|
|
106
|
+
) -> str:
|
|
107
|
+
if asyncio.iscoroutinefunction(process_input_async):
|
|
108
|
+
request = await process_input_async(request, runtime)
|
|
109
|
+
else:
|
|
110
|
+
request = cast(str, process_input_async(request, runtime))
|
|
111
|
+
|
|
112
|
+
messages = [HumanMessage(content=request)]
|
|
113
|
+
response = await agent.ainvoke({"messages": messages})
|
|
114
|
+
|
|
115
|
+
if asyncio.iscoroutinefunction(process_output_async):
|
|
116
|
+
response = await process_output_async(
|
|
117
|
+
request, response["messages"], runtime
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
response = process_output(request, response["messages"], runtime)
|
|
121
|
+
|
|
122
|
+
return response
|
|
123
|
+
|
|
124
|
+
if tool_name is None:
|
|
125
|
+
tool_name = f"transfor_to_{agent.name}"
|
|
126
|
+
if not tool_name.endswith("_agent"):
|
|
127
|
+
tool_name += "_agent"
|
|
128
|
+
|
|
129
|
+
if tool_description is None:
|
|
130
|
+
tool_description = f"This tool transforms input to {agent.name}"
|
|
131
|
+
|
|
132
|
+
return StructuredTool.from_function(
|
|
133
|
+
func=call_agent,
|
|
134
|
+
coroutine=acall_agent,
|
|
135
|
+
name=tool_name,
|
|
136
|
+
description=tool_description,
|
|
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_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
|
+
)
|