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.
@@ -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
- ReasoningContentKeepType = Literal["discard", "temp", "retain"]
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
- reasoning_content_keep_type: NotRequired[ReasoningContentKeepType]
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 _SUPPORTED_PROVIDERS:
222
- return init_embeddings(model, provider=provider, **kwargs)
223
-
224
- embeddings = _EMBEDDINGS_PROVIDERS_DICT[provider]["embeddings_model"]
225
- if isinstance(embeddings, str):
226
- if not (api_key := kwargs.get("api_key")):
227
- api_key = secret_from_env(f"{provider.upper()}_API_KEY", default=None)()
228
- if not api_key:
229
- raise ValueError(
230
- f"API key for {provider} not found. Please set it in the environment."
231
- )
232
- kwargs["api_key"] = api_key
233
- if embeddings == "openai-compatible":
234
- kwargs["check_embedding_ctx_length"] = False
235
- embeddings = "openai"
236
- return init_embeddings(
237
- model=model,
238
- provider=embeddings,
239
- base_url=_EMBEDDINGS_PROVIDERS_DICT[provider]["base_url"],
240
- **kwargs,
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
- if base_url := _EMBEDDINGS_PROVIDERS_DICT[provider].get("base_url"):
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.content = (
56
- f"{think_tag[0]}{reasoning_content}{think_tag[1]}{model_response.content}"
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.content = f"{think_tag[0]}{reasoning_content}"
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 = reasoning_content
109
+ chunk = chunk.model_copy(update={"content": reasoning_content})
106
110
  elif chunk.content and isend and not isfirst:
107
- chunk.content = f"{think_tag[1]}{chunk.content}"
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.content = f"{think_tag[0]}{reasoning_content}"
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 = reasoning_content
163
+ chunk = chunk.model_copy(update={"content": reasoning_content})
156
164
  elif chunk.content and isend and not isfirst:
157
- chunk.content = f"{think_tag[1]}{chunk.content}"
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
- tool_call = None
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
- tool_call = tool_call_blocks
71
+ tool_calls = tool_call_blocks
72
72
 
73
- if not tool_call:
74
- tool_call = message.tool_calls
73
+ if not tool_calls:
74
+ tool_calls = message.tool_calls
75
75
 
76
- if not tool_call:
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 (tool_call[0]["name"], tool_call[0]["args"])
81
- return [(tool_call["name"], tool_call["args"]) for tool_call in tool_call]
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.8
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 features:
195
+ Includes the following capabilities:
196
196
 
197
- - Predefined agent factory functions
198
- - Common middleware components
197
+ - Multi-agent construction
198
+ - Commonly used middleware components
199
199
 
200
- #### 4.1 Agent Factory Functions
200
+ #### 4.1 Multi-Agent Construction
201
201
 
202
- In LangChain v1, the official `create_agent` function can be used to create a single agent; its `model` parameter accepts either a BaseChatModel instance or a specific string (when a string is provided, only models supported by `init_chat_model` are allowed). To extend the flexibility of specifying models via string, this library provides an equivalent `create_agent` function that lets you designate any model supported by `load_chat_model` (registration required beforehand).
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 example:
204
+ **Usage Example**:
205
205
 
206
206
  ```python
207
- from langchain_dev_utils.agents import create_agent
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
- agent = create_agent("vllm:qwen3-4b", tools=[get_current_time], name="time-agent")
211
- response = agent.invoke({"messages": [{"role": "user", "content": "What time is it?"}]})
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 some commonly used middleware components. Below, we illustrate with `ToolCallRepairMiddleware` and `PlanMiddleware`.
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
- `PlanMiddleware` is used for agent planning.
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
- ToolcallRepairMiddleware,
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=[ToolCallRepairMiddleware(), PlanMiddleware(
233
- use_read_plan_tool=False
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 to New York"}]})
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 information about agent development and all built-in middleware, please refer to**: [Pre-built Agent Functions](https://tbice123123.github.io/langchain-dev-utils-docs/en/agent-development/prebuilt.html), [Middleware](https://tbice123123.github.io/langchain-dev-utils-docs/en/agent-development/middleware.html)
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=CfVXm0wwlKPW0khOcwhWw61TpgtZiLijCePsAIOK3aU,22
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=w9Zu2I_HtpWQ1jNEUE9QkEunxD6UUtIh0hGJVb7b5gk,690
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=hH713hs4LRfqUbYlqJKR0geJUjXkQAbU6-segYyuLCE,21599
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=l4uCB5ecr3GAkfYGpYxqamOPIM6fkP1H_QK-277YEic,9295
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=LhrFXL1zYkkpp4ave6SBorDLig5xnllQ2VYCgFz-eR4,7681
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=W2ZRRMhn7SHHZxFfCXVaPIh2uFkY2XkO6EWrdRuv6VE,2757
34
- langchain_dev_utils-1.2.8.dist-info/METADATA,sha256=gzvPmy60STe1Yg-qZHJVMBRPRwXZ8_3uabCEdZjeXmE,13100
35
- langchain_dev_utils-1.2.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
36
- langchain_dev_utils-1.2.8.dist-info/licenses/LICENSE,sha256=AWAOzNEcsvCEzHOF0qby5OKxviVH_eT9Yce1sgJTico,1084
37
- langchain_dev_utils-1.2.8.dist-info/RECORD,,
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,,