@@ -243,15 +243,18 @@ def _convert_messages_to_langchain_format(prompt: List[dict]) -> List:
243243def _store_reasoning_traces (response ) -> None :
244244 """Store reasoning traces from response in context variable.
245245
246- Extracts reasoning content from response.additional_kwargs["reasoning_content"]
247- if available. Otherwise, falls back to extracting from <think> tags in the
248- response content (and removes the tags from content).
246+ Tries multiple extraction methods in order of preference:
247+ 1. content_blocks with type="reasoning" (LangChain v1 standard)
248+ 2. additional_kwargs["reasoning_content"] (provider-specific)
249+ 3. <think> tags in content (legacy fallback)
249250
250251 Args:
251252 response: The LLM response object
252253 """
254+ reasoning_content = _extract_reasoning_from_content_blocks (response )
253255
254- reasoning_content = _extract_reasoning_content (response )
256+ if not reasoning_content :
257+ reasoning_content = _extract_reasoning_from_additional_kwargs (response )
255258
256259 if not reasoning_content :
257260 # Some LLM providers (e.g., certain NVIDIA models) embed reasoning in <think> tags
@@ -263,14 +266,27 @@ def _store_reasoning_traces(response) -> None:
263266 reasoning_trace_var .set (reasoning_content )
264267
265268
266- def _extract_reasoning_content (response ):
269+ def _extract_reasoning_from_content_blocks (response ) -> Optional [str ]:
270+ """Extract reasoning from content_blocks with type='reasoning'.
271+
272+ This is the LangChain v1 standard for structured content blocks.
273+ """
274+ if hasattr (response , "content_blocks" ):
275+ for block in response .content_blocks :
276+ if block .get ("type" ) == "reasoning" :
277+ return block .get ("reasoning" )
278+ return None
279+
280+
281+ def _extract_reasoning_from_additional_kwargs (response ) -> Optional [str ]:
282+ """Extract reasoning from additional_kwargs['reasoning_content'].
283+
284+ This is used by some providers for backward compatibility.
285+ """
267286 if hasattr (response , "additional_kwargs" ):
268287 additional_kwargs = response .additional_kwargs
269- if (
270- isinstance (additional_kwargs , dict )
271- and "reasoning_content" in additional_kwargs
272- ):
273- return additional_kwargs ["reasoning_content" ]
288+ if isinstance (additional_kwargs , dict ):
289+ return additional_kwargs .get ("reasoning_content" )
274290 return None
275291
276292
@@ -317,10 +333,26 @@ def _extract_and_remove_think_tags(response) -> Optional[str]:
317333
318334def _store_tool_calls (response ) -> None :
319335 """Extract and store tool calls from response in context."""
320- tool_calls = getattr (response , "tool_calls" , None )
336+ tool_calls = _extract_tool_calls_from_content_blocks (response )
337+ if not tool_calls :
338+ tool_calls = _extract_tool_calls_from_attribute (response )
321339 tool_calls_var .set (tool_calls )
322340
323341
342+ def _extract_tool_calls_from_content_blocks (response ) -> List | None :
343+ if hasattr (response , "content_blocks" ):
344+ tool_calls = []
345+ for block in response .content_blocks :
346+ if block .get ("type" ) == "tool_call" :
347+ tool_calls .append (block )
348+ return tool_calls if tool_calls else None
349+ return None
350+
351+
352+ def _extract_tool_calls_from_attribute (response ) -> List | None :
353+ return getattr (response , "tool_calls" , None )
354+
355+
324356def _store_response_metadata (response ) -> None :
325357 """Store response metadata excluding content for metadata preservation.
326358
0 commit comments