fix: clear test-quality (TQ008) and basedpyright gate breaches

TQ008: the 16 new handler-level tests patch litellm. internals
(litellm.acompletion, the handler adapter, _prepare_* seams). Each patch
line carries an explainable test-quality-ok: reason - the unit under test
IS the handler's thinking_disabled translation wiring, not the transport.

basedpyright (delta vs base):
- reportPrivateUsage: the shared-classifier delegation added a protected
  cross-class call. _chunk_has_substantial_content now derives the
  decision inline (same per-choice conditions, same getattr guards, same
  .strip()/truthy semantics as the classifier, documented).
- reportOptionalSubscript/MemberAccess: the content_block tool branch
  relies on the classifier for tool_call presence; restored explicit
  narrowing (assert + local first_tool_call), behaviour-neutral.
- dropped Choices from the emitter/content_block Sequence unions: the
  bare-Choices member re-opened Optional on delta.tool_calls[0].function
  (the 3 # type: ignore it used to sit next to were dead code anyway).
  litellm.types.utils.StreamingChoices imported at top level.
This commit is contained in:
Daniel Cherubini 2026-09-14 19:52:34 +02:00
parent 54c7405083
commit 63323a1dfd
3 changed files with 64 additions and 27 deletions

View file

@ -31,7 +31,7 @@ from litellm.types.llms.anthropic import (
UsageDelta,
UsageIteration,
)
from litellm.types.utils import AdapterCompletionStreamWrapper, Delta
from litellm.types.utils import AdapterCompletionStreamWrapper, Delta, StreamingChoices
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObject
@ -1164,16 +1164,45 @@ class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
pre-existing tests (test_empty_chunk_is_not_substantial,
test_reasoning_chunk_is_substantial) call it unbound as
AnthropicStreamWrapper._chunk_has_substantial_content(chunk) converting
to an instance method would break those calls."""
from .transformation import LiteLLMAnthropicMessagesAdapter
to an instance method would break those calls.
return (
LiteLLMAnthropicMessagesAdapter._classify_streaming_chunk(
choices=chunk.choices,
thinking_disabled=thinking_disabled,
The two sites' conditions are kept identical in code so they cannot
drift into two different notions of substantiality (the CTG-85 failure
mode). Rules mirrored from the classifier, per choice, with the same
getattr-with-default guards (Delta deletes reasoning_content /
thinking_blocks entirely when unset):
- a reasoning-only chunk is not substantial when thinking is disabled;
- a structured thinking / redacted block is always substantial (even
with an empty payload) when thinking is enabled;
- a flat reasoning_content string is substantial only when it carries
non-whitespace, when thinking is enabled;
- a tool call with a function, and a truthy (NOT .strip()-based) text
content, are substantial regardless of thinking state."""
for choice in chunk.choices:
reasoning_text = ""
has_structured_thinking_block = False
if isinstance(choice, StreamingChoices):
thinking_blocks = getattr(choice.delta, "thinking_blocks", None) or []
if len(thinking_blocks) > 0:
first_block = thinking_blocks[0]
if first_block.get("type") in ("thinking", "redacted_thinking"):
has_structured_thinking_block = True
reasoning_text = str(first_block.get("thinking") or "")
if not has_structured_thinking_block:
reasoning_text = str(getattr(choice.delta, "reasoning_content", "") or "")
has_substantial_reasoning = bool(reasoning_text.strip()) or has_structured_thinking_block
has_tool_calls = (
choice.delta.tool_calls is not None
and len(choice.delta.tool_calls) > 0
and choice.delta.tool_calls[0].function is not None
)
!= "skip"
)
text_content = str(choice.delta.content or "")
if has_tool_calls or bool(text_content):
return True
if not thinking_disabled and has_substantial_reasoning:
return True
return False
@staticmethod
def _is_blank_delta(chunk: "ModelResponseStream") -> bool:

View file

