fix(lint): apply Black formatting to 14 files (#24532)

This commit is contained in:
Joe Reyna 2026-03-31 07:45:55 -07:00 committed by GitHub
parent 08be1e52ae
commit 9942d59631
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 305 additions and 123 deletions

View File

@ -96,7 +96,9 @@ def _build_reasoning_item(
}
def _reasoning_item_to_response_input(r_item: Union[ChatCompletionReasoningItem, Dict[str, Any]]) -> Dict[str, Any]:
def _reasoning_item_to_response_input(
r_item: Union[ChatCompletionReasoningItem, Dict[str, Any]]
) -> Dict[str, Any]:
"""Convert a stored ChatCompletionReasoningItem back to a Responses API input item."""
r_input: Dict[str, Any] = {
"type": "reasoning",

View File

@ -2789,9 +2789,7 @@ class PrometheusLogger(CustomLogger):
)
return
async def fetch_orgs(
page_size: int, page: int
) -> Tuple[list, Optional[int]]:
async def fetch_orgs(page_size: int, page: int) -> Tuple[list, Optional[int]]:
skip = (page - 1) * page_size
orgs = await prisma_client.db.litellm_organizationtable.find_many(
skip=skip,

View File

@ -238,8 +238,8 @@ class A2AGuardrailHandler(BaseTranslation):
if not valid_parsed:
return responses_so_far
combined_text, chunk_indices_with_text = (
self._collect_text_from_parsed_chunks(valid_parsed)
combined_text, chunk_indices_with_text = self._collect_text_from_parsed_chunks(
valid_parsed
)
if not combined_text:
return responses_so_far
@ -316,9 +316,7 @@ class A2AGuardrailHandler(BaseTranslation):
def _parse_streaming_responses(
self,
responses_so_far: List[Any],
) -> Tuple[
List[Optional[Dict[str, Any]]], List[Tuple[int, Dict[str, Any]]]
]:
) -> Tuple[List[Optional[Dict[str, Any]]], List[Tuple[int, Dict[str, Any]]]]:
"""Parse JSON-RPC items, returning aligned parsed list and valid entries."""
parsed: List[Optional[Dict[str, Any]]] = [None] * len(responses_so_far)
for i, item in enumerate(responses_so_far):

View File

@ -284,18 +284,27 @@ class AnthropicMessagesHandler(BaseTranslation):
# Step 1: Extract all text content and tool calls from response
self._extract_from_content_blocks(
response_content, texts_to_check, images_to_check,
task_mappings, tool_calls_to_check,
response_content,
texts_to_check,
images_to_check,
task_mappings,
tool_calls_to_check,
)
# Step 2: Apply guardrail to all texts in batch
if texts_to_check or tool_calls_to_check:
request_data = self._prepare_request_data(
request_data, response, user_api_key_dict, key="response",
request_data,
response,
user_api_key_dict,
key="response",
)
inputs = self._build_guardrail_inputs(
texts_to_check, images_to_check, tool_calls_to_check, response,
texts_to_check,
images_to_check,
tool_calls_to_check,
response,
)
guardrailed_inputs = await guardrail_to_apply.apply_guardrail(

View File

@ -160,7 +160,8 @@ class AmazonConverseConfig(BaseConfig):
if isinstance(content, list):
has_guarded_text = any(
isinstance(item, dict) and item.get("type") == "guarded_text" for item in content
isinstance(item, dict) and item.get("type") == "guarded_text"
for item in content
)
if has_guarded_text:
continue # Skip this message if it already has guarded_text
@ -321,9 +322,13 @@ class AmazonConverseConfig(BaseConfig):
# Check if the model is a Nova 2 model (matches nova-2-lite, nova-2-pro, etc.)
# Also check for nova-2/ spec prefix for imported models
return model_without_region.startswith("amazon.nova-2-") or model_without_region.startswith("nova-2/")
return model_without_region.startswith(
"amazon.nova-2-"
) or model_without_region.startswith("nova-2/")
def _map_web_search_options(self, web_search_options: dict, model: str) -> Optional[BedrockToolBlock]:
def _map_web_search_options(
self, web_search_options: dict, model: str
) -> Optional[BedrockToolBlock]:
"""
Map web_search_options to Nova grounding systemTool.
@ -352,7 +357,9 @@ class AmazonConverseConfig(BaseConfig):
# (unlike Anthropic), so we just enable grounding with no options
return BedrockToolBlock(systemTool={"name": "nova_grounding"})
def _transform_reasoning_effort_to_reasoning_config(self, reasoning_effort: str) -> dict:
def _transform_reasoning_effort_to_reasoning_config(
self, reasoning_effort: str
) -> dict:
"""
Transform reasoning_effort parameter to Nova 2 reasoningConfig structure.
@ -397,7 +404,9 @@ class AmazonConverseConfig(BaseConfig):
}
}
def _handle_reasoning_effort_parameter(self, model: str, reasoning_effort: str, optional_params: dict) -> None:
def _handle_reasoning_effort_parameter(
self, model: str, reasoning_effort: str, optional_params: dict
) -> None:
"""
Handle the reasoning_effort parameter based on the model type.
@ -434,7 +443,9 @@ class AmazonConverseConfig(BaseConfig):
optional_params["reasoning_effort"] = reasoning_effort
elif self._is_nova_2_model(model):
# Nova 2 models: transform to reasoningConfig
reasoning_config = self._transform_reasoning_effort_to_reasoning_config(reasoning_effort)
reasoning_config = self._transform_reasoning_effort_to_reasoning_config(
reasoning_effort
)
optional_params.update(reasoning_config)
else:
# Anthropic and other models: convert to thinking parameter
@ -478,7 +489,9 @@ class AmazonConverseConfig(BaseConfig):
"parallel_tool_calls",
]
if "arn" in model: # we can't infer the model from the arn, so just add all params
if (
"arn" in model
): # we can't infer the model from the arn, so just add all params
supported_params.append("tools")
supported_params.append("tool_choice")
supported_params.append("thinking")
@ -500,7 +513,9 @@ class AmazonConverseConfig(BaseConfig):
or base_model.startswith("meta.llama3-3")
or base_model.startswith("meta.llama4")
or base_model.startswith("amazon.nova")
or supports_function_calling(model=model, custom_llm_provider=self.custom_llm_provider)
or supports_function_calling(
model=model, custom_llm_provider=self.custom_llm_provider
)
):
supported_params.append("tools")
@ -510,7 +525,9 @@ class AmazonConverseConfig(BaseConfig):
if litellm.utils.supports_tool_choice(
model=model, custom_llm_provider=self.custom_llm_provider
) or litellm.utils.supports_tool_choice(model=base_model, custom_llm_provider=self.custom_llm_provider):
) or litellm.utils.supports_tool_choice(
model=base_model, custom_llm_provider=self.custom_llm_provider
):
# only anthropic and mistral support tool choice config. otherwise (E.g. cohere) will fail the call - https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolChoice.html
supported_params.append("tool_choice")
@ -529,7 +546,9 @@ class AmazonConverseConfig(BaseConfig):
model=model,
custom_llm_provider=self.custom_llm_provider,
)
or supports_reasoning(model=base_model, custom_llm_provider=self.custom_llm_provider)
or supports_reasoning(
model=base_model, custom_llm_provider=self.custom_llm_provider
)
):
supported_params.append("thinking")
supported_params.append("reasoning_effort")
@ -554,7 +573,9 @@ class AmazonConverseConfig(BaseConfig):
return ToolChoiceValuesBlock(auto={})
elif isinstance(tool_choice, dict):
# only supported for anthropic + mistral models - https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_ToolChoice.html
specific_tool = SpecificToolChoiceBlock(name=tool_choice.get("function", {}).get("name", ""))
specific_tool = SpecificToolChoiceBlock(
name=tool_choice.get("function", {}).get("name", "")
)
return ToolChoiceValuesBlock(tool=specific_tool)
else:
raise litellm.utils.UnsupportedParamsError(
@ -574,9 +595,15 @@ class AmazonConverseConfig(BaseConfig):
return ["mp4", "mov", "mkv", "webm", "flv", "mpeg", "mpg", "wmv", "3gp"]
def get_all_supported_content_types(self) -> List[str]:
return self.get_supported_image_types() + self.get_supported_document_types() + self.get_supported_video_types()
return (
self.get_supported_image_types()
+ self.get_supported_document_types()
+ self.get_supported_video_types()
)
def is_computer_use_tool_used(self, tools: Optional[List[OpenAIChatCompletionToolParam]], model: str) -> bool:
def is_computer_use_tool_used(
self, tools: Optional[List[OpenAIChatCompletionToolParam]], model: str
) -> bool:
"""Check if computer use tools are being used in the request."""
if tools is None:
return False
@ -589,7 +616,9 @@ class AmazonConverseConfig(BaseConfig):
return True
return False
def _transform_computer_use_tools(self, computer_use_tools: List[OpenAIChatCompletionToolParam]) -> List[dict]:
def _transform_computer_use_tools(
self, computer_use_tools: List[OpenAIChatCompletionToolParam]
) -> List[dict]:
"""Transform computer use tools to Bedrock format."""
transformed_tools: List[dict] = []
@ -631,7 +660,9 @@ class AmazonConverseConfig(BaseConfig):
def _separate_computer_use_tools(
self, tools: List[OpenAIChatCompletionToolParam], model: str
) -> Tuple[List[OpenAIChatCompletionToolParam], List[OpenAIChatCompletionToolParam]]:
) -> Tuple[
List[OpenAIChatCompletionToolParam], List[OpenAIChatCompletionToolParam]
]:
"""
Separate computer use tools from regular function tools.
@ -740,18 +771,25 @@ class AmazonConverseConfig(BaseConfig):
# Recurse into nested schemas
if "properties" in result and isinstance(result["properties"], dict):
result["properties"] = {
k: AmazonConverseConfig._add_additional_properties_to_schema(v) for k, v in result["properties"].items()
k: AmazonConverseConfig._add_additional_properties_to_schema(v)
for k, v in result["properties"].items()
}
if "items" in result and isinstance(result["items"], dict):
result["items"] = AmazonConverseConfig._add_additional_properties_to_schema(result["items"])
result["items"] = AmazonConverseConfig._add_additional_properties_to_schema(
result["items"]
)
for defs_key in ("$defs", "definitions"):
if defs_key in result and isinstance(result[defs_key], dict):
result[defs_key] = {
k: AmazonConverseConfig._add_additional_properties_to_schema(v) for k, v in result[defs_key].items()
k: AmazonConverseConfig._add_additional_properties_to_schema(v)
for k, v in result[defs_key].items()
}
for key in ("anyOf", "allOf", "oneOf"):
if key in result and isinstance(result[key], list):
result[key] = [AmazonConverseConfig._add_additional_properties_to_schema(item) for item in result[key]]
result[key] = [
AmazonConverseConfig._add_additional_properties_to_schema(item)
for item in result[key]
]
return result
@ -781,7 +819,9 @@ class AmazonConverseConfig(BaseConfig):
}
"""
if json_schema is not None:
json_schema = AmazonConverseConfig._add_additional_properties_to_schema(json_schema)
json_schema = AmazonConverseConfig._add_additional_properties_to_schema(
json_schema
)
schema_str = json.dumps(json_schema) if json_schema is not None else "{}"
json_schema_def: JsonSchemaDefinition = {"schema": schema_str}
if name is not None:
@ -803,9 +843,14 @@ class AmazonConverseConfig(BaseConfig):
non_default_params: dict,
optional_params: dict,
):
optional_params = self._add_tools_to_optional_params(optional_params=optional_params, tools=tools)
optional_params = self._add_tools_to_optional_params(
optional_params=optional_params, tools=tools
)
if "meta.llama3-3-70b-instruct-v1:0" in model and non_default_params.get("stream", False) is True:
if (
"meta.llama3-3-70b-instruct-v1:0" in model
and non_default_params.get("stream", False) is True
):
optional_params["fake_stream"] = True
def map_openai_params(
@ -944,7 +989,10 @@ class AmazonConverseConfig(BaseConfig):
if "type" in value and value["type"] == "text":
return optional_params
if self._supports_native_structured_outputs(model, self.custom_llm_provider) and json_schema is not None:
if (
self._supports_native_structured_outputs(model, self.custom_llm_provider)
and json_schema is not None
):
# Use Bedrock's native structured outputs API (outputConfig.textFormat)
# No synthetic tool injection, no fake_stream needed.
# Requires an explicit schema — json_object with no schema falls through
@ -962,10 +1010,14 @@ class AmazonConverseConfig(BaseConfig):
json_schema=json_schema,
description=description,
)
optional_params = self._add_tools_to_optional_params(optional_params=optional_params, tools=[_tool])
optional_params = self._add_tools_to_optional_params(
optional_params=optional_params, tools=[_tool]
)
if (
litellm.utils.supports_tool_choice(model=model, custom_llm_provider=self.custom_llm_provider)
litellm.utils.supports_tool_choice(
model=model, custom_llm_provider=self.custom_llm_provider
)
and not is_thinking_enabled
):
optional_params["tool_choice"] = ToolChoiceValuesBlock(
@ -977,7 +1029,9 @@ class AmazonConverseConfig(BaseConfig):
optional_params["json_mode"] = True
return optional_params
def update_optional_params_with_thinking_tokens(self, non_default_params: dict, optional_params: dict):
def update_optional_params_with_thinking_tokens(
self, non_default_params: dict, optional_params: dict
):
"""
Handles scenario where max tokens is not specified. For anthropic models (anthropic api/bedrock/vertex ai), this requires having the max tokens being set and being greater than the thinking token budget.
@ -995,9 +1049,13 @@ class AmazonConverseConfig(BaseConfig):
is_thinking_enabled = self.is_thinking_enabled(optional_params)
is_max_tokens_in_request = self.is_max_tokens_in_request(non_default_params)
if is_thinking_enabled and not is_max_tokens_in_request:
thinking_token_budget = cast(dict, optional_params["thinking"]).get("budget_tokens", None)
thinking_token_budget = cast(dict, optional_params["thinking"]).get(
"budget_tokens", None
)
if thinking_token_budget is not None:
optional_params["maxTokens"] = thinking_token_budget + DEFAULT_MAX_TOKENS
optional_params["maxTokens"] = (
thinking_token_budget + DEFAULT_MAX_TOKENS
)
@overload
def _get_cache_point_block(
@ -1063,15 +1121,23 @@ class AmazonConverseConfig(BaseConfig):
if message["role"] == "system":
system_prompt_indices.append(idx)
if isinstance(message["content"], str) and message["content"]:
system_content_blocks.append(SystemContentBlock(text=message["content"]))
cache_block = self._get_cache_point_block(message, block_type="system", model=model)
system_content_blocks.append(
SystemContentBlock(text=message["content"])
)
cache_block = self._get_cache_point_block(
message, block_type="system", model=model
)
if cache_block:
system_content_blocks.append(cache_block)
elif isinstance(message["content"], list):
for m in message["content"]:
if m.get("type") == "text" and m.get("text"):
system_content_blocks.append(SystemContentBlock(text=m["text"]))
cache_block = self._get_cache_point_block(m, block_type="system", model=model)
system_content_blocks.append(
SystemContentBlock(text=m["text"])
)
cache_block = self._get_cache_point_block(
m, block_type="system", model=model
)
if cache_block:
system_content_blocks.append(cache_block)
if len(system_prompt_indices) > 0:
@ -1109,10 +1175,16 @@ class AmazonConverseConfig(BaseConfig):
# Exceptions should not be stored in optional_params (this is a defensive fix)
cleaned_params = filter_exceptions_from_params(optional_params)
inference_params = safe_deep_copy(cleaned_params)
supported_converse_params = list(AmazonConverseConfig.__annotations__.keys()) + ["top_k"]
supported_converse_params = list(
AmazonConverseConfig.__annotations__.keys()
) + ["top_k"]
supported_tool_call_params = ["tools", "tool_choice"]
supported_config_params = list(self.get_config_blocks().keys())
total_supported_params = supported_converse_params + supported_tool_call_params + supported_config_params
total_supported_params = (
supported_converse_params
+ supported_tool_call_params
+ supported_config_params
)
inference_params.pop("json_mode", None) # used for handling json_schema
# Anthropic-only key. Bedrock expects `outputConfig` (camelCase) and
# will reject `output_config` if it leaks through pass-through routes.
@ -1123,15 +1195,25 @@ class AmazonConverseConfig(BaseConfig):
if request_metadata is not None:
self._validate_request_metadata(request_metadata)
output_config: Optional[OutputConfigBlock] = inference_params.pop("outputConfig", None)
inference_params.pop("output_config", None) # Bedrock Converse doesn't support it
output_config: Optional[OutputConfigBlock] = inference_params.pop(
"outputConfig", None
)
inference_params.pop(
"output_config", None
) # Bedrock Converse doesn't support it
# keep supported params in 'inference_params', and set all model-specific params in 'additional_request_params'
additional_request_params = {k: v for k, v in inference_params.items() if k not in total_supported_params}
inference_params = {k: v for k, v in inference_params.items() if k in total_supported_params}
additional_request_params = {
k: v for k, v in inference_params.items() if k not in total_supported_params
}
inference_params = {
k: v for k, v in inference_params.items() if k in total_supported_params
}
# Handle parallel_tool_calls configuration
parallel_tool_use_config = additional_request_params.pop("_parallel_tool_use_config", None)
parallel_tool_use_config = additional_request_params.pop(
"_parallel_tool_use_config", None
)
if parallel_tool_use_config is not None and is_claude_4_5_on_bedrock(model):
for key, value in parallel_tool_use_config.items():
if (
@ -1146,7 +1228,9 @@ class AmazonConverseConfig(BaseConfig):
additional_request_params.pop("parallel_tool_calls", None)
# Only set the topK value in for models that support it
additional_request_params.update(self._handle_top_k_value(model, inference_params))
additional_request_params.update(
self._handle_top_k_value(model, inference_params)
)
# Filter out internal/MCP-related parameters that shouldn't be sent to the API
# These are LiteLLM internal parameters, not API parameters
@ -1155,7 +1239,9 @@ class AmazonConverseConfig(BaseConfig):
# Filter out non-serializable objects (exceptions, callables, logging objects, etc.)
# from additional_request_params to prevent JSON serialization errors
# This filters: Exception objects, callable objects (functions), Logging objects, etc.
additional_request_params = filter_exceptions_from_params(additional_request_params)
additional_request_params = filter_exceptions_from_params(
additional_request_params
)
return (
inference_params,
@ -1202,7 +1288,9 @@ class AmazonConverseConfig(BaseConfig):
# Only separate tools if computer use tools are actually present
if filtered_tools and self.is_computer_use_tool_used(filtered_tools, model):
# Separate computer use tools from regular function tools
computer_use_tools, regular_tools = self._separate_computer_use_tools(filtered_tools, model)
computer_use_tools, regular_tools = self._separate_computer_use_tools(
filtered_tools, model
)
# Process regular function tools using existing logic
bedrock_tools = _bedrock_tools_pt(regular_tools)
@ -1263,7 +1351,9 @@ class AmazonConverseConfig(BaseConfig):
anthropic_beta_list.append(computer_use_header)
# Transform computer use tools to proper Bedrock format
transformed_computer_tools = self._transform_computer_use_tools(computer_use_tools)
transformed_computer_tools = self._transform_computer_use_tools(
computer_use_tools
)
additional_request_params["tools"] = transformed_computer_tools
else:
# No computer use tools, process all tools as regular tools
@ -1292,9 +1382,15 @@ class AmazonConverseConfig(BaseConfig):
"""
Bedrock doesn't support tool calling without `tools=` param specified.
"""
if "tools" not in optional_params and messages is not None and has_tool_call_blocks(messages):
if (
"tools" not in optional_params
and messages is not None
and has_tool_call_blocks(messages)
):
if litellm.modify_params:
optional_params["tools"] = add_dummy_tool(custom_llm_provider="bedrock_converse")
optional_params["tools"] = add_dummy_tool(
custom_llm_provider="bedrock_converse"
)
else:
raise litellm.UnsupportedParamsError(
message="Bedrock doesn't support tool calling without `tools=` param specified. Pass `tools=` param OR set `litellm.modify_params = True` // `litellm_settings::modify_params: True` to add dummy tool to the request.",
@ -1348,7 +1444,9 @@ class AmazonConverseConfig(BaseConfig):
bedrock_tool_config: Optional[ToolConfigBlock] = None
if len(bedrock_tools) > 0:
tool_choice_values: ToolChoiceValuesBlock = inference_params.pop("tool_choice", None)
tool_choice_values: ToolChoiceValuesBlock = inference_params.pop(
"tool_choice", None
)
bedrock_tool_config = ToolConfigBlock(
tools=bedrock_tools,
)
@ -1358,7 +1456,9 @@ class AmazonConverseConfig(BaseConfig):
data: CommonRequestObject = {
"additionalModelRequestFields": additional_request_params,
"system": system_content_blocks,
"inferenceConfig": self._transform_inference_params(inference_params=inference_params),
"inferenceConfig": self._transform_inference_params(
inference_params=inference_params
),
}
# Handle all config blocks
@ -1388,10 +1488,14 @@ class AmazonConverseConfig(BaseConfig):
litellm_params: dict,
headers: Optional[dict] = None,
) -> RequestObject:
messages, system_content_blocks = self._transform_system_message(messages, model=model)
messages, system_content_blocks = self._transform_system_message(
messages, model=model
)
# Convert last user message to guarded_text if guardrailConfig is present
messages = self._convert_consecutive_user_messages_to_guarded_text(messages, optional_params)
messages = self._convert_consecutive_user_messages_to_guarded_text(
messages, optional_params
)
## TRANSFORMATION ##
_data: CommonRequestObject = self._transform_request_helper(
@ -1402,11 +1506,13 @@ class AmazonConverseConfig(BaseConfig):
headers=headers,
)
bedrock_messages = await BedrockConverseMessagesProcessor._bedrock_converse_messages_pt_async(
messages=messages,
model=model,
llm_provider="bedrock_converse",
user_continue_message=litellm_params.pop("user_continue_message", None),
bedrock_messages = (
await BedrockConverseMessagesProcessor._bedrock_converse_messages_pt_async(
messages=messages,
model=model,
llm_provider="bedrock_converse",
user_continue_message=litellm_params.pop("user_continue_message", None),
)
)
data: RequestObject = {"messages": bedrock_messages, **_data}
@ -1440,10 +1546,14 @@ class AmazonConverseConfig(BaseConfig):
litellm_params: dict,
headers: Optional[dict] = None,
) -> RequestObject:
messages, system_content_blocks = self._transform_system_message(messages, model=model)
messages, system_content_blocks = self._transform_system_message(
messages, model=model
)
# Convert last user message to guarded_text if guardrailConfig is present
messages = self._convert_consecutive_user_messages_to_guarded_text(messages, optional_params)
messages = self._convert_consecutive_user_messages_to_guarded_text(
messages, optional_params
)
_data: CommonRequestObject = self._transform_request_helper(
model=model,
@ -1492,7 +1602,9 @@ class AmazonConverseConfig(BaseConfig):
encoding=encoding,
)
def _transform_reasoning_content(self, reasoning_content_blocks: List[BedrockConverseReasoningContentBlock]) -> str:
def _transform_reasoning_content(
self, reasoning_content_blocks: List[BedrockConverseReasoningContentBlock]
) -> str:
"""
Extract the reasoning text from the reasoning content blocks
@ -1508,7 +1620,9 @@ class AmazonConverseConfig(BaseConfig):
self, thinking_blocks: List[BedrockConverseReasoningContentBlock]
) -> List[Union[ChatCompletionThinkingBlock, ChatCompletionRedactedThinkingBlock]]:
"""Return a consistent format for thinking blocks between Anthropic and Bedrock."""
thinking_blocks_list: List[Union[ChatCompletionThinkingBlock, ChatCompletionRedactedThinkingBlock]] = []
thinking_blocks_list: List[
Union[ChatCompletionThinkingBlock, ChatCompletionRedactedThinkingBlock]
] = []
for block in thinking_blocks:
if "reasoningText" in block:
_thinking_block = ChatCompletionThinkingBlock(type="thinking")
@ -1544,11 +1658,21 @@ class AmazonConverseConfig(BaseConfig):
cache_creation_input_tokens = usage["cacheWriteInputTokens"]
input_tokens += cache_creation_input_tokens
prompt_tokens_details = PromptTokensDetailsWrapper(cached_tokens=cache_read_input_tokens)
reasoning_tokens = token_counter(text=reasoning_content, count_response_tokens=True) if reasoning_content else 0
prompt_tokens_details = PromptTokensDetailsWrapper(
cached_tokens=cache_read_input_tokens
)
reasoning_tokens = (
token_counter(text=reasoning_content, count_response_tokens=True)
if reasoning_content
else 0
)
completion_tokens_details = CompletionTokensDetailsWrapper(
reasoning_tokens=reasoning_tokens,
text_tokens=(output_tokens - reasoning_tokens if reasoning_tokens > 0 else output_tokens),
text_tokens=(
output_tokens - reasoning_tokens
if reasoning_tokens > 0
else output_tokens
),
)
openai_usage = Usage(
prompt_tokens=input_tokens,
@ -1563,7 +1687,9 @@ class AmazonConverseConfig(BaseConfig):
def get_tool_call_names(
self,
tools: Optional[Union[List[ToolBlock], List[OpenAIChatCompletionToolParam]]] = None,
tools: Optional[
Union[List[ToolBlock], List[OpenAIChatCompletionToolParam]]
] = None,
) -> List[str]:
if tools is None:
return []
@ -1602,8 +1728,13 @@ class AmazonConverseConfig(BaseConfig):
try:
tool_call_names = self.get_tool_call_names(tools)
json_content = json.loads(message.content)
if json_content.get("type") == "function" and json_content.get("name") in tool_call_names:
tool_calls = [ChatCompletionMessageToolCall(function=Function(**json_content))]
if (
json_content.get("type") == "function"
and json_content.get("name") in tool_call_names
):
tool_calls = [
ChatCompletionMessageToolCall(function=Function(**json_content))
]
message.tool_calls = tool_calls
message.content = None
@ -1632,7 +1763,9 @@ class AmazonConverseConfig(BaseConfig):
"""
content_str = ""
tools: List[ChatCompletionToolCallChunk] = []
reasoningContentBlocks: Optional[List[BedrockConverseReasoningContentBlock]] = None
reasoningContentBlocks: Optional[
List[BedrockConverseReasoningContentBlock]
] = None
citationsContentBlocks: Optional[List[CitationsContentBlock]] = None
for idx, content in enumerate(content_blocks):
"""
@ -1649,7 +1782,9 @@ class AmazonConverseConfig(BaseConfig):
if "toolUse" in content:
## check tool name was formatted by litellm
_response_tool_name = content["toolUse"]["name"]
response_tool_name = get_bedrock_tool_name(response_tool_name=_response_tool_name)
response_tool_name = get_bedrock_tool_name(
response_tool_name=_response_tool_name
)
_function_chunk = ChatCompletionToolCallFunctionChunk(
name=response_tool_name,
arguments=json.dumps(content["toolUse"]["input"]),
@ -1700,7 +1835,11 @@ class AmazonConverseConfig(BaseConfig):
"""
try:
response_data = json.loads(json_str)
if isinstance(response_data, dict) and "properties" in response_data and len(response_data) == 1:
if (
isinstance(response_data, dict)
and "properties" in response_data
and len(response_data) == 1
):
response_data = response_data["properties"]
return json.dumps(response_data)
except json.JSONDecodeError:
@ -1724,7 +1863,11 @@ class AmazonConverseConfig(BaseConfig):
if not json_mode or not tools:
return tools if tools else None
json_tool_indices = [i for i, t in enumerate(tools) if t["function"].get("name") == RESPONSE_FORMAT_TOOL_NAME]
json_tool_indices = [
i
for i, t in enumerate(tools)
if t["function"].get("name") == RESPONSE_FORMAT_TOOL_NAME
]
if not json_tool_indices:
# No json_tool_call found, return tools unchanged
@ -1732,10 +1875,14 @@ class AmazonConverseConfig(BaseConfig):
if len(json_tool_indices) == len(tools):
# All tools are json_tool_call — convert first one to content
verbose_logger.debug("Processing JSON tool call response for response_format")
verbose_logger.debug(
"Processing JSON tool call response for response_format"
)
json_mode_content_str: Optional[str] = tools[0]["function"].get("arguments")
if json_mode_content_str is not None:
json_mode_content_str = AmazonConverseConfig._unwrap_bedrock_properties(json_mode_content_str)
json_mode_content_str = AmazonConverseConfig._unwrap_bedrock_properties(
json_mode_content_str
)
chat_completion_message["content"] = json_mode_content_str
return None
@ -1745,9 +1892,13 @@ class AmazonConverseConfig(BaseConfig):
first_idx = json_tool_indices[0]
json_mode_args = tools[first_idx]["function"].get("arguments")
if json_mode_args is not None:
json_mode_args = AmazonConverseConfig._unwrap_bedrock_properties(json_mode_args)
json_mode_args = AmazonConverseConfig._unwrap_bedrock_properties(
json_mode_args
)
existing = chat_completion_message.get("content") or ""
chat_completion_message["content"] = existing + json_mode_args if existing else json_mode_args
chat_completion_message["content"] = (
existing + json_mode_args if existing else json_mode_args
)
real_tools = [t for i, t in enumerate(tools) if i not in json_tool_indices]
return real_tools if real_tools else None
@ -1825,7 +1976,9 @@ class AmazonConverseConfig(BaseConfig):
chat_completion_message: ChatCompletionResponseMessage = {"role": "assistant"}
content_str = ""
tools: List[ChatCompletionToolCallChunk] = []
reasoningContentBlocks: Optional[List[BedrockConverseReasoningContentBlock]] = None
reasoningContentBlocks: Optional[
List[BedrockConverseReasoningContentBlock]
] = None
citationsContentBlocks: Optional[List[CitationsContentBlock]] = None
if message is not None:
@ -1844,11 +1997,17 @@ class AmazonConverseConfig(BaseConfig):
provider_specific_fields["citationsContent"] = citationsContentBlocks
if provider_specific_fields:
chat_completion_message["provider_specific_fields"] = provider_specific_fields
chat_completion_message[
"provider_specific_fields"
] = provider_specific_fields
if reasoningContentBlocks is not None:
chat_completion_message["reasoning_content"] = self._transform_reasoning_content(reasoningContentBlocks)
chat_completion_message["thinking_blocks"] = self._transform_thinking_blocks(reasoningContentBlocks)
chat_completion_message[
"reasoning_content"
] = self._transform_reasoning_content(reasoningContentBlocks)
chat_completion_message[
"thinking_blocks"
] = self._transform_thinking_blocks(reasoningContentBlocks)
chat_completion_message["content"] = content_str
filtered_tools = self._filter_json_mode_tools(
json_mode=json_mode,

View File

@ -86,9 +86,9 @@ class OpenAIChatCompletionsHandler(BaseTranslation):
if tool_calls_to_check:
inputs["tool_calls"] = tool_calls_to_check # type: ignore
if messages:
inputs["structured_messages"] = (
messages # pass the openai /chat/completions messages to the guardrail, as-is
)
inputs[
"structured_messages"
] = messages # pass the openai /chat/completions messages to the guardrail, as-is
# Pass tools (function definitions) to the guardrail
tools = data.get("tools")
if tools:

View File

@ -3078,10 +3078,7 @@ async def _team_max_budget_check(
BudgetExceededError if the team is over it's max budget.
Triggers a budget alert if the team is over it's max budget.
"""
if (
team_object is not None
and team_object.max_budget is not None
):
if team_object is not None and team_object.max_budget is not None:
from litellm.proxy.proxy_server import get_current_spend
# Read spend from cross-pod counter (Redis-first) or cached object (fallback)

View File

@ -621,9 +621,7 @@ class JWTHandler:
)
# Check cache first
cache_key = (
f"oidc_userinfo_{hashlib.sha256(token.encode()).hexdigest()}"
)
cache_key = f"oidc_userinfo_{hashlib.sha256(token.encode()).hexdigest()}"
cached_userinfo = await self.user_api_key_cache.async_get_cache(cache_key)
if cached_userinfo is not None:

View File

@ -66,10 +66,8 @@ class ResetBudgetJob:
try:
from litellm.proxy.proxy_server import spend_counter_cache
memberships = (
await self.prisma_client.db.litellm_teammembership.find_many(
where={"budget_id": {"in": budget_ids}}
)
memberships = await self.prisma_client.db.litellm_teammembership.find_many(
where={"budget_id": {"in": budget_ids}}
)
for m in memberships:
counter_key = f"spend:team_member:{m.user_id}:{m.team_id}"
@ -574,7 +572,11 @@ class ResetBudgetJob:
counter_key = None
if item_type == "key" and hasattr(item, "token") and item.token is not None:
counter_key = f"spend:key:{item.token}"
elif item_type == "team" and hasattr(item, "team_id") and item.team_id is not None:
elif (
item_type == "team"
and hasattr(item, "team_id")
and item.team_id is not None
):
counter_key = f"spend:team:{item.team_id}"
if counter_key is not None:

View File

@ -707,7 +707,7 @@ def _build_user_info_response(
response_model=UserInfoResponse,
)
@management_endpoint_wrapper
async def user_info(
async def user_info( # noqa: PLR0915
request: Request,
user_id: Optional[str] = fastapi.Query(
default=None, description="User ID in the request parameters"

View File

@ -827,7 +827,9 @@ async def get_generic_sso_response(
], # sso specific jwt handler - used for restricted sso group access control
generic_client_id: str,
redirect_url: str,
) -> Tuple[Union[OpenID, dict], Optional[dict], Optional[dict]]: # (result, received_response, access_token_payload)
) -> Tuple[
Union[OpenID, dict], Optional[dict], Optional[dict]
]: # (result, received_response, access_token_payload)
# make generic sso provider
from fastapi_sso.sso.base import DiscoveryDocument
from fastapi_sso.sso.generic import create_provider
@ -1369,7 +1371,11 @@ async def auth_callback(request: Request, state: Optional[str] = None): # noqa:
)
elif generic_client_id is not None:
result, received_response, access_token_payload = await get_generic_sso_response(
(
result,
received_response,
access_token_payload,
) = await get_generic_sso_response(
request=request,
jwt_handler=jwt_handler,
generic_client_id=generic_client_id,

View File

@ -51,7 +51,11 @@ def _build_audit_log_payload(
if request_data.updated_at is not None:
updated_at = request_data.updated_at.isoformat()
table_name_str: str = request_data.table_name.value if isinstance(request_data.table_name, LitellmTableNames) else str(request_data.table_name)
table_name_str: str = (
request_data.table_name.value
if isinstance(request_data.table_name, LitellmTableNames)
else str(request_data.table_name)
)
return StandardAuditLogPayload(
id=request_data.id,
@ -89,7 +93,9 @@ async def _dispatch_audit_log_to_callbacks(
for callback in litellm.audit_log_callbacks:
try:
resolved: Optional[CustomLogger] = callback if isinstance(callback, CustomLogger) else None
resolved: Optional[CustomLogger] = (
callback if isinstance(callback, CustomLogger) else None
)
if isinstance(callback, str):
resolved = _resolve_audit_log_callback(callback)
if resolved is None:
@ -138,9 +144,7 @@ async def create_object_audit_log(
return
_changed_by = (
litellm_changed_by
or user_api_key_dict.user_id
or litellm_proxy_admin_name
litellm_changed_by or user_api_key_dict.user_id or litellm_proxy_admin_name
)
await create_audit_log_for_update(

View File

@ -873,12 +873,14 @@ async def proxy_startup_event(app: FastAPI): # noqa: PLR0915
)
if prisma_client is not None:
async def _run_pw_migration():
try:
result = await migrate_passwords_to_scrypt_async(prisma_client)
verbose_proxy_logger.info(f"Password migration: {result}")
except Exception as e:
verbose_proxy_logger.warning(f"Password migration skipped: {e}")
asyncio.create_task(_run_pw_migration())
ProxyStartupEvent._initialize_startup_logging(
@ -1725,9 +1727,7 @@ async def get_current_spend(counter_key: str, fallback_spend: float) -> float:
# 1. Try Redis first (cross-pod authoritative)
if spend_counter_cache.redis_cache is not None:
try:
val = await spend_counter_cache.redis_cache.async_get_cache(
key=counter_key
)
val = await spend_counter_cache.redis_cache.async_get_cache(key=counter_key)
if val is not None:
return float(val)
except Exception as e:
@ -1831,9 +1831,7 @@ async def _init_and_increment_spend_counter(
key=counter_key, value=base_spend
)
await spend_counter_cache.async_increment_cache(
key=counter_key, value=increment
)
await spend_counter_cache.async_increment_cache(key=counter_key, value=increment)
async def update_cache( # noqa: PLR0915
@ -12156,7 +12154,10 @@ async def invitation_delete(
dependencies=[Depends(user_api_key_auth)],
include_in_schema=False,
)
async def update_config(config_info: ConfigYAML, user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth)): # noqa: PLR0915
async def update_config( # noqa: PLR0915
config_info: ConfigYAML,
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
):
"""
For Admin UI - allows admin to update config via UI
@ -12165,7 +12166,9 @@ async def update_config(config_info: ConfigYAML, user_api_key_dict: UserAPIKeyAu
global llm_router, llm_model_list, general_settings, proxy_config, proxy_logging_obj, master_key, prisma_client
try:
if user_api_key_dict.user_role != LitellmUserRoles.PROXY_ADMIN:
raise HTTPException(status_code=403, detail="Only proxy admins can update config")
raise HTTPException(
status_code=403, detail="Only proxy admins can update config"
)
import base64
"""

View File

@ -4575,7 +4575,9 @@ def verify_password(password: str, stored: str) -> bool:
try:
raw = base64.b64decode(stored[7:])
salt, dk = raw[:16], raw[16:]
dk2 = hashlib.scrypt(password.encode(), salt=salt, n=16384, r=8, p=1, dklen=32)
dk2 = hashlib.scrypt(
password.encode(), salt=salt, n=16384, r=8, p=1, dklen=32
)
return secrets.compare_digest(dk, dk2)
except Exception:
return False
@ -4596,12 +4598,16 @@ async def migrate_passwords_to_scrypt_async(prisma_client) -> str:
all_with_pw = await prisma_client.db.litellm_usertable.find_many(
where={"password": {"not": None}},
)
def _is_sha256_hex(s: str) -> bool:
return len(s) == 64 and all(c in "0123456789abcdef" for c in s)
plaintext_users = [
u for u in all_with_pw
if u.password and not u.password.startswith("scrypt:") and not _is_sha256_hex(u.password)
u
for u in all_with_pw
if u.password
and not u.password.startswith("scrypt:")
and not _is_sha256_hex(u.password)
]
if not plaintext_users:
return "No plaintext passwords found"