mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-12 08:42:18 +00:00
fix(type_discipline): satisfy type discipline gate and ruff formatting
This commit is contained in:
parent
065df9d7fb
commit
800b123a1b
3 changed files with 101 additions and 80 deletions
|
|
@ -662,7 +662,9 @@ class AnthropicMessagesHandler(BaseTranslation):
|
|||
)
|
||||
if not scan_only_tool_results:
|
||||
retained_names: Final = frozenset(
|
||||
name for t in (data.get("tools") or []) for name in anthropic_tool_names(t)
|
||||
name
|
||||
for t in (data.get("tools") or ())
|
||||
for name in anthropic_tool_names(t) # comprehension-ok: flattens tool names across tools
|
||||
)
|
||||
self._strip_disallowed_tool_additions(data, retained_names)
|
||||
|
||||
|
|
@ -940,55 +942,71 @@ class AnthropicMessagesHandler(BaseTranslation):
|
|||
"""Extract every tool name in an Anthropic messages request: tools[].name, plus
|
||||
tools[].function.name for OpenAI-format tools the bridge forwards verbatim,
|
||||
as well as mid-conversation tool additions in messages."""
|
||||
names = [name for tool in list_field(data, "tools") for name in anthropic_tool_names(tool)]
|
||||
messages = data.get("messages")
|
||||
if isinstance(messages, Sequence):
|
||||
for m in messages:
|
||||
if isinstance(m, Mapping):
|
||||
content = m.get("content")
|
||||
if isinstance(content, Sequence) and not isinstance(content, (str, bytes)):
|
||||
for block in content:
|
||||
if isinstance(block, Mapping) and block.get("type") == "tool_addition":
|
||||
tool = extract_tool_from_addition_block(block)
|
||||
if tool is not None:
|
||||
for name in anthropic_tool_names(tool):
|
||||
if name not in names:
|
||||
names.append(name)
|
||||
return names
|
||||
top_names: Final = [ # comprehension-ok: flattens tool names across tools
|
||||
name for tool in list_field(data, "tools") for name in anthropic_tool_names(tool)
|
||||
]
|
||||
messages: Final = data.get("messages")
|
||||
if not isinstance(messages, Sequence):
|
||||
return top_names
|
||||
extra_names: Final[list[str]] = [] # mutable-ok: accumulator
|
||||
for m in messages:
|
||||
if isinstance(m, Mapping):
|
||||
content: Final = m.get("content")
|
||||
if isinstance(content, Sequence) and not isinstance(content, (str, bytes)):
|
||||
for block in content:
|
||||
if isinstance(block, Mapping) and block.get("type") == "tool_addition":
|
||||
tool: Final = extract_tool_from_addition_block(block)
|
||||
if tool is not None:
|
||||
for name in anthropic_tool_names(tool):
|
||||
if name not in top_names and name not in extra_names:
|
||||
extra_names.append(name)
|
||||
return [*top_names, *extra_names]
|
||||
|
||||
@classmethod
|
||||
def _strip_disallowed_tool_additions(cls, data: dict, retained_names: frozenset[str]) -> None:
|
||||
messages = data.get("messages")
|
||||
def _strip_disallowed_tool_additions(cls, data: Mapping[str, object], retained_names: frozenset[str]) -> None:
|
||||
messages: Final = data.get("messages")
|
||||
if not isinstance(messages, list):
|
||||
return
|
||||
cleaned_messages: list[object] = []
|
||||
cleaned_messages: Final[list[object]] = [] # mutable-ok: accumulator
|
||||
for m in messages:
|
||||
if not isinstance(m, dict):
|
||||
cleaned_messages.append(m)
|
||||
continue
|
||||
content = m.get("content")
|
||||
content: Final = m.get("content")
|
||||
if not isinstance(content, list):
|
||||
cleaned_messages.append(m)
|
||||
continue
|
||||
had_disallowed_addition = False
|
||||
remaining_blocks: list[object] = []
|
||||
for block in content:
|
||||
if isinstance(block, dict) and block.get("type") == "tool_addition":
|
||||
tool = extract_tool_from_addition_block(block)
|
||||
tool_names = anthropic_tool_names(tool) if tool else ()
|
||||
if tool_names and all(name in retained_names for name in tool_names):
|
||||
remaining_blocks.append(block)
|
||||
else:
|
||||
had_disallowed_addition = True
|
||||
else:
|
||||
remaining_blocks.append(block)
|
||||
had_disallowed_addition: Final = any(
|
||||
isinstance(block, dict)
|
||||
and block.get("type") == "tool_addition"
|
||||
and (
|
||||
not (tool := extract_tool_from_addition_block(block))
|
||||
or not (tool_names := anthropic_tool_names(tool))
|
||||
or not all(name in retained_names for name in tool_names)
|
||||
)
|
||||
for block in content
|
||||
)
|
||||
if not had_disallowed_addition:
|
||||
cleaned_messages.append(m)
|
||||
continue
|
||||
remaining_blocks: Final[list[object]] = [ # mutable-ok: accumulator
|
||||
block
|
||||
for block in content
|
||||
if not (
|
||||
isinstance(block, dict)
|
||||
and block.get("type") == "tool_addition"
|
||||
and (
|
||||
not (tool := extract_tool_from_addition_block(block))
|
||||
or not (tool_names := anthropic_tool_names(tool))
|
||||
or not all(name in retained_names for name in tool_names)
|
||||
)
|
||||
)
|
||||
]
|
||||
if remaining_blocks:
|
||||
new_m = dict(m)
|
||||
new_m: Final[dict[str, object]] = dict(m) # mutable-ok: message clone
|
||||
new_m["content"] = remaining_blocks
|
||||
cleaned_messages.append(new_m)
|
||||
elif not had_disallowed_addition:
|
||||
cleaned_messages.append(m)
|
||||
data["messages"] = cleaned_messages
|
||||
data["messages"] = cleaned_messages # rebind-ok: update request in place
|
||||
|
||||
@classmethod
|
||||
def _extract_input_text_and_images(
|
||||
|
|
|
|||
|
|
@ -1236,73 +1236,78 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
self,
|
||||
messages: Sequence[Mapping[str, object] | AllAnthropicPassThroughMessageValues],
|
||||
initial_tools: Sequence[AllAnthropicToolsValues] | None = None,
|
||||
) -> tuple[list[AllAnthropicToolsValues] | None, list[AllAnthropicPassThroughMessageValues]]:
|
||||
) -> tuple[tuple[AllAnthropicToolsValues, ...] | None, tuple[AllAnthropicPassThroughMessageValues, ...]]:
|
||||
"""
|
||||
Extract and apply mid-conversation tool changes (tool_addition, tool_removal)
|
||||
such as those sent by Claude Code under mid-conversation-tool-changes beta.
|
||||
|
||||
Returns:
|
||||
Tuple of (active_tools, cleaned_messages)
|
||||
- active_tools: Updated tools list with additions applied and removals removed,
|
||||
- active_tools: Updated tools tuple with additions applied and removals removed,
|
||||
or None if no tools exist and none were added.
|
||||
- cleaned_messages: Messages list with tool_addition and tool_removal blocks stripped,
|
||||
- cleaned_messages: Messages tuple with tool_addition and tool_removal blocks stripped,
|
||||
and carrier messages left without content dropped.
|
||||
"""
|
||||
active_tools: list[AllAnthropicToolsValues] = list(initial_tools) if initial_tools else []
|
||||
had_tool_changes = False
|
||||
cleaned_messages: list[AllAnthropicPassThroughMessageValues] = []
|
||||
active_tools: tuple[AllAnthropicToolsValues, ...] = tuple(initial_tools or ()) # rebind-ok: tool updates
|
||||
had_tool_changes: bool = False # rebind-ok: state flag
|
||||
cleaned_messages: Final[list[AllAnthropicPassThroughMessageValues]] = [] # mutable-ok: accumulator
|
||||
|
||||
for m in messages:
|
||||
if not isinstance(m, dict):
|
||||
cleaned_messages.append(m) # type: ignore[arg-type]
|
||||
if not isinstance(m, Mapping):
|
||||
cleaned_messages.append(m) # pyright: ignore[reportArgumentType] # non-mapping passthrough
|
||||
continue
|
||||
content = m.get("content")
|
||||
if not isinstance(content, list):
|
||||
cleaned_messages.append(cast(AllAnthropicPassThroughMessageValues, m))
|
||||
content: Final = m.get("content")
|
||||
if not isinstance(content, Sequence) or isinstance(content, (str, bytes)):
|
||||
msg_val: Final = cast(AllAnthropicPassThroughMessageValues, m) # cast-ok: msg
|
||||
cleaned_messages.append(msg_val)
|
||||
continue
|
||||
|
||||
remaining_blocks: list[dict[str, Any]] = []
|
||||
remaining_blocks: Final[list[object]] = [] # mutable-ok: accumulator
|
||||
for block in content:
|
||||
if not isinstance(block, dict):
|
||||
if not isinstance(block, Mapping):
|
||||
remaining_blocks.append(block)
|
||||
continue
|
||||
btype = block.get("type")
|
||||
btype: Final = block.get("type")
|
||||
if btype == "tool_addition":
|
||||
had_tool_changes = True
|
||||
raw_tool = extract_tool_from_addition_block(block)
|
||||
if isinstance(raw_tool, dict) and raw_tool.get("name"):
|
||||
tool_name = str(raw_tool["name"])
|
||||
if not any(isinstance(t, dict) and t.get("name") == tool_name for t in active_tools):
|
||||
active_tools.append(cast(AllAnthropicToolsValues, raw_tool))
|
||||
raw_tool: Final = extract_tool_from_addition_block(block)
|
||||
if isinstance(raw_tool, Mapping) and raw_tool.get("name"):
|
||||
tool_name: Final = str(raw_tool["name"])
|
||||
if not any(isinstance(t, Mapping) and t.get("name") == tool_name for t in active_tools):
|
||||
added_tool: Final = cast(AllAnthropicToolsValues, dict(raw_tool)) # cast-ok: tool
|
||||
active_tools = (*active_tools, added_tool)
|
||||
elif btype == "tool_removal":
|
||||
had_tool_changes = True
|
||||
rem_name = (
|
||||
rem_name: Final = (
|
||||
block.get("tool_name")
|
||||
or block.get("name")
|
||||
or (block.get("tool", {}).get("name") if isinstance(block.get("tool"), dict) else None)
|
||||
or (block.get("tool", {}).get("name") if isinstance(block.get("tool"), Mapping) else None)
|
||||
or (block.get("tool") if isinstance(block.get("tool"), str) else None)
|
||||
)
|
||||
if rem_name:
|
||||
rem_name_str = str(rem_name)
|
||||
active_tools = [
|
||||
t for t in active_tools if not (isinstance(t, dict) and t.get("name") == rem_name_str)
|
||||
]
|
||||
rem_name_str: Final = str(rem_name)
|
||||
active_tools = tuple(
|
||||
t for t in active_tools if not (isinstance(t, Mapping) and t.get("name") == rem_name_str)
|
||||
)
|
||||
else:
|
||||
remaining_blocks.append(block)
|
||||
|
||||
if remaining_blocks:
|
||||
new_m = dict(m)
|
||||
new_m: Final[dict[str, object]] = dict(m) # mutable-ok: message clone
|
||||
new_m["content"] = remaining_blocks
|
||||
cleaned_messages.append(cast(AllAnthropicPassThroughMessageValues, new_m))
|
||||
new_msg: Final = cast(AllAnthropicPassThroughMessageValues, new_m) # cast-ok: msg
|
||||
cleaned_messages.append(new_msg)
|
||||
elif not had_tool_changes:
|
||||
cleaned_messages.append(cast(AllAnthropicPassThroughMessageValues, m))
|
||||
clean_msg: Final = cast(AllAnthropicPassThroughMessageValues, m) # cast-ok: msg
|
||||
cleaned_messages.append(clean_msg)
|
||||
|
||||
if not had_tool_changes and initial_tools is None:
|
||||
return None, cleaned_messages
|
||||
return None, tuple(cleaned_messages)
|
||||
|
||||
return active_tools if active_tools else (
|
||||
[] if initial_tools is not None or had_tool_changes else None
|
||||
), cleaned_messages
|
||||
return (
|
||||
active_tools if active_tools else (() if initial_tools is not None or had_tool_changes else None),
|
||||
tuple(cleaned_messages),
|
||||
)
|
||||
|
||||
def translate_anthropic_to_openai(
|
||||
self,
|
||||
|
|
@ -1320,8 +1325,6 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
- tool_name_mapping maps truncated tool names back to original names
|
||||
for tools that exceeded OpenAI's 64-char limit
|
||||
"""
|
||||
# Debug: Processing Anthropic message request
|
||||
new_messages: list[AllMessageValues] = []
|
||||
tool_name_mapping: dict[str, str] = {}
|
||||
|
||||
## CONVERT ANTHROPIC MESSAGES TO OPENAI
|
||||
|
|
@ -1336,17 +1339,17 @@ class LiteLLMAnthropicMessagesAdapter:
|
|||
messages=messages_list,
|
||||
initial_tools=anthropic_message_request.get("tools"),
|
||||
)
|
||||
request_for_translation: Final[AnthropicMessagesRequest] = cast(
|
||||
request_for_translation: Final[AnthropicMessagesRequest] = cast( # cast-ok: anthropic request type
|
||||
AnthropicMessagesRequest, dict(anthropic_message_request)
|
||||
)
|
||||
if active_tools is not None:
|
||||
if active_tools:
|
||||
request_for_translation["tools"] = active_tools
|
||||
request_for_translation["tools"] = list(active_tools)
|
||||
else:
|
||||
request_for_translation.pop("tools", None)
|
||||
|
||||
new_messages = self.translate_anthropic_messages_to_openai(
|
||||
messages=cleaned_messages,
|
||||
new_messages: Final = self.translate_anthropic_messages_to_openai(
|
||||
messages=list(cleaned_messages),
|
||||
model=request_for_translation.get("model"),
|
||||
custom_llm_provider=custom_llm_provider,
|
||||
preserve_midturn_system=preserve_midturn_system,
|
||||
|
|
|
|||
|
|
@ -319,22 +319,22 @@ def anthropic_tool_name(tool: object) -> str | None:
|
|||
return names[0] if names else None
|
||||
|
||||
|
||||
def extract_tool_from_addition_block(block: object) -> dict[str, object] | None:
|
||||
def extract_tool_from_addition_block(block: object) -> Mapping[str, object] | None:
|
||||
"""Extract raw tool dict from an Anthropic tool_addition block."""
|
||||
if not isinstance(block, Mapping):
|
||||
return None
|
||||
tool_data = block.get("tool")
|
||||
if isinstance(tool_data, dict):
|
||||
tool_data: Final = block.get("tool")
|
||||
if isinstance(tool_data, Mapping):
|
||||
if (
|
||||
tool_data.get("type") == "tool_definition"
|
||||
and "definition" in tool_data
|
||||
and isinstance(tool_data["definition"], dict)
|
||||
and isinstance(tool_data["definition"], Mapping)
|
||||
):
|
||||
return tool_data["definition"]
|
||||
return tool_data
|
||||
if "definition" in block and isinstance(block["definition"], dict):
|
||||
if "definition" in block and isinstance(block["definition"], Mapping):
|
||||
return block["definition"]
|
||||
if isinstance(block.get("tool_definition"), dict):
|
||||
if isinstance(block.get("tool_definition"), Mapping):
|
||||
return block["tool_definition"]
|
||||
return None
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue