Skip to content

Commit d2052c7

Browse files
committed
custom exception for missing api key
1 parent 4126bda commit d2052c7

File tree

7 files changed

+63
-12
lines changed

7 files changed

+63
-12
lines changed

src/any_llm/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
from any_llm.main import (
22
completion,
33
)
4+
from any_llm.exceptions import (
5+
MissingApiKeyError,
6+
)
47

58
__all__ = [
69
"completion",
10+
"MissingApiKeyError",
711
]

src/any_llm/exceptions.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Custom exceptions for any-llm package."""
2+
3+
4+
class MissingApiKeyError(Exception):
5+
"""Exception raised when an API key is missing or not provided."""
6+
7+
def __init__(self, provider_name: str, env_var_name: str) -> None:
8+
"""Initialize the exception.
9+
10+
Args:
11+
provider_name: Name of the provider (e.g., "OpenAI", "Google", "Mistral")
12+
env_var_name: Name of the environment variable that should contain the API key
13+
message: Optional custom message. If not provided, a default message will be used.
14+
"""
15+
self.provider_name = provider_name
16+
self.env_var_name = env_var_name
17+
18+
message = (
19+
f"No {provider_name} API key provided. "
20+
f"Please provide it in the config or set the {env_var_name} environment variable."
21+
)
22+
23+
super().__init__(message)

src/any_llm/providers/google/google.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from openai.types.chat.chat_completion_message import ChatCompletionMessage
1515
from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall, Function
1616
from any_llm.provider import Provider, ApiConfig
17+
from any_llm.exceptions import MissingApiKeyError
1718

1819
DEFAULT_TEMPERATURE = 0.7
1920

@@ -196,8 +197,10 @@ def __init__(self, config: ApiConfig) -> None:
196197
self.location = os.getenv("GOOGLE_REGION", "us-central1")
197198

198199
if not self.project_id:
199-
msg = "GOOGLE_PROJECT_ID environment variable is required for Vertex AI"
200-
raise ValueError(msg)
200+
raise MissingApiKeyError(
201+
"Google Vertex AI",
202+
"GOOGLE_PROJECT_ID",
203+
)
201204

202205
# Initialize client for Vertex AI
203206
self.client = genai.Client(vertexai=True, project=self.project_id, location=self.location)
@@ -207,8 +210,10 @@ def __init__(self, config: ApiConfig) -> None:
207210
api_key = getattr(config, "api_key", None) or os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
208211

209212
if not api_key:
210-
msg = "API key is required for Gemini Developer API. Provide it via ApiConfig or set GEMINI_API_KEY/GOOGLE_API_KEY environment variable"
211-
raise ValueError(msg)
213+
raise MissingApiKeyError(
214+
"Google Gemini Developer API",
215+
"GEMINI_API_KEY/GOOGLE_API_KEY",
216+
)
212217

213218
# Initialize client for Gemini Developer API
214219
self.client = genai.Client(api_key=api_key)

src/any_llm/providers/mistral/mistral.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from openai.types.chat.chat_completion_message import ChatCompletionMessage
1616
from openai.types.chat.chat_completion_message_tool_call import ChatCompletionMessageToolCall, Function
1717
from any_llm.provider import Provider, ApiConfig
18+
from any_llm.exceptions import MissingApiKeyError
1819

1920

2021
def _convert_kwargs(kwargs: dict[str, Any]) -> dict[str, Any]:
@@ -97,8 +98,7 @@ def __init__(self, config: ApiConfig) -> None:
9798
if not config.api_key:
9899
config.api_key = os.getenv("MISTRAL_API_KEY")
99100
if not config.api_key:
100-
msg = "No Mistral API key provided. Please provide it in the config or set the MISTRAL_API_KEY environment variable."
101-
raise ValueError(msg)
101+
raise MissingApiKeyError("Mistral", "MISTRAL_API_KEY")
102102
self.client = Mistral(api_key=config.api_key, server_url=config.api_base)
103103

104104
def completion(

src/any_llm/providers/openai/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from openai.types.chat.chat_completion import ChatCompletion
77

88
from any_llm.provider import Provider, ApiConfig
9+
from any_llm.exceptions import MissingApiKeyError
910

1011

1112
class BaseOpenAIProvider(Provider, ABC):
@@ -34,8 +35,7 @@ def __init__(self, config: ApiConfig) -> None:
3435
client_kwargs["base_url"] = config.api_base
3536

3637
if not config.api_key and not os.getenv(self.ENV_API_KEY_NAME):
37-
msg = f"No {self.PROVIDER_NAME} API key provided. Please provide it in the config or set the {self.ENV_API_KEY_NAME} environment variable."
38-
raise ValueError(msg)
38+
raise MissingApiKeyError(self.PROVIDER_NAME, self.ENV_API_KEY_NAME)
3939

4040
# Get API key from environment if not provided in config
4141
api_key = config.api_key or os.getenv(self.ENV_API_KEY_NAME)

test_exception.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env python3
2+
"""Simple test to verify the custom exception works correctly."""
3+
4+
from any_llm.exceptions import MissingApiKeyError
5+
6+
# Test the custom exception
7+
try:
8+
raise MissingApiKeyError("Test Provider", "TEST_API_KEY")
9+
except MissingApiKeyError as e:
10+
print(f"✅ Custom exception works: {e}")
11+
print(f" Provider: {e.provider_name}")
12+
print(f" Env var: {e.env_var_name}")
13+
14+
# Test with custom message
15+
try:
16+
raise MissingApiKeyError("Test Provider", "TEST_API_KEY", "Custom error message")
17+
except MissingApiKeyError as e:
18+
print(f"✅ Custom message works: {e}")
19+
20+
print("All tests passed! 🎉")

tests/integration/test_completion.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import httpx
22
import pytest
33
from any_llm import completion
4+
from any_llm.exceptions import MissingApiKeyError
45

56

67
# Use small models for testing to make sure they work
@@ -25,10 +26,8 @@ def test_providers(provider: str) -> None:
2526
model_id = provider_model_map[provider]
2627
try:
2728
result = completion(f"{provider}/{model_id}", messages=[{"role": "user", "content": "Hello"}])
28-
except ValueError as e:
29-
if "API key provided" in str(e):
30-
pytest.skip(f"{provider} API key not provided, skipping")
31-
raise e
29+
except MissingApiKeyError:
30+
pytest.skip(f"{provider} API key not provided, skipping")
3231
except (httpx.HTTPStatusError, httpx.ConnectError):
3332
if provider == "ollama":
3433
pytest.skip("Ollama is not set up, skipping")

0 commit comments

Comments
 (0)