indoxrouter 0.1.12__py3-none-any.whl → 0.1.16__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- indoxrouter/__init__.py +10 -1
- indoxrouter/client.py +39 -7
- indoxrouter/constants.py +1 -1
- indoxrouter/exceptions.py +26 -2
- {indoxrouter-0.1.12.dist-info → indoxrouter-0.1.16.dist-info}/METADATA +9 -23
- indoxrouter-0.1.16.dist-info/RECORD +8 -0
- {indoxrouter-0.1.12.dist-info → indoxrouter-0.1.16.dist-info}/WHEEL +1 -1
- indoxrouter/test_api_key.py +0 -98
- indoxrouter-0.1.12.dist-info/RECORD +0 -9
- {indoxrouter-0.1.12.dist-info → indoxrouter-0.1.16.dist-info}/top_level.txt +0 -0
indoxrouter/__init__.py
CHANGED
@@ -30,7 +30,7 @@ For custom server URLs:
|
|
30
30
|
```
|
31
31
|
"""
|
32
32
|
|
33
|
-
from .client import Client
|
33
|
+
from .client import Client, IndoxRouter
|
34
34
|
from .exceptions import (
|
35
35
|
IndoxRouterError,
|
36
36
|
AuthenticationError,
|
@@ -39,13 +39,18 @@ from .exceptions import (
|
|
39
39
|
ProviderError,
|
40
40
|
ModelNotFoundError,
|
41
41
|
ProviderNotFoundError,
|
42
|
+
ModelNotAvailableError,
|
42
43
|
InvalidParametersError,
|
44
|
+
RequestError,
|
43
45
|
InsufficientCreditsError,
|
46
|
+
ValidationError,
|
47
|
+
APIError,
|
44
48
|
)
|
45
49
|
|
46
50
|
__version__ = "0.2.1"
|
47
51
|
__all__ = [
|
48
52
|
"Client",
|
53
|
+
"IndoxRouter",
|
49
54
|
"IndoxRouterError",
|
50
55
|
"AuthenticationError",
|
51
56
|
"NetworkError",
|
@@ -53,6 +58,10 @@ __all__ = [
|
|
53
58
|
"ProviderError",
|
54
59
|
"ModelNotFoundError",
|
55
60
|
"ProviderNotFoundError",
|
61
|
+
"ModelNotAvailableError",
|
56
62
|
"InvalidParametersError",
|
63
|
+
"RequestError",
|
57
64
|
"InsufficientCreditsError",
|
65
|
+
"ValidationError",
|
66
|
+
"APIError",
|
58
67
|
]
|
indoxrouter/client.py
CHANGED
@@ -57,10 +57,14 @@ from .exceptions import (
|
|
57
57
|
NetworkError,
|
58
58
|
ProviderNotFoundError,
|
59
59
|
ModelNotFoundError,
|
60
|
+
ModelNotAvailableError,
|
60
61
|
InvalidParametersError,
|
61
62
|
RateLimitError,
|
62
63
|
ProviderError,
|
64
|
+
RequestError,
|
63
65
|
InsufficientCreditsError,
|
66
|
+
ValidationError,
|
67
|
+
APIError,
|
64
68
|
)
|
65
69
|
from .constants import (
|
66
70
|
DEFAULT_BASE_URL,
|
@@ -316,29 +320,57 @@ class Client:
|
|
316
320
|
if "provider" in error_message.lower():
|
317
321
|
raise ProviderNotFoundError(error_message)
|
318
322
|
elif "model" in error_message.lower():
|
319
|
-
|
323
|
+
# Check if it's a model not found vs model not available
|
324
|
+
if (
|
325
|
+
"not supported" in error_message.lower()
|
326
|
+
or "disabled" in error_message.lower()
|
327
|
+
or "unavailable" in error_message.lower()
|
328
|
+
):
|
329
|
+
raise ModelNotAvailableError(error_message)
|
330
|
+
else:
|
331
|
+
raise ModelNotFoundError(error_message)
|
320
332
|
else:
|
321
|
-
raise
|
322
|
-
f"Resource not found: {error_message} (URL: {url})"
|
323
|
-
)
|
333
|
+
raise APIError(f"Resource not found: {error_message} (URL: {url})")
|
324
334
|
elif status_code == 429:
|
325
335
|
raise RateLimitError(f"Rate limit exceeded: {error_message}")
|
326
336
|
elif status_code == 400:
|
327
|
-
|
337
|
+
# Check if it's a validation error or invalid parameters
|
338
|
+
if (
|
339
|
+
"validation" in error_message.lower()
|
340
|
+
or "invalid format" in error_message.lower()
|
341
|
+
):
|
342
|
+
raise ValidationError(f"Request validation failed: {error_message}")
|
343
|
+
else:
|
344
|
+
raise InvalidParametersError(f"Invalid parameters: {error_message}")
|
328
345
|
elif status_code == 402:
|
329
346
|
raise InsufficientCreditsError(f"Insufficient credits: {error_message}")
|
347
|
+
elif status_code == 422:
|
348
|
+
# Unprocessable Entity - typically validation errors
|
349
|
+
raise ValidationError(f"Request validation failed: {error_message}")
|
350
|
+
elif status_code == 503:
|
351
|
+
# Service Unavailable - model might be temporarily unavailable
|
352
|
+
if "model" in error_message.lower():
|
353
|
+
raise ModelNotAvailableError(
|
354
|
+
f"Model temporarily unavailable: {error_message}"
|
355
|
+
)
|
356
|
+
else:
|
357
|
+
raise APIError(f"Service unavailable: {error_message}")
|
330
358
|
elif status_code == 500:
|
331
359
|
# Provide more detailed information for server errors
|
332
360
|
error_detail = error_data.get("detail", "No details provided")
|
333
361
|
# Include the request data in the error message for better debugging
|
334
362
|
request_data_str = json.dumps(data, indent=2) if data else "None"
|
335
|
-
raise
|
363
|
+
raise RequestError(
|
336
364
|
f"Server error (500): {error_detail}. URL: {url}.\n"
|
337
365
|
f"Request data: {request_data_str}\n"
|
338
366
|
f"This may indicate an issue with the server configuration or a problem with the provider service."
|
339
367
|
)
|
368
|
+
elif status_code >= 400 and status_code < 500:
|
369
|
+
# Client errors
|
370
|
+
raise APIError(f"Client error ({status_code}): {error_message}")
|
340
371
|
else:
|
341
|
-
|
372
|
+
# Server errors
|
373
|
+
raise RequestError(f"Server error ({status_code}): {error_message}")
|
342
374
|
except requests.RequestException as e:
|
343
375
|
logger.error(f"Request exception: {str(e)}")
|
344
376
|
raise NetworkError(f"Network error: {str(e)}")
|
indoxrouter/constants.py
CHANGED
@@ -4,7 +4,7 @@ Constants for the IndoxRouter client.
|
|
4
4
|
|
5
5
|
# API settings
|
6
6
|
DEFAULT_API_VERSION = "v1"
|
7
|
-
DEFAULT_BASE_URL = "https://api.
|
7
|
+
DEFAULT_BASE_URL = "https://api.indoxrouter.com" # Production server URL with HTTPS
|
8
8
|
# DEFAULT_BASE_URL = "http://localhost:8000" # Local development server
|
9
9
|
DEFAULT_TIMEOUT = 60
|
10
10
|
USE_COOKIES = True # Always use cookie-based authentication
|
indoxrouter/exceptions.py
CHANGED
@@ -38,14 +38,20 @@ class ProviderError(IndoxRouterError):
|
|
38
38
|
pass
|
39
39
|
|
40
40
|
|
41
|
+
class ProviderNotFoundError(ProviderError):
|
42
|
+
"""Raised when a requested provider is not found."""
|
43
|
+
|
44
|
+
pass
|
45
|
+
|
46
|
+
|
41
47
|
class ModelNotFoundError(ProviderError):
|
42
48
|
"""Raised when a requested model is not found."""
|
43
49
|
|
44
50
|
pass
|
45
51
|
|
46
52
|
|
47
|
-
class
|
48
|
-
"""Raised when a
|
53
|
+
class ModelNotAvailableError(ProviderError):
|
54
|
+
"""Raised when a model is disabled or not supported by the provider."""
|
49
55
|
|
50
56
|
pass
|
51
57
|
|
@@ -56,7 +62,25 @@ class InvalidParametersError(IndoxRouterError):
|
|
56
62
|
pass
|
57
63
|
|
58
64
|
|
65
|
+
class RequestError(IndoxRouterError):
|
66
|
+
"""Raised when a request to a provider fails."""
|
67
|
+
|
68
|
+
pass
|
69
|
+
|
70
|
+
|
59
71
|
class InsufficientCreditsError(IndoxRouterError):
|
60
72
|
"""Raised when the user doesn't have enough credits."""
|
61
73
|
|
62
74
|
pass
|
75
|
+
|
76
|
+
|
77
|
+
class ValidationError(IndoxRouterError):
|
78
|
+
"""Raised when request validation fails."""
|
79
|
+
|
80
|
+
pass
|
81
|
+
|
82
|
+
|
83
|
+
class APIError(IndoxRouterError):
|
84
|
+
"""Raised when the API returns an error."""
|
85
|
+
|
86
|
+
pass
|
@@ -1,10 +1,13 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: indoxrouter
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.16
|
4
4
|
Summary: A unified client for various AI providers
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
Author-email: indoxRouter Team <ashkan.eskandari.dev@gmail.com>
|
6
|
+
License: MIT
|
7
|
+
Project-URL: Homepage, https://github.com/indoxrouter/indoxrouter
|
8
|
+
Project-URL: Repository, https://github.com/indoxrouter/indoxrouter
|
9
|
+
Project-URL: Issues, https://github.com/indoxrouter/indoxrouter/issues
|
10
|
+
Keywords: ai,api,client,openai,anthropic,google,mistral
|
8
11
|
Classifier: Development Status :: 3 - Alpha
|
9
12
|
Classifier: Intended Audience :: Developers
|
10
13
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -18,27 +21,10 @@ Requires-Python: >=3.8
|
|
18
21
|
Description-Content-Type: text/markdown
|
19
22
|
Requires-Dist: requests>=2.25.0
|
20
23
|
Requires-Dist: python-dotenv>=1.0.0
|
21
|
-
Provides-Extra: dev
|
22
|
-
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
23
|
-
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
24
|
-
Requires-Dist: black>=23.0.0; extra == "dev"
|
25
|
-
Requires-Dist: isort>=5.0.0; extra == "dev"
|
26
|
-
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
27
|
-
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
28
|
-
Dynamic: author
|
29
|
-
Dynamic: author-email
|
30
|
-
Dynamic: classifier
|
31
|
-
Dynamic: description
|
32
|
-
Dynamic: description-content-type
|
33
|
-
Dynamic: home-page
|
34
|
-
Dynamic: provides-extra
|
35
|
-
Dynamic: requires-dist
|
36
|
-
Dynamic: requires-python
|
37
|
-
Dynamic: summary
|
38
24
|
|
39
25
|
# IndoxRouter Client
|
40
26
|
|
41
|
-
A unified client for various AI providers, including OpenAI,
|
27
|
+
A unified client for various AI providers, including OpenAI, anthropic, Google, and Mistral.
|
42
28
|
|
43
29
|
## Features
|
44
30
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
indoxrouter/__init__.py,sha256=kwGvH8F5oqm2O4kLs-UtPfcY0AYiy5ZDUg-Sh3iYJA4,1627
|
2
|
+
indoxrouter/client.py,sha256=SQ0ulzkHQZBKFK1dyywjZI_vUQfEd0qVBYQx_8mu7uU,30533
|
3
|
+
indoxrouter/constants.py,sha256=tEmfhfCpuKVos1TxVhJfPlv1P0ePWnjnwso4ZHJ0VhM,1190
|
4
|
+
indoxrouter/exceptions.py,sha256=qs7f9AnJ7SkOyf9N5qRaZIKpECE8uBq1Pvcg19Jif-U,1718
|
5
|
+
indoxrouter-0.1.16.dist-info/METADATA,sha256=wE-sWdHgDNp42hULYxQTx5bO-PB_ydwyvZn4iOTIFYM,5692
|
6
|
+
indoxrouter-0.1.16.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
7
|
+
indoxrouter-0.1.16.dist-info/top_level.txt,sha256=v6FGWkw0QAnXhyYtnXLI1cxzna0iveNvZUotVzCWabM,12
|
8
|
+
indoxrouter-0.1.16.dist-info/RECORD,,
|
indoxrouter/test_api_key.py
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python
|
2
|
-
"""
|
3
|
-
Test script for the IndoxRouter client.
|
4
|
-
This script tests authentication and basic functionality of the client.
|
5
|
-
"""
|
6
|
-
|
7
|
-
import os
|
8
|
-
import sys
|
9
|
-
import logging
|
10
|
-
from indoxrouter import Client, AuthenticationError, NetworkError
|
11
|
-
|
12
|
-
# Set up logging
|
13
|
-
logging.basicConfig(
|
14
|
-
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
15
|
-
)
|
16
|
-
logger = logging.getLogger("indoxrouter_test")
|
17
|
-
|
18
|
-
|
19
|
-
def test_client(api_key=None, debug=False):
|
20
|
-
"""
|
21
|
-
Test the IndoxRouter client with the provided API key.
|
22
|
-
|
23
|
-
Args:
|
24
|
-
api_key: API key to use for authentication
|
25
|
-
debug: Whether to enable debug logging
|
26
|
-
"""
|
27
|
-
if debug:
|
28
|
-
logging.getLogger("indoxrouter").setLevel(logging.DEBUG)
|
29
|
-
|
30
|
-
logger.info("Testing IndoxRouter client...")
|
31
|
-
|
32
|
-
# If no API key is provided, try to get it from environment variable
|
33
|
-
if not api_key:
|
34
|
-
api_key = os.environ.get("INDOX_ROUTER_API_KEY")
|
35
|
-
if not api_key:
|
36
|
-
logger.error(
|
37
|
-
"No API key provided. Please provide an API key as an argument or set the INDOX_ROUTER_API_KEY environment variable."
|
38
|
-
)
|
39
|
-
sys.exit(1)
|
40
|
-
|
41
|
-
try:
|
42
|
-
# Initialize client
|
43
|
-
logger.info("Initializing client...")
|
44
|
-
client = Client(api_key=api_key)
|
45
|
-
|
46
|
-
# Test connection
|
47
|
-
logger.info("Testing connection...")
|
48
|
-
connection_info = client.test_connection()
|
49
|
-
logger.info(f"Connection test: {connection_info['status']}")
|
50
|
-
|
51
|
-
# Try to get available models
|
52
|
-
logger.info("Fetching available models...")
|
53
|
-
models = client.models()
|
54
|
-
|
55
|
-
# Display some models
|
56
|
-
providers = [p.get("name") for p in models]
|
57
|
-
logger.info(f"Available providers: {', '.join(providers)}")
|
58
|
-
|
59
|
-
# Try a simple chat completion
|
60
|
-
logger.info("Testing chat completion...")
|
61
|
-
response = client.chat(
|
62
|
-
messages=[{"role": "user", "content": "Hello, who are you?"}],
|
63
|
-
model="openai/gpt-3.5-turbo",
|
64
|
-
max_tokens=30,
|
65
|
-
)
|
66
|
-
|
67
|
-
logger.info("Chat completion successful!")
|
68
|
-
logger.info(f"Response: {response['choices'][0]['message']['content']}")
|
69
|
-
|
70
|
-
logger.info("All tests passed! The client is working correctly.")
|
71
|
-
|
72
|
-
except AuthenticationError as e:
|
73
|
-
logger.error(f"Authentication error: {e}")
|
74
|
-
logger.error("Please check that your API key is correct.")
|
75
|
-
sys.exit(1)
|
76
|
-
except NetworkError as e:
|
77
|
-
logger.error(f"Network error: {e}")
|
78
|
-
logger.error(
|
79
|
-
"Please check your internet connection and that the IndoxRouter server is accessible."
|
80
|
-
)
|
81
|
-
sys.exit(1)
|
82
|
-
except Exception as e:
|
83
|
-
logger.error(f"Unexpected error: {e}")
|
84
|
-
sys.exit(1)
|
85
|
-
|
86
|
-
|
87
|
-
if __name__ == "__main__":
|
88
|
-
import argparse
|
89
|
-
|
90
|
-
parser = argparse.ArgumentParser(description="Test the IndoxRouter client")
|
91
|
-
parser.add_argument("--api-key", "-k", help="API key to use for authentication")
|
92
|
-
parser.add_argument(
|
93
|
-
"--debug", "-d", action="store_true", help="Enable debug logging"
|
94
|
-
)
|
95
|
-
|
96
|
-
args = parser.parse_args()
|
97
|
-
|
98
|
-
test_client(api_key=args.api_key, debug=args.debug)
|
@@ -1,9 +0,0 @@
|
|
1
|
-
indoxrouter/__init__.py,sha256=28pdx482uGFF_S1msov0LTTGsFTvVBKRqMkDmoXWUBY,1416
|
2
|
-
indoxrouter/client.py,sha256=ixzCJP28ddYFQxwRiqdY5Fp1CB978DJpI4yCiOI3s4o,28898
|
3
|
-
indoxrouter/constants.py,sha256=D-l5qhEmeDIXOEfogvf6lJI1Sw_fXSKZy-Nj_l_MV7E,1184
|
4
|
-
indoxrouter/exceptions.py,sha256=0ULxtK9va4718PGTO5VoClXYEJeojpiM-7AganeiZZ4,1263
|
5
|
-
indoxrouter/test_api_key.py,sha256=u4fUTJwxC7-kTrLdCq8u5a1Mx66jeExfsBlHLhyzNmA,3229
|
6
|
-
indoxrouter-0.1.12.dist-info/METADATA,sha256=pRupY-6V6GrozkXidKGm_qf6fBY2clAqYwgKAU5OJww,6004
|
7
|
-
indoxrouter-0.1.12.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
8
|
-
indoxrouter-0.1.12.dist-info/top_level.txt,sha256=v6FGWkw0QAnXhyYtnXLI1cxzna0iveNvZUotVzCWabM,12
|
9
|
-
indoxrouter-0.1.12.dist-info/RECORD,,
|
File without changes
|