fix: drop invalid top-level params (strict) from OpenAI extra_body

This commit is contained in:
Snehal58 2026-04-06 01:18:49 +05:30
parent d251238bd7
commit 45a5d29f81
2 changed files with 118 additions and 0 deletions

View file

@ -1,6 +1,12 @@
from typing import Dict, Optional
import litellm
from litellm._logging import verbose_logger
# Params that callers (e.g. LangChain) sometimes pass at the top level but
# that are NOT valid top-level OpenAI API fields. Forwarding them in
# extra_body causes OpenAI to return "400 Unrecognized request argument".
_OPENAI_INVALID_TOP_LEVEL_PARAMS = frozenset({"strict"})
def _ensure_extra_body_is_safe(extra_body: Optional[Dict]) -> Optional[Dict]:
@ -26,6 +32,18 @@ def _ensure_extra_body_is_safe(extra_body: Optional[Dict]) -> Optional[Dict]:
# Langfuse TextPromptClients have .__dict__ attribute
if _prompt is not None and hasattr(_prompt, "__dict__"):
extra_body["metadata"]["prompt"] = _prompt.__dict__
# Drop params that are invalid at the OpenAI top-level request body.
# Some callers (e.g. LangChain) pass these at the completion() top level;
# they end up in extra_body and cause "400 Unrecognized request argument".
dropped = [k for k in _OPENAI_INVALID_TOP_LEVEL_PARAMS if k in extra_body]
for k in dropped:
extra_body.pop(k)
if dropped:
verbose_logger.debug(
"LiteLLM: dropped invalid top-level OpenAI params from extra_body: %s",
dropped,
)
return extra_body

View file

@ -0,0 +1,100 @@
"""
Tests for litellm_core_utils.llm_request_utils module.
Covers _ensure_extra_body_is_safe, including the fix for top-level kwargs
(e.g. `strict=True`) that callers like LangChain pass to litellm.completion()
but that are not valid at the OpenAI top-level request body.
"""
from litellm.litellm_core_utils.llm_request_utils import _ensure_extra_body_is_safe
from litellm.utils import get_optional_params
# ---------------------------------------------------------------------------
# _ensure_extra_body_is_safe
# ---------------------------------------------------------------------------
def test_ensure_extra_body_is_safe_strips_strict():
"""strict is not a valid top-level OpenAI param and must be removed."""
result = _ensure_extra_body_is_safe({"strict": True, "some_custom_key": "value"})
assert isinstance(result, dict)
body: dict = result
assert "strict" not in body
assert body.get("some_custom_key") == "value"
def test_ensure_extra_body_is_safe_none_passthrough():
assert _ensure_extra_body_is_safe(None) is None
def test_ensure_extra_body_is_safe_non_dict_passthrough():
sentinel = object()
assert _ensure_extra_body_is_safe(sentinel) is sentinel # type: ignore[arg-type]
def test_ensure_extra_body_is_safe_empty_dict():
assert _ensure_extra_body_is_safe({}) == {}
def test_ensure_extra_body_is_safe_preserves_valid_keys():
body = {"some_provider_param": 42, "another": "hello"}
result = _ensure_extra_body_is_safe(body)
assert result == {"some_provider_param": 42, "another": "hello"}
def test_ensure_extra_body_is_safe_strict_false_also_stripped():
"""strict=False is equally invalid at the top level."""
result = _ensure_extra_body_is_safe({"strict": False})
assert isinstance(result, dict)
body: dict = result
assert "strict" not in body
# ---------------------------------------------------------------------------
# get_optional_params — end-to-end: strict must not reach extra_body for OpenAI
# ---------------------------------------------------------------------------
def test_get_optional_params_openai_strict_not_in_extra_body():
"""
Regression test for: litellm.BadRequestError: OpenAIException - Unrecognized
request argument supplied: strict
When strict=True is passed as a top-level kwarg (LangChain-style),
it must NOT appear in extra_body for OpenAI models.
"""
params = get_optional_params(
model="gpt-4o",
custom_llm_provider="openai",
strict=True,
temperature=0.7,
)
extra_body: dict = params.get("extra_body", {})
assert "strict" not in extra_body, (
"'strict' must not be forwarded in extra_body for OpenAI — "
"it causes a 400 'Unrecognized request argument' error."
)
def test_get_optional_params_openai_strict_false_not_in_extra_body():
params = get_optional_params(
model="gpt-4o",
custom_llm_provider="openai",
strict=False,
)
extra_body: dict = params.get("extra_body", {})
assert "strict" not in extra_body
def test_get_optional_params_openai_strict_does_not_block_other_extra_body_keys():
"""Unrecognized provider-extension keys other than strict still flow through."""
params = get_optional_params(
model="gpt-4o",
custom_llm_provider="openai",
strict=True,
my_custom_provider_extension="foo",
)
extra_body: dict = params.get("extra_body", {})
assert "strict" not in extra_body
assert extra_body.get("my_custom_provider_extension") == "foo"