From 203703725866f489d64455fd3e7e5044de57c5e6 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Thu, 7 Aug 2025 16:35:00 -0700 Subject: [PATCH] [Bug Fix] OpenAI gpt-5 series does not support "max_tokens" parameter and `temperature` values that are not = 1 (#13390) * add OpenAIGPT5Config * add map_openai_params for gpt5 * add OpenAIGPT5Config * add OpenAI gpt 5 transform * docs gpt 5 openai --- docs/my-website/docs/providers/openai.md | 8 +++ litellm/__init__.py | 4 ++ .../llms/openai/chat/gpt_5_transformation.py | 58 +++++++++++++++++++ litellm/llms/openai/openai.py | 11 ++++ .../llms/openai/test_gpt5_transformation.py | 40 +++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 litellm/llms/openai/chat/gpt_5_transformation.py create mode 100644 tests/test_litellm/llms/openai/test_gpt5_transformation.py diff --git a/docs/my-website/docs/providers/openai.md b/docs/my-website/docs/providers/openai.md index b1c2198a9d..d820215948 100644 --- a/docs/my-website/docs/providers/openai.md +++ b/docs/my-website/docs/providers/openai.md @@ -163,6 +163,14 @@ os.environ["OPENAI_BASE_URL"] = "https://your_host/v1" # OPTIONAL | Model Name | Function Call | |-----------------------|-----------------------------------------------------------------| +| gpt-5 | `response = completion(model="gpt-5", messages=messages)` | +| gpt-5-mini | `response = completion(model="gpt-5-mini", messages=messages)` | +| gpt-5-nano | `response = completion(model="gpt-5-nano", messages=messages)` | +| gpt-5-chat | `response = completion(model="gpt-5-chat", messages=messages)` | +| gpt-5-chat-latest | `response = completion(model="gpt-5-chat-latest", messages=messages)` | +| gpt-5-2025-08-07 | `response = completion(model="gpt-5-2025-08-07", messages=messages)` | +| gpt-5-mini-2025-08-07 | `response = completion(model="gpt-5-mini-2025-08-07", messages=messages)` | +| gpt-5-nano-2025-08-07 | `response = completion(model="gpt-5-nano-2025-08-07", messages=messages)` | | gpt-4.1 | `response = completion(model="gpt-4.1", messages=messages)` | | gpt-4.1-mini | `response = completion(model="gpt-4.1-mini", messages=messages)` | | gpt-4.1-nano | `response = completion(model="gpt-4.1-nano", messages=messages)` | diff --git a/litellm/__init__.py b/litellm/__init__.py index 1330584cdf..90b278df9f 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -1145,6 +1145,9 @@ openaiOSeriesConfig = OpenAIOSeriesConfig() from .llms.openai.chat.gpt_transformation import ( OpenAIGPTConfig, ) +from .llms.openai.chat.gpt_5_transformation import ( + OpenAIGPT5Config, +) from .llms.openai.transcriptions.whisper_transformation import ( OpenAIWhisperAudioTranscriptionConfig, ) @@ -1158,6 +1161,7 @@ from .llms.openai.chat.gpt_audio_transformation import ( ) openAIGPTAudioConfig = OpenAIGPTAudioConfig() +openAIGPT5Config = OpenAIGPT5Config() from .llms.nvidia_nim.chat.transformation import NvidiaNimConfig from .llms.nvidia_nim.embed import NvidiaNimEmbeddingConfig diff --git a/litellm/llms/openai/chat/gpt_5_transformation.py b/litellm/llms/openai/chat/gpt_5_transformation.py new file mode 100644 index 0000000000..0de2553376 --- /dev/null +++ b/litellm/llms/openai/chat/gpt_5_transformation.py @@ -0,0 +1,58 @@ +"""Support for OpenAI gpt-5 model family.""" + +from typing import Optional + +import litellm + +from .gpt_transformation import OpenAIGPTConfig + + +class OpenAIGPT5Config(OpenAIGPTConfig): + """Configuration for gpt-5 models. + + Handles OpenAI API quirks for the gpt-5 series like: + + - Mapping ``max_tokens`` -> ``max_completion_tokens``. + - Dropping unsupported ``temperature`` values when requested. + """ + @classmethod + def is_model_gpt_5_model(cls, model: str) -> bool: + return "gpt-5" in model + + def map_openai_params( + self, + non_default_params: dict, + optional_params: dict, + model: str, + drop_params: bool, + ) -> dict: + ################################################################ + # max_tokens is not supported for gpt-5 models on OpenAI API + # Relevant issue: https://github.com/BerriAI/litellm/issues/13381 + ################################################################ + if "max_tokens" in non_default_params: + optional_params["max_completion_tokens"] = non_default_params.pop( + "max_tokens" + ) + + if "temperature" in non_default_params: + temperature_value: Optional[float] = non_default_params.pop("temperature") + if temperature_value is not None: + if temperature_value == 1: + optional_params["temperature"] = temperature_value + elif litellm.drop_params or drop_params: + pass + else: + raise litellm.utils.UnsupportedParamsError( + message=( + "gpt-5 models don't support temperature={}. Only temperature=1 is supported. To drop unsupported params set `litellm.drop_params = True`" + ).format(temperature_value), + status_code=400, + ) + return super()._map_openai_params( + non_default_params=non_default_params, + optional_params=optional_params, + model=model, + drop_params=drop_params, + ) + diff --git a/litellm/llms/openai/openai.py b/litellm/llms/openai/openai.py index e9bed019a9..1f3cf24457 100644 --- a/litellm/llms/openai/openai.py +++ b/litellm/llms/openai/openai.py @@ -47,6 +47,7 @@ from litellm.utils import ( from ...types.llms.openai import * from ..base import BaseLLM +from .chat.gpt_5_transformation import OpenAIGPT5Config from .chat.o_series_transformation import OpenAIOSeriesConfig from .common_utils import ( BaseOpenAILLM, @@ -55,6 +56,7 @@ from .common_utils import ( ) openaiOSeriesConfig = OpenAIOSeriesConfig() +openAIGPT5Config = OpenAIGPT5Config() class MistralEmbeddingConfig: @@ -183,6 +185,8 @@ class OpenAIConfig(BaseConfig): """ if openaiOSeriesConfig.is_model_o_series_model(model=model): return openaiOSeriesConfig.get_supported_openai_params(model=model) + elif openAIGPT5Config.is_model_gpt_5_model(model=model): + return openAIGPT5Config.get_supported_openai_params(model=model) elif litellm.openAIGPTAudioConfig.is_model_gpt_audio_model(model=model): return litellm.openAIGPTAudioConfig.get_supported_openai_params(model=model) else: @@ -217,6 +221,13 @@ class OpenAIConfig(BaseConfig): model=model, drop_params=drop_params, ) + elif openAIGPT5Config.is_model_gpt_5_model(model=model): + return openAIGPT5Config.map_openai_params( + non_default_params=non_default_params, + optional_params=optional_params, + model=model, + drop_params=drop_params, + ) elif litellm.openAIGPTAudioConfig.is_model_gpt_audio_model(model=model): return litellm.openAIGPTAudioConfig.map_openai_params( non_default_params=non_default_params, diff --git a/tests/test_litellm/llms/openai/test_gpt5_transformation.py b/tests/test_litellm/llms/openai/test_gpt5_transformation.py new file mode 100644 index 0000000000..01426dc898 --- /dev/null +++ b/tests/test_litellm/llms/openai/test_gpt5_transformation.py @@ -0,0 +1,40 @@ +import pytest + +import litellm +from litellm.llms.openai.openai import OpenAIConfig + + +@pytest.fixture() +def config() -> OpenAIConfig: + return OpenAIConfig() + + +def test_gpt5_maps_max_tokens(config: OpenAIConfig): + params = config.map_openai_params( + non_default_params={"max_tokens": 10}, + optional_params={}, + model="gpt-5", + drop_params=False, + ) + assert params["max_completion_tokens"] == 10 + assert "max_tokens" not in params + + +def test_gpt5_temperature_drop(config: OpenAIConfig): + params = config.map_openai_params( + non_default_params={"temperature": 0.2}, + optional_params={}, + model="gpt-5", + drop_params=True, + ) + assert "temperature" not in params + + +def test_gpt5_temperature_error(config: OpenAIConfig): + with pytest.raises(litellm.utils.UnsupportedParamsError): + config.map_openai_params( + non_default_params={"temperature": 0.2}, + optional_params={}, + model="gpt-5", + drop_params=False, + )