indoxrouter 0.1.0__py3-none-any.whl → 0.1.3__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-0.1.3.dist-info/METADATA +188 -0
- indoxrouter-0.1.3.dist-info/RECORD +4 -0
- indoxrouter-0.1.3.dist-info/top_level.txt +1 -0
- indoxRouter/__init__.py +0 -0
- indoxRouter/api_endpoints.py +0 -336
- indoxRouter/client.py +0 -286
- indoxRouter/client_package.py +0 -138
- indoxRouter/init_db.py +0 -71
- indoxRouter/main.py +0 -711
- indoxRouter/migrations/__init__.py +0 -1
- indoxRouter/migrations/env.py +0 -98
- indoxRouter/migrations/versions/__init__.py +0 -1
- indoxRouter/migrations/versions/initial_schema.py +0 -84
- indoxRouter/providers/__init__.py +0 -108
- indoxRouter/providers/ai21.py +0 -268
- indoxRouter/providers/base_provider.py +0 -69
- indoxRouter/providers/claude.py +0 -177
- indoxRouter/providers/cohere.py +0 -171
- indoxRouter/providers/databricks.py +0 -166
- indoxRouter/providers/deepseek.py +0 -166
- indoxRouter/providers/google.py +0 -216
- indoxRouter/providers/llama.py +0 -164
- indoxRouter/providers/meta.py +0 -227
- indoxRouter/providers/mistral.py +0 -182
- indoxRouter/providers/nvidia.py +0 -164
- indoxRouter/providers/openai.py +0 -122
- indoxrouter-0.1.0.dist-info/METADATA +0 -179
- indoxrouter-0.1.0.dist-info/RECORD +0 -27
- indoxrouter-0.1.0.dist-info/top_level.txt +0 -1
- {indoxrouter-0.1.0.dist-info → indoxrouter-0.1.3.dist-info}/WHEEL +0 -0
indoxRouter/client.py
DELETED
@@ -1,286 +0,0 @@
|
|
1
|
-
from typing import Dict, Optional, Any, List
|
2
|
-
import os
|
3
|
-
import sys
|
4
|
-
import json
|
5
|
-
import requests
|
6
|
-
|
7
|
-
# Add the parent directory to the path to make imports work correctly
|
8
|
-
current_dir = os.path.dirname(os.path.abspath(__file__))
|
9
|
-
parent_dir = os.path.dirname(current_dir)
|
10
|
-
sys.path.append(parent_dir)
|
11
|
-
|
12
|
-
# Use absolute imports
|
13
|
-
from indoxRouter.utils.auth import AuthManager
|
14
|
-
from indoxRouter.providers.base_provider import BaseProvider
|
15
|
-
|
16
|
-
|
17
|
-
class Client:
|
18
|
-
"""
|
19
|
-
Client for making API requests to the IndoxRouter API.
|
20
|
-
"""
|
21
|
-
|
22
|
-
def __init__(self, api_key: str, base_url: str = None):
|
23
|
-
"""
|
24
|
-
Initialize the client.
|
25
|
-
|
26
|
-
Args:
|
27
|
-
api_key: API key for authentication
|
28
|
-
base_url: Base URL for the API (default: http://localhost:8000)
|
29
|
-
"""
|
30
|
-
self.api_key = api_key
|
31
|
-
self.base_url = base_url or "http://localhost:8000"
|
32
|
-
self.auth_manager = AuthManager()
|
33
|
-
|
34
|
-
# Verify the API key
|
35
|
-
self.user_data = self.auth_manager.verify_api_key(api_key)
|
36
|
-
if not self.user_data:
|
37
|
-
raise ValueError("Invalid API key")
|
38
|
-
|
39
|
-
def generate(
|
40
|
-
self,
|
41
|
-
provider: str,
|
42
|
-
model: str,
|
43
|
-
prompt: str,
|
44
|
-
temperature: float = 0.7,
|
45
|
-
max_tokens: int = 1000,
|
46
|
-
**kwargs,
|
47
|
-
) -> str:
|
48
|
-
"""
|
49
|
-
Generate a response from a model.
|
50
|
-
|
51
|
-
Args:
|
52
|
-
provider: Provider name
|
53
|
-
model: Model name
|
54
|
-
prompt: Prompt
|
55
|
-
temperature: Temperature
|
56
|
-
max_tokens: Maximum tokens
|
57
|
-
**kwargs: Additional parameters
|
58
|
-
|
59
|
-
Returns:
|
60
|
-
Model response
|
61
|
-
"""
|
62
|
-
url = f"{self.base_url}/v1/completions"
|
63
|
-
|
64
|
-
headers = {
|
65
|
-
"Content-Type": "application/json",
|
66
|
-
"Authorization": f"Bearer {self.api_key}",
|
67
|
-
}
|
68
|
-
|
69
|
-
data = {
|
70
|
-
"provider": provider,
|
71
|
-
"model": model,
|
72
|
-
"prompt": prompt,
|
73
|
-
"temperature": temperature,
|
74
|
-
"max_tokens": max_tokens,
|
75
|
-
**kwargs,
|
76
|
-
}
|
77
|
-
|
78
|
-
response = requests.post(url, headers=headers, json=data)
|
79
|
-
|
80
|
-
if response.status_code != 200:
|
81
|
-
error_message = (
|
82
|
-
response.json().get("error", {}).get("message", "Unknown error")
|
83
|
-
)
|
84
|
-
raise Exception(f"Error: {error_message}")
|
85
|
-
|
86
|
-
return response.json().get("choices", [{}])[0].get("text", "")
|
87
|
-
|
88
|
-
def list_models(self, provider: Optional[str] = None) -> List[Dict[str, Any]]:
|
89
|
-
"""
|
90
|
-
List available models.
|
91
|
-
|
92
|
-
Args:
|
93
|
-
provider: Provider name (optional)
|
94
|
-
|
95
|
-
Returns:
|
96
|
-
List of models
|
97
|
-
"""
|
98
|
-
url = f"{self.base_url}/v1/models"
|
99
|
-
|
100
|
-
if provider:
|
101
|
-
url += f"?provider={provider}"
|
102
|
-
|
103
|
-
headers = {
|
104
|
-
"Authorization": f"Bearer {self.api_key}",
|
105
|
-
}
|
106
|
-
|
107
|
-
response = requests.get(url, headers=headers)
|
108
|
-
|
109
|
-
if response.status_code != 200:
|
110
|
-
error_message = (
|
111
|
-
response.json().get("error", {}).get("message", "Unknown error")
|
112
|
-
)
|
113
|
-
raise Exception(f"Error: {error_message}")
|
114
|
-
|
115
|
-
return response.json().get("data", [])
|
116
|
-
|
117
|
-
def list_providers(self) -> List[str]:
|
118
|
-
"""
|
119
|
-
List available providers.
|
120
|
-
|
121
|
-
Returns:
|
122
|
-
List of providers
|
123
|
-
"""
|
124
|
-
url = f"{self.base_url}/v1/providers"
|
125
|
-
|
126
|
-
headers = {
|
127
|
-
"Authorization": f"Bearer {self.api_key}",
|
128
|
-
}
|
129
|
-
|
130
|
-
response = requests.get(url, headers=headers)
|
131
|
-
|
132
|
-
if response.status_code != 200:
|
133
|
-
error_message = (
|
134
|
-
response.json().get("error", {}).get("message", "Unknown error")
|
135
|
-
)
|
136
|
-
raise Exception(f"Error: {error_message}")
|
137
|
-
|
138
|
-
return response.json().get("data", [])
|
139
|
-
|
140
|
-
def _parse_model_name(self, model_name: str) -> tuple:
|
141
|
-
"""
|
142
|
-
Parse model name into provider and model parts
|
143
|
-
|
144
|
-
Args:
|
145
|
-
model_name: Full model name (e.g., 'openai/gpt-4')
|
146
|
-
|
147
|
-
Returns:
|
148
|
-
Tuple of (provider_name, model_part)
|
149
|
-
"""
|
150
|
-
if "/" not in model_name:
|
151
|
-
raise ValueError(
|
152
|
-
f"Invalid model name format: {model_name}. Expected format: 'provider/model'"
|
153
|
-
)
|
154
|
-
|
155
|
-
provider_name, model_part = model_name.split("/", 1)
|
156
|
-
return provider_name, model_part
|
157
|
-
|
158
|
-
def _load_provider_class(self, provider_name: str):
|
159
|
-
"""
|
160
|
-
Dynamically load provider class
|
161
|
-
|
162
|
-
Args:
|
163
|
-
provider_name: Name of the provider
|
164
|
-
|
165
|
-
Returns:
|
166
|
-
Provider class
|
167
|
-
"""
|
168
|
-
try:
|
169
|
-
# Import the provider module dynamically
|
170
|
-
module_path = f".providers.{provider_name}"
|
171
|
-
provider_module = __import__(
|
172
|
-
module_path, fromlist=["Provider"], globals=globals()
|
173
|
-
)
|
174
|
-
return provider_module.Provider
|
175
|
-
except (ImportError, AttributeError) as e:
|
176
|
-
raise ValueError(f"Provider not supported: {provider_name}") from e
|
177
|
-
|
178
|
-
def _get_provider(self, model_name: str) -> BaseProvider:
|
179
|
-
"""
|
180
|
-
Get provider instance with cached credentials
|
181
|
-
|
182
|
-
Args:
|
183
|
-
model_name: Full model name (e.g., 'openai/gpt-4')
|
184
|
-
|
185
|
-
Returns:
|
186
|
-
Provider instance
|
187
|
-
"""
|
188
|
-
if model_name in self.provider_cache:
|
189
|
-
return self.provider_cache[model_name]
|
190
|
-
|
191
|
-
provider_name, model_part = self._parse_model_name(model_name)
|
192
|
-
provider_class = self._load_provider_class(provider_name)
|
193
|
-
|
194
|
-
# Get provider API key from secure storage
|
195
|
-
provider_api_key = self._get_provider_credentials(provider_name)
|
196
|
-
|
197
|
-
instance = provider_class(api_key=provider_api_key, model_name=model_part)
|
198
|
-
self.provider_cache[model_name] = instance
|
199
|
-
return instance
|
200
|
-
|
201
|
-
def _get_provider_credentials(self, provider_name: str) -> str:
|
202
|
-
"""
|
203
|
-
Retrieve provider API key from secure storage
|
204
|
-
|
205
|
-
Args:
|
206
|
-
provider_name: Name of the provider
|
207
|
-
|
208
|
-
Returns:
|
209
|
-
Provider API key
|
210
|
-
"""
|
211
|
-
# Implement your secure credential storage (e.g., AWS Secrets Manager)
|
212
|
-
# Example using environment variables:
|
213
|
-
env_var = f"{provider_name.upper()}_API_KEY"
|
214
|
-
if env_var not in os.environ:
|
215
|
-
raise ValueError(
|
216
|
-
f"Missing API key for provider: {provider_name}. Set {env_var} environment variable."
|
217
|
-
)
|
218
|
-
|
219
|
-
return os.environ[env_var]
|
220
|
-
|
221
|
-
def generate(self, model_name: str, prompt: str, **kwargs) -> Dict[str, Any]:
|
222
|
-
"""
|
223
|
-
Generate completion with credit handling
|
224
|
-
|
225
|
-
Args:
|
226
|
-
model_name: Provider/model name (e.g., 'openai/gpt-4')
|
227
|
-
prompt: User input prompt
|
228
|
-
**kwargs: Generation parameters
|
229
|
-
|
230
|
-
Returns:
|
231
|
-
Dictionary with response and credit information
|
232
|
-
"""
|
233
|
-
provider = self._get_provider(model_name)
|
234
|
-
|
235
|
-
# Estimate max possible cost
|
236
|
-
max_tokens = kwargs.get("max_tokens", 2048)
|
237
|
-
estimated_cost = provider.estimate_cost(prompt, max_tokens)
|
238
|
-
|
239
|
-
# Check balance
|
240
|
-
if self.user_data["balance"] < estimated_cost:
|
241
|
-
raise ValueError(
|
242
|
-
f"Insufficient credits. Required: {estimated_cost:.6f}, Available: {self.user_data['balance']:.6f}"
|
243
|
-
)
|
244
|
-
|
245
|
-
# Make API call
|
246
|
-
response = provider.generate(prompt, **kwargs)
|
247
|
-
|
248
|
-
# Deduct actual cost
|
249
|
-
success = self.auth_manager.deduct_credits(
|
250
|
-
self.user_data["id"], response["cost"]
|
251
|
-
)
|
252
|
-
|
253
|
-
if not success:
|
254
|
-
raise RuntimeError("Credit deduction failed")
|
255
|
-
|
256
|
-
# Get updated user data
|
257
|
-
self.user_data = self.auth_manager.get_user_by_id(self.user_data["id"])
|
258
|
-
|
259
|
-
return {
|
260
|
-
"text": response["text"],
|
261
|
-
"cost": response["cost"],
|
262
|
-
"remaining_credits": self.user_data["balance"],
|
263
|
-
"model": model_name,
|
264
|
-
}
|
265
|
-
|
266
|
-
def get_balance(self) -> float:
|
267
|
-
"""
|
268
|
-
Get current user balance
|
269
|
-
|
270
|
-
Returns:
|
271
|
-
Current credit balance
|
272
|
-
"""
|
273
|
-
# Refresh user data to get the latest balance
|
274
|
-
self.user_data = self.auth_manager.get_user_by_id(self.user_data["id"])
|
275
|
-
return self.user_data["balance"]
|
276
|
-
|
277
|
-
def get_user_info(self) -> Dict[str, Any]:
|
278
|
-
"""
|
279
|
-
Get current user information
|
280
|
-
|
281
|
-
Returns:
|
282
|
-
User information dictionary
|
283
|
-
"""
|
284
|
-
# Refresh user data
|
285
|
-
self.user_data = self.auth_manager.get_user_by_id(self.user_data["id"])
|
286
|
-
return self.user_data
|
indoxRouter/client_package.py
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
IndoxRouter Client - A Python client for the IndoxRouter API
|
3
|
-
"""
|
4
|
-
|
5
|
-
import os
|
6
|
-
import requests
|
7
|
-
from typing import Dict, List, Optional, Any, Union
|
8
|
-
|
9
|
-
|
10
|
-
class IndoxRouterClient:
|
11
|
-
"""
|
12
|
-
Client for making API requests to the IndoxRouter API.
|
13
|
-
|
14
|
-
This client allows users to interact with the IndoxRouter API
|
15
|
-
to access multiple LLM providers through a unified interface.
|
16
|
-
"""
|
17
|
-
|
18
|
-
def __init__(self, api_key: str, base_url: str = None):
|
19
|
-
"""
|
20
|
-
Initialize the IndoxRouter client.
|
21
|
-
|
22
|
-
Args:
|
23
|
-
api_key: API key for authentication (generated from the IndoxRouter website)
|
24
|
-
base_url: Base URL for the API (default: https://api.indoxrouter.com)
|
25
|
-
"""
|
26
|
-
self.api_key = api_key
|
27
|
-
self.base_url = base_url or "https://api.indoxrouter.com"
|
28
|
-
self.session = requests.Session()
|
29
|
-
self.session.headers.update(
|
30
|
-
{"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
31
|
-
)
|
32
|
-
|
33
|
-
# Verify the API key by making a test request
|
34
|
-
self._verify_api_key()
|
35
|
-
|
36
|
-
def _verify_api_key(self):
|
37
|
-
"""Verify that the API key is valid by making a test request."""
|
38
|
-
try:
|
39
|
-
response = self.session.get(f"{self.base_url}/api/v1/user")
|
40
|
-
response.raise_for_status()
|
41
|
-
self.user_data = response.json()
|
42
|
-
except requests.exceptions.RequestException as e:
|
43
|
-
raise ValueError(f"Invalid API key or connection error: {str(e)}")
|
44
|
-
|
45
|
-
def generate(
|
46
|
-
self,
|
47
|
-
prompt: str,
|
48
|
-
model: str = None,
|
49
|
-
provider: str = None,
|
50
|
-
temperature: float = 0.7,
|
51
|
-
max_tokens: int = 1000,
|
52
|
-
**kwargs,
|
53
|
-
) -> Dict[str, Any]:
|
54
|
-
"""
|
55
|
-
Generate a response from a model.
|
56
|
-
|
57
|
-
Args:
|
58
|
-
prompt: The prompt to send to the model
|
59
|
-
model: The model to use (e.g., "gpt-4", "claude-3-opus")
|
60
|
-
provider: The provider to use (e.g., "openai", "anthropic")
|
61
|
-
temperature: Controls randomness (0-1)
|
62
|
-
max_tokens: Maximum number of tokens to generate
|
63
|
-
**kwargs: Additional parameters to pass to the API
|
64
|
-
|
65
|
-
Returns:
|
66
|
-
A dictionary containing the response from the API
|
67
|
-
"""
|
68
|
-
if not model and not provider:
|
69
|
-
# Use the default model if none specified
|
70
|
-
model = "gpt-4o-mini"
|
71
|
-
provider = "openai"
|
72
|
-
|
73
|
-
payload = {
|
74
|
-
"prompt": prompt,
|
75
|
-
"temperature": temperature,
|
76
|
-
"max_tokens": max_tokens,
|
77
|
-
**kwargs,
|
78
|
-
}
|
79
|
-
|
80
|
-
if model:
|
81
|
-
payload["model"] = model
|
82
|
-
if provider:
|
83
|
-
payload["provider"] = provider
|
84
|
-
|
85
|
-
response = self.session.post(f"{self.base_url}/api/v1/generate", json=payload)
|
86
|
-
response.raise_for_status()
|
87
|
-
return response.json()
|
88
|
-
|
89
|
-
def list_models(self, provider: Optional[str] = None) -> List[Dict[str, Any]]:
|
90
|
-
"""
|
91
|
-
List available models.
|
92
|
-
|
93
|
-
Args:
|
94
|
-
provider: Filter models by provider
|
95
|
-
|
96
|
-
Returns:
|
97
|
-
A list of available models
|
98
|
-
"""
|
99
|
-
url = f"{self.base_url}/api/v1/models"
|
100
|
-
if provider:
|
101
|
-
url += f"?provider={provider}"
|
102
|
-
|
103
|
-
response = self.session.get(url)
|
104
|
-
response.raise_for_status()
|
105
|
-
return response.json()
|
106
|
-
|
107
|
-
def list_providers(self) -> List[Dict[str, Any]]:
|
108
|
-
"""
|
109
|
-
List available providers.
|
110
|
-
|
111
|
-
Returns:
|
112
|
-
A list of available providers
|
113
|
-
"""
|
114
|
-
response = self.session.get(f"{self.base_url}/api/v1/providers")
|
115
|
-
response.raise_for_status()
|
116
|
-
return response.json()
|
117
|
-
|
118
|
-
def get_balance(self) -> Dict[str, Any]:
|
119
|
-
"""
|
120
|
-
Get the user's current balance.
|
121
|
-
|
122
|
-
Returns:
|
123
|
-
A dictionary containing the user's balance information
|
124
|
-
"""
|
125
|
-
response = self.session.get(f"{self.base_url}/api/v1/user/balance")
|
126
|
-
response.raise_for_status()
|
127
|
-
return response.json()
|
128
|
-
|
129
|
-
def get_user_info(self) -> Dict[str, Any]:
|
130
|
-
"""
|
131
|
-
Get information about the authenticated user.
|
132
|
-
|
133
|
-
Returns:
|
134
|
-
A dictionary containing user information
|
135
|
-
"""
|
136
|
-
response = self.session.get(f"{self.base_url}/api/v1/user")
|
137
|
-
response.raise_for_status()
|
138
|
-
return response.json()
|
indoxRouter/init_db.py
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
Initialize the database for IndoxRouter.
|
4
|
-
|
5
|
-
This script creates the database tables and an admin user.
|
6
|
-
"""
|
7
|
-
|
8
|
-
import os
|
9
|
-
import sys
|
10
|
-
import logging
|
11
|
-
import argparse
|
12
|
-
from pathlib import Path
|
13
|
-
|
14
|
-
# Add the parent directory to sys.path
|
15
|
-
sys.path.append(str(Path(__file__).parent.parent))
|
16
|
-
|
17
|
-
# Configure logging
|
18
|
-
logging.basicConfig(
|
19
|
-
level=logging.INFO,
|
20
|
-
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
21
|
-
handlers=[
|
22
|
-
logging.StreamHandler(),
|
23
|
-
logging.FileHandler(os.path.join("logs", "init_db.log"), mode="a"),
|
24
|
-
],
|
25
|
-
)
|
26
|
-
logger = logging.getLogger(__name__)
|
27
|
-
|
28
|
-
|
29
|
-
def main():
|
30
|
-
"""Initialize the database."""
|
31
|
-
parser = argparse.ArgumentParser(description="Initialize the IndoxRouter database")
|
32
|
-
parser.add_argument(
|
33
|
-
"--force", action="store_true", help="Force initialization even if tables exist"
|
34
|
-
)
|
35
|
-
parser.add_argument("--admin-email", help="Admin user email")
|
36
|
-
parser.add_argument("--admin-password", help="Admin user password")
|
37
|
-
args = parser.parse_args()
|
38
|
-
|
39
|
-
try:
|
40
|
-
# Ensure logs directory exists
|
41
|
-
os.makedirs("logs", exist_ok=True)
|
42
|
-
|
43
|
-
# Import here to avoid circular imports
|
44
|
-
from indoxRouter.utils.migrations import create_tables, create_admin_user
|
45
|
-
|
46
|
-
# Create the tables
|
47
|
-
logger.info("Creating database tables...")
|
48
|
-
create_tables(force=args.force)
|
49
|
-
|
50
|
-
# Create the admin user
|
51
|
-
admin_email = args.admin_email or os.environ.get(
|
52
|
-
"ADMIN_EMAIL", "admin@example.com"
|
53
|
-
)
|
54
|
-
admin_password = args.admin_password or os.environ.get(
|
55
|
-
"ADMIN_PASSWORD", "admin"
|
56
|
-
)
|
57
|
-
|
58
|
-
logger.info(f"Creating admin user with email: {admin_email}")
|
59
|
-
create_admin_user(email=admin_email, password=admin_password)
|
60
|
-
|
61
|
-
logger.info("Database initialization complete.")
|
62
|
-
logger.info("You can now log in with the following credentials:")
|
63
|
-
logger.info(f"Email: {admin_email}")
|
64
|
-
logger.info(f"Password: {admin_password}")
|
65
|
-
except Exception as e:
|
66
|
-
logger.error(f"Error initializing database: {e}", exc_info=True)
|
67
|
-
sys.exit(1)
|
68
|
-
|
69
|
-
|
70
|
-
if __name__ == "__main__":
|
71
|
-
main()
|