Spaces:
Paused
Paused
| import json | |
| import os | |
| import sys | |
| from datetime import datetime | |
| sys.path.insert( | |
| 0, os.path.abspath("../../") | |
| ) # Adds the parent directory to the system path | |
| import litellm | |
| import pytest | |
| from datetime import timedelta | |
| from litellm.types.utils import ImageResponse, ImageObject | |
| from litellm.litellm_core_utils.llm_response_utils.convert_dict_to_response import ( | |
| LiteLLMResponseObjectHandler, | |
| ) | |
| def test_convert_to_image_response_basic(): | |
| # Test basic conversion with minimal input | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [{"url": "http://example.com/image.jpg"}], | |
| } | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response(response_dict) | |
| assert isinstance(result, ImageResponse) | |
| assert result.created == 1234567890 | |
| assert result.data[0].url == "http://example.com/image.jpg" | |
| def test_convert_to_image_response_with_hidden_params(): | |
| # Test with hidden params | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [{"url": "http://example.com/image.jpg"}], | |
| } | |
| hidden_params = {"api_key": "test_key"} | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response( | |
| response_dict, hidden_params=hidden_params | |
| ) | |
| assert result._hidden_params == {"api_key": "test_key"} | |
| def test_convert_to_image_response_multiple_images(): | |
| # Test handling multiple images in response | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [ | |
| {"url": "http://example.com/image1.jpg"}, | |
| {"url": "http://example.com/image2.jpg"}, | |
| ], | |
| } | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response(response_dict) | |
| assert len(result.data) == 2 | |
| assert result.data[0].url == "http://example.com/image1.jpg" | |
| assert result.data[1].url == "http://example.com/image2.jpg" | |
| def test_convert_to_image_response_with_b64_json(): | |
| # Test handling b64_json in response | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [{"b64_json": "base64encodedstring"}], | |
| } | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response(response_dict) | |
| assert result.data[0].b64_json == "base64encodedstring" | |
| def test_convert_to_image_response_with_extra_fields(): | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [ | |
| { | |
| "url": "http://example.com/image1.jpg", | |
| "content_filter_results": {"category": "violence", "flagged": True}, | |
| }, | |
| { | |
| "url": "http://example.com/image2.jpg", | |
| "content_filter_results": {"category": "violence", "flagged": True}, | |
| }, | |
| ], | |
| } | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response(response_dict) | |
| assert result.data[0].url == "http://example.com/image1.jpg" | |
| assert result.data[1].url == "http://example.com/image2.jpg" | |
| def test_convert_to_image_response_with_extra_fields_2(): | |
| """ | |
| Date from a non-OpenAI API could have some obscure field in addition to the expected ones. This should not break the conversion. | |
| """ | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [ | |
| { | |
| "url": "http://example.com/image1.jpg", | |
| "very_obscure_field": "some_value", | |
| }, | |
| { | |
| "url": "http://example.com/image2.jpg", | |
| "very_obscure_field2": "some_other_value", | |
| }, | |
| ], | |
| } | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response(response_dict) | |
| assert result.data[0].url == "http://example.com/image1.jpg" | |
| assert result.data[1].url == "http://example.com/image2.jpg" | |
| def test_convert_to_image_response_with_none_usage_fields(): | |
| """ | |
| Test handling of None values in usage fields, specifically for gpt-image-1 responses. | |
| This test verifies the fix for the bug where gpt-image-1 returns None values | |
| for usage statistics fields, which caused Pydantic validation errors. | |
| The fix should clean these None values and let ImageResponse constructor | |
| handle the default values. | |
| """ | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [{"b64_json": "base64encodedstring"}], | |
| "usage": { | |
| "input_tokens": None, # gpt-image-1 returns None instead of integer | |
| "input_tokens_details": None, # gpt-image-1 returns None instead of object | |
| "output_tokens": None, # gpt-image-1 returns None instead of integer | |
| "total_tokens": None, # gpt-image-1 returns None instead of integer | |
| } | |
| } | |
| # This should not raise a ValidationError | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response(response_dict) | |
| assert isinstance(result, ImageResponse) | |
| assert result.created == 1234567890 | |
| assert result.data[0].b64_json == "base64encodedstring" | |
| # Usage should be properly initialized with default values | |
| assert result.usage is not None | |
| assert result.usage.input_tokens == 0 | |
| assert result.usage.output_tokens == 0 | |
| assert result.usage.total_tokens == 0 | |
| assert result.usage.input_tokens_details is not None | |
| assert result.usage.input_tokens_details.image_tokens == 0 | |
| assert result.usage.input_tokens_details.text_tokens == 0 | |
| def test_convert_to_image_response_with_partial_none_usage_fields(): | |
| """ | |
| Test handling of mixed None and valid values in usage fields. | |
| """ | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [{"b64_json": "base64encodedstring"}], | |
| "usage": { | |
| "input_tokens": 10, # Valid value | |
| "input_tokens_details": None, # None value (should be cleaned) | |
| "output_tokens": None, # None value (should be cleaned) | |
| "total_tokens": 10, # Valid value | |
| } | |
| } | |
| # This should not raise a ValidationError | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response(response_dict) | |
| assert isinstance(result, ImageResponse) | |
| assert result.created == 1234567890 | |
| assert result.data[0].b64_json == "base64encodedstring" | |
| # Usage should be properly initialized with defaults where needed | |
| # Valid values should be preserved, None values should be cleaned and use defaults | |
| assert result.usage is not None | |
| assert result.usage.input_tokens == 10 # Valid value should be preserved | |
| assert result.usage.output_tokens == 0 # None value should become 0 | |
| assert result.usage.total_tokens == 10 # Calculated as input_tokens + output_tokens (10 + 0) | |
| assert result.usage.input_tokens_details is not None | |
| assert result.usage.input_tokens_details.image_tokens == 0 | |
| assert result.usage.input_tokens_details.text_tokens == 0 | |
| def test_convert_to_image_response_with_valid_usage_fields(): | |
| """ | |
| Test that valid usage fields are preserved correctly. | |
| """ | |
| response_dict = { | |
| "created": 1234567890, | |
| "data": [{"b64_json": "base64encodedstring"}], | |
| "usage": { | |
| "input_tokens": 50, | |
| "input_tokens_details": { | |
| "image_tokens": 30, | |
| "text_tokens": 20, | |
| }, | |
| "output_tokens": 10, | |
| "total_tokens": 60, | |
| } | |
| } | |
| result = LiteLLMResponseObjectHandler.convert_to_image_response(response_dict) | |
| assert isinstance(result, ImageResponse) | |
| assert result.created == 1234567890 | |
| assert result.data[0].b64_json == "base64encodedstring" | |
| # Valid usage fields should be preserved | |
| assert result.usage is not None | |
| assert result.usage.input_tokens == 50 | |
| assert result.usage.output_tokens == 10 | |
| assert result.usage.total_tokens == 60 | |
| assert result.usage.input_tokens_details is not None | |
| assert result.usage.input_tokens_details.image_tokens == 30 | |
| assert result.usage.input_tokens_details.text_tokens == 20 | |