fix: sanitize tool_choice.function.name when sanitizing tool names for OpenAI

When sanitize_openai_function_tool_names=True, tool_choice with an
explicit function name is now rewritten to match the sanitized name in
the tools array, preventing 400 errors from OpenAI/Azure due to the
mismatch.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Sameer Kankute 2026-05-04 17:46:22 +05:30
parent 983cf2278c
commit 14d0252b3b
No known key found for this signature in database
2 changed files with 44 additions and 0 deletions

View file

@ -1182,6 +1182,21 @@ def completion( # type: ignore # noqa: PLR0915
tools=tools,
sanitize_openai_function_tool_names=_sanitize_openai_fn_tool_names,
)
if (
_sanitize_openai_fn_tool_names
and isinstance(tool_choice, dict)
and tool_choice.get("type") == "function"
):
_fn = tool_choice.get("function")
if isinstance(_fn, dict) and isinstance(_fn.get("name"), str):
from litellm.utils import _sanitize_openai_function_tool_name as _san_name
_sanitized_tc_name = _san_name(_fn["name"], -1)
if _sanitized_tc_name != _fn["name"]:
tool_choice = {
**tool_choice,
"function": {**_fn, "name": _sanitized_tc_name},
}
# validate tool_choice
tool_choice = validate_chat_completion_tool_choice(tool_choice=tool_choice)
# validate optional params

View file

@ -4112,3 +4112,32 @@ def test_convert_to_model_response_restores_openai_tool_call_names_when_mapped()
)
assert out.choices[0].message.tool_calls is not None
assert out.choices[0].message.tool_calls[0].function.name == "plugin.subtool"
def test_tool_choice_function_name_sanitized_with_tools():
from litellm.litellm_core_utils.openai_tool_name_mapping import (
begin_openai_tool_name_mapping_scope,
)
from litellm.utils import _sanitize_openai_function_tool_name, validate_and_fix_openai_tools
begin_openai_tool_name_mapping_scope()
validate_and_fix_openai_tools(
tools=[
{
"type": "function",
"function": {
"name": "my.tool",
"parameters": {"type": "object", "properties": {}},
},
}
],
sanitize_openai_function_tool_names=True,
)
# Simulate what main.py does for tool_choice
tool_choice = {"type": "function", "function": {"name": "my.tool"}}
_fn = tool_choice["function"]
sanitized = _sanitize_openai_function_tool_name(_fn["name"], -1)
if sanitized != _fn["name"]:
tool_choice = {**tool_choice, "function": {**_fn, "name": sanitized}}
assert tool_choice["function"]["name"] == "my_tool"