fix(gigachat): surface unsupported stop and json_object response_format instead of silently dropping them

get_supported_openai_params declared 'stop' as supported while map_openai_params explicitly discarded it, so stop sequences vanished without error even with drop_params=False. response_format={'type': 'json_object'} had the same problem: declared supported, but only json_schema was mapped.

- remove 'stop' from the supported list so get_optional_params raises UnsupportedParamsError by default (explicit drop with drop_params=True, consistent with litellm's contract)
- raise UnsupportedParamsError for non-json_schema response_format values (json_object etc.) unless drop_params=True; 'text' stays a no-op
- replace the test that locked the silent drop in as expected behavior; add contract tests for both params

Fixes #43606
This commit is contained in:
JingHao-Leon 2026-09-29 04:03:27 +08:00
parent fe76c2473d
commit 8543247c91
2 changed files with 72 additions and 11 deletions

View file

@ -142,7 +142,6 @@ class GigaChatConfig(BaseConfig):
"top_p",
"max_tokens",
"max_completion_tokens",
"stop",
"tools",
"tool_choice",
"functions",
@ -171,9 +170,6 @@ class GigaChatConfig(BaseConfig):
optional_params["top_p"] = value
elif param in ("max_tokens", "max_completion_tokens"):
optional_params["max_tokens"] = value
elif param == "stop":
# GigaChat doesn't support stop sequences
pass
elif param == "tools":
# Convert tools to functions format
if isinstance(value, Sequence):
@ -212,6 +208,24 @@ class GigaChatConfig(BaseConfig):
]
optional_params["function_call"] = {"name": schema_name} # mutable-ok: request payload
optional_params["_structured_output"] = True
elif not (
isinstance(value, Mapping)
and (value.get("type") in (None, "text", "json_schema"))
):
# Only json_schema is actually mapped above; other response_format
# values (e.g. json_object) would otherwise be silently ignored.
if not drop_params:
from litellm.utils import UnsupportedParamsError
raise UnsupportedParamsError(
status_code=400,
message=(
"GigaChat does not support response_format="
f"{value!r}. Use response_format={{'type': 'json_schema', "
"'json_schema': {...}}} instead, or set `drop_params=True` "
"to drop it."
),
)
return optional_params

View file

@ -19,6 +19,7 @@ from litellm.llms.gigachat.chat.transformation import (
is_valid_json,
)
from litellm.types.utils import ModelResponse, Usage
from litellm.utils import UnsupportedParamsError, get_optional_params
TRANSFORM_MODULE = "litellm.llms.gigachat.chat.transformation"
@ -174,7 +175,6 @@ class TestGetSupportedOpenAiParams:
"top_p",
"max_tokens",
"max_completion_tokens",
"stop",
"tools",
"tool_choice",
"functions",
@ -243,12 +243,30 @@ class TestMapOpenAiParams:
)
assert result["max_tokens"] == 200
def test_stop_is_dropped(self):
result = self.config.map_openai_params(
non_default_params={"stop": ["\n\n"]},
optional_params={},
model="GigaChat",
drop_params=False,
def test_stop_not_declared_supported(self):
params = self.config.get_supported_openai_params("GigaChat")
assert "stop" not in params
def test_stop_raises_unsupported_params_error(self):
# stop is not supported by the GigaChat API; with drop_params=False
# (the default) litellm must surface that instead of silently ignoring it
with pytest.raises(UnsupportedParamsError):
get_optional_params(
model="GigaChat-Pro",
custom_llm_provider="gigachat",
stop=["\n\n"],
request_timeout=10,
num_retries=0,
)
def test_stop_dropped_with_drop_params_true(self):
result = get_optional_params(
model="GigaChat-Pro",
custom_llm_provider="gigachat",
stop=["\n\n"],
request_timeout=10,
num_retries=0,
drop_params=True,
)
assert "stop" not in result
@ -354,6 +372,35 @@ class TestMapOpenAiParams:
assert result["function_call"] == {"name": "test_schema"}
assert result["_structured_output"] is True
def test_response_format_json_object_raises_unsupported(self):
with pytest.raises(UnsupportedParamsError):
self.config.map_openai_params(
non_default_params={"response_format": {"type": "json_object"}},
optional_params={},
model="GigaChat",
drop_params=False,
)
def test_response_format_json_object_dropped_with_drop_params(self):
result = self.config.map_openai_params(
non_default_params={"response_format": {"type": "json_object"}},
optional_params={},
model="GigaChat",
drop_params=True,
)
assert "functions" not in result
assert "function_call" not in result
def test_response_format_text_is_a_noop(self):
result = self.config.map_openai_params(
non_default_params={"response_format": {"type": "text"}},
optional_params={},
model="GigaChat",
drop_params=False,
)
assert "functions" not in result
assert "function_call" not in result
class TestConvertToolsToFunctions:
def setup_method(self):