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.
Files changed (72) hide show
  1. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/.gitignore +15 -15
  2. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/.python-version +1 -1
  3. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/PKG-INFO +1 -1
  4. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/README_cn.md +85 -85
  5. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/pyproject.toml +47 -47
  6. langchain_dev_utils-1.3.4/src/langchain_dev_utils/__init__.py +1 -0
  7. langchain_dev_utils-1.3.4/src/langchain_dev_utils/agents/__init__.py +4 -0
  8. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/handoffs.py +17 -2
  9. langchain_dev_utils-1.3.4/src/langchain_dev_utils/agents/wrap.py +274 -0
  10. langchain_dev_utils-1.3.4/src/langchain_dev_utils/chat_models/adapters/__init__.py +3 -0
  11. langchain_dev_utils-1.3.4/src/langchain_dev_utils/chat_models/adapters/create_utils.py +53 -0
  12. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/chat_models/adapters/openai_compatible.py +23 -4
  13. langchain_dev_utils-1.3.4/src/langchain_dev_utils/chat_models/adapters/register_profiles.py +15 -0
  14. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/chat_models/base.py +4 -11
  15. langchain_dev_utils-1.3.4/src/langchain_dev_utils/embeddings/adapters/__init__.py +3 -0
  16. langchain_dev_utils-1.3.4/src/langchain_dev_utils/embeddings/adapters/create_utils.py +45 -0
  17. langchain_dev_utils-1.3.4/src/langchain_dev_utils/embeddings/adapters/openai_compatible.py +75 -0
  18. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/embeddings/base.py +11 -25
  19. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/message_convert/__init__.py +15 -15
  20. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/message_convert/format.py +69 -69
  21. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_chat_models.py +191 -185
  22. langchain_dev_utils-1.3.4/tests/test_embedding.py +22 -0
  23. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_handoffs_middleware.py +34 -39
  24. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_load_model.py +104 -104
  25. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_messages.py +164 -164
  26. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_plan_middleware.py +68 -68
  27. langchain_dev_utils-1.3.4/tests/test_wrap_agent.py +186 -0
  28. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/uv.lock +2883 -2883
  29. langchain_dev_utils-1.3.2/src/langchain_dev_utils/__init__.py +0 -1
  30. langchain_dev_utils-1.3.2/src/langchain_dev_utils/agents/__init__.py +0 -4
  31. langchain_dev_utils-1.3.2/src/langchain_dev_utils/agents/wrap.py +0 -140
  32. langchain_dev_utils-1.3.2/tests/test_wrap_agent.py +0 -102
  33. langchain_dev_utils-1.3.2/tests/utils/__init__.py +0 -0
  34. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/.vscode/settings.json +0 -0
  35. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/LICENSE +0 -0
  36. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/README.md +0 -0
  37. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/_utils.py +0 -0
  38. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/factory.py +0 -0
  39. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/file_system.py +0 -0
  40. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/__init__.py +0 -0
  41. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/format_prompt.py +0 -0
  42. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/model_fallback.py +0 -0
  43. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/model_router.py +0 -0
  44. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/plan.py +0 -0
  45. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/summarization.py +0 -0
  46. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/tool_call_repair.py +0 -0
  47. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/tool_emulator.py +0 -0
  48. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/middleware/tool_selection.py +0 -0
  49. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/agents/plan.py +0 -0
  50. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/chat_models/__init__.py +0 -0
  51. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/chat_models/types.py +0 -0
  52. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/embeddings/__init__.py +0 -0
  53. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/message_convert/content.py +0 -0
  54. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/pipeline/__init__.py +0 -0
  55. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/pipeline/parallel.py +0 -0
  56. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/pipeline/sequential.py +0 -0
  57. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/pipeline/types.py +0 -0
  58. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/py.typed +0 -0
  59. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/tool_calling/__init__.py +0 -0
  60. {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
  61. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/src/langchain_dev_utils/tool_calling/utils.py +0 -0
  62. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/__init__.py +0 -0
  63. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_agent.py +0 -0
  64. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_human_in_the_loop.py +0 -0
  65. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_load_embbeding.py +0 -0
  66. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_model_tool_emulator.py +0 -0
  67. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_pipline.py +0 -0
  68. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_router_model.py +0 -0
  69. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_tool_call_repair.py +0 -0
  70. {langchain_dev_utils-1.3.2 → langchain_dev_utils-1.3.4}/tests/test_tool_calling.py +0 -0
  71. {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
  72. {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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langchain-dev-utils
3
- Version: 1.3.2
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
- [![PyPI](https://img.shields.io/pypi/v/langchain-dev-utils.svg?color=%2334D058&label=pypi%20package)](https://pypi.org/project/langchain-dev-utils/)
13
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
- [![Python](https://img.shields.io/badge/python-3.11|3.12|3.13|3.14-%2334D058)](https://www.python.org/downloads)
15
- [![Downloads](https://static.pepy.tech/badge/langchain-dev-utils/month)](https://pepy.tech/project/langchain-dev-utils)
16
- [![Documentation](https://img.shields.io/badge/docs-latest-blue)](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
+ [![PyPI](https://img.shields.io/pypi/v/langchain-dev-utils.svg?color=%2334D058&label=pypi%20package)](https://pypi.org/project/langchain-dev-utils/)
13
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
14
+ [![Python](https://img.shields.io/badge/python-3.11|3.12|3.13|3.14-%2334D058)](https://www.python.org/downloads)
15
+ [![Downloads](https://static.pepy.tech/badge/langchain-dev-utils/month)](https://pepy.tech/project/langchain-dev-utils)
16
+ [![Documentation](https://img.shields.io/badge/docs-latest-blue)](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.2"
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"
@@ -0,0 +1,4 @@
1
+ from .factory import create_agent
2
+ from .wrap import wrap_agent_as_tool, wrap_all_agents_as_tool
3
+
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):
@@ -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
+ )
@@ -0,0 +1,3 @@
1
+ from .create_utils import create_openai_compatible_model
2
+
3
+ __all__ = ["create_openai_compatible_model"]