indoxrouter 0.1.0__py3-none-any.whl → 0.1.2__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.
Files changed (53) hide show
  1. indoxRouter/__init__.py +83 -0
  2. indoxRouter/client.py +564 -218
  3. indoxRouter/client_resourses/__init__.py +20 -0
  4. indoxRouter/client_resourses/base.py +67 -0
  5. indoxRouter/client_resourses/chat.py +144 -0
  6. indoxRouter/client_resourses/completion.py +138 -0
  7. indoxRouter/client_resourses/embedding.py +83 -0
  8. indoxRouter/client_resourses/image.py +116 -0
  9. indoxRouter/client_resourses/models.py +114 -0
  10. indoxRouter/config.py +151 -0
  11. indoxRouter/constants/__init__.py +81 -0
  12. indoxRouter/exceptions/__init__.py +70 -0
  13. indoxRouter/models/__init__.py +111 -0
  14. indoxRouter/providers/__init__.py +50 -50
  15. indoxRouter/providers/ai21labs.json +128 -0
  16. indoxRouter/providers/base_provider.py +62 -30
  17. indoxRouter/providers/claude.json +164 -0
  18. indoxRouter/providers/cohere.json +116 -0
  19. indoxRouter/providers/databricks.json +110 -0
  20. indoxRouter/providers/deepseek.json +110 -0
  21. indoxRouter/providers/google.json +128 -0
  22. indoxRouter/providers/meta.json +128 -0
  23. indoxRouter/providers/mistral.json +146 -0
  24. indoxRouter/providers/nvidia.json +110 -0
  25. indoxRouter/providers/openai.json +308 -0
  26. indoxRouter/providers/openai.py +471 -72
  27. indoxRouter/providers/qwen.json +110 -0
  28. indoxRouter/utils/__init__.py +240 -0
  29. indoxrouter-0.1.2.dist-info/LICENSE +21 -0
  30. indoxrouter-0.1.2.dist-info/METADATA +259 -0
  31. indoxrouter-0.1.2.dist-info/RECORD +33 -0
  32. indoxRouter/api_endpoints.py +0 -336
  33. indoxRouter/client_package.py +0 -138
  34. indoxRouter/init_db.py +0 -71
  35. indoxRouter/main.py +0 -711
  36. indoxRouter/migrations/__init__.py +0 -1
  37. indoxRouter/migrations/env.py +0 -98
  38. indoxRouter/migrations/versions/__init__.py +0 -1
  39. indoxRouter/migrations/versions/initial_schema.py +0 -84
  40. indoxRouter/providers/ai21.py +0 -268
  41. indoxRouter/providers/claude.py +0 -177
  42. indoxRouter/providers/cohere.py +0 -171
  43. indoxRouter/providers/databricks.py +0 -166
  44. indoxRouter/providers/deepseek.py +0 -166
  45. indoxRouter/providers/google.py +0 -216
  46. indoxRouter/providers/llama.py +0 -164
  47. indoxRouter/providers/meta.py +0 -227
  48. indoxRouter/providers/mistral.py +0 -182
  49. indoxRouter/providers/nvidia.py +0 -164
  50. indoxrouter-0.1.0.dist-info/METADATA +0 -179
  51. indoxrouter-0.1.0.dist-info/RECORD +0 -27
  52. {indoxrouter-0.1.0.dist-info → indoxrouter-0.1.2.dist-info}/WHEEL +0 -0
  53. {indoxrouter-0.1.0.dist-info → indoxrouter-0.1.2.dist-info}/top_level.txt +0 -0
@@ -1,336 +0,0 @@
1
- """
2
- API endpoints for the IndoxRouter client package.
3
- """
4
-
5
- import logging
6
- from typing import Dict, Any, Optional, List
7
- from datetime import datetime
8
-
9
- from fastapi import APIRouter, Depends, HTTPException, Request
10
- from pydantic import BaseModel, Field
11
-
12
- from .utils.database import execute_query
13
- from .utils.auth import get_current_user
14
- from .models.database import User, RequestLog, Credit
15
- from .utils.config import get_config
16
- from .providers import get_provider
17
-
18
- # Configure logging
19
- logger = logging.getLogger(__name__)
20
-
21
- # Create API router
22
- router = APIRouter(prefix="/api/v1", tags=["Client API"])
23
-
24
-
25
- # Model definitions
26
- class GenerateRequest(BaseModel):
27
- """Request model for text generation."""
28
-
29
- prompt: str = Field(..., description="The prompt to send to the model")
30
- model: Optional[str] = Field(None, description="The model to use")
31
- provider: Optional[str] = Field(None, description="The provider to use")
32
- temperature: Optional[float] = Field(0.7, description="Temperature for sampling")
33
- max_tokens: Optional[int] = Field(
34
- 1000, description="Maximum number of tokens to generate"
35
- )
36
- top_p: Optional[float] = Field(1.0, description="Top-p sampling parameter")
37
- stop: Optional[List[str]] = Field(
38
- None, description="Sequences where the API will stop generating"
39
- )
40
- stream: Optional[bool] = Field(False, description="Whether to stream the response")
41
-
42
-
43
- class GenerateResponse(BaseModel):
44
- """Response model for text generation."""
45
-
46
- id: str = Field(..., description="Unique identifier for the completion")
47
- text: str = Field(..., description="The generated text")
48
- provider: str = Field(..., description="The provider used")
49
- model: str = Field(..., description="The model used")
50
- usage: Dict[str, int] = Field(..., description="Token usage information")
51
- created_at: datetime = Field(..., description="Timestamp of creation")
52
-
53
-
54
- class ModelInfo(BaseModel):
55
- """Model for LLM model information."""
56
-
57
- id: str = Field(..., description="Model identifier")
58
- name: str = Field(..., description="Model name")
59
- provider: str = Field(..., description="Provider name")
60
- description: Optional[str] = Field(None, description="Model description")
61
- max_tokens: Optional[int] = Field(None, description="Maximum tokens supported")
62
- pricing: Optional[Dict[str, float]] = Field(None, description="Pricing information")
63
-
64
-
65
- class ProviderInfo(BaseModel):
66
- """Model for LLM provider information."""
67
-
68
- id: str = Field(..., description="Provider identifier")
69
- name: str = Field(..., description="Provider name")
70
- description: Optional[str] = Field(None, description="Provider description")
71
- website: Optional[str] = Field(None, description="Provider website")
72
- models: List[str] = Field(..., description="Available models")
73
-
74
-
75
- class UserInfo(BaseModel):
76
- """Model for user information."""
77
-
78
- id: int = Field(..., description="User ID")
79
- email: str = Field(..., description="User email")
80
- first_name: Optional[str] = Field(None, description="User first name")
81
- last_name: Optional[str] = Field(None, description="User last name")
82
- is_admin: bool = Field(False, description="Whether the user is an admin")
83
- created_at: datetime = Field(..., description="Account creation timestamp")
84
-
85
-
86
- class BalanceInfo(BaseModel):
87
- """Model for user balance information."""
88
-
89
- credits: float = Field(..., description="Available credits")
90
- usage: Dict[str, float] = Field(..., description="Usage statistics")
91
- last_updated: datetime = Field(..., description="Last updated timestamp")
92
-
93
-
94
- # API endpoints
95
- @router.post("/generate", response_model=GenerateResponse)
96
- async def generate(
97
- request: GenerateRequest,
98
- user_data: Dict[str, Any] = Depends(get_current_user),
99
- req: Request = None,
100
- ):
101
- """Generate text using the specified model and provider."""
102
- try:
103
- # Set default model and provider if not specified
104
- provider_name = request.provider or get_config().get(
105
- "default_provider", "openai"
106
- )
107
- model_name = request.model or get_config().get("default_model", "gpt-4o-mini")
108
-
109
- # Get the provider
110
- provider = get_provider(provider_name)
111
- if not provider:
112
- raise HTTPException(
113
- status_code=400, detail=f"Provider '{provider_name}' not found"
114
- )
115
-
116
- # Generate the completion
117
- completion = await provider.generate(
118
- model=model_name,
119
- prompt=request.prompt,
120
- max_tokens=request.max_tokens,
121
- temperature=request.temperature,
122
- top_p=request.top_p,
123
- stop=request.stop,
124
- stream=request.stream,
125
- )
126
-
127
- # Log the request
128
- log_entry = RequestLog(
129
- user_id=user_data["user_id"],
130
- provider=provider_name,
131
- model=model_name,
132
- prompt_tokens=completion.get("usage", {}).get("prompt_tokens", 0),
133
- completion_tokens=completion.get("usage", {}).get("completion_tokens", 0),
134
- total_tokens=completion.get("usage", {}).get("total_tokens", 0),
135
- created_at=datetime.utcnow(),
136
- )
137
- execute_query(lambda session: session.add(log_entry))
138
-
139
- # Return the response
140
- return {
141
- "id": completion.get("id", ""),
142
- "text": completion.get("text", ""),
143
- "provider": provider_name,
144
- "model": model_name,
145
- "usage": completion.get("usage", {}),
146
- "created_at": datetime.utcnow(),
147
- }
148
- except Exception as e:
149
- logger.error(f"Error generating completion: {str(e)}")
150
- raise HTTPException(status_code=500, detail=str(e))
151
-
152
-
153
- @router.get("/models", response_model=List[ModelInfo])
154
- async def list_models(
155
- provider: Optional[str] = None,
156
- user_data: Dict[str, Any] = Depends(get_current_user),
157
- ):
158
- """List available models, optionally filtered by provider."""
159
- try:
160
- # Get configuration
161
- config = get_config()
162
- models = []
163
-
164
- # If provider is specified, only get models for that provider
165
- if provider:
166
- provider_obj = get_provider(provider)
167
- if not provider_obj:
168
- raise HTTPException(
169
- status_code=400, detail=f"Provider '{provider}' not found"
170
- )
171
- provider_models = provider_obj.list_models()
172
- for model in provider_models:
173
- models.append(
174
- {
175
- "id": model.get("id", ""),
176
- "name": model.get("name", ""),
177
- "provider": provider,
178
- "description": model.get("description", ""),
179
- "max_tokens": model.get("max_tokens", 0),
180
- "pricing": model.get("pricing", {}),
181
- }
182
- )
183
- else:
184
- # Get models for all providers
185
- for provider_name in config.get("providers", {}).keys():
186
- try:
187
- provider_obj = get_provider(provider_name)
188
- if provider_obj:
189
- provider_models = provider_obj.list_models()
190
- for model in provider_models:
191
- models.append(
192
- {
193
- "id": model.get("id", ""),
194
- "name": model.get("name", ""),
195
- "provider": provider_name,
196
- "description": model.get("description", ""),
197
- "max_tokens": model.get("max_tokens", 0),
198
- "pricing": model.get("pricing", {}),
199
- }
200
- )
201
- except Exception as e:
202
- logger.error(
203
- f"Error getting models for provider {provider_name}: {str(e)}"
204
- )
205
-
206
- return models
207
- except Exception as e:
208
- logger.error(f"Error listing models: {str(e)}")
209
- raise HTTPException(status_code=500, detail=str(e))
210
-
211
-
212
- @router.get("/providers", response_model=List[ProviderInfo])
213
- async def list_providers(
214
- user_data: Dict[str, Any] = Depends(get_current_user),
215
- ):
216
- """List available providers."""
217
- try:
218
- # Get configuration
219
- config = get_config()
220
- providers = []
221
-
222
- # Get all providers
223
- for provider_name, provider_config in config.get("providers", {}).items():
224
- try:
225
- provider_obj = get_provider(provider_name)
226
- if provider_obj:
227
- provider_models = provider_obj.list_models()
228
- providers.append(
229
- {
230
- "id": provider_name,
231
- "name": provider_config.get("name", provider_name),
232
- "description": provider_config.get("description", ""),
233
- "website": provider_config.get("website", ""),
234
- "models": [
235
- model.get("id", "") for model in provider_models
236
- ],
237
- }
238
- )
239
- except Exception as e:
240
- logger.error(f"Error getting provider {provider_name}: {str(e)}")
241
-
242
- return providers
243
- except Exception as e:
244
- logger.error(f"Error listing providers: {str(e)}")
245
- raise HTTPException(status_code=500, detail=str(e))
246
-
247
-
248
- @router.get("/user", response_model=UserInfo)
249
- async def get_user(
250
- user_data: Dict[str, Any] = Depends(get_current_user),
251
- ):
252
- """Get information about the authenticated user."""
253
- try:
254
- # Get user from database
255
- user = None
256
-
257
- def get_user_from_db(session):
258
- nonlocal user
259
- user = session.query(User).filter(User.id == user_data["user_id"]).first()
260
- return user
261
-
262
- execute_query(get_user_from_db)
263
-
264
- if not user:
265
- raise HTTPException(status_code=404, detail="User not found")
266
-
267
- return {
268
- "id": user.id,
269
- "email": user.email,
270
- "first_name": user.first_name,
271
- "last_name": user.last_name,
272
- "is_admin": user.is_admin,
273
- "created_at": user.created_at,
274
- }
275
- except Exception as e:
276
- logger.error(f"Error getting user: {str(e)}")
277
- raise HTTPException(status_code=500, detail=str(e))
278
-
279
-
280
- @router.get("/user/balance", response_model=BalanceInfo)
281
- async def get_balance(
282
- user_data: Dict[str, Any] = Depends(get_current_user),
283
- ):
284
- """Get the user's current balance."""
285
- try:
286
- # Get user's credit from database
287
- credit = None
288
- usage_data = {}
289
-
290
- def get_credit_from_db(session):
291
- nonlocal credit
292
- credit = (
293
- session.query(Credit)
294
- .filter(Credit.user_id == user_data["user_id"])
295
- .first()
296
- )
297
- return credit
298
-
299
- def get_usage_from_db(session):
300
- nonlocal usage_data
301
- # Get total usage by provider
302
- usage_by_provider = {}
303
- logs = (
304
- session.query(RequestLog)
305
- .filter(RequestLog.user_id == user_data["user_id"])
306
- .all()
307
- )
308
- for log in logs:
309
- provider = log.provider
310
- if provider not in usage_by_provider:
311
- usage_by_provider[provider] = 0
312
- usage_by_provider[provider] += log.total_tokens
313
-
314
- usage_data = usage_by_provider
315
- return usage_by_provider
316
-
317
- execute_query(get_credit_from_db)
318
- execute_query(get_usage_from_db)
319
-
320
- if not credit:
321
- # Create a new credit entry with default values
322
- credit = Credit(
323
- user_id=user_data["user_id"],
324
- amount=0.0,
325
- last_updated=datetime.utcnow(),
326
- )
327
- execute_query(lambda session: session.add(credit))
328
-
329
- return {
330
- "credits": credit.amount,
331
- "usage": usage_data,
332
- "last_updated": credit.last_updated,
333
- }
334
- except Exception as e:
335
- logger.error(f"Error getting balance: {str(e)}")
336
- raise HTTPException(status_code=500, detail=str(e))
@@ -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()