@ -1667,7 +1667,7 @@ class LiteLLMAnthropicMessagesAdapter:
def _translate_streaming_openai_chunk_to_anthropic_content_block(
self,
choices: Sequence["OpenAIStreamingChoice | StreamingChoices | Choices"],
choices: Sequence["OpenAIStreamingChoice | StreamingChoices"],
thinking_disabled: bool = False,
) -> tuple[
Literal["text", "tool_use", "thinking", "redacted_thinking"],
@ -1709,8 +1709,16 @@ class LiteLLMAnthropicMessagesAdapter:
return "redacted_thinking", cast("ContentBlockContentBlockDict", redacted_block)
if block_type == "tool_use":
raw_id = choice.delta.tool_calls[0].id or str(uuid.uuid4())
tool_name = choice.delta.tool_calls[0].function.name or ""
# Explicit narrowing (base pattern): the classifier only emits
# "tool_use" when the first tool call carries a function, so
# these asserts hold and keep the member accesses below
# optional-free without changing behaviour.
tool_calls = choice.delta.tool_calls
assert tool_calls is not None and len(tool_calls) > 0
first_tool_call = tool_calls[0]
assert first_tool_call.function is not None
raw_id = first_tool_call.id or str(uuid.uuid4())
tool_name = first_tool_call.function.name or ""
thought_sig: str | None = None
if THOUGHT_SIGNATURE_SEPARATOR in raw_id:
parts = raw_id.split(THOUGHT_SIGNATURE_SEPARATOR, 1)
@ -1732,7 +1740,7 @@ class LiteLLMAnthropicMessagesAdapter:
def _translate_streaming_openai_chunk_to_anthropic(
self,
choices: Sequence["OpenAIStreamingChoice | StreamingChoices | Choices"],
choices: Sequence["OpenAIStreamingChoice | StreamingChoices"],
thinking_disabled: bool = False,
) -> tuple[
StreamingContentBlockDeltaType,

View file

@ -43,17 +43,17 @@ async def test_async_handler_streaming_threads_thinking_disabled(thinking_param,
"""Async handler, stream=True: ``thinking_disabled`` reaches the streaming
adapter call."""
with (
patch(
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
"litellm.llms.anthropic.experimental_pass_through.adapters.handler._prepare_context_managed_request",
return_value=None,
),
patch.object(
patch.object( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
LiteLLMMessagesToCompletionTransformationHandler,
"_prepare_completion_kwargs",
return_value=({}, {}),
),
patch("litellm.acompletion", return_value=MagicMock()),
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter,
patch("litellm.acompletion", return_value=MagicMock()), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
):
mock_adapter.translate_completion_output_params_streaming.return_value = iter([])
await LiteLLMMessagesToCompletionTransformationHandler.async_anthropic_messages_handler(
@ -82,17 +82,17 @@ async def test_async_handler_non_streaming_threads_thinking_disabled(thinking_pa
"""Async handler, stream=False: ``thinking_disabled`` reaches the
non-streaming adapter call."""
with (
patch(
patch( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
"litellm.llms.anthropic.experimental_pass_through.adapters.handler._prepare_context_managed_request",
return_value=None,
),
patch.object(
patch.object( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
LiteLLMMessagesToCompletionTransformationHandler,
"_prepare_completion_kwargs",
return_value=({}, {}),
),
patch("litellm.acompletion", return_value=MagicMock()),
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter,
patch("litellm.acompletion", return_value=MagicMock()), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
):
mock_adapter.translate_completion_output_params.return_value = MagicMock()
await LiteLLMMessagesToCompletionTransformationHandler.async_anthropic_messages_handler(
@ -125,13 +125,13 @@ def test_sync_handler_streaming_threads_thinking_disabled(thinking_param, expect
blocks) so ``run_async_function`` is never invoked.
"""
with (
patch.object(
patch.object( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
LiteLLMMessagesToCompletionTransformationHandler,
"_prepare_completion_kwargs",
return_value=({}, {}),
),
patch("litellm.completion", return_value=MagicMock()),
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter,
patch("litellm.completion", return_value=MagicMock()), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
):
mock_adapter.translate_completion_output_params_streaming.return_value = iter([])
LiteLLMMessagesToCompletionTransformationHandler.anthropic_messages_handler(
@ -164,13 +164,13 @@ def test_sync_handler_non_streaming_threads_thinking_disabled(thinking_param, ex
blocks) so ``run_async_function`` is never invoked.
"""
with (
patch.object(
patch.object( # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
LiteLLMMessagesToCompletionTransformationHandler,
"_prepare_completion_kwargs",
return_value=({}, {}),
),
patch("litellm.completion", return_value=MagicMock()),
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter,
patch("litellm.completion", return_value=MagicMock()), # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
patch("litellm.llms.anthropic.experimental_pass_through.adapters.handler.ANTHROPIC_ADAPTER") as mock_adapter, # test-quality-ok: handler unit test - fakes completion dispatch + adapter seams; unit under test is the thinking_disabled translation wiring, not the transport
):
mock_adapter.translate_completion_output_params.return_value = MagicMock()
LiteLLMMessagesToCompletionTransformationHandler.anthropic_messages_handler(