Skip to content

Fix LiteLLM response_model and native tool call handling#4819

Open
echoVic wants to merge 1 commit intocrewAIInc:mainfrom
echoVic:fix/issue-4697-function-calling-tools
Open

Fix LiteLLM response_model and native tool call handling#4819
echoVic wants to merge 1 commit intocrewAIInc:mainfrom
echoVic:fix/issue-4697-function-calling-tools

Conversation

@echoVic
Copy link
Copy Markdown

@echoVic echoVic commented Mar 11, 2026

This pull request fixes two related bugs in the LiteLLM integration used by the LLM wrapper:

  • When supports_function_calling() is true and a response_model is provided, the internal instructor path was used even when native tools were also passed. This caused custom agent tools to be ignored in favor of the structured output model.
  • Native tool calls could be dropped when the LLM returned both text and tool calls in the same response. In that case, the text branch won and the tool calls were never surfaced back to the executor.

Key changes:

  1. Only route through InternalInstructor when no tools are being passed.
    This ensures that when tools are provided, the call behaves like a normal tool-enabled completion and agent tools are preserved.

  2. Prefer tool calls over plain text when both are present.
    The non-streaming response handlers now return tool calls first when they exist, and only fall back to text when there are no tool calls.

These changes are applied to both the synchronous and asynchronous non-streaming handlers so that behavior is consistent across code paths.

Closes #4697.
Closes #4788.


Note

Medium Risk
Changes LiteLLM non-streaming return semantics around response_model and tool-calling, which can affect downstream executors that previously received plain text instead of tool call objects. Risk is moderate due to altered control flow in a core LLM wrapper, but scoped to LiteLLM non-streaming sync/async handlers.

Overview
Fixes LiteLLM non-streaming handling so response_model routing through InternalInstructor is skipped when native tools are provided, preserving tool-enabled completions.

Adjusts both sync and async non-streaming response handlers to prefer returning tool_calls when present but available_functions isn’t supplied (so callers can execute tools), and only fall back to returning text when no tool calls exist.

Written by Cursor Bugbot for commit f647049. This will update automatically on new commits. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

"""
# --- 1) Handle response_model with InternalInstructor for LiteLLM
if response_model and self.is_litellm:
# Only use this path when no native tools are being passed.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Streaming handlers still drop tool calls, creating inconsistency

High Severity

The PR fixes tool calls being dropped in favor of text in both non-streaming handlers, but the same bug remains in _handle_streaming_response (line 970: if not tool_calls or not available_functions) and _ahandle_streaming_response (line 1517: if accumulated_tool_args and available_functions). When llm.stream is True and the executor passes available_functions=None, both streaming handlers still return text instead of tool calls. This means the fix only works when streaming is disabled, and the executor will silently miss tool calls when streaming is on.

Additional Locations (1)
Fix in Cursor Fix in Web

@HydrogenC
Copy link
Copy Markdown

I met with a bug where my LiteLLM connected model (that supports native tool calling) keeps outputting ReAct-style tool calls. This PR may be a solution of it.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 1, 2026

This PR is stale because it has been open for 45 days with no activity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants