mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
Merge pull request #23133 from BerriAI/litellm_fix_cicd_090326
Litellm fix cicd 090326
This commit is contained in:
commit
ba25d652e3
5 changed files with 107 additions and 41 deletions
|
|
@ -2,7 +2,7 @@ import asyncio
|
|||
import json
|
||||
import time
|
||||
import traceback
|
||||
from typing import Dict, Iterable, List, Literal, Optional, Tuple, Union
|
||||
from typing import Dict, Iterable, List, Literal, Optional, Tuple, Union, cast
|
||||
|
||||
import litellm
|
||||
from litellm._logging import verbose_logger
|
||||
|
|
@ -13,6 +13,7 @@ from litellm.litellm_core_utils.prompt_templates.common_utils import (
|
|||
from litellm.types.llms.databricks import DatabricksTool
|
||||
from litellm.types.llms.openai import (
|
||||
ChatCompletionThinkingBlock,
|
||||
ImageURLListItem,
|
||||
OpenAIModerationResponse,
|
||||
)
|
||||
from litellm.types.utils import (
|
||||
|
|
@ -26,13 +27,13 @@ from litellm.types.utils import (
|
|||
Function,
|
||||
HiddenParams,
|
||||
ImageResponse,
|
||||
PromptTokensDetailsWrapper,
|
||||
)
|
||||
from litellm.types.utils import Logprobs as TextCompletionLogprobs
|
||||
from litellm.types.utils import (
|
||||
Message,
|
||||
ModelResponse,
|
||||
ModelResponseStream,
|
||||
PromptTokensDetailsWrapper,
|
||||
RerankResponse,
|
||||
StreamingChoices,
|
||||
TextChoices,
|
||||
|
|
@ -52,6 +53,24 @@ _MODEL_RESPONSE_FIELDS: frozenset = frozenset(ModelResponse.model_fields.keys())
|
|||
}
|
||||
|
||||
|
||||
def _normalize_images_for_message(
|
||||
images: Optional[List[dict]],
|
||||
) -> Optional[List[ImageURLListItem]]:
|
||||
"""
|
||||
Ensure each image has an 'index' field, as required by ImageURLListItem.
|
||||
Some providers (e.g. OpenRouter) return images without index.
|
||||
"""
|
||||
if not images:
|
||||
return cast(Optional[List[ImageURLListItem]], images)
|
||||
normalized: List[ImageURLListItem] = []
|
||||
for i, img in enumerate(images):
|
||||
if isinstance(img, dict) and "index" not in img:
|
||||
normalized.append(cast(ImageURLListItem, {**img, "index": i}))
|
||||
else:
|
||||
normalized.append(cast(ImageURLListItem, img))
|
||||
return normalized
|
||||
|
||||
|
||||
def _safe_convert_created_field(created_value) -> int:
|
||||
"""
|
||||
Safely convert a 'created' field value to an integer.
|
||||
|
|
@ -591,7 +610,9 @@ def convert_to_model_response_object( # noqa: PLR0915
|
|||
reasoning_content=reasoning_content,
|
||||
thinking_blocks=thinking_blocks,
|
||||
annotations=choice["message"].get("annotations", None),
|
||||
images=choice["message"].get("images", None),
|
||||
images=_normalize_images_for_message(
|
||||
choice["message"].get("images", None)
|
||||
),
|
||||
)
|
||||
finish_reason = choice.get("finish_reason", None)
|
||||
if finish_reason is None:
|
||||
|
|
|
|||
|
|
@ -16799,6 +16799,42 @@
|
|||
"supports_vision": true,
|
||||
"supports_web_search": true
|
||||
},
|
||||
"gemini/gemini-3.1-flash-image-preview": {
|
||||
"input_cost_per_token": 2.5e-07,
|
||||
"input_cost_per_token_batches": 1.25e-07,
|
||||
"litellm_provider": "gemini",
|
||||
"max_input_tokens": 65536,
|
||||
"max_output_tokens": 32768,
|
||||
"max_tokens": 32768,
|
||||
"mode": "image_generation",
|
||||
"output_cost_per_image": 0.045,
|
||||
"output_cost_per_image_token": 6e-05,
|
||||
"output_cost_per_image_token_batches": 3e-05,
|
||||
"output_cost_per_token": 1.5e-06,
|
||||
"output_cost_per_token_batches": 7.5e-07,
|
||||
"rpm": 1000,
|
||||
"tpm": 4000000,
|
||||
"source": "https://ai.google.dev/gemini-api/docs/pricing#gemini-3.1-flash-image-preview",
|
||||
"supported_endpoints": [
|
||||
"/v1/chat/completions",
|
||||
"/v1/completions",
|
||||
"/v1/batch"
|
||||
],
|
||||
"supported_modalities": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"supported_output_modalities": [
|
||||
"text",
|
||||
"image"
|
||||
],
|
||||
"supports_function_calling": false,
|
||||
"supports_prompt_caching": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_system_messages": true,
|
||||
"supports_vision": true,
|
||||
"supports_web_search": true
|
||||
},
|
||||
"gemini/deep-research-pro-preview-12-2025": {
|
||||
"input_cost_per_image": 0.0011,
|
||||
"input_cost_per_token": 2e-06,
|
||||
|
|
|
|||
|
|
@ -42,44 +42,52 @@ def image_url():
|
|||
image_file = BytesIO()
|
||||
img.save(image_file, format="PNG")
|
||||
image_file.seek(0)
|
||||
# openai>=2.24.0 requires BytesIO to have .name for MIME type detection in multipart uploads
|
||||
image_file.name = "litellm_logo.png"
|
||||
|
||||
return image_file
|
||||
|
||||
|
||||
def test_openai_image_variation_openai_sdk(image_url):
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI()
|
||||
response = client.images.create_variation(image=image_url, n=2, size="1024x1024")
|
||||
print(response)
|
||||
# Commented out: OpenAI /images/variations endpoint deprecated (DALL-E 2 shutdown May 12, 2026)
|
||||
# def test_openai_image_variation_openai_sdk(image_url):
|
||||
# from openai import OpenAI
|
||||
#
|
||||
# client = OpenAI()
|
||||
# response = client.images.create_variation(image=image_url, n=2, size="1024x1024")
|
||||
# print(response)
|
||||
#
|
||||
#
|
||||
# @pytest.mark.parametrize("sync_mode", [True, False])
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_openai_image_variation_litellm_sdk(image_url, sync_mode):
|
||||
# from litellm import image_variation, aimage_variation
|
||||
#
|
||||
# if sync_mode:
|
||||
# image_variation(image=image_url, n=2, size="1024x1024")
|
||||
# else:
|
||||
# await aimage_variation(image=image_url, n=2, size="1024x1024")
|
||||
#
|
||||
#
|
||||
# def test_topaz_image_variation(image_url):
|
||||
# from litellm import image_variation, aimage_variation
|
||||
# from litellm.llms.custom_httpx.http_handler import HTTPHandler
|
||||
# from unittest.mock import patch
|
||||
#
|
||||
# client = HTTPHandler()
|
||||
# with patch.object(client, "post") as mock_post:
|
||||
# try:
|
||||
# image_variation(
|
||||
# model="topaz/Standard V2",
|
||||
# image=image_url,
|
||||
# n=2,
|
||||
# size="1024x1024",
|
||||
# client=client,
|
||||
# )
|
||||
# except Exception as e:
|
||||
# print(e)
|
||||
# mock_post.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("sync_mode", [True, False])
|
||||
@pytest.mark.asyncio
|
||||
async def test_openai_image_variation_litellm_sdk(image_url, sync_mode):
|
||||
from litellm import image_variation, aimage_variation
|
||||
|
||||
if sync_mode:
|
||||
image_variation(image=image_url, n=2, size="1024x1024")
|
||||
else:
|
||||
await aimage_variation(image=image_url, n=2, size="1024x1024")
|
||||
|
||||
|
||||
def test_topaz_image_variation(image_url):
|
||||
from litellm import image_variation, aimage_variation
|
||||
from litellm.llms.custom_httpx.http_handler import HTTPHandler
|
||||
from unittest.mock import patch
|
||||
|
||||
client = HTTPHandler()
|
||||
with patch.object(client, "post") as mock_post:
|
||||
try:
|
||||
image_variation(
|
||||
model="topaz/Standard V2",
|
||||
image=image_url,
|
||||
n=2,
|
||||
size="1024x1024",
|
||||
client=client,
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
mock_post.assert_called_once()
|
||||
def test_image_variation_placeholder():
|
||||
"""Placeholder: variation tests commented out - OpenAI /images/variations deprecated (DALL-E 2 shutdown May 12, 2026)."""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -868,8 +868,9 @@ class BaseLLMChatTest(ABC):
|
|||
base_completion_call_args = self.get_base_completion_call_args()
|
||||
if not supports_vision(base_completion_call_args["model"], None):
|
||||
pytest.skip("Model does not support image input")
|
||||
elif "http://" in image_url and "fireworks_ai" in base_completion_call_args.get(
|
||||
"model"
|
||||
elif "http://" in image_url and (
|
||||
"fireworks_ai" in base_completion_call_args.get("model", "")
|
||||
or "mistral" in base_completion_call_args.get("model", "")
|
||||
):
|
||||
pytest.skip("Model does not support http:// input")
|
||||
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ async def test_anthropic_messages_openai_model_streaming_cost_injection():
|
|||
|
||||
payload = {
|
||||
"model": "openai/gpt-4o",
|
||||
"max_tokens": 10,
|
||||
"max_tokens": 20,
|
||||
"stream": True,
|
||||
"messages": [{"role": "user", "content": "Say 'Hi'"}],
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue