Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions lib/crewai/src/crewai/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1234,8 +1234,17 @@ def _handle_non_streaming_response(
# --- 4) Check for tool calls
tool_calls = getattr(response_message, "tool_calls", [])

# --- 5) If no tool calls or no available functions, return the text response directly as long as there is a text response
if (not tool_calls or not available_functions) and text_response:
# --- 5) If there are tool calls but no available functions, return the tool calls
# This allows the caller (e.g., executor) to handle tool execution.
# This must be checked before returning text_response, because some LLMs
# (e.g., Anthropic via OpenRouter/Bedrock) return both text and tool_calls
# in the same response. The tool calls should take priority so the executor
# can execute them instead of treating the text as a final answer.
if tool_calls and not available_functions:
return tool_calls

# --- 6) If no tool calls, return the text response directly
if not tool_calls and text_response:
self._handle_emit_call_events(
response=text_response,
call_type=LLMCallType.LLM_CALL,
Expand All @@ -1245,11 +1254,6 @@ def _handle_non_streaming_response(
)
return text_response

# --- 6) If there are tool calls but no available functions, return the tool calls
# This allows the caller (e.g., executor) to handle tool execution
if tool_calls and not available_functions:
return tool_calls

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Async method not updated with same fix

High Severity

The fix to prioritize tool calls over text when available_functions is None was only applied to _handle_non_streaming_response but not to its async counterpart _ahandle_non_streaming_response. The async method at line 1371 still has the original buggy condition if (not tool_calls or not available_functions) and text_response:, which means the exact same bug — discarding tool calls when the LLM returns both text and tool calls — still occurs for all async code paths.

Additional Locations (1)
Fix in Cursor Fix in Web

# --- 7) Handle tool calls if present (execute when available_functions provided)
if tool_calls and available_functions:
tool_result = self._handle_tool_call(
Expand Down