langchain-dev-utils 1.2.8__py3-none-any.whl → 1.2.10__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.
- langchain_dev_utils/__init__.py +1 -1
- langchain_dev_utils/chat_models/adapters/openai_compatible.py +584 -577
- langchain_dev_utils/chat_models/types.py +2 -2
- langchain_dev_utils/embeddings/base.py +26 -26
- langchain_dev_utils/message_convert/content.py +18 -8
- langchain_dev_utils/tool_calling/utils.py +7 -7
- {langchain_dev_utils-1.2.8.dist-info → langchain_dev_utils-1.2.10.dist-info}/METADATA +38 -20
- {langchain_dev_utils-1.2.8.dist-info → langchain_dev_utils-1.2.10.dist-info}/RECORD +10 -10
- {langchain_dev_utils-1.2.8.dist-info → langchain_dev_utils-1.2.10.dist-info}/WHEEL +0 -0
- {langchain_dev_utils-1.2.8.dist-info → langchain_dev_utils-1.2.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,11 +9,11 @@ ToolChoiceType = list[Literal["auto", "none", "required", "specific"]]
|
|
|
9
9
|
|
|
10
10
|
ResponseFormatType = list[Literal["json_schema", "json_mode"]]
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
ReasoningKeepPolicy = Literal["never", "current", "all"]
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class CompatibilityOptions(TypedDict):
|
|
16
16
|
supported_tool_choice: NotRequired[ToolChoiceType]
|
|
17
17
|
supported_response_format: NotRequired[ResponseFormatType]
|
|
18
|
-
|
|
18
|
+
reasoning_keep_policy: NotRequired[ReasoningKeepPolicy]
|
|
19
19
|
include_usage: NotRequired[bool]
|
|
@@ -218,30 +218,30 @@ def load_embeddings(
|
|
|
218
218
|
):
|
|
219
219
|
raise ValueError(f"Provider {provider} not registered")
|
|
220
220
|
|
|
221
|
-
if provider in
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
221
|
+
if provider in _EMBEDDINGS_PROVIDERS_DICT:
|
|
222
|
+
embeddings = _EMBEDDINGS_PROVIDERS_DICT[provider]["embeddings_model"]
|
|
223
|
+
if isinstance(embeddings, str):
|
|
224
|
+
if not (api_key := kwargs.get("api_key")):
|
|
225
|
+
api_key = secret_from_env(f"{provider.upper()}_API_KEY", default=None)()
|
|
226
|
+
if not api_key:
|
|
227
|
+
raise ValueError(
|
|
228
|
+
f"API key for {provider} not found. Please set it in the environment."
|
|
229
|
+
)
|
|
230
|
+
kwargs["api_key"] = api_key
|
|
231
|
+
if embeddings == "openai-compatible":
|
|
232
|
+
kwargs["check_embedding_ctx_length"] = False
|
|
233
|
+
embeddings = "openai"
|
|
234
|
+
return init_embeddings(
|
|
235
|
+
model=model,
|
|
236
|
+
provider=embeddings,
|
|
237
|
+
base_url=_EMBEDDINGS_PROVIDERS_DICT[provider]["base_url"],
|
|
238
|
+
**kwargs,
|
|
239
|
+
)
|
|
240
|
+
else:
|
|
241
|
+
if base_url := _EMBEDDINGS_PROVIDERS_DICT[provider].get("base_url"):
|
|
242
|
+
url_key = _get_base_url_field_name(embeddings)
|
|
243
|
+
if url_key is not None:
|
|
244
|
+
kwargs.update({url_key: base_url})
|
|
245
|
+
return embeddings(model=model, **kwargs)
|
|
242
246
|
else:
|
|
243
|
-
|
|
244
|
-
url_key = _get_base_url_field_name(embeddings)
|
|
245
|
-
if url_key is not None:
|
|
246
|
-
kwargs.update({url_key: base_url})
|
|
247
|
-
return embeddings(model=model, **kwargs)
|
|
247
|
+
return init_embeddings(model, provider=provider, **kwargs)
|
|
@@ -52,8 +52,10 @@ def convert_reasoning_content_for_ai_message(
|
|
|
52
52
|
reasoning_content = _get_reasoning_content(model_response)
|
|
53
53
|
|
|
54
54
|
if reasoning_content:
|
|
55
|
-
model_response.
|
|
56
|
-
|
|
55
|
+
return model_response.model_copy(
|
|
56
|
+
update={
|
|
57
|
+
"content": f"{think_tag[0]}{reasoning_content}{think_tag[1]}{model_response.content}"
|
|
58
|
+
}
|
|
57
59
|
)
|
|
58
60
|
return model_response
|
|
59
61
|
|
|
@@ -99,12 +101,16 @@ def convert_reasoning_content_for_chunk_iterator(
|
|
|
99
101
|
reasoning_content = _get_reasoning_content(chunk)
|
|
100
102
|
if reasoning_content:
|
|
101
103
|
if isfirst:
|
|
102
|
-
chunk
|
|
104
|
+
chunk = chunk.model_copy(
|
|
105
|
+
update={"content": f"{think_tag[0]}{reasoning_content}"}
|
|
106
|
+
)
|
|
103
107
|
isfirst = False
|
|
104
108
|
else:
|
|
105
|
-
chunk.content
|
|
109
|
+
chunk = chunk.model_copy(update={"content": reasoning_content})
|
|
106
110
|
elif chunk.content and isend and not isfirst:
|
|
107
|
-
chunk
|
|
111
|
+
chunk = chunk.model_copy(
|
|
112
|
+
update={"content": f"{think_tag[1]}{chunk.content}"}
|
|
113
|
+
)
|
|
108
114
|
isend = False
|
|
109
115
|
yield chunk
|
|
110
116
|
|
|
@@ -149,12 +155,16 @@ async def aconvert_reasoning_content_for_chunk_iterator(
|
|
|
149
155
|
reasoning_content = _get_reasoning_content(chunk)
|
|
150
156
|
if reasoning_content:
|
|
151
157
|
if isfirst:
|
|
152
|
-
chunk
|
|
158
|
+
chunk = chunk.model_copy(
|
|
159
|
+
update={"content": f"{think_tag[0]}{reasoning_content}"}
|
|
160
|
+
)
|
|
153
161
|
isfirst = False
|
|
154
162
|
else:
|
|
155
|
-
chunk.content
|
|
163
|
+
chunk = chunk.model_copy(update={"content": reasoning_content})
|
|
156
164
|
elif chunk.content and isend and not isfirst:
|
|
157
|
-
chunk
|
|
165
|
+
chunk = chunk.model_copy(
|
|
166
|
+
update={"content": f"{think_tag[1]}{chunk.content}"}
|
|
167
|
+
)
|
|
158
168
|
isend = False
|
|
159
169
|
yield chunk
|
|
160
170
|
|
|
@@ -62,20 +62,20 @@ def parse_tool_calling(
|
|
|
62
62
|
... tool_calls = parse_tool_calling(response)
|
|
63
63
|
"""
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
tool_calls = None
|
|
66
66
|
|
|
67
67
|
tool_call_blocks = [
|
|
68
68
|
block for block in message.content_blocks if block["type"] == "tool_call"
|
|
69
69
|
]
|
|
70
70
|
if tool_call_blocks:
|
|
71
|
-
|
|
71
|
+
tool_calls = tool_call_blocks
|
|
72
72
|
|
|
73
|
-
if not
|
|
74
|
-
|
|
73
|
+
if not tool_calls:
|
|
74
|
+
tool_calls = message.tool_calls
|
|
75
75
|
|
|
76
|
-
if not
|
|
76
|
+
if not tool_calls:
|
|
77
77
|
raise ValueError("No tool call found in message")
|
|
78
78
|
|
|
79
79
|
if first_tool_call_only:
|
|
80
|
-
return (
|
|
81
|
-
return [(tool_call["name"], tool_call["args"]) for tool_call in
|
|
80
|
+
return (tool_calls[0]["name"], tool_calls[0]["args"])
|
|
81
|
+
return [(tool_call["name"], tool_call["args"]) for tool_call in tool_calls]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: langchain-dev-utils
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.10
|
|
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
|
|
@@ -192,52 +192,70 @@ def get_current_time() -> str:
|
|
|
192
192
|
|
|
193
193
|
### 4. **Agent Development**
|
|
194
194
|
|
|
195
|
-
Includes the following
|
|
195
|
+
Includes the following capabilities:
|
|
196
196
|
|
|
197
|
-
-
|
|
198
|
-
-
|
|
197
|
+
- Multi-agent construction
|
|
198
|
+
- Commonly used middleware components
|
|
199
199
|
|
|
200
|
-
#### 4.1 Agent
|
|
200
|
+
#### 4.1 Multi-Agent Construction
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
Wrapping an agent as a tool is a common implementation pattern in multi-agent systems, as elaborated in the official LangChain documentation. To support this pattern, this library provides a pre-built utility function `wrap_agent_as_tool`, which encapsulates an agent instance into a tool that can be invoked by other agents.
|
|
203
203
|
|
|
204
|
-
Usage
|
|
204
|
+
**Usage Example**:
|
|
205
205
|
|
|
206
206
|
```python
|
|
207
|
-
|
|
207
|
+
import datetime
|
|
208
|
+
from langchain_dev_utils.agents import create_agent, wrap_agent_as_tool
|
|
208
209
|
from langchain.agents import AgentState
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
@tool
|
|
212
|
+
def get_current_time() -> str:
|
|
213
|
+
"""Get the current time"""
|
|
214
|
+
return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
215
|
+
|
|
216
|
+
time_agent = create_agent("vllm:qwen3-4b", tools=[get_current_time], name="time-agent")
|
|
217
|
+
call_time_agent_tool = wrap_agent_as_tool(time_agent)
|
|
218
|
+
|
|
219
|
+
agent = create_agent(
|
|
220
|
+
"vllm:qwen3-4b",
|
|
221
|
+
name="agent",
|
|
222
|
+
tools=[call_time_agent_tool],
|
|
223
|
+
)
|
|
224
|
+
response = agent.invoke(
|
|
225
|
+
{"messages": [{"role": "user", "content": "What time is it now?"}]}
|
|
226
|
+
)
|
|
212
227
|
print(response)
|
|
213
228
|
```
|
|
214
229
|
|
|
215
230
|
#### 4.2 Middleware
|
|
216
231
|
|
|
217
|
-
Provides
|
|
218
|
-
|
|
219
|
-
`ToolCallRepairMiddleware` is used to repair `invalid_tool_calls` generated by large language models.
|
|
232
|
+
Provides several commonly used middleware components. Below are examples using `ToolCallRepairMiddleware` and `PlanMiddleware`.
|
|
220
233
|
|
|
221
|
-
`
|
|
234
|
+
- `ToolCallRepairMiddleware` automatically repairs malformed tool calls found in the model's `invalid_tool_calls` output.
|
|
235
|
+
- `PlanMiddleware` enables task planning capabilities for agents.
|
|
222
236
|
|
|
223
237
|
```python
|
|
224
238
|
from langchain_dev_utils.agents.middleware import (
|
|
225
|
-
|
|
239
|
+
ToolCallRepairMiddleware,
|
|
226
240
|
PlanMiddleware,
|
|
227
241
|
)
|
|
228
242
|
|
|
229
243
|
agent = create_agent(
|
|
230
244
|
"vllm:qwen3-4b",
|
|
231
245
|
name="plan-agent",
|
|
232
|
-
middleware=[
|
|
233
|
-
|
|
234
|
-
|
|
246
|
+
middleware=[
|
|
247
|
+
ToolCallRepairMiddleware(),
|
|
248
|
+
PlanMiddleware(use_read_plan_tool=False)
|
|
249
|
+
]
|
|
235
250
|
)
|
|
236
|
-
response = agent.invoke({"messages": [{"role": "user", "content": "Give me a travel plan
|
|
251
|
+
response = agent.invoke({"messages": [{"role": "user", "content": "Give me a travel plan for visiting New York."}]})
|
|
237
252
|
print(response)
|
|
238
253
|
```
|
|
239
254
|
|
|
240
|
-
**For more
|
|
255
|
+
**For more details on agent development and a complete list of built-in middleware, please refer to**:
|
|
256
|
+
[Multi-Agent Construction](https://tbice123123.github.io/langchain-dev-utils-docs/en/agent-development/multi-agent.html),
|
|
257
|
+
[Middleware](https://tbice123123.github.io/langchain-dev-utils-docs/en/agent-development/middleware.html)
|
|
258
|
+
|
|
241
259
|
|
|
242
260
|
### 5. **State Graph Orchestration**
|
|
243
261
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
langchain_dev_utils/__init__.py,sha256=
|
|
1
|
+
langchain_dev_utils/__init__.py,sha256=GI4rgymQsPWdk2_d96NgmZBRuFM6yOZB-kysnrjBjVo,23
|
|
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
|
|
@@ -16,13 +16,13 @@ langchain_dev_utils/agents/middleware/tool_emulator.py,sha256=OgtPhqturaWzF4fRSJ
|
|
|
16
16
|
langchain_dev_utils/agents/middleware/tool_selection.py,sha256=dRH5ejR6N02Djwxt6Gd63MYkg6SV5pySlzaRt53OoZk,3113
|
|
17
17
|
langchain_dev_utils/chat_models/__init__.py,sha256=YSLUyHrWEEj4y4DtGFCOnDW02VIYZdfAH800m4Klgeg,224
|
|
18
18
|
langchain_dev_utils/chat_models/base.py,sha256=CVMfgqMRnIKv8z4babusa2c4RKVuiWTL39mPD8cHAf4,11880
|
|
19
|
-
langchain_dev_utils/chat_models/types.py,sha256=
|
|
19
|
+
langchain_dev_utils/chat_models/types.py,sha256=kVLbT-IbvNtWPVmyVmh58le5r8XCqrEwuFB9-TWCBJk,672
|
|
20
20
|
langchain_dev_utils/chat_models/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
langchain_dev_utils/chat_models/adapters/openai_compatible.py,sha256=
|
|
21
|
+
langchain_dev_utils/chat_models/adapters/openai_compatible.py,sha256=v9ewQFn75Fl6urtVo4fZgg2RRrzRXGluKf-egYAI5A0,22658
|
|
22
22
|
langchain_dev_utils/embeddings/__init__.py,sha256=zbEOaV86TUi9Zrg_dH9dpdgacWg31HMJTlTQknA9EKk,244
|
|
23
|
-
langchain_dev_utils/embeddings/base.py,sha256=
|
|
23
|
+
langchain_dev_utils/embeddings/base.py,sha256=BGoWY0L7nG9iRV3d4sSagXhECXrwvS1xA-A_OVltn3k,9406
|
|
24
24
|
langchain_dev_utils/message_convert/__init__.py,sha256=ZGrHGXPKMrZ_p9MqfIVZ4jgbEyb7aC4Q7X-muuThIYU,457
|
|
25
|
-
langchain_dev_utils/message_convert/content.py,sha256=
|
|
25
|
+
langchain_dev_utils/message_convert/content.py,sha256=2V1g21byg3iLv5RjUW8zv3jwYwV7IH2hNim7jGRsIes,8096
|
|
26
26
|
langchain_dev_utils/message_convert/format.py,sha256=1TOcJ09atH7LRtn_IIuBshKDXAyqoy3Q9b0Po-S-F9g,2377
|
|
27
27
|
langchain_dev_utils/pipeline/__init__.py,sha256=eE6WktaLHDkqMeXDIDaLtm-OPTwtsX_Av8iK9uYrceo,186
|
|
28
28
|
langchain_dev_utils/pipeline/parallel.py,sha256=nwZWbdSNeyanC9WufoJBTceotgT--UnPOfStXjgNMOc,5271
|
|
@@ -30,8 +30,8 @@ langchain_dev_utils/pipeline/sequential.py,sha256=sYJXQzVHDKUc-UV-HMv38JTPnse1A7
|
|
|
30
30
|
langchain_dev_utils/pipeline/types.py,sha256=T3aROKKXeWvd0jcH5XkgMDQfEkLfPaiOhhV2q58fDHs,112
|
|
31
31
|
langchain_dev_utils/tool_calling/__init__.py,sha256=mu_WxKMcu6RoTf4vkTPbA1WSBSNc6YIqyBtOQ6iVQj4,322
|
|
32
32
|
langchain_dev_utils/tool_calling/human_in_the_loop.py,sha256=7Z_QO5OZUR6K8nLoIcafc6osnvX2IYNorOJcbx6bVso,9672
|
|
33
|
-
langchain_dev_utils/tool_calling/utils.py,sha256=
|
|
34
|
-
langchain_dev_utils-1.2.
|
|
35
|
-
langchain_dev_utils-1.2.
|
|
36
|
-
langchain_dev_utils-1.2.
|
|
37
|
-
langchain_dev_utils-1.2.
|
|
33
|
+
langchain_dev_utils/tool_calling/utils.py,sha256=S4-KXQ8jWmpGTXYZitovF8rxKpaSSUkFruM8LDwvcvE,2765
|
|
34
|
+
langchain_dev_utils-1.2.10.dist-info/METADATA,sha256=2KpH7VEYB7v6uyWHJSvq0hyfNSLxlSsIZNueFbCIv44,13372
|
|
35
|
+
langchain_dev_utils-1.2.10.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
36
|
+
langchain_dev_utils-1.2.10.dist-info/licenses/LICENSE,sha256=AWAOzNEcsvCEzHOF0qby5OKxviVH_eT9Yce1sgJTico,1084
|
|
37
|
+
langchain_dev_utils-1.2.10.dist-info/RECORD,,
|
|
File without changes
|
{langchain_dev_utils-1.2.8.dist-info → langchain_dev_utils-1.2.10.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|