fix(providers): SAGG requires its own API key (fail closed, no OPENAI_API_KEY fallback)

Opt-in special_handling.require_api_key for the JSON provider; SAGG sets it.
Without a key the call now raises AuthenticationError before dispatch, so the
application's OpenAI key is never sent to SAGG. Tests added.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
privatedeskai 2026-10-26 13:38:15 -07:00
parent caf615b750
commit b7b401eda5
3 changed files with 37 additions and 1 deletions

View file

@ -79,6 +79,17 @@ def create_config_class(provider: SimpleProviderConfig):
# Resolve API key
resolved_key: Final = api_key or get_secret_str(provider.api_key_env)
if provider.special_handling.get("require_api_key") and not resolved_key:
# Fail closed: without this the caller's OPENAI_API_KEY would be used as a fallback
# further down the dispatch path and sent to this provider.
from litellm.exceptions import AuthenticationError
raise AuthenticationError(
message=f"{provider.slug}: no API key. Pass api_key or set {provider.api_key_env}.",
llm_provider=provider.slug,
model="",
)
return resolved_base, resolved_key
def get_openai_compatible_provider_info(

View file

@ -308,6 +308,9 @@
"api_key_env": "SAGG_API_KEY",
"param_mappings": {
"max_completion_tokens": "max_tokens"
},
"special_handling": {
"require_api_key": true
}
}
}

View file

@ -38,7 +38,7 @@ class TestSaggProviderConfig:
model="sagg/deepseek-ai/DeepSeek-V4-Flash-0731",
custom_llm_provider=None,
api_base=None,
api_key=None,
api_key="test-key",
)
assert model == "deepseek-ai/DeepSeek-V4-Flash-0731"
@ -84,3 +84,25 @@ class TestSaggProviderConfig:
assert result["max_tokens"] == 100
assert "max_completion_tokens" not in result
assert result["temperature"] == 0.7
class TestSaggRequiresItsOwnKey:
def _config(self):
from litellm.llms.openai_like.dynamic_config import create_config_class
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
return create_config_class(JSONProviderRegistry.get("sagg"))()
def test_missing_key_fails_closed_even_with_openai_key_set(self, monkeypatch):
import pytest
monkeypatch.delenv("SAGG_API_KEY", raising=False)
monkeypatch.setenv("OPENAI_API_KEY", "sk-not-for-sagg")
with pytest.raises(litellm.AuthenticationError):
self._config()._get_openai_compatible_provider_info(None, None)
def test_explicit_key_and_env_key_still_work(self, monkeypatch):
monkeypatch.delenv("SAGG_API_KEY", raising=False)
assert self._config()._get_openai_compatible_provider_info(None, "k1")[1] == "k1"
monkeypatch.setenv("SAGG_API_KEY", "k2")
assert self._config()._get_openai_compatible_provider_info(None, None)[1] == "k2"