From e4194cded24680709d7ab21306edb9c84825bdfb Mon Sep 17 00:00:00 2001 From: Li-john1021 <215793081+Li-john1021@users.noreply.github.com> Date: Thu, 15 Oct 2026 09:34:33 +0800 Subject: [PATCH] fix(anthropic): quiet basedpyright deltas on thought_signature path Drop the four Unknown-typed locals introduced when stripping embedded signatures from tool_use / tool_result ids (use a typed helper instead), and keep streaming `.function` on the narrowed tool_calls[0] expression so reportOptionalMemberAccess stays at the merge-base ceiling. --- litellm/llms/anthropic/common_utils.py | 4 +- .../pass_through/adapters/transformation.py | 54 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/litellm/llms/anthropic/common_utils.py b/litellm/llms/anthropic/common_utils.py index 9a58d85238a..ff623d644d8 100644 --- a/litellm/llms/anthropic/common_utils.py +++ b/litellm/llms/anthropic/common_utils.py @@ -1792,7 +1792,7 @@ def encode_thought_signature_for_anthropic_tool_id(signature: str) -> str: def decode_thought_signature_from_anthropic_tool_id(encoded: str) -> str: """Inverse of ``encode_thought_signature_for_anthropic_tool_id``.""" - pad: Final = (-len(encoded)) % 4 + pad: Final[int] = (-len(encoded)) % 4 return base64.urlsafe_b64decode(encoded + ("=" * pad)).decode("utf-8") @@ -1806,7 +1806,7 @@ def format_anthropic_tool_use_id_with_signature(raw_id: str, thought_signature: signature after ``__thought__`` lets those clients round-trip the real signature the same way OpenAI-compatible clients do via ``tool_calls[].id``. """ - base_id: Final = normalize_anthropic_tool_use_id(raw_id) + base_id: Final[str] = normalize_anthropic_tool_use_id(raw_id) if not thought_signature: return base_id return f"{base_id}{THOUGHT_SIGNATURE_SEPARATOR}{encode_thought_signature_for_anthropic_tool_id(thought_signature)}" diff --git a/litellm/llms/anthropic/pass_through/adapters/transformation.py b/litellm/llms/anthropic/pass_through/adapters/transformation.py index 459469cda5c..4c2fa0dc490 100644 --- a/litellm/llms/anthropic/pass_through/adapters/transformation.py +++ b/litellm/llms/anthropic/pass_through/adapters/transformation.py @@ -136,6 +136,7 @@ from litellm.llms.anthropic.common_utils import ( split_anthropic_tool_use_id_signature, strip_encrypted_reasoning_blocks_from_anthropic_messages, ) + from litellm.llms.anthropic.pass_through.context_management import ( PolyfillResult, ) @@ -347,6 +348,13 @@ class AnthropicAdapter: return anthropic_wrapper.anthropic_sse_wrapper() +def _base_tool_use_id(raw_id: object) -> str: + """Strip an embedded thought signature from a tool_use / tool_result id.""" + if isinstance(raw_id, str): + return split_anthropic_tool_use_id_signature(raw_id)[0] + return "" + + _BlockT: Final = TypeVar("_BlockT", bound=Mapping[str, object]) @@ -365,21 +373,21 @@ class LiteLLMAnthropicMessagesAdapter: the id verbatim). Do not URL-decode here — that encoding is only used on Anthropic ``tool_use.id`` values. """ - fields: Final = _optional_attr(tool_call, "provider_specific_fields") + fields: Final[object] = _optional_attr(tool_call, "provider_specific_fields") if fields: - signature: Final = _thought_signature(fields) + signature: Final[str | None] = _thought_signature(fields) if signature: return signature - function_fields: Final = _optional_attr(_optional_attr(tool_call, "function"), "provider_specific_fields") + function_fields: Final[object] = _optional_attr(_optional_attr(tool_call, "function"), "provider_specific_fields") if function_fields: - function_signature: Final = _thought_signature(function_fields) + function_signature: Final[str | None] = _thought_signature(function_fields) if function_signature: return function_signature - raw_id: Final = _optional_attr(tool_call, "id") + raw_id: Final[object] = _optional_attr(tool_call, "id") if isinstance(raw_id, str) and THOUGHT_SIGNATURE_SEPARATOR in raw_id: - parts: Final = raw_id.split(THOUGHT_SIGNATURE_SEPARATOR, 1) + parts: Final[list[str]] = raw_id.split(THOUGHT_SIGNATURE_SEPARATOR, 1) if len(parts) == 2 and parts[1]: return parts[1] @@ -394,12 +402,14 @@ class LiteLLMAnthropicMessagesAdapter: (URL-safe encoded so the id stays inside Anthropic's charset). Clients that drop unknown fields, such as Claude Code, only echo ``id``. """ - provider_specific_fields: Final = _as_string_mapping(content.get("provider_specific_fields", {})) + provider_specific_fields: Final[Mapping[str, object] | None] = _as_string_mapping( + content.get("provider_specific_fields", {}) + ) if provider_specific_fields: - signature: Final = provider_specific_fields.get("signature") + signature: Final[object] = provider_specific_fields.get("signature") if isinstance(signature, str) and signature: return signature - raw_id: Final = content.get("id") + raw_id: Final[object] = content.get("id") if isinstance(raw_id, str): _, embedded = split_anthropic_tool_use_id_signature(raw_id) if embedded: @@ -551,15 +561,9 @@ class LiteLLMAnthropicMessagesAdapter: self._add_cache_control_if_applicable(content, doc_obj, model) new_user_content_list.append(doc_obj) elif content.get("type") == "tool_result": - raw_tool_result_id = content.get("tool_use_id", "") - tool_result_id = ( - split_anthropic_tool_use_id_signature(raw_tool_result_id)[0] - if isinstance(raw_tool_result_id, str) - else raw_tool_result_id - ) tool_result = ChatCompletionToolMessage( role="tool", - tool_call_id=tool_result_id, + tool_call_id=_base_tool_use_id(content.get("tool_use_id", "")), content=self._tool_result_content(content.get("content")), ) self._add_cache_control_if_applicable(content, tool_result, model) @@ -615,14 +619,8 @@ class LiteLLMAnthropicMessagesAdapter: provider_specific_fields["thought_signature"] = signature function_chunk["provider_specific_fields"] = provider_specific_fields - raw_tool_use_id = content.get("id", "") - tool_call_id = ( - split_anthropic_tool_use_id_signature(raw_tool_use_id)[0] - if isinstance(raw_tool_use_id, str) - else raw_tool_use_id - ) tool_call = ChatCompletionAssistantToolCall( - id=tool_call_id, + id=_base_tool_use_id(content.get("id", "")), type="function", function=function_chunk, ) @@ -1675,10 +1673,12 @@ class LiteLLMAnthropicMessagesAdapter: and len(choice.delta.tool_calls) > 0 and choice.delta.tool_calls[0].function is not None ): - delta_tool_call = choice.delta.tool_calls[0] - raw_id = delta_tool_call.id or str(uuid.uuid4()) - tool_name = delta_tool_call.function.name or "" - thought_sig = self._extract_signature_from_tool_call(delta_tool_call) + raw_id = choice.delta.tool_calls[0].id or str(uuid.uuid4()) + # Keep `.function` on the same narrowed expression as the guard + # (`choice.delta.tool_calls[0].function is not None`) so + # reportOptionalMemberAccess stays flat. + tool_name = choice.delta.tool_calls[0].function.name or "" + thought_sig = self._extract_signature_from_tool_call(choice.delta.tool_calls[0]) tool_block: dict[str, object] = { "type": "tool_use", "id": format_anthropic_tool_use_id_with_signature(raw_id, thought_sig),