mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-16 10:23:14 +00:00
fix(gemini): use JSON instead of form-data for image edit requests (#18012)
* fix(gemini): use JSON instead of form-data for image edit requests Gemini's image edit API expects JSON body, not multipart/form-data. The handler was sending form-encoded data which caused 400 errors: "Invalid JSON payload received. Unexpected token." Changes: - Add use_multipart_form_data() method to BaseImageEditConfig (default True) - Modify image_edit_handler to use json= when use_multipart_form_data() is False - Override use_multipart_form_data() in GeminiImageEditConfig to return False * test(gemini): add test for use_multipart_form_data
This commit is contained in:
@@ -109,6 +109,15 @@ class BaseImageEditConfig(ABC):
|
||||
) -> ImageResponse:
|
||||
pass
|
||||
|
||||
def use_multipart_form_data(self) -> bool:
|
||||
"""
|
||||
Return True if the provider uses multipart/form-data for image edit requests.
|
||||
Return False if the provider uses JSON requests.
|
||||
|
||||
Default is True for backwards compatibility with OpenAI-style providers.
|
||||
"""
|
||||
return True
|
||||
|
||||
def get_error_class(
|
||||
self, error_message: str, status_code: int, headers: Union[dict, httpx.Headers]
|
||||
) -> BaseLLMException:
|
||||
|
||||
@@ -3768,13 +3768,24 @@ class BaseLLMHTTPHandler:
|
||||
)
|
||||
|
||||
try:
|
||||
response = sync_httpx_client.post(
|
||||
url=api_base,
|
||||
headers=headers,
|
||||
data=data,
|
||||
files=files,
|
||||
timeout=timeout,
|
||||
)
|
||||
# Check if provider uses multipart/form-data or JSON
|
||||
if image_edit_provider_config.use_multipart_form_data():
|
||||
# Use form-data (OpenAI style)
|
||||
response = sync_httpx_client.post(
|
||||
url=api_base,
|
||||
headers=headers,
|
||||
data=data,
|
||||
files=files,
|
||||
timeout=timeout,
|
||||
)
|
||||
else:
|
||||
# Use JSON (Gemini style)
|
||||
response = sync_httpx_client.post(
|
||||
url=api_base,
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise self._handle_error(
|
||||
@@ -3853,13 +3864,24 @@ class BaseLLMHTTPHandler:
|
||||
)
|
||||
|
||||
try:
|
||||
response = await async_httpx_client.post(
|
||||
url=api_base,
|
||||
headers=headers,
|
||||
data=data,
|
||||
files=files,
|
||||
timeout=timeout,
|
||||
)
|
||||
# Check if provider uses multipart/form-data or JSON
|
||||
if image_edit_provider_config.use_multipart_form_data():
|
||||
# Use form-data (OpenAI style)
|
||||
response = await async_httpx_client.post(
|
||||
url=api_base,
|
||||
headers=headers,
|
||||
data=data,
|
||||
files=files,
|
||||
timeout=timeout,
|
||||
)
|
||||
else:
|
||||
# Use JSON (Gemini style)
|
||||
response = await async_httpx_client.post(
|
||||
url=api_base,
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
raise self._handle_error(
|
||||
|
||||
@@ -63,6 +63,10 @@ class GeminiImageEditConfig(BaseImageEditConfig):
|
||||
headers["Content-Type"] = "application/json"
|
||||
return headers
|
||||
|
||||
def use_multipart_form_data(self) -> bool:
|
||||
"""Gemini uses JSON requests, not multipart/form-data."""
|
||||
return False
|
||||
|
||||
def get_complete_url(
|
||||
self,
|
||||
model: str,
|
||||
|
||||
@@ -147,3 +147,14 @@ class TestGeminiImageEditTransformation:
|
||||
headers={},
|
||||
)
|
||||
|
||||
def test_use_multipart_form_data_returns_false(self) -> None:
|
||||
"""
|
||||
Gemini uses JSON requests, not multipart/form-data.
|
||||
This is critical because httpx sends data differently:
|
||||
- data=dict sends form-encoded
|
||||
- json=dict sends JSON
|
||||
|
||||
Without this, Gemini returns: "Invalid JSON payload received. Unexpected token."
|
||||
"""
|
||||
assert self.config.use_multipart_form_data() is False
|
||||
|
||||
|
||||
Reference in New Issue
Block a user