indoxrouter 0.1.0__tar.gz

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 (32) hide show
  1. indoxrouter-0.1.0/PKG-INFO +179 -0
  2. indoxrouter-0.1.0/README.md +155 -0
  3. indoxrouter-0.1.0/indoxRouter/__init__.py +0 -0
  4. indoxrouter-0.1.0/indoxRouter/api_endpoints.py +336 -0
  5. indoxrouter-0.1.0/indoxRouter/client.py +286 -0
  6. indoxrouter-0.1.0/indoxRouter/client_package.py +138 -0
  7. indoxrouter-0.1.0/indoxRouter/init_db.py +71 -0
  8. indoxrouter-0.1.0/indoxRouter/main.py +711 -0
  9. indoxrouter-0.1.0/indoxRouter/migrations/__init__.py +1 -0
  10. indoxrouter-0.1.0/indoxRouter/migrations/env.py +98 -0
  11. indoxrouter-0.1.0/indoxRouter/migrations/versions/__init__.py +1 -0
  12. indoxrouter-0.1.0/indoxRouter/migrations/versions/initial_schema.py +84 -0
  13. indoxrouter-0.1.0/indoxRouter/providers/__init__.py +108 -0
  14. indoxrouter-0.1.0/indoxRouter/providers/ai21.py +268 -0
  15. indoxrouter-0.1.0/indoxRouter/providers/base_provider.py +69 -0
  16. indoxrouter-0.1.0/indoxRouter/providers/claude.py +177 -0
  17. indoxrouter-0.1.0/indoxRouter/providers/cohere.py +171 -0
  18. indoxrouter-0.1.0/indoxRouter/providers/databricks.py +166 -0
  19. indoxrouter-0.1.0/indoxRouter/providers/deepseek.py +166 -0
  20. indoxrouter-0.1.0/indoxRouter/providers/google.py +216 -0
  21. indoxrouter-0.1.0/indoxRouter/providers/llama.py +164 -0
  22. indoxrouter-0.1.0/indoxRouter/providers/meta.py +227 -0
  23. indoxrouter-0.1.0/indoxRouter/providers/mistral.py +182 -0
  24. indoxrouter-0.1.0/indoxRouter/providers/nvidia.py +164 -0
  25. indoxrouter-0.1.0/indoxRouter/providers/openai.py +122 -0
  26. indoxrouter-0.1.0/indoxrouter.egg-info/PKG-INFO +179 -0
  27. indoxrouter-0.1.0/indoxrouter.egg-info/SOURCES.txt +30 -0
  28. indoxrouter-0.1.0/indoxrouter.egg-info/dependency_links.txt +1 -0
  29. indoxrouter-0.1.0/indoxrouter.egg-info/requires.txt +2 -0
  30. indoxrouter-0.1.0/indoxrouter.egg-info/top_level.txt +1 -0
  31. indoxrouter-0.1.0/setup.cfg +4 -0
  32. indoxrouter-0.1.0/setup.py +26 -0
@@ -0,0 +1,179 @@
1
+ Metadata-Version: 2.2
2
+ Name: indoxrouter
3
+ Version: 0.1.0
4
+ Summary: Client library for IndoxRouter - A unified API for multiple LLM providers
5
+ Home-page: https://github.com/yourusername/indoxRouter
6
+ Author: Ashkan Eskandari
7
+ Author-email: ashkan.eskandari.dev@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: requests>=2.25.0
14
+ Requires-Dist: pyjwt>=2.0.0
15
+ Dynamic: author
16
+ Dynamic: author-email
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: home-page
21
+ Dynamic: requires-dist
22
+ Dynamic: requires-python
23
+ Dynamic: summary
24
+
25
+ # IndoxRouter
26
+
27
+ A unified API for multiple LLM providers, allowing you to switch between different models seamlessly.
28
+
29
+ ## Features
30
+
31
+ - Support for multiple providers (OpenAI, Anthropic, Mistral, Cohere, Google, Meta, AI21, Llama, NVIDIA, Deepseek, Databricks)
32
+ - Unified API for all providers
33
+ - User authentication and API key management
34
+ - Request logging and monitoring
35
+ - Docker support for easy deployment
36
+ - Ready for Railway deployment
37
+
38
+ ## Quick Start
39
+
40
+ ### Local Installation
41
+
42
+ 1. Clone the repository:
43
+
44
+ ```bash
45
+ git clone https://github.com/yourusername/indoxRouter.git
46
+ cd indoxRouter
47
+ ```
48
+
49
+ 2. Create a virtual environment and install dependencies:
50
+
51
+ ```bash
52
+ python -m venv venv
53
+ # On Windows
54
+ venv\Scripts\activate
55
+ # On Unix or MacOS
56
+ source venv/bin/activate
57
+ pip install -r requirements.txt
58
+ ```
59
+
60
+ 3. Initialize the database:
61
+
62
+ ```bash
63
+ python -m indoxRouter.init_db
64
+ ```
65
+
66
+ 4. Run the application:
67
+
68
+ ```bash
69
+ python run.py
70
+ ```
71
+
72
+ 5. Access the application:
73
+ - API: http://localhost:8000
74
+ - API Documentation: http://localhost:8000/docs
75
+
76
+ ### Docker Installation
77
+
78
+ 1. Clone the repository and configure:
79
+
80
+ ```bash
81
+ git clone https://github.com/yourusername/indoxRouter.git
82
+ cd indoxRouter
83
+ cp .env.example .env
84
+ # Edit .env with your configuration
85
+ ```
86
+
87
+ 2. Start with Docker Compose:
88
+
89
+ ```bash
90
+ docker-compose up -d
91
+ ```
92
+
93
+ 3. Access the application:
94
+ - API: http://localhost:8000
95
+ - API Documentation: http://localhost:8000/docs
96
+ - pgAdmin: http://localhost:5050
97
+
98
+ ## Railway Deployment
99
+
100
+ IndoxRouter is ready to be deployed on Railway. Follow these steps:
101
+
102
+ 1. Fork the repository on GitHub.
103
+
104
+ 2. Create a new project on Railway and connect it to your GitHub repository.
105
+
106
+ 3. Add the following environment variables in Railway:
107
+
108
+ - `DATABASE_URL`: Your PostgreSQL connection string
109
+ - `JWT_SECRET`: A secure secret key for JWT tokens
110
+ - Provider API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
111
+
112
+ 4. Railway will automatically detect the Dockerfile and deploy your application.
113
+
114
+ 5. Once deployed, you can access your API at the URL provided by Railway.
115
+
116
+ ## Detailed Documentation
117
+
118
+ For detailed deployment instructions, configuration options, and troubleshooting, please refer to the [Deployment Guide](DEPLOYMENT_GUIDE.md).
119
+
120
+ ## API Usage
121
+
122
+ ### Authentication
123
+
124
+ 1. Register a new user through the API.
125
+ 2. Generate an API key for the user.
126
+ 3. Use the API key in your requests.
127
+
128
+ ### Making Requests
129
+
130
+ ```python
131
+ import requests
132
+
133
+ api_key = "your_api_key"
134
+ url = "http://localhost:8000/v1/completions"
135
+
136
+ payload = {
137
+ "provider": "openai",
138
+ "model": "gpt-3.5-turbo",
139
+ "prompt": "Hello, world!",
140
+ "temperature": 0.7,
141
+ "max_tokens": 100
142
+ }
143
+
144
+ headers = {
145
+ "Authorization": f"Bearer {api_key}",
146
+ "Content-Type": "application/json"
147
+ }
148
+
149
+ response = requests.post(url, json=payload, headers=headers)
150
+ print(response.json())
151
+ ```
152
+
153
+ ## Client Library
154
+
155
+ IndoxRouter includes a Python client library for easy integration:
156
+
157
+ ```python
158
+ from indoxRouter.client import Client
159
+
160
+ client = Client(api_key="your_api_key")
161
+
162
+ response = client.completions(
163
+ provider="openai",
164
+ model="gpt-3.5-turbo",
165
+ prompt="Hello, world!",
166
+ temperature=0.7,
167
+ max_tokens=100
168
+ )
169
+
170
+ print(response)
171
+ ```
172
+
173
+ ## Contributing
174
+
175
+ Contributions are welcome! Please feel free to submit a Pull Request.
176
+
177
+ ## License
178
+
179
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,155 @@
1
+ # IndoxRouter
2
+
3
+ A unified API for multiple LLM providers, allowing you to switch between different models seamlessly.
4
+
5
+ ## Features
6
+
7
+ - Support for multiple providers (OpenAI, Anthropic, Mistral, Cohere, Google, Meta, AI21, Llama, NVIDIA, Deepseek, Databricks)
8
+ - Unified API for all providers
9
+ - User authentication and API key management
10
+ - Request logging and monitoring
11
+ - Docker support for easy deployment
12
+ - Ready for Railway deployment
13
+
14
+ ## Quick Start
15
+
16
+ ### Local Installation
17
+
18
+ 1. Clone the repository:
19
+
20
+ ```bash
21
+ git clone https://github.com/yourusername/indoxRouter.git
22
+ cd indoxRouter
23
+ ```
24
+
25
+ 2. Create a virtual environment and install dependencies:
26
+
27
+ ```bash
28
+ python -m venv venv
29
+ # On Windows
30
+ venv\Scripts\activate
31
+ # On Unix or MacOS
32
+ source venv/bin/activate
33
+ pip install -r requirements.txt
34
+ ```
35
+
36
+ 3. Initialize the database:
37
+
38
+ ```bash
39
+ python -m indoxRouter.init_db
40
+ ```
41
+
42
+ 4. Run the application:
43
+
44
+ ```bash
45
+ python run.py
46
+ ```
47
+
48
+ 5. Access the application:
49
+ - API: http://localhost:8000
50
+ - API Documentation: http://localhost:8000/docs
51
+
52
+ ### Docker Installation
53
+
54
+ 1. Clone the repository and configure:
55
+
56
+ ```bash
57
+ git clone https://github.com/yourusername/indoxRouter.git
58
+ cd indoxRouter
59
+ cp .env.example .env
60
+ # Edit .env with your configuration
61
+ ```
62
+
63
+ 2. Start with Docker Compose:
64
+
65
+ ```bash
66
+ docker-compose up -d
67
+ ```
68
+
69
+ 3. Access the application:
70
+ - API: http://localhost:8000
71
+ - API Documentation: http://localhost:8000/docs
72
+ - pgAdmin: http://localhost:5050
73
+
74
+ ## Railway Deployment
75
+
76
+ IndoxRouter is ready to be deployed on Railway. Follow these steps:
77
+
78
+ 1. Fork the repository on GitHub.
79
+
80
+ 2. Create a new project on Railway and connect it to your GitHub repository.
81
+
82
+ 3. Add the following environment variables in Railway:
83
+
84
+ - `DATABASE_URL`: Your PostgreSQL connection string
85
+ - `JWT_SECRET`: A secure secret key for JWT tokens
86
+ - Provider API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.)
87
+
88
+ 4. Railway will automatically detect the Dockerfile and deploy your application.
89
+
90
+ 5. Once deployed, you can access your API at the URL provided by Railway.
91
+
92
+ ## Detailed Documentation
93
+
94
+ For detailed deployment instructions, configuration options, and troubleshooting, please refer to the [Deployment Guide](DEPLOYMENT_GUIDE.md).
95
+
96
+ ## API Usage
97
+
98
+ ### Authentication
99
+
100
+ 1. Register a new user through the API.
101
+ 2. Generate an API key for the user.
102
+ 3. Use the API key in your requests.
103
+
104
+ ### Making Requests
105
+
106
+ ```python
107
+ import requests
108
+
109
+ api_key = "your_api_key"
110
+ url = "http://localhost:8000/v1/completions"
111
+
112
+ payload = {
113
+ "provider": "openai",
114
+ "model": "gpt-3.5-turbo",
115
+ "prompt": "Hello, world!",
116
+ "temperature": 0.7,
117
+ "max_tokens": 100
118
+ }
119
+
120
+ headers = {
121
+ "Authorization": f"Bearer {api_key}",
122
+ "Content-Type": "application/json"
123
+ }
124
+
125
+ response = requests.post(url, json=payload, headers=headers)
126
+ print(response.json())
127
+ ```
128
+
129
+ ## Client Library
130
+
131
+ IndoxRouter includes a Python client library for easy integration:
132
+
133
+ ```python
134
+ from indoxRouter.client import Client
135
+
136
+ client = Client(api_key="your_api_key")
137
+
138
+ response = client.completions(
139
+ provider="openai",
140
+ model="gpt-3.5-turbo",
141
+ prompt="Hello, world!",
142
+ temperature=0.7,
143
+ max_tokens=100
144
+ )
145
+
146
+ print(response)
147
+ ```
148
+
149
+ ## Contributing
150
+
151
+ Contributions are welcome! Please feel free to submit a Pull Request.
152
+
153
+ ## License
154
+
155
+ This project is licensed under the MIT License - see the LICENSE file for details.
File without changes
@@ -0,0 +1,336 @@
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))