mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-06 07:48:06 +00:00
fix(ollama): leave JSON mode off when drop_params drops every hosted MCP tool
Ollama generate set format json for any request with tools, even after the hosted MCP check dropped all of them, so a plain answer came back as JSON or as a call to a tool the model never had. The check now runs before the JSON mode switch, which stays on only while a tool is left for the prompt.
This commit is contained in:
parent
aa4e8c552e
commit
5bb6fff01e
2 changed files with 27 additions and 8 deletions
|
|
@ -97,7 +97,11 @@ from litellm.litellm_core_utils.model_param_helper import peek_reasoning_summary
|
|||
from litellm.litellm_core_utils.offline_mode import is_offline_mode
|
||||
from litellm.litellm_core_utils.sensitive_data_masker import redact_credentials_in_payload
|
||||
from litellm.litellm_core_utils.tokenizer import strip_special_tokens
|
||||
from litellm.litellm_core_utils.tool_fields import MalformedField, resolve_deployment_tool_fields
|
||||
from litellm.litellm_core_utils.tool_fields import (
|
||||
MalformedField,
|
||||
resolve_deployment_tool_fields,
|
||||
tools_without_hosted_mcp,
|
||||
)
|
||||
from litellm.rust_bridge import tokenizer as tokenizer_dispatch
|
||||
from litellm.rust_bridge.catalog import decision
|
||||
from litellm.rust_bridge.configuration import Decision
|
||||
|
|
@ -5168,7 +5172,12 @@ def remove_sensitive_keys_from_dict(d: dict) -> dict:
|
|||
return d
|
||||
|
||||
|
||||
def pre_process_optional_params(passed_params: dict, non_default_params: dict, custom_llm_provider: str) -> dict:
|
||||
_TOOL_LIST: Final = TypeAdapter(list[object])
|
||||
|
||||
|
||||
def pre_process_optional_params(
|
||||
passed_params: dict, non_default_params: dict, custom_llm_provider: str, model: str, drop_params: bool | None
|
||||
) -> dict:
|
||||
"""For .completion(), preprocess optional params"""
|
||||
optional_params: dict = {}
|
||||
|
||||
|
|
@ -5225,14 +5234,20 @@ def pre_process_optional_params(passed_params: dict, non_default_params: dict, c
|
|||
and custom_llm_provider not in litellm.openai_compatible_providers
|
||||
):
|
||||
if custom_llm_provider == "ollama":
|
||||
if "tools" in non_default_params:
|
||||
sent_tools: Final = _TOOL_LIST.validate_python(non_default_params.pop("tools"))
|
||||
non_default_params.pop("tool_choice", None) # causes ollama requests to hang
|
||||
prompt_tools: Final = tools_without_hosted_mcp(
|
||||
sent_tools, model=model, route=custom_llm_provider, drop_params=bool(drop_params)
|
||||
)
|
||||
if sent_tools and not prompt_tools:
|
||||
return optional_params
|
||||
optional_params["functions_unsupported_model"] = prompt_tools
|
||||
elif "functions" in non_default_params:
|
||||
optional_params["functions_unsupported_model"] = non_default_params.pop("functions")
|
||||
# ollama actually supports json output
|
||||
optional_params["format"] = "json"
|
||||
litellm.add_function_to_prompt = True # so that main.py adds the function call to the prompt
|
||||
if "tools" in non_default_params:
|
||||
optional_params["functions_unsupported_model"] = non_default_params.pop("tools")
|
||||
non_default_params.pop("tool_choice", None) # causes ollama requests to hang
|
||||
elif "functions" in non_default_params:
|
||||
optional_params["functions_unsupported_model"] = non_default_params.pop("functions")
|
||||
elif litellm.add_function_to_prompt: # if user opts to add it to prompt instead
|
||||
optional_params["functions_unsupported_model"] = non_default_params.pop(
|
||||
"tools", non_default_params.pop("functions", None)
|
||||
|
|
@ -5367,6 +5382,8 @@ def get_optional_params(
|
|||
passed_params=passed_params,
|
||||
non_default_params=non_default_params,
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
model=model,
|
||||
drop_params=drop_params,
|
||||
)
|
||||
|
||||
def _check_valid_arg(supported_params: list[str]):
|
||||
|
|
|
|||
|
|
@ -1053,7 +1053,9 @@ def test_ollama_drop_params_keeps_a_hosted_mcp_tool_out_of_the_prompt(
|
|||
drop_params=True,
|
||||
)
|
||||
|
||||
prompt: Final = json.loads(route.calls.last.request.content)["prompt"]
|
||||
body: Final = json.loads(route.calls.last.request.content)
|
||||
prompt: Final = body["prompt"]
|
||||
assert (body.get("format") == "json") is prompt_names_weather
|
||||
assert "deepwiki" not in prompt
|
||||
assert ("get_weather" in prompt) is prompt_names_weather
|
||||
assert prompt.startswith("### User:\nweather in Paris?")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue