fix(zai): return a new optional_params dict instead of mutating the caller's

The override now builds passthrough and reasoning params as read-only views and
returns a fresh dict, so the caller's optional_params and its extra_body are left
untouched. The regression test asserts both identity and content of the caller's
dict after the call

Co-authored-by: songkuan-zheng <252822057+songkuan-zheng@users.noreply.github.com>
Co-authored-by: songkuan-zheng <songkuan-zheng@users.noreply.github.com>
This commit is contained in:
songkuan-zheng 2026-09-11 11:53:57 +00:00
parent 9660f22e85
commit 7fa910f0ba
2 changed files with 29 additions and 14 deletions

View file

@ -68,14 +68,21 @@ class ZAIChatConfig(OpenAIGPTConfig):
reasoning_params: Final = MappingProxyType(
{k: v for k, v in non_default_params.items() if k in ZAI_REASONING_PARAMS and k in supported_openai_params}
)
optional_params.update(
(k, v)
for k, v in non_default_params.items()
if k in supported_openai_params and k not in ZAI_REASONING_PARAMS
)
if reasoning_params:
optional_params["extra_body"] = { # mutable-ok: the OpenAI SDK json-encodes extra_body from a plain dict
**(optional_params.get("extra_body") or MappingProxyType({})),
**reasoning_params,
passthrough_params: Final = MappingProxyType(
{
k: v
for k, v in non_default_params.items()
if k in supported_openai_params and k not in ZAI_REASONING_PARAMS
}
return optional_params
)
if not reasoning_params:
return {**optional_params, **passthrough_params} # mutable-ok: base class returns a dict
extra_body: Final = { # mutable-ok: the OpenAI SDK json-encodes extra_body from a plain dict
**(optional_params.get("extra_body") or MappingProxyType({})),
**reasoning_params,
}
return { # mutable-ok: base class returns a dict
**optional_params,
**passthrough_params,
"extra_body": extra_body,
}

View file

@ -175,17 +175,25 @@ def test_thinking_and_reasoning_effort_move_into_extra_body(local_model_cost_map
assert result["extra_body"] == {"thinking": {"type": "disabled"}, "reasoning_effort": "low"}
def test_existing_extra_body_is_kept_and_not_mutated(local_model_cost_map):
def test_caller_optional_params_and_extra_body_are_not_mutated(local_model_cost_map):
from litellm.llms.zai.chat.transformation import ZAIChatConfig
caller_extra_body = {"already_here": True}
caller_optional_params = {"stream": False, "extra_body": caller_extra_body}
result = ZAIChatConfig()._map_openai_params(
non_default_params={"thinking": {"type": "disabled"}},
optional_params={"extra_body": caller_extra_body},
non_default_params={"max_tokens": 100, "thinking": {"type": "disabled"}},
optional_params=caller_optional_params,
model="glm-4.7",
drop_params=False,
)
assert result["extra_body"] == {"already_here": True, "thinking": {"type": "disabled"}}
assert result == {
"stream": False,
"max_tokens": 100,
"extra_body": {"already_here": True, "thinking": {"type": "disabled"}},
}
assert result is not caller_optional_params
assert caller_optional_params == {"stream": False, "extra_body": {"already_here": True}}
assert caller_optional_params["extra_body"] is caller_extra_body
assert caller_extra_body == {"already_here": True}