diff --git a/litellm/llms/openai/chat/gpt_transformation.py b/litellm/llms/openai/chat/gpt_transformation.py index b9fd217f99a..e3fdaa35308 100644 --- a/litellm/llms/openai/chat/gpt_transformation.py +++ b/litellm/llms/openai/chat/gpt_transformation.py @@ -56,7 +56,7 @@ from litellm.types.utils import ( from litellm.utils import convert_to_model_response_object from ..common_utils import OpenAIError -from ..common_utils import patch_deepseek_v4_reasoning_messages +from ..deepseek_reasoning_utils import patch_deepseek_v4_reasoning_messages if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj diff --git a/litellm/llms/openai/common_utils.py b/litellm/llms/openai/common_utils.py index a34433240b0..c13a976c1b9 100644 --- a/litellm/llms/openai/common_utils.py +++ b/litellm/llms/openai/common_utils.py @@ -258,43 +258,6 @@ class BaseOpenAILLM: class OpenAICredentials(NamedTuple): api_base: str api_key: Optional[str] - - -def requires_deepseek_v4_reasoning_content(model: Optional[str]) -> bool: - """Return True when the model requires DeepSeek V4 thinking history.""" - if not model: - return False - - normalized_model = model.lower() - if normalized_model.startswith("responses/"): - normalized_model = normalized_model.split("responses/", 1)[1] - - return "deepseek-v4" in normalized_model - - -def patch_deepseek_v4_reasoning_messages( - model: Optional[str], messages: List[Any] -) -> List[Any]: - """ - Ensure assistant tool-call messages include reasoning_content for DeepSeek V4. - - DeepSeek V4 rejects multi-turn requests when prior assistant tool-call messages - omit the reasoning_content field, even if the value is empty. - """ - if not requires_deepseek_v4_reasoning_content(model): - return messages - - for message in messages: - if not isinstance(message, dict): - continue - if message.get("role") != "assistant": - continue - if not (message.get("tool_calls") or message.get("tool_call_id")): - continue - if message.get("reasoning_content") is None: - message["reasoning_content"] = "" - - return messages organization: Optional[str] diff --git a/litellm/llms/openai/deepseek_reasoning_utils.py b/litellm/llms/openai/deepseek_reasoning_utils.py new file mode 100644 index 00000000000..630bf0d3291 --- /dev/null +++ b/litellm/llms/openai/deepseek_reasoning_utils.py @@ -0,0 +1,38 @@ +from typing import Any, List, Optional + + +def requires_deepseek_v4_reasoning_content(model: Optional[str]) -> bool: + """Return True when the model requires DeepSeek V4 thinking history.""" + if not model: + return False + + normalized_model = model.lower() + if normalized_model.startswith("responses/"): + normalized_model = normalized_model.split("responses/", 1)[1] + + return "deepseek-v4" in normalized_model + + +def patch_deepseek_v4_reasoning_messages( + model: Optional[str], messages: List[Any] +) -> List[Any]: + """ + Ensure assistant tool-call messages include reasoning_content for DeepSeek V4. + + DeepSeek V4 rejects multi-turn requests when prior assistant tool-call messages + omit the reasoning_content field, even if the value is empty. + """ + if not requires_deepseek_v4_reasoning_content(model): + return messages + + for message in messages: + if not isinstance(message, dict): + continue + if message.get("role") != "assistant": + continue + if not (message.get("tool_calls") or message.get("tool_call_id")): + continue + if message.get("reasoning_content") is None: + message["reasoning_content"] = "" + + return messages diff --git a/litellm/llms/openai/openai.py b/litellm/llms/openai/openai.py index d79a5f0288f..da1954cc8d6 100644 --- a/litellm/llms/openai/openai.py +++ b/litellm/llms/openai/openai.py @@ -58,9 +58,9 @@ from .chat.o_series_transformation import OpenAIOSeriesConfig from .common_utils import ( BaseOpenAILLM, OpenAIError, - patch_deepseek_v4_reasoning_messages, drop_params_from_unprocessable_entity_error, ) +from .deepseek_reasoning_utils import patch_deepseek_v4_reasoning_messages openaiOSeriesConfig = OpenAIOSeriesConfig() openAIGPT5Config = OpenAIGPT5Config() diff --git a/tests/llm_translation/test_deepseek_completion.py b/tests/llm_translation/test_deepseek_completion.py index c37a15ab5d4..4ee182cdba6 100644 --- a/tests/llm_translation/test_deepseek_completion.py +++ b/tests/llm_translation/test_deepseek_completion.py @@ -241,3 +241,15 @@ def test_deepseek_reasoner_transform_request_does_not_inject_reasoning_content() ) assert "reasoning_content" not in request["messages"][1] + + +def test_get_openai_credentials_preserves_organization_field(): + from litellm.llms.openai.common_utils import get_openai_credentials + + credentials = get_openai_credentials( + api_base="https://example.com/v1", + api_key="test-key", + organization="test-org", + ) + + assert credentials.organization == "test-org"