fix: prevent output_pydantic from injecting tool schema when LLM lacks function calling#5085
Open
giulio-leone wants to merge 2 commits intocrewAIInc:mainfrom
Open
Conversation
…s function calling When a Task has output_pydantic set and the LLM does NOT support native function calling (e.g., Ollama models), the executor falls back to the ReAct text-based loop. Previously, response_model was still forwarded to the LLM call, causing instructor to inject the Pydantic schema as a tool/function — something models without function-calling support cannot handle. Root cause: _invoke_loop_react() and _ainvoke_loop_react() passed self.response_model to get_llm_response(), which triggered InternalInstructor in Mode.TOOLS mode regardless of model capabilities. Fix: Pass response_model=None in ReAct mode. The task-level convert_to_model() already handles text → Pydantic conversion after the loop finishes, so instructor-based extraction is unnecessary and harmful in this path. Closes crewAIInc#4695
Keep the no-function-calling ReAct path string-only after forcing\nresponse_model=None, and add focused sync/async coverage for the\ndirect-JSON path so the fallback parser is only used when needed.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #4695
When a Task has
output_pydanticset and the LLM does not support native function calling (e.g., Ollama models), the executor correctly falls back to the ReAct text-based loop. However,response_modelwas still forwarded to the LLM call, causingInternalInstructorto inject the Pydantic schema as a tool viainstructor.from_litellm(completion)inMode.TOOLSmode — which models without function-calling support cannot handle.Root Cause
_invoke_loop_react()and_ainvoke_loop_react()both passedself.response_modeltoget_llm_response()/aget_llm_response(). This triggered the instructor path inLLM._process_call_params()(line 1153), which wraps the LLM withinstructor.from_litellm(completion)using the defaultMode.TOOLS— injecting the Pydantic model as a native tool regardless of whether the model actually supports function calling.Fix
Pass
response_model=Nonein both_invoke_loop_react()and_ainvoke_loop_react(). The task-levelconvert_to_model()intask.py(line 1021-1040) already handles text → Pydantic conversion after the ReAct loop finishes, so instructor-based extraction is unnecessary and harmful in this code path.Changes
lib/crewai/src/crewai/agents/crew_agent_executor.py: Setresponse_model=Nonein both sync and async ReAct loopslib/crewai/tests/agents/test_react_response_model.py: Added 2 regression tests:response_model=Noneis passed toget_llm_responsein ReAct mode_invoke_loop_reactis selected (not native tools) when function calling is unsupportedTest Results
Note
Medium Risk
Changes how
output_pydanticis handled in the ReAct execution path, which can affect structured output behavior for models without native tool/function calling. Risk is mitigated by added regression tests covering the selection of ReAct mode and ensuringresponse_modelis not forwarded.Overview
Prevents
output_pydanticfrom being forwarded to the LLM call when the executor falls back to the ReAct text loop, by forcingresponse_model=Nonein both sync and async ReAct invocations so non-function-calling models don’t receive an injected tool schema.Adds regression tests ensuring ReAct mode is chosen when
supports_function_calling()is false and thatget_llm_responseis called withresponse_model=Nonein this path.Written by Cursor Bugbot for commit 16be9ac. This will update automatically on new commits. Configure here.