mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-27 09:45:16 +00:00
fix(openai): force reasoning_effort=none even when the caller sent none at all
The learned fast path only rewrote `reasoning_effort` when it was already present
and not "none", so a learned model with the parameter absent kept sending nothing
-- which these models reject exactly like "high", because they apply a server-side
default. The cache recorded the conflict but every call still paid the 400.
Reproduced before fixing:
LLM(model='openai/gpt-5.6-sol') # no reasoning_effort anywhere
1st call -> 400, recovered, model learned
2nd call -> reasoning_effort ABSENT # 400 again, forever
Now "none" is forced whenever the model is known and the value isn't already
"none". Same call after the fix sends reasoning_effort=none.
`_retry_params_without_reasoning_effort` already handled the absent case correctly;
only the fast path was wrong.
tests/llms/openai -> 160 passed, 1 skipped. Ruff + mypy clean.
This commit is contained in:
@@ -1929,14 +1929,15 @@ class OpenAICompletion(BaseLLM):
|
||||
# request; the first occurrence is recovered in `_call_completions`.
|
||||
# Checked after the tools block on purpose: `reasoning_effort` can also
|
||||
# arrive through `additional_params`, which bypasses the typed field.
|
||||
# An absent `reasoning_effort` is not safe either: these models apply a
|
||||
# server-side default, so sending nothing is rejected exactly like sending
|
||||
# "high". Only an explicit "none" is accepted alongside tools.
|
||||
if (
|
||||
params.get("tools")
|
||||
and params.get("reasoning_effort") not in (None, "none")
|
||||
and params.get("reasoning_effort") != "none"
|
||||
and not self._supports_reasoning_effort_with_tools(self.model)
|
||||
):
|
||||
requested = params["reasoning_effort"]
|
||||
# Explicit "none" rather than removing the key: gpt-5.6-* apply a
|
||||
# server-side default, so omitting it fails the same way.
|
||||
requested = params.get("reasoning_effort")
|
||||
params["reasoning_effort"] = "none"
|
||||
logging.debug(
|
||||
'Forcing reasoning_effort="none" (requested %r) for model %r: '
|
||||
|
||||
@@ -108,6 +108,20 @@ class TestLearnedFastPath:
|
||||
assert params["tools"]
|
||||
assert params["tool_choice"] == "auto"
|
||||
|
||||
def test_absent_reasoning_effort_is_also_forced_to_none(self):
|
||||
"""Sending nothing is rejected exactly like sending "high".
|
||||
|
||||
These models apply a server-side default, so a learned model with no
|
||||
`reasoning_effort` set would 400 on every call and never benefit from the
|
||||
cache.
|
||||
"""
|
||||
llm = build("gpt-5.6-sol")
|
||||
llm._remember_reasoning_effort_conflict()
|
||||
|
||||
params = llm._prepare_completion_params(MESSAGES, tools=TOOLS)
|
||||
|
||||
assert params["reasoning_effort"] == "none"
|
||||
|
||||
def test_learning_applies_to_the_additional_params_leak(self):
|
||||
"""additional_params bypasses the typed field, so it must be checked too."""
|
||||
llm = build("gpt-5.5", additional_params={"reasoning_effort": "medium"})
|
||||
|
||||
Reference in New Issue
Block a user