Merge pull request #18692 from BerriAI/litellm_fix_stability_issues

[Fix] Bedrock stability model usage issues
This commit is contained in:
Sameer Kankute 2026-01-08 15:26:50 +05:30 committed by GitHub
commit 178bf4af54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 255 additions and 378 deletions

View file

@ -173,6 +173,15 @@ Stability AI returns images in base64 format. The response is OpenAI-compatible:
Stability AI supports various image editing operations including inpainting, upscaling, outpainting, background removal, and more.
:::info Optional Parameters
**Important:** Different Stability models have different parameter requirements:
- Some models don't require a `prompt` (e.g., upscaling, background removal)
- The `style-transfer` model uses `init_image` and `style_image` instead of `image`
- The `outpaint` model requires numeric parameters (`left`, `right`, `up`, `down`)
LiteLLM automatically handles these differences for you.
:::
### Usage - LiteLLM Python SDK
#### Inpainting (Edit with Mask)
@ -217,11 +226,11 @@ response = image_edit(
creativity=0.3, # 0-0.35, higher = more creative
)
# Fast upscaling - quick upscaling
# Fast upscaling - quick upscaling (no prompt needed)
response = image_edit(
model="stability/stable-fast-upscale-v1:0",
image=open("low_res_image.png", "rb"),
prompt="Quickly upscale this image",
# No prompt required for fast upscale
)
print(response)
```
@ -259,7 +268,7 @@ os.environ['STABILITY_API_KEY'] = "your-api-key"
response = image_edit(
model="stability/stable-image-remove-background-v1:0",
image=open("portrait.png", "rb"),
prompt="Remove the background",
# No prompt needed
)
print(response)
```
@ -329,7 +338,27 @@ response = image_edit(
model="stability/stable-image-erase-object-v1:0",
image=open("scene.png", "rb"),
mask=open("object_mask.png", "rb"), # Mask the object to erase
prompt="Remove the object",
# No prompt needed
)
print(response)
```
#### Style Transfer
```python showLineNumbers
from litellm import image_edit
import os
os.environ['STABILITY_API_KEY'] = "your-api-key"
# Transfer style from one image to another
# Note: Uses init_image (via image param) and style_image
response = image_edit(
model="stability/stable-style-transfer-v1:0",
image=open("content_image.png", "rb"), # Maps to init_image
style_image=open("style_reference.png", "rb"), # Style to apply
fidelity=0.5, # 0-1, balance between content and style
# No prompt needed
)
print(response)
```
@ -416,8 +445,24 @@ response = image_edit(
image=open("original_image.png", "rb"),
mask=open("mask_image.png", "rb"),
prompt="Add flowers in the masked area",
size="1024x1024",
)
# Fast upscale without prompt
response = image_edit(
model="bedrock/stability.stable-fast-upscale-v1:0",
image=open("low_res_image.png", "rb"),
)
# Outpaint with numeric parameters
response = image_edit(
model="bedrock/stability.stable-outpaint-v1:0",
image=open("original_image.png", "rb"),
left=100, # Automatically converted to int
right=100,
up=50,
down=50,
)
print(response)
```

View file

@ -672,8 +672,8 @@ def image_variation(
@client
def image_edit( # noqa: PLR0915
image: Union[FileTypes, List[FileTypes]],
prompt: str,
image: Optional[Union[FileTypes, List[FileTypes]]] = None,
prompt: Optional[str] = None,
model: Optional[str] = None,
mask: Optional[str] = None,
n: Optional[int] = None,
@ -724,7 +724,7 @@ def image_edit( # noqa: PLR0915
_is_async = kwargs.pop("async_call", False) is True
# add images / or return a single image
images = image if isinstance(image, list) else [image]
images = image if isinstance(image, list) else ([image] if image is not None else [])
headers_from_kwargs = kwargs.get("headers")
merged_extra_headers: Dict[str, Any] = {}

View file

@ -92,8 +92,8 @@ class BaseImageEditConfig(ABC):
def transform_image_edit_request(
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict,
litellm_params: GenericLiteLLMParams,
headers: dict,

View file

@ -62,7 +62,7 @@ class BedrockImageEdit(BaseAWSLLM):
self,
model: str,
image: list,
prompt: str,
prompt: Optional[str],
model_response: ImageResponse,
optional_params: dict,
logging_obj: LitellmLogging,
@ -127,7 +127,7 @@ class BedrockImageEdit(BaseAWSLLM):
timeout: Optional[Union[float, httpx.Timeout]],
model: str,
logging_obj: LitellmLogging,
prompt: str,
prompt: Optional[str],
model_response: ImageResponse,
client: Optional[AsyncHTTPHandler] = None,
) -> ImageResponse:
@ -163,7 +163,7 @@ class BedrockImageEdit(BaseAWSLLM):
self,
model: str,
image: list,
prompt: str,
prompt: Optional[str],
optional_params: dict,
api_base: Optional[str],
extra_headers: Optional[dict],
@ -248,7 +248,7 @@ class BedrockImageEdit(BaseAWSLLM):
self,
model: str,
image: list,
prompt: str,
prompt: Optional[str],
optional_params: dict,
) -> dict:
"""
@ -261,7 +261,7 @@ class BedrockImageEdit(BaseAWSLLM):
"""
config_class = self.get_config_class(model=model)
config_instance = config_class()
request_body = config_instance.transform_image_edit_request(
request_body, _ = config_instance.transform_image_edit_request(
model=model,
prompt=prompt,
image=image[0] if image else None,
@ -276,7 +276,7 @@ class BedrockImageEdit(BaseAWSLLM):
model_response: ImageResponse,
model: str,
logging_obj: LitellmLogging,
prompt: str,
prompt: Optional[str],
response: httpx.Response,
data: dict,
) -> ImageResponse:

View file

@ -153,8 +153,8 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig):
def transform_image_edit_request(
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict,
litellm_params: GenericLiteLLMParams,
headers: dict,
@ -166,27 +166,36 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig):
"""
# Build Bedrock Stability request
data: Dict[str, Any] = {
"prompt": prompt,
"output_format": "png", # Default to PNG
}
# Convert image to base64
image_b64: str
if hasattr(image, 'read') and callable(getattr(image, 'read', None)):
# File-like object (e.g., BufferedReader from open())
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')
elif isinstance(image, str):
# Already a base64 string
image_b64 = image
else:
# Try to handle as bytes
image_b64 = base64.b64encode(bytes(image)).decode('utf-8') # type: ignore
# Add prompt only if provided (some models don't require it)
if prompt is not None and prompt != "":
data["prompt"] = prompt
# Convert image to base64 if provided
if image is not None:
image_b64: str
if hasattr(image, 'read') and callable(getattr(image, 'read', None)):
# File-like object (e.g., BufferedReader from open())
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')
elif isinstance(image, str):
# Already a base64 string
image_b64 = image
else:
# Try to handle as bytes
image_b64 = base64.b64encode(bytes(image)).decode('utf-8') # type: ignore
data["image"] = image_b64
# For style-transfer models, map image to init_image
model_lower = model.lower()
if "style-transfer" in model_lower:
data["init_image"] = image_b64
else:
data["image"] = image_b64
# Add optional params (already mapped in map_openai_params)
for key, value in image_edit_optional_request_params.items(): # type: ignore
@ -219,29 +228,41 @@ class BedrockStabilityImageEditConfig(BaseImageEditConfig):
data[key] = file_b64
continue
# Supported text fields
if key in [
"negative_prompt",
"aspect_ratio",
"seed",
"output_format",
"model",
"mode",
# Numeric fields that need to be converted to int/float
numeric_int_fields = ["left", "right", "up", "down", "seed"]
numeric_float_fields = [
"strength",
"style_preset",
"creativity",
"control_strength",
"grow_mask",
"left",
"right",
"up",
"down",
"select_prompt",
"search_prompt",
"fidelity",
"composition_fidelity",
"style_strength",
"change_strength",
]
if key in numeric_int_fields:
# Convert to int (these are pixel values for outpaint)
try:
data[key] = int(value) # type: ignore
except (ValueError, TypeError):
data[key] = value # type: ignore
elif key in numeric_float_fields:
# Convert to float
try:
data[key] = float(value) # type: ignore
except (ValueError, TypeError):
data[key] = value # type: ignore
# Supported text fields
elif key in [
"negative_prompt",
"aspect_ratio",
"output_format",
"model",
"mode",
"style_preset",
"select_prompt",
"search_prompt",
]:
data[key] = value # type: ignore

View file

@ -3684,7 +3684,7 @@ class BaseLLMHTTPHandler:
self,
model: str,
image: Any,
prompt: str,
prompt: Optional[str],
image_edit_provider_config: BaseImageEditConfig,
image_edit_optional_request_params: Dict,
custom_llm_provider: str,
@ -3803,7 +3803,7 @@ class BaseLLMHTTPHandler:
self,
model: str,
image: FileTypes,
prompt: str,
prompt: Optional[str],
image_edit_provider_config: BaseImageEditConfig,
image_edit_optional_request_params: Dict,
custom_llm_provider: str,

View file

@ -201,7 +201,7 @@ class CustomLLM(BaseLLM):
self,
model: str,
image: Any,
prompt: str,
prompt: Optional[str],
model_response: ImageResponse,
api_key: Optional[str],
api_base: Optional[str],
@ -216,7 +216,7 @@ class CustomLLM(BaseLLM):
self,
model: str,
image: Any,
prompt: str,
prompt: Optional[str],
model_response: ImageResponse,
api_key: Optional[str],
api_base: Optional[str],

View file

@ -80,19 +80,24 @@ class GeminiImageEditConfig(BaseImageEditConfig):
def transform_image_edit_request( # type: ignore[override]
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict[str, Any],
litellm_params: GenericLiteLLMParams,
headers: dict,
) -> Tuple[Dict[str, Any], Optional[RequestFiles]]:
inline_parts = self._prepare_inline_image_parts(image)
inline_parts = self._prepare_inline_image_parts(image) if image else []
if not inline_parts:
raise ValueError("Gemini image edit requires at least one image.")
# Build parts list with image and prompt (if provided)
parts = inline_parts.copy()
if prompt is not None and prompt != "":
parts.append({"text": prompt})
contents = [
{
"parts": inline_parts + [{"text": prompt}],
"parts": parts,
}
]

View file

@ -1,5 +1,5 @@
from io import BufferedReader
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, cast
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, cast, Optional
from httpx._types import RequestFiles
@ -30,8 +30,8 @@ class DallE2ImageEditConfig(OpenAIImageEditConfig):
def transform_image_edit_request(
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict,
litellm_params: GenericLiteLLMParams,
headers: dict,
@ -41,12 +41,17 @@ class DallE2ImageEditConfig(OpenAIImageEditConfig):
DALL-E-2 only accepts a single image with field name "image" (not "image[]").
"""
request = ImageEditRequestParams(
model=model,
image=image,
prompt=prompt,
# Build request params, only including non-None values
request_params = {
"model": model,
**image_edit_optional_request_params,
)
}
if image is not None:
request_params["image"] = image
if prompt is not None:
request_params["prompt"] = prompt
request = ImageEditRequestParams(**request_params)
request_dict = cast(Dict, request)
#########################################################

View file

@ -79,8 +79,8 @@ class OpenAIImageEditConfig(BaseImageEditConfig):
def transform_image_edit_request(
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict,
litellm_params: GenericLiteLLMParams,
headers: dict,
@ -91,12 +91,17 @@ class OpenAIImageEditConfig(BaseImageEditConfig):
Handles multipart/form-data for images. Uses "image[]" field name
to support multiple images (e.g., for gpt-image-1).
"""
request = ImageEditRequestParams(
model=model,
image=image,
prompt=prompt,
# Build request params, only including non-None values
request_params = {
"model": model,
**image_edit_optional_request_params,
)
}
if image is not None:
request_params["image"] = image
if prompt is not None:
request_params["prompt"] = prompt
request = ImageEditRequestParams(**request_params)
request_dict = cast(Dict, request)
#########################################################

View file

@ -101,8 +101,8 @@ class RecraftImageEditConfig(BaseImageEditConfig):
def transform_image_edit_request(
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict,
litellm_params: GenericLiteLLMParams,
headers: dict,
@ -114,17 +114,21 @@ class RecraftImageEditConfig(BaseImageEditConfig):
https://www.recraft.ai/docs#image-to-image
"""
request_body: RecraftImageEditRequestParams = RecraftImageEditRequestParams(
model=model,
prompt=prompt,
strength=image_edit_optional_request_params.pop("strength", self.DEFAULT_STRENGTH),
# Build request params, only including non-None values
request_params = {
"model": model,
"strength": image_edit_optional_request_params.pop("strength", self.DEFAULT_STRENGTH),
**image_edit_optional_request_params,
)
}
if prompt is not None:
request_params["prompt"] = prompt
request_body: RecraftImageEditRequestParams = RecraftImageEditRequestParams(**request_params)
request_dict = cast(Dict, request_body)
#########################################################
# Reuse OpenAI logic: Separate images as `files` and send other parameters as `data`
#########################################################
files_list = self._get_image_files_for_request(image=image)
files_list = self._get_image_files_for_request(image=image) if image is not None else []
data_without_images = {k: v for k, v in request_dict.items() if k != "image"}
return data_without_images, files_list
@ -132,7 +136,7 @@ class RecraftImageEditConfig(BaseImageEditConfig):
def _get_image_files_for_request(
self,
image: FileTypes,
image: Optional[FileTypes],
) -> List[Tuple[str, Any]]:
files_list: List[Tuple[str, Any]] = []

View file

@ -170,8 +170,8 @@ class StabilityImageEditConfig(BaseImageEditConfig):
def transform_image_edit_request(
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict,
litellm_params: GenericLiteLLMParams,
headers: dict,
@ -186,12 +186,18 @@ class StabilityImageEditConfig(BaseImageEditConfig):
# 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: Dict[str, Any] = {
"prompt": prompt,
"output_format": "png", # Default to PNG
}
# Add prompt only if provided
if prompt is not None and prompt != "":
data["prompt"] = prompt
# 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}
files: Dict[str, Any] = {}
if image is not None:
image_file = image[0] if isinstance(image, list) else image # type: ignore
files["image"] = image_file
# Add optional params (already mapped in map_openai_params)
for key, value in image_edit_optional_request_params.items(): # type: ignore

View file

@ -154,20 +154,25 @@ class VertexAIGeminiImageEditConfig(BaseImageEditConfig, VertexLLM):
def transform_image_edit_request( # type: ignore[override]
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict[str, Any],
litellm_params: GenericLiteLLMParams,
headers: dict,
) -> Tuple[Dict[str, Any], Optional[RequestFiles]]:
inline_parts = self._prepare_inline_image_parts(image)
inline_parts = self._prepare_inline_image_parts(image) if image else []
if not inline_parts:
raise ValueError("Vertex AI Gemini image edit requires at least one image.")
# Build parts list with image and prompt (if provided)
parts = inline_parts.copy()
if prompt is not None and prompt != "":
parts.append({"text": prompt})
# Correct format for Vertex AI Gemini image editing
contents = {
"role": "USER",
"parts": inline_parts + [{"text": prompt}]
"parts": parts
}
request_body: Dict[str, Any] = {"contents": contents}

View file

@ -143,13 +143,15 @@ class VertexAIImagenImageEditConfig(BaseImageEditConfig, VertexLLM):
def transform_image_edit_request( # type: ignore[override]
self,
model: str,
prompt: str,
image: FileTypes,
prompt: Optional[str],
image: Optional[FileTypes],
image_edit_optional_request_params: Dict[str, Any],
litellm_params: GenericLiteLLMParams,
headers: dict,
) -> Tuple[Dict[str, Any], Optional[RequestFiles]]:
# Prepare reference images in the correct Imagen format
if image is None:
raise ValueError("Vertex AI Imagen image edit requires at least one reference image.")
reference_images = self._prepare_reference_images(image, image_edit_optional_request_params)
if not reference_images:
raise ValueError("Vertex AI Imagen image edit requires at least one reference image.")

View file

@ -24513,85 +24513,85 @@
},
"stability/inpaint": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/outpaint": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.004,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/erase": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/search-and-replace": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/search-and-recolor": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/remove-background": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/replace-background-and-relight": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.008,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/sketch": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/structure": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/style": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/style-transfer": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.008,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/fast": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.002,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/conservative": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.04,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/creative": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.06,
"supported_endpoints": ["/v1/images/edits"]
},
@ -24625,79 +24625,79 @@
"stability.stable-conservative-upscale-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.40
},
"stability.stable-creative-upscale-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.60
},
"stability.stable-fast-upscale-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.03
},
"stability.stable-outpaint-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.06
},
"stability.stable-image-control-sketch-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-control-structure-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-erase-object-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-inpaint-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-remove-background-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-search-recolor-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-search-replace-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-style-guide-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-style-transfer-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.08
},
"stability.stable-image-core-v1:1": {

View file

@ -244,8 +244,10 @@ async def image_edit_api(
if mask is None and mask_array is not None:
mask = mask_array
if image is None:
raise HTTPException(status_code=422, detail="Field required: image")
# if image is None:
# raise HTTPException(status_code=422, detail="Field required: image")
# Note: Image is optional for some models (e.g., Bedrock Stability style-transfer)
# The validation will be done at the model level if image is truly required
from litellm.proxy.proxy_server import (
_read_request_body,
@ -273,6 +275,10 @@ async def image_edit_api(
if mask_files:
data["mask"] = mask_files
# Ensure prompt exists in data (default to None for models that don't require it)
if "prompt" not in data:
data["prompt"] = None
data["model"] = (
model
or general_settings.get("image_generation_model", None) # server default

View file

@ -24513,85 +24513,85 @@
},
"stability/inpaint": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/outpaint": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.004,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/erase": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/search-and-replace": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/search-and-recolor": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/remove-background": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/replace-background-and-relight": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.008,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/sketch": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/structure": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/style": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.005,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/style-transfer": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.008,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/fast": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.002,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/conservative": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.04,
"supported_endpoints": ["/v1/images/edits"]
},
"stability/creative": {
"litellm_provider": "stability",
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.06,
"supported_endpoints": ["/v1/images/edits"]
},
@ -24625,79 +24625,79 @@
"stability.stable-conservative-upscale-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.40
},
"stability.stable-creative-upscale-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.60
},
"stability.stable-fast-upscale-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.03
},
"stability.stable-outpaint-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.06
},
"stability.stable-image-control-sketch-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-control-structure-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-erase-object-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-inpaint-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-remove-background-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-search-recolor-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-search-replace-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-image-style-guide-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.07
},
"stability.stable-style-transfer-v1:0": {
"litellm_provider": "bedrock",
"max_input_tokens": 77,
"mode": "image_edit",
"mode": "image_edits",
"output_cost_per_image": 0.08
},
"stability.stable-image-core-v1:1": {

View file

@ -1,231 +1,4 @@
model_list:
- model_name: gpt-3.5-turbo-end-user-test
- model_name: bedrock/stability.stable-creative-upscale-v1:0
litellm_params:
model: gpt-3.5-turbo
region_name: "eu"
model_info:
id: "1"
- model_name: gpt-3.5-turbo-end-user-test
litellm_params:
model: openai/gpt-4.1-mini
api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault
- model_name: gpt-3.5-turbo
litellm_params:
model: openai/gpt-4.1-mini
api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault
- model_name: gpt-3.5-turbo-large
litellm_params:
model: "gpt-3.5-turbo-1106"
api_key: os.environ/OPENAI_API_KEY
rpm: 480
timeout: 300
stream_timeout: 60
- model_name: gpt-4
litellm_params:
model: openai/gpt-4.1-mini
api_key: os.environ/OPENAI_API_KEY # The `os.environ/` prefix tells litellm to read this from the env. See https://docs.litellm.ai/docs/simple_proxy#load-api-keys-from-vault
rpm: 480
timeout: 300
stream_timeout: 60
- model_name: sagemaker-completion-model
litellm_params:
model: sagemaker/berri-benchmarking-Llama-2-70b-chat-hf-4
input_cost_per_second: 0.000420
- model_name: text-embedding-ada-002
litellm_params:
model: openai/text-embedding-ada-002
api_key: os.environ/OPENAI_API_KEY
model_info:
mode: embedding
base_model: text-embedding-ada-002
- model_name: dall-e-2 # some tests use dall-e-2 which is now deprecated, alias to dall-e-3
litellm_params:
model: openai/dall-e-3
- model_name: openai-dall-e-3
litellm_params:
model: dall-e-3
- model_name: fake-openai-endpoint
litellm_params:
model: openai/fake
api_key: fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.app/
- model_name: fake-openai-endpoint-2
litellm_params:
model: openai/my-fake-model
api_key: my-fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.app/
stream_timeout: 0.001
rpm: 1
- model_name: fake-openai-endpoint-3
litellm_params:
model: openai/my-fake-model
api_key: my-fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.app/
stream_timeout: 0.001
rpm: 1000
- model_name: fake-openai-endpoint-4
litellm_params:
model: openai/my-fake-model
api_key: my-fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.app/
num_retries: 50
- model_name: fake-openai-endpoint-3
litellm_params:
model: openai/my-fake-model-2
api_key: my-fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.app/
stream_timeout: 0.001
rpm: 1000
- model_name: bad-model
litellm_params:
model: openai/bad-model
api_key: os.environ/OPENAI_API_KEY
api_base: https://exampleopenaiendpoint-production.up.railway.app/
mock_timeout: True
timeout: 60
rpm: 1000
model_info:
health_check_timeout: 1
- model_name: good-model
litellm_params:
model: openai/bad-model
api_key: os.environ/OPENAI_API_KEY
api_base: https://exampleopenaiendpoint-production.up.railway.app/
rpm: 1000
model_info:
health_check_timeout: 1
- model_name: "*"
litellm_params:
model: openai/*
api_key: os.environ/OPENAI_API_KEY
- model_name: realtime-v1
litellm_params:
model: azure/gpt-realtime-20250828-standard
api_version: "2025-08-28"
realtime_protocol: GA # Possible values: "GA"/ "v1", "beta"
- model_name: realtime-beta
litellm_params:
model: azure/gpt-realtime-20250828-standard
api_version: 2025-04-01-preview
# provider specific wildcard routing
- model_name: "anthropic/*"
litellm_params:
model: "anthropic/*"
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: "bedrock/*"
litellm_params:
model: "bedrock/*"
- model_name: "groq/*"
litellm_params:
model: "groq/*"
api_key: os.environ/GROQ_API_KEY
- model_name: mistral-embed
litellm_params:
model: mistral/mistral-embed
- model_name: gpt-instruct # [PROD TEST] - tests if `/health` automatically infers this to be a text completion model
litellm_params:
model: text-completion-openai/gpt-3.5-turbo-instruct
- model_name: fake-openai-endpoint-5
litellm_params:
model: openai/my-fake-model
api_key: my-fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.app/
timeout: 1
- model_name: badly-configured-openai-endpoint
litellm_params:
model: openai/my-fake-model
api_key: my-fake-key
api_base: https://exampleopenaiendpoint-production.up.railway.appxxxx/
- model_name: gemini-1.5-flash
litellm_params:
model: gemini/gemini-1.5-flash
api_key: os.environ/GOOGLE_API_KEY
- model_name: gpt-4o
litellm_params:
model: gpt-4o
api_key: os.environ/OPENAI_API_KEY
litellm_settings:
# set_verbose: True # Uncomment this if you want to see verbose logs; not recommended in production
drop_params: True
success_callback: ["prometheus"]
# max_budget: 100
# budget_duration: 30d
num_retries: 5
request_timeout: 600
telemetry: False
context_window_fallbacks: [{"gpt-3.5-turbo": ["gpt-3.5-turbo-large"]}]
default_team_settings:
- team_id: team-1
success_callback: ["langfuse"]
failure_callback: ["langfuse"]
langfuse_public_key: os.environ/LANGFUSE_PROJECT1_PUBLIC # Project 1
langfuse_secret: os.environ/LANGFUSE_PROJECT1_SECRET # Project 1
- team_id: team-2
success_callback: ["langfuse"]
failure_callback: ["langfuse"]
langfuse_public_key: os.environ/LANGFUSE_PROJECT2_PUBLIC # Project 2
langfuse_secret: os.environ/LANGFUSE_PROJECT2_SECRET # Project 2
langfuse_host: https://us.cloud.langfuse.com
# cache: true # [OPTIONAL] use for caching responses
# enable_caching_on_provider_specific_optional_params: True # Include provider-specific params in cache keys
# cache_params: # And for shared health check
# type: redis
# host: localhost
# port: 6379
# For /fine_tuning/jobs endpoints
finetune_settings:
- custom_llm_provider: azure
api_base: os.environ/AZURE_API_BASE
api_key: os.environ/AZURE_API_KEY
api_version: "2023-03-15-preview"
- custom_llm_provider: openai
api_key: os.environ/OPENAI_API_KEY
# for /files endpoints
files_settings:
- custom_llm_provider: azure
api_base: os.environ/AZURE_API_BASE
api_key: os.environ/AZURE_API_KEY
api_version: "2023-03-15-preview"
- custom_llm_provider: openai
api_key: os.environ/OPENAI_API_KEY
router_settings:
routing_strategy: usage-based-routing-v2
redis_host: os.environ/REDIS_HOST
redis_password: os.environ/REDIS_PASSWORD
redis_port: os.environ/REDIS_PORT
enable_pre_call_checks: true
model_group_alias: {"my-special-fake-model-alias-name": "fake-openai-endpoint-3"}
general_settings:
master_key: sk-1234 # [OPTIONAL] Use to enforce auth on proxy. See - https://docs.litellm.ai/docs/proxy/virtual_keys
store_model_in_db: True
proxy_budget_rescheduler_min_time: 60
proxy_budget_rescheduler_max_time: 64
proxy_batch_write_at: 1
database_connection_pool_limit: 10
# background_health_checks: true
# use_shared_health_check: true
# health_check_interval: 30
# database_url: "postgresql://<user>:<password>@<host>:<port>/<dbname>" # [OPTIONAL] use for token-based auth to proxy
pass_through_endpoints:
- path: "/v1/rerank" # route you want to add to LiteLLM Proxy Server
target: "https://api.cohere.com/v1/rerank" # URL this route should forward requests to
headers: # headers to forward to this URL
content-type: application/json # (Optional) Extra Headers to pass to this endpoint
accept: application/json
forward_headers: True
# environment_variables:
# settings for using redis caching
# REDIS_HOST: redis-16337.c322.us-east-1-2.ec2.cloud.redislabs.com
# REDIS_PORT: "16337"
# REDIS_PASSWORD:
model: bedrock/us.stability.stable-creative-upscale-v1:0