fix(types): widen untyped request dicts for the allowed_tools refusal on lts/1.108.x

The line types the hook loop's kwargs and optional_params as plain
dicts, so passing them to the picked refusal helpers added unknown
argument errors that main does not have. They are now cast to
Mapping[str, object] at the call sites, and the loop settings reader
takes a Mapping

The new cast import in the agentic loop would grow the banned-API
count, so the duplicate function-level cast import in
llm_http_handler.py goes, since the module already imports cast
This commit is contained in:
mateo-berri 2026-11-07 01:53:59 -08:00
parent 81df46e9ae
commit 185dc2dca6
2 changed files with 8 additions and 6 deletions

View file

@ -4,7 +4,7 @@ import json
from collections.abc import Iterable, Mapping
from itertools import chain
from types import MappingProxyType
from typing import Final
from typing import Final, cast
from pydantic import TypeAdapter, ValidationError
@ -85,7 +85,7 @@ def _coerce_int(value: object, default: int) -> int:
return int(value) if isinstance(value, (int, str)) else default
def _agentic_loop_settings(kwargs: dict[str, object]) -> tuple[int, int, list[str]]:
def _agentic_loop_settings(kwargs: Mapping[str, object]) -> tuple[int, int, list[str]]:
depth: Final = _coerce_int(kwargs.get("_agentic_loop_depth"), 0)
configured: Final = validated_max_agentic_loops(
kwargs.get("max_agentic_loops"), field="litellm_params.max_agentic_loops"
@ -334,7 +334,10 @@ async def maybe_run_chat_completion_agentic_loop(
import litellm
callbacks: Final = litellm.callbacks + (getattr(logging_obj, "dynamic_success_callbacks", None) or [])
request_kwargs: Final = with_caller_tool_choice(kwargs, optional_params)
requested_optional_params: Final = cast( # cast-ok: optional_params is a str-keyed dict
"Mapping[str, object]", optional_params
)
request_kwargs: Final = with_caller_tool_choice(kwargs, requested_optional_params)
depth, max_loops, fingerprints = _agentic_loop_settings(request_kwargs)
tools: Final = optional_params.get("tools", [])

View file

@ -5965,8 +5965,6 @@ class BaseLLMHTTPHandler:
else False
)
if websearch_converted_stream and isinstance(response, dict):
from typing import cast
from litellm._logging import verbose_logger
from litellm.llms.anthropic.pass_through.messages.fake_stream_iterator import (
FakeAnthropicMessagesStreamIterator,
@ -6209,6 +6207,7 @@ class BaseLLMHTTPHandler:
callbacks: Final = litellm.callbacks + (logging_obj.dynamic_success_callbacks or [])
tools: Final = optional_params.get("tools", [])
depth, max_loops, fingerprints = self._get_agentic_loop_settings(kwargs=kwargs)
request_kwargs: Final = cast("Mapping[str, object]", kwargs) # cast-ok: kwargs is a str-keyed dict
for callback in callbacks:
if not isinstance(callback, CustomLogger):
@ -6243,7 +6242,7 @@ class BaseLLMHTTPHandler:
# Safety guards must run OUTSIDE the callback try/except — they are
# bounded-loop / cycle-break rails that must propagate to the caller.
refuse_a_call_the_choice_excludes(response, kwargs, callbacks, model)
refuse_a_call_the_choice_excludes(response, request_kwargs, callbacks, model)
fingerprint = self._check_agentic_loop_safety(
tool_calls=tool_calls,
fingerprints=fingerprints,