diff --git a/litellm/images/main.py b/litellm/images/main.py index 9f0d67eeb24..6bdccbfe705 100644 --- a/litellm/images/main.py +++ b/litellm/images/main.py @@ -714,8 +714,8 @@ def image_edit( "style", "async_call", ] - litellm_params = all_litellm_params - default_params = openai_params + litellm_params + litellm_params_list = all_litellm_params + default_params = openai_params + litellm_params_list non_default_params = { k: v for k, v in kwargs.items() if k not in default_params } # model-specific params - pass them straight to the model/provider diff --git a/litellm/llms/bedrock/image_edit/stability_transformation.py b/litellm/llms/bedrock/image_edit/stability_transformation.py index 3d83222a478..bcaf0923f69 100644 --- a/litellm/llms/bedrock/image_edit/stability_transformation.py +++ b/litellm/llms/bedrock/image_edit/stability_transformation.py @@ -23,14 +23,13 @@ API Reference: https://docs.aws.amazon.com/bedrock/latest/userguide/model-parame import json import base64 -from typing import TYPE_CHECKING, Any, Dict, List, Optional +from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple import httpx from litellm.llms.base_llm.image_edit.transformation import BaseImageEditConfig -from litellm.types.llms.openai import ( - OpenAIImageEditOptionalParams, -) +from litellm.types.images.main import ImageEditOptionalRequestParams +from litellm.types.router import GenericLiteLLMParams from litellm.types.llms.stability import ( OPENAI_SIZE_TO_STABILITY_ASPECT_RATIO, ) @@ -86,7 +85,7 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): def get_supported_openai_params( self, model: str - ) -> List[OpenAIImageEditOptionalParams]: + ) -> list: """ Return list of OpenAI params supported by Bedrock Stability. """ @@ -99,10 +98,10 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): def map_openai_params( self, - image_edit_optional_params: dict, + image_edit_optional_params: ImageEditOptionalRequestParams, model: str, drop_params: bool, - ) -> dict: + ) -> Dict: """ Map OpenAI parameters to Bedrock Stability parameters. @@ -117,14 +116,14 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): # "n" and "response_format" are handled separately } - # Create a copy to not mutate original - mapped_params = image_edit_optional_params.copy() + # Create a copy to not mutate original - convert TypedDict to regular dict + mapped_params: Dict[str, Any] = dict(image_edit_optional_params) for k, v in image_edit_optional_params.items(): if k in param_mapping: # Map param if mapping exists and value is valid if k == "size" and v in OPENAI_SIZE_TO_STABILITY_ASPECT_RATIO: - mapped_params[param_mapping[k]] = OPENAI_SIZE_TO_STABILITY_ASPECT_RATIO[v] + mapped_params[param_mapping[k]] = OPENAI_SIZE_TO_STABILITY_ASPECT_RATIO[v] # type: ignore # Don't copy "size" itself to final dict elif k == "n": # Store for logic but do not add to outgoing params @@ -156,10 +155,10 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): model: str, prompt: str, image: FileTypes, - image_edit_optional_request_params: OpenAIImageEditOptionalParams, - litellm_params: dict, + image_edit_optional_request_params: Dict, + litellm_params: GenericLiteLLMParams, headers: dict, - ) -> Dict[str, Any]: + ) -> Tuple[Dict, Any]: """ Transform OpenAI-style request to Bedrock Stability request format. @@ -173,10 +172,10 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): # Convert image to base64 image_b64: str - if hasattr(image, 'read'): + if hasattr(image, 'read') and callable(getattr(image, 'read', None)): # File-like object (e.g., BufferedReader from open()) - image_bytes = image.read() - image_b64 = base64.b64encode(image_bytes).decode('utf-8') + image_bytes = image.read() # type: ignore + image_b64 = base64.b64encode(image_bytes).decode('utf-8') # type: ignore elif isinstance(image, bytes): # Raw bytes image_b64 = base64.b64encode(image).decode('utf-8') @@ -185,12 +184,12 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): image_b64 = image else: # Try to handle as bytes - image_b64 = base64.b64encode(image).decode('utf-8') + image_b64 = base64.b64encode(bytes(image)).decode('utf-8') # type: ignore data["image"] = image_b64 # Add optional params (already mapped in map_openai_params) - for key, value in image_edit_optional_request_params.items(): + for key, value in image_edit_optional_request_params.items(): # type: ignore # Skip internal params (prefixed with _) if key.startswith("_") or value is None: continue @@ -202,8 +201,8 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): if isinstance(value, list) and len(value) > 0: file_value = value[0] - if hasattr(file_value, 'read'): - file_bytes = file_value.read() + if hasattr(file_value, 'read') and callable(getattr(file_value, 'read', None)): + file_bytes = file_value.read() # type: ignore elif isinstance(file_value, bytes): file_bytes = file_value elif isinstance(file_value, str): @@ -211,12 +210,12 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): data[key] = file_value continue else: - file_bytes = file_value + file_bytes = file_value # type: ignore if isinstance(file_bytes, bytes): file_b64 = base64.b64encode(file_bytes).decode('utf-8') else: - file_b64 = file_bytes + file_b64 = str(file_bytes) data[key] = file_b64 continue @@ -246,7 +245,7 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): ]: data[key] = value # type: ignore - return data + return data, {} def transform_image_edit_response( self, @@ -315,7 +314,8 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): # Set cost based on model model_info = get_model_info(model, custom_llm_provider="bedrock") cost_per_image = model_info.get("output_cost_per_image", 0) - model_response._hidden_params["additional_headers"]["llm_provider-x-litellm-response-cost"] = float(cost_per_image) + if cost_per_image is not None: + model_response._hidden_params["additional_headers"]["llm_provider-x-litellm-response-cost"] = float(cost_per_image) return model_response @@ -327,8 +327,8 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig): def get_complete_url( self, - api_base: Optional[str], model: str, + api_base: Optional[str], litellm_params: dict, ) -> str: """ diff --git a/litellm/llms/stability/image_edit/transformations.py b/litellm/llms/stability/image_edit/transformations.py index d43e2e4be39..6c9389d3ac4 100644 --- a/litellm/llms/stability/image_edit/transformations.py +++ b/litellm/llms/stability/image_edit/transformations.py @@ -13,9 +13,8 @@ from httpx._types import RequestFiles from litellm.llms.base_llm.image_edit.transformation import BaseImageEditConfig from litellm.secret_managers.main import get_secret_str -from litellm.types.llms.openai import ( - OpenAIImageEditOptionalParams, -) +from litellm.types.images.main import ImageEditOptionalRequestParams +from litellm.types.router import GenericLiteLLMParams from litellm.types.llms.stability import ( OPENAI_SIZE_TO_STABILITY_ASPECT_RATIO, STABILITY_EDIT_ENDPOINTS, @@ -44,7 +43,7 @@ class StabilityImageEditConfig(BaseImageEditConfig): def get_supported_openai_params( self, model: str - ) -> List[OpenAIImageEditOptionalParams]: + ) -> list: """ Return list of OpenAI params supported by Stability AI. @@ -59,10 +58,10 @@ class StabilityImageEditConfig(BaseImageEditConfig): def map_openai_params( self, - image_edit_optional_params: dict, + image_edit_optional_params: ImageEditOptionalRequestParams, model: str, drop_params: bool, - ) -> dict: + ) -> Dict: """ Map OpenAI parameters to Stability AI parameters. @@ -77,21 +76,21 @@ class StabilityImageEditConfig(BaseImageEditConfig): # "n" and "response_format" are handled separately } - # Create a copy to not mutate original - mapped_params = image_edit_optional_params.copy() + # Create a copy to not mutate original - convert TypedDict to regular dict + mapped_params: Dict[str, Any] = dict(image_edit_optional_params) for k, v in image_edit_optional_params.items(): if k in param_mapping: # Map param if mapping exists and value is valid if k == "size" and v in OPENAI_SIZE_TO_STABILITY_ASPECT_RATIO: - mapped_params[param_mapping[k]] = OPENAI_SIZE_TO_STABILITY_ASPECT_RATIO[v] + mapped_params[param_mapping[k]] = OPENAI_SIZE_TO_STABILITY_ASPECT_RATIO[v] # type: ignore # Don't copy "size" itself to final dict elif k == "n": # Store for logic but do not add to outgoing params mapped_params["_n"] = v elif k == "response_format": # Only b64 supported at Stability; store for postprocessing - mapped_params["output_format"] = v + mapped_params["_response_format"] = v elif k not in supported_params: if not drop_params: raise ValueError( @@ -130,8 +129,8 @@ class StabilityImageEditConfig(BaseImageEditConfig): def get_complete_url( self, - api_base: Optional[str], model: str, + api_base: Optional[str], litellm_params: dict, ) -> str: """ @@ -174,8 +173,8 @@ class StabilityImageEditConfig(BaseImageEditConfig): model: str, prompt: str, image: FileTypes, - image_edit_optional_request_params: OpenAIImageEditOptionalParams, - litellm_params: dict, + image_edit_optional_request_params: Dict, + litellm_params: GenericLiteLLMParams, headers: dict, ) -> Tuple[Dict, RequestFiles]: """ @@ -187,14 +186,16 @@ class StabilityImageEditConfig(BaseImageEditConfig): # Build Stability request # Populate multipart form-data as separate text fields (data) and files. # Stability expects prompt/output_format/etc. as normal form fields, not file parts. - data: StabilityImageEditRequest = { + data: Dict[str, Any] = { "prompt": prompt, "output_format": "png", # Default to PNG } - files: Dict[str, Any] = {"image": image[0]} + # Handle image parameter - could be a single file or list + image_file = image[0] if isinstance(image, list) else image # type: ignore + files: Dict[str, Any] = {"image": image_file} # Add optional params (already mapped in map_openai_params) - for key, value in image_edit_optional_request_params.items(): + for key, value in image_edit_optional_request_params.items(): # type: ignore # Skip internal params (prefixed with _) if key.startswith("_") or value is None: continue @@ -303,7 +304,8 @@ class StabilityImageEditConfig(BaseImageEditConfig): # Override: fetch model-cost from model_cost map based on the provided model name model_info = get_model_info(model, custom_llm_provider="stability") cost_per_image = model_info.get("output_cost_per_image", 0) - model_response._hidden_params["additional_headers"]["llm_provider-x-litellm-response-cost"] = float(cost_per_image) + if cost_per_image is not None: + model_response._hidden_params["additional_headers"]["llm_provider-x-litellm-response-cost"] = float(cost_per_image) return model_response def use_multipart_form_data(self) -> bool: