Files
crewAI/lib/crewai/tests/llms
alex-clawd 4f455a3511 fix(openai): recover from tools + reasoning_effort 400 at runtime
Any tool-using agent on gpt-5.4 or newer fails with a hard 400 on the default
endpoint:

    Function tools with reasoning_effort are not supported for gpt-5.5 in
    /v1/chat/completions. To use function tools, use /v1/responses or set
    reasoning_effort to 'none'.

`_prepare_completion_params` gated `reasoning_effort` behind o1, but the value
still reaches the payload for any model through `params.update(self.additional_params)`,
which bypasses the typed field. Tools are then attached unconditionally.

Probed against the live endpoint rather than inferred from the report:

    model                tools + 'high'   tools + 'none'
    gpt-5.6/-sol/...     rejected         accepted
    gpt-5.5              rejected         accepted
    gpt-5.4/-mini/-nano  rejected         accepted
    gpt-5.2 and earlier  accepted         accepted
    o1 / o3 / o4-mini    accepted         rejected

The boundary is gpt-5.4, not 5.6. Note the last row: "none" is not a safe blanket
fallback either.

The 400 is the source of truth: catch it, drop reasoning_effort, retry once, and
remember the model so the round trip is paid once per process. The known families
are a fast path only, so OpenAI extending the restriction doesn't need a release
here. Detection matches the structured `param` field plus the message, so the
unsupported-value 400 that o1/o3 return for "none" doesn't trigger a useless retry.

Logged at warning, not debug -- silently downgrading a requested reasoning effort
should be visible.

Composed with the responses-only fallback from #6656 in a single handler in
_call_completions / _acall_completions, covering sync, async, streaming and
non-streaming. Verified both recoveries still work live after rebasing:

    gpt-5.5 + tools + reasoning_effort -> tool call returned (was 400)
    gpt-5-pro                          -> 'PONG', learned {'gpt-5-pro'}

Tests: 28 cases covering the fast path, error classification, sync + async retry,
no-retry on unrelated 400s, and a guard against the drop tests passing vacuously
(the typed reasoning_effort field is gated behind is_o1_model, so additional_params
is the path that actually leaks). tests/llms/openai -> 149 passed, 1 skipped.
2026-07-26 02:12:29 -07:00
..
2026-06-14 04:19:48 -03:00
2025-10-20 14:10:19 -07:00