diff --git a/litellm/images/main.py b/litellm/images/main.py index 9920f3ce1bf..770635baacd 100644 --- a/litellm/images/main.py +++ b/litellm/images/main.py @@ -440,8 +440,14 @@ def image_generation( # noqa: PLR0915 _api_base = api_base or litellm.api_base if custom_llm_provider == litellm.LlmProviders.COMETAPI: + from litellm.llms.cometapi.common_utils import ( + require_cometapi_api_key, + ) + _api_base = api_base - api_key = api_key or dynamic_api_key or litellm.cometapi_key + api_key = require_cometapi_api_key( + api_key or dynamic_api_key or litellm.cometapi_key + ) litellm_params_dict.pop("api_key", None) litellm_params_dict["api_base"] = _api_base diff --git a/litellm/llms/cometapi/common_utils.py b/litellm/llms/cometapi/common_utils.py index b99dc94497a..4ac71b63ffb 100644 --- a/litellm/llms/cometapi/common_utils.py +++ b/litellm/llms/cometapi/common_utils.py @@ -15,18 +15,14 @@ def get_cometapi_api_key(api_key: Optional[str] = None) -> Optional[str]: or litellm.cometapi_key or get_secret_str("COMETAPI_KEY") or get_secret_str("COMETAPI_API_KEY") - or litellm.api_key ) def get_cometapi_api_base(api_base: Optional[str] = None) -> str: - import litellm - return ( api_base or get_secret_str("COMETAPI_BASE_URL") or get_secret_str("COMETAPI_API_BASE") - or litellm.api_base or DEFAULT_COMETAPI_API_BASE ) diff --git a/tests/test_litellm/llms/cometapi/test_cometapi_endpoints.py b/tests/test_litellm/llms/cometapi/test_cometapi_endpoints.py index 8fd965c6254..d6bfa936c20 100644 --- a/tests/test_litellm/llms/cometapi/test_cometapi_endpoints.py +++ b/tests/test_litellm/llms/cometapi/test_cometapi_endpoints.py @@ -94,12 +94,12 @@ def test_cometapi_key_and_base_precedence(monkeypatch): assert get_cometapi_api_base() == "https://proxy.example.com/openai/v1" -def test_cometapi_key_and_base_fall_back_to_litellm_globals(monkeypatch): +def test_cometapi_key_and_base_ignore_litellm_globals(monkeypatch): _clear_cometapi_env(monkeypatch) _pollute_openai_globals(monkeypatch) - assert get_cometapi_api_key() == "openai-global-key" - assert get_cometapi_api_base() == "https://openai.invalid/v1" + assert get_cometapi_api_key() is None + assert get_cometapi_api_base() == "https://api.cometapi.com/v1" def test_cometapi_complete_url_preserves_existing_v1_path(monkeypatch): @@ -277,6 +277,38 @@ def test_cometapi_image_generation_validate_environment_uses_api_key_alias(monke assert headers["Content-Type"] == "application/json" +def test_cometapi_image_generation_missing_key_fails_closed(monkeypatch): + _clear_cometapi_env(monkeypatch) + _pollute_openai_globals(monkeypatch) + + with pytest.raises(ValueError, match="COMETAPI_KEY or COMETAPI_API_KEY"): + CometAPIImageGenerationConfig().validate_environment( + headers={}, + model="gpt-image-2", + messages=[], + optional_params={}, + litellm_params={}, + ) + + +def test_cometapi_image_generation_missing_key_does_not_call_handler(monkeypatch): + _clear_cometapi_env(monkeypatch) + _pollute_openai_globals(monkeypatch) + + with patch( + "litellm.images.main.llm_http_handler.image_generation_handler" + ) as mock_image_handler: + with pytest.raises( + litellm.APIConnectionError, match="COMETAPI_KEY or COMETAPI_API_KEY" + ): + image_generation( + model="cometapi/gpt-image-2", + prompt="A small comet over a clean API diagram", + ) + + mock_image_handler.assert_not_called() + + def test_cometapi_image_generation_maps_new_openai_params(monkeypatch): _clear_cometapi_env(monkeypatch) _pollute_openai_globals(monkeypatch) @@ -479,6 +511,17 @@ def test_cometapi_speech_uses_dynamic_api_key(monkeypatch): assert mock_speech.call_args.kwargs["api_key"] == "dynamic-comet-key" +def test_cometapi_speech_missing_key_does_not_call_openai_audio(monkeypatch): + _clear_cometapi_env(monkeypatch) + _pollute_openai_globals(monkeypatch) + + with patch("litellm.main.openai_chat_completions.audio_speech") as mock_speech: + with pytest.raises(ValueError, match="COMETAPI_KEY or COMETAPI_API_KEY"): + speech(model="cometapi/tts-1", input="hello", voice="alloy") + + mock_speech.assert_not_called() + + def test_cometapi_speech_requires_voice(monkeypatch): _clear_cometapi_env(monkeypatch) @@ -646,6 +689,18 @@ def test_cometapi_moderation_accepts_bare_model_with_custom_provider(monkeypatch ) +def test_cometapi_moderation_missing_key_does_not_create_openai_client(monkeypatch): + _clear_cometapi_env(monkeypatch) + _pollute_openai_globals(monkeypatch) + + _OpenAIClient.instances = [] + with patch("litellm.main.openai.OpenAI", _OpenAIClient): + with pytest.raises(ValueError, match="COMETAPI_KEY or COMETAPI_API_KEY"): + moderation(model="cometapi/omni-moderation-latest", input="hello") + + assert _OpenAIClient.instances == [] + + def test_openai_moderation_fallback_is_unchanged(monkeypatch): _clear_cometapi_env(monkeypatch) _pollute_openai_globals(monkeypatch)