indoxrouter 0.1.8__py3-none-any.whl → 0.1.11__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/client.py +97 -4
- indoxrouter/constants.py +1 -0
- indoxrouter/test_api_key.py +98 -0
- {indoxrouter-0.1.8.dist-info → indoxrouter-0.1.11.dist-info}/METADATA +39 -3
- indoxrouter-0.1.11.dist-info/RECORD +9 -0
- indoxrouter-0.1.8.dist-info/RECORD +0 -8
- {indoxrouter-0.1.8.dist-info → indoxrouter-0.1.11.dist-info}/WHEEL +0 -0
- {indoxrouter-0.1.8.dist-info → indoxrouter-0.1.11.dist-info}/top_level.txt +0 -0
indoxrouter/client.py
CHANGED
@@ -5,6 +5,9 @@ This module provides a client for interacting with the IndoxRouter API, which se
|
|
5
5
|
interface to multiple AI providers and models. The client handles authentication, rate limiting,
|
6
6
|
error handling, and provides a standardized response format across different AI services.
|
7
7
|
|
8
|
+
IMPORTANT: The IndoxRouter server now supports only cookie-based authentication. This client
|
9
|
+
automatically handles authentication by exchanging your API key for a JWT token through the login endpoint.
|
10
|
+
|
8
11
|
The Client class offers methods for:
|
9
12
|
- Authentication and session management
|
10
13
|
- Making API requests with automatic token refresh
|
@@ -71,6 +74,7 @@ from .constants import (
|
|
71
74
|
IMAGE_ENDPOINT,
|
72
75
|
MODEL_ENDPOINT,
|
73
76
|
USAGE_ENDPOINT,
|
77
|
+
USE_COOKIES,
|
74
78
|
)
|
75
79
|
|
76
80
|
logger = logging.getLogger(__name__)
|
@@ -84,7 +88,6 @@ class Client:
|
|
84
88
|
def __init__(
|
85
89
|
self,
|
86
90
|
api_key: Optional[str] = None,
|
87
|
-
base_url: Optional[str] = None,
|
88
91
|
timeout: int = DEFAULT_TIMEOUT,
|
89
92
|
):
|
90
93
|
"""
|
@@ -93,19 +96,91 @@ class Client:
|
|
93
96
|
Args:
|
94
97
|
api_key: API key for authentication. If not provided, the client will look for the
|
95
98
|
INDOX_ROUTER_API_KEY environment variable.
|
96
|
-
base_url: Custom base URL for the API. If not provided, the default base URL will be used.
|
97
99
|
timeout: Request timeout in seconds.
|
98
100
|
"""
|
101
|
+
|
102
|
+
use_cookies = USE_COOKIES
|
99
103
|
self.api_key = api_key or os.environ.get("INDOX_ROUTER_API_KEY")
|
100
104
|
if not self.api_key:
|
101
105
|
raise ValueError(
|
102
106
|
"API key must be provided either as an argument or as the INDOX_ROUTER_API_KEY environment variable."
|
103
107
|
)
|
104
108
|
|
105
|
-
self.base_url =
|
109
|
+
self.base_url = DEFAULT_BASE_URL
|
106
110
|
self.timeout = timeout
|
111
|
+
self.use_cookies = use_cookies
|
107
112
|
self.session = requests.Session()
|
108
|
-
|
113
|
+
|
114
|
+
# Authenticate and get JWT tokens
|
115
|
+
self._authenticate()
|
116
|
+
|
117
|
+
def _authenticate(self):
|
118
|
+
"""
|
119
|
+
Authenticate with the server and get JWT tokens.
|
120
|
+
This uses the /auth/token endpoint to get JWT tokens using the API key.
|
121
|
+
"""
|
122
|
+
try:
|
123
|
+
# First try using the API key as a username with empty password (API key flow)
|
124
|
+
logger.debug("Authenticating with API key as username")
|
125
|
+
response = self.session.post(
|
126
|
+
f"{self.base_url}/api/v1/auth/token",
|
127
|
+
data={
|
128
|
+
"username": self.api_key,
|
129
|
+
"password": "",
|
130
|
+
},
|
131
|
+
timeout=self.timeout,
|
132
|
+
)
|
133
|
+
|
134
|
+
if response.status_code != 200:
|
135
|
+
# Try using email:api_key format if the first method fails
|
136
|
+
logger.debug(
|
137
|
+
"First auth method failed, trying with API key as password"
|
138
|
+
)
|
139
|
+
response = self.session.post(
|
140
|
+
f"{self.base_url}/api/v1/auth/login",
|
141
|
+
json={
|
142
|
+
"username": "pip_client",
|
143
|
+
"password": self.api_key,
|
144
|
+
},
|
145
|
+
timeout=self.timeout,
|
146
|
+
)
|
147
|
+
|
148
|
+
if response.status_code != 200:
|
149
|
+
error_data = {}
|
150
|
+
try:
|
151
|
+
error_data = response.json()
|
152
|
+
except:
|
153
|
+
error_data = {"detail": response.text}
|
154
|
+
|
155
|
+
raise AuthenticationError(
|
156
|
+
f"Authentication failed: {error_data.get('detail', 'Unknown error')}"
|
157
|
+
)
|
158
|
+
|
159
|
+
# At this point, the cookies should be set in the session
|
160
|
+
logger.debug("Authentication successful")
|
161
|
+
|
162
|
+
# Check if we have the cookies we need
|
163
|
+
if "access_token" not in self.session.cookies:
|
164
|
+
logger.warning(
|
165
|
+
"Authentication succeeded but no access_token cookie was set"
|
166
|
+
)
|
167
|
+
|
168
|
+
except requests.RequestException as e:
|
169
|
+
logger.error(f"Authentication request failed: {str(e)}")
|
170
|
+
raise NetworkError(f"Network error during authentication: {str(e)}")
|
171
|
+
|
172
|
+
def _get_domain(self):
|
173
|
+
"""
|
174
|
+
Extract domain from the base URL for cookie setting.
|
175
|
+
"""
|
176
|
+
try:
|
177
|
+
from urllib.parse import urlparse
|
178
|
+
|
179
|
+
parsed_url = urlparse(self.base_url)
|
180
|
+
return parsed_url.netloc
|
181
|
+
except Exception:
|
182
|
+
# If parsing fails, return a default value
|
183
|
+
return ""
|
109
184
|
|
110
185
|
def enable_debug(self, level=logging.DEBUG):
|
111
186
|
"""
|
@@ -177,6 +252,24 @@ class Client:
|
|
177
252
|
if stream:
|
178
253
|
return response
|
179
254
|
|
255
|
+
# Check if we need to reauthenticate (401 Unauthorized)
|
256
|
+
if response.status_code == 401:
|
257
|
+
logger.debug("Received 401, attempting to reauthenticate")
|
258
|
+
self._authenticate()
|
259
|
+
|
260
|
+
# Retry the request after reauthentication
|
261
|
+
response = self.session.request(
|
262
|
+
method,
|
263
|
+
url,
|
264
|
+
headers=headers,
|
265
|
+
json=data,
|
266
|
+
timeout=self.timeout,
|
267
|
+
stream=stream,
|
268
|
+
)
|
269
|
+
|
270
|
+
if stream:
|
271
|
+
return response
|
272
|
+
|
180
273
|
response.raise_for_status()
|
181
274
|
return response.json()
|
182
275
|
except requests.HTTPError as e:
|
indoxrouter/constants.py
CHANGED
@@ -7,6 +7,7 @@ DEFAULT_API_VERSION = "v1"
|
|
7
7
|
DEFAULT_BASE_URL = "https://api.indox.org" # Production server URL with HTTPS
|
8
8
|
# DEFAULT_BASE_URL = "http://localhost:8000" # Local development server
|
9
9
|
DEFAULT_TIMEOUT = 60
|
10
|
+
USE_COOKIES = True # Always use cookie-based authentication
|
10
11
|
|
11
12
|
# Default models
|
12
13
|
DEFAULT_MODEL = "openai/gpt-4o-mini"
|
@@ -0,0 +1,98 @@
|
|
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,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: indoxrouter
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.11
|
4
4
|
Summary: A unified client for various AI providers
|
5
5
|
Home-page: https://github.com/indoxrouter/indoxrouter
|
6
6
|
Author: indoxRouter Team
|
@@ -45,7 +45,7 @@ A unified client for various AI providers, including OpenAI, Anthropic, Google,
|
|
45
45
|
- **Unified API**: Access multiple AI providers through a single API
|
46
46
|
- **Simple Interface**: Easy-to-use methods for chat, completion, embeddings, and image generation
|
47
47
|
- **Error Handling**: Standardized error handling across providers
|
48
|
-
- **Authentication**:
|
48
|
+
- **Authentication**: Secure cookie-based authentication
|
49
49
|
|
50
50
|
## Installation
|
51
51
|
|
@@ -60,7 +60,7 @@ pip install indoxrouter
|
|
60
60
|
```python
|
61
61
|
from indoxrouter import Client
|
62
62
|
|
63
|
-
# Initialize with API key
|
63
|
+
# Initialize with API key
|
64
64
|
client = Client(api_key="your_api_key")
|
65
65
|
|
66
66
|
# Using environment variables
|
@@ -68,6 +68,42 @@ client = Client(api_key="your_api_key")
|
|
68
68
|
import os
|
69
69
|
os.environ["INDOX_ROUTER_API_KEY"] = "your_api_key"
|
70
70
|
client = Client()
|
71
|
+
|
72
|
+
# Connect to a custom server
|
73
|
+
client = Client(
|
74
|
+
api_key="your_api_key",
|
75
|
+
base_url="https://your-indoxrouter-server.com"
|
76
|
+
)
|
77
|
+
```
|
78
|
+
|
79
|
+
### Authentication
|
80
|
+
|
81
|
+
IndoxRouter uses cookie-based authentication with JWT tokens. The client handles this automatically by:
|
82
|
+
|
83
|
+
1. Taking your API key and exchanging it for JWT tokens using the server's authentication endpoints
|
84
|
+
2. Storing the JWT tokens in cookies
|
85
|
+
3. Using the cookies for subsequent requests
|
86
|
+
4. Automatically refreshing tokens when they expire
|
87
|
+
|
88
|
+
```python
|
89
|
+
# Authentication is handled automatically when creating the client
|
90
|
+
client = Client(api_key="your_api_key")
|
91
|
+
```
|
92
|
+
|
93
|
+
### Testing Your API Key
|
94
|
+
|
95
|
+
The package includes a test script to verify your API key and connection:
|
96
|
+
|
97
|
+
```bash
|
98
|
+
# Run the test script with your API key
|
99
|
+
python -m indoxrouter.test_api_key --api-key YOUR_API_KEY
|
100
|
+
|
101
|
+
# Or set the environment variable and run
|
102
|
+
export INDOX_ROUTER_API_KEY=YOUR_API_KEY
|
103
|
+
python -m indoxrouter.test_api_key
|
104
|
+
|
105
|
+
# To see detailed debugging information
|
106
|
+
python -m indoxrouter.test_api_key --debug
|
71
107
|
```
|
72
108
|
|
73
109
|
### Chat Completions
|
@@ -0,0 +1,9 @@
|
|
1
|
+
indoxrouter/__init__.py,sha256=28pdx482uGFF_S1msov0LTTGsFTvVBKRqMkDmoXWUBY,1416
|
2
|
+
indoxrouter/client.py,sha256=Bt8JuU7G_TSX2sVn3Voi6UBXeZnmG-vLCGniAGh_r5U,27387
|
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.11.dist-info/METADATA,sha256=3SEOZwK_ZKPQCfZc7BRKYQ8q3u4meMtmSIbSY4VYZxg,6004
|
7
|
+
indoxrouter-0.1.11.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
8
|
+
indoxrouter-0.1.11.dist-info/top_level.txt,sha256=v6FGWkw0QAnXhyYtnXLI1cxzna0iveNvZUotVzCWabM,12
|
9
|
+
indoxrouter-0.1.11.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
indoxrouter/__init__.py,sha256=28pdx482uGFF_S1msov0LTTGsFTvVBKRqMkDmoXWUBY,1416
|
2
|
-
indoxrouter/client.py,sha256=1XhBiz6CBoN_jNgKmmHvyXecdHZftDckssY2lAir_tA,24044
|
3
|
-
indoxrouter/constants.py,sha256=m5EW5DOxjpvjwj_u2AxMf_5RuPpjpx2Iih35b0E0LEY,1123
|
4
|
-
indoxrouter/exceptions.py,sha256=0ULxtK9va4718PGTO5VoClXYEJeojpiM-7AganeiZZ4,1263
|
5
|
-
indoxrouter-0.1.8.dist-info/METADATA,sha256=HZqWq-PhdEuCfSzROo7L5rQPCvnbSJVur4CKLjMqD98,4971
|
6
|
-
indoxrouter-0.1.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
7
|
-
indoxrouter-0.1.8.dist-info/top_level.txt,sha256=v6FGWkw0QAnXhyYtnXLI1cxzna0iveNvZUotVzCWabM,12
|
8
|
-
indoxrouter-0.1.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|