aisbf 0.2.3__py3-none-any.whl → 0.2.4__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.
@@ -116,7 +116,170 @@ app.add_middleware(
116
116
 
117
117
  @app.get("/")
118
118
  async def root():
119
- return {"message": "AI Proxy Server is running", "providers": list(config.providers.keys())}
119
+ return {
120
+ "message": "AI Proxy Server is running",
121
+ "providers": list(config.providers.keys()),
122
+ "rotations": list(config.rotations.keys()),
123
+ "autoselect": list(config.autoselect.keys())
124
+ }
125
+
126
+ @app.get("/api/rotations")
127
+ async def list_rotations():
128
+ """List all available rotations"""
129
+ logger.info("=== LIST ROTATIONS REQUEST ===")
130
+ rotations_info = {}
131
+ for rotation_id, rotation_config in config.rotations.items():
132
+ models = []
133
+ for provider in rotation_config.providers:
134
+ for model in provider['models']:
135
+ models.append({
136
+ "name": model['name'],
137
+ "provider_id": provider['provider_id'],
138
+ "weight": model['weight'],
139
+ "rate_limit": model.get('rate_limit')
140
+ })
141
+ rotations_info[rotation_id] = {
142
+ "model_name": rotation_config.model_name,
143
+ "models": models
144
+ }
145
+ logger.info(f"Available rotations: {list(rotations_info.keys())}")
146
+ return rotations_info
147
+
148
+ @app.post("/api/rotations/chat/completions")
149
+ async def rotation_chat_completions(request: Request, body: ChatCompletionRequest):
150
+ """Handle chat completions for rotations using model name to select rotation"""
151
+ logger.info(f"=== ROTATION CHAT COMPLETION REQUEST START ===")
152
+ logger.info(f"Request path: {request.url.path}")
153
+ logger.info(f"Model requested: {body.model}")
154
+ logger.info(f"Request headers: {dict(request.headers)}")
155
+ logger.info(f"Request body: {body}")
156
+ logger.info(f"Available rotations: {list(config.rotations.keys())}")
157
+
158
+ body_dict = body.model_dump()
159
+
160
+ # Check if the model name corresponds to a rotation
161
+ if body.model not in config.rotations:
162
+ logger.error(f"Model '{body.model}' not found in rotations")
163
+ logger.error(f"Available rotations: {list(config.rotations.keys())}")
164
+ raise HTTPException(
165
+ status_code=400,
166
+ detail=f"Model '{body.model}' not found. Available rotations: {list(config.rotations.keys())}"
167
+ )
168
+
169
+ logger.info(f"Model '{body.model}' found in rotations")
170
+ logger.debug("Handling rotation request")
171
+
172
+ try:
173
+ if body.stream:
174
+ logger.debug("Handling streaming rotation request")
175
+ return await rotation_handler.handle_rotation_request(body.model, body_dict)
176
+ else:
177
+ logger.debug("Handling non-streaming rotation request")
178
+ result = await rotation_handler.handle_rotation_request(body.model, body_dict)
179
+ logger.debug(f"Rotation response result: {result}")
180
+ return result
181
+ except Exception as e:
182
+ logger.error(f"Error handling rotation chat_completions: {str(e)}", exc_info=True)
183
+ raise
184
+
185
+ @app.get("/api/rotations/models")
186
+ async def list_rotation_models():
187
+ """List all models across all rotations"""
188
+ logger.info("=== LIST ROTATION MODELS REQUEST ===")
189
+ all_models = []
190
+ for rotation_id, rotation_config in config.rotations.items():
191
+ for provider in rotation_config.providers:
192
+ for model in provider['models']:
193
+ all_models.append({
194
+ "id": f"{rotation_id}/{model['name']}",
195
+ "name": rotation_id, # Use rotation name as the model name for selection
196
+ "object": "model",
197
+ "owned_by": provider['provider_id'],
198
+ "rotation_id": rotation_id,
199
+ "actual_model": model['name'],
200
+ "provider_id": provider['provider_id'],
201
+ "weight": model['weight'],
202
+ "rate_limit": model.get('rate_limit')
203
+ })
204
+ logger.info(f"Total rotation models available: {len(all_models)}")
205
+ return {"data": all_models}
206
+
207
+ @app.get("/api/autoselect")
208
+ async def list_autoselect():
209
+ """List all available autoselect configurations"""
210
+ logger.info("=== LIST AUTOSELECT REQUEST ===")
211
+ autoselect_info = {}
212
+ for autoselect_id, autoselect_config in config.autoselect.items():
213
+ autoselect_info[autoselect_id] = {
214
+ "model_name": autoselect_config.model_name,
215
+ "description": autoselect_config.description,
216
+ "fallback": autoselect_config.fallback,
217
+ "available_models": [
218
+ {
219
+ "model_id": m.model_id,
220
+ "description": m.description
221
+ }
222
+ for m in autoselect_config.available_models
223
+ ]
224
+ }
225
+ logger.info(f"Available autoselect: {list(autoselect_info.keys())}")
226
+ return autoselect_info
227
+
228
+ @app.post("/api/autoselect/chat/completions")
229
+ async def autoselect_chat_completions(request: Request, body: ChatCompletionRequest):
230
+ """Handle chat completions for autoselect using model name to select autoselect configuration"""
231
+ logger.info(f"=== AUTOSELECT CHAT COMPLETION REQUEST START ===")
232
+ logger.info(f"Request path: {request.url.path}")
233
+ logger.info(f"Model requested: {body.model}")
234
+ logger.info(f"Request headers: {dict(request.headers)}")
235
+ logger.info(f"Request body: {body}")
236
+ logger.info(f"Available autoselect: {list(config.autoselect.keys())}")
237
+
238
+ body_dict = body.model_dump()
239
+
240
+ # Check if the model name corresponds to an autoselect configuration
241
+ if body.model not in config.autoselect:
242
+ logger.error(f"Model '{body.model}' not found in autoselect")
243
+ logger.error(f"Available autoselect: {list(config.autoselect.keys())}")
244
+ raise HTTPException(
245
+ status_code=400,
246
+ detail=f"Model '{body.model}' not found. Available autoselect: {list(config.autoselect.keys())}"
247
+ )
248
+
249
+ logger.info(f"Model '{body.model}' found in autoselect")
250
+ logger.debug("Handling autoselect request")
251
+
252
+ try:
253
+ if body.stream:
254
+ logger.debug("Handling streaming autoselect request")
255
+ return await autoselect_handler.handle_autoselect_streaming_request(body.model, body_dict)
256
+ else:
257
+ logger.debug("Handling non-streaming autoselect request")
258
+ result = await autoselect_handler.handle_autoselect_request(body.model, body_dict)
259
+ logger.debug(f"Autoselect response result: {result}")
260
+ return result
261
+ except Exception as e:
262
+ logger.error(f"Error handling autoselect chat_completions: {str(e)}", exc_info=True)
263
+ raise
264
+
265
+ @app.get("/api/autoselect/models")
266
+ async def list_autoselect_models():
267
+ """List all models across all autoselect configurations"""
268
+ logger.info("=== LIST AUTOSELECT MODELS REQUEST ===")
269
+ all_models = []
270
+ for autoselect_id, autoselect_config in config.autoselect.items():
271
+ for model_info in autoselect_config.available_models:
272
+ all_models.append({
273
+ "id": model_info.model_id,
274
+ "name": autoselect_id, # Use autoselect name as the model name for selection
275
+ "object": "model",
276
+ "owned_by": "autoselect",
277
+ "autoselect_id": autoselect_id,
278
+ "description": model_info.description,
279
+ "fallback": autoselect_config.fallback
280
+ })
281
+ logger.info(f"Total autoselect models available: {len(all_models)}")
282
+ return {"data": all_models}
120
283
 
121
284
  @app.post("/api/{provider_id}/chat/completions")
122
285
  async def chat_completions(provider_id: str, request: Request, body: ChatCompletionRequest):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aisbf
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: AISBF - AI Service Broker Framework || AI Should Be Free - A modular proxy server for managing multiple AI provider integrations
5
5
  Home-page: https://git.nexlab.net/nexlab/aisbf.git
6
6
  Author: AISBF Contributors
@@ -0,0 +1,24 @@
1
+ cli.py,sha256=SpjVC1iBdDhQXuhJcjVFkODu4BH-nj_1hNFD_d8wPbw,2503
2
+ aisbf/__init__.py,sha256=D3-tZRWCu31CltN_pjx8IikwPl0OGEJkvoASm8QjvcQ,2156
3
+ aisbf/config.py,sha256=xXcUQG3OcteCi8__qm6-yEnwjwFVpLBYLrs-zMnDfl0,9643
4
+ aisbf/handlers.py,sha256=tXYOQYkhIK6X1EmgivVyO6fElkrRj1cqNQfQ5KGldO8,31557
5
+ aisbf/models.py,sha256=LT1NaQVAw0VWXL-j3hdfNlXCA9HeiET_O3GDj3t9XC4,1883
6
+ aisbf/providers.py,sha256=gh3qe-bthAxb-hRPPpp_YLgGzbdTKRN5HKVjf-Yvnp0,19920
7
+ aisbf-0.2.4.data/data/share/aisbf/aisbf.sh,sha256=ntI4UPefBtU2jrTwMR3hddHEPG_pDyJyO0J3SD7e5PA,4574
8
+ aisbf-0.2.4.data/data/share/aisbf/autoselect.json,sha256=Anud0hTE1mehonmMmhOTPK2ANUxfruE2yMdLqiEkEUA,659
9
+ aisbf-0.2.4.data/data/share/aisbf/autoselect.md,sha256=F8PilhaYBs0qdpIxIkokrjtIOdhAx5Bi1tA0hyfnqps,4301
10
+ aisbf-0.2.4.data/data/share/aisbf/main.py,sha256=hCTxLA-txJpSpMTE5mV1XG_j8Kn17ZTHJsnx59nKscg,17241
11
+ aisbf-0.2.4.data/data/share/aisbf/providers.json,sha256=9L5GO6sQ2Z6zndGed0AckvYNV1DMr9r7tSdZ9fJxYlA,3934
12
+ aisbf-0.2.4.data/data/share/aisbf/requirements.txt,sha256=lp6cPakAO3lpTCwQ27THf-PNz_HIpzCELrtpdgo6-2o,133
13
+ aisbf-0.2.4.data/data/share/aisbf/rotations.json,sha256=SzbmMeTRR0vVTrYTMwxSPxjXLVr8zxjaI4HYRxjyExQ,2123
14
+ aisbf-0.2.4.data/data/share/aisbf/aisbf/__init__.py,sha256=D3-tZRWCu31CltN_pjx8IikwPl0OGEJkvoASm8QjvcQ,2156
15
+ aisbf-0.2.4.data/data/share/aisbf/aisbf/config.py,sha256=xXcUQG3OcteCi8__qm6-yEnwjwFVpLBYLrs-zMnDfl0,9643
16
+ aisbf-0.2.4.data/data/share/aisbf/aisbf/handlers.py,sha256=tXYOQYkhIK6X1EmgivVyO6fElkrRj1cqNQfQ5KGldO8,31557
17
+ aisbf-0.2.4.data/data/share/aisbf/aisbf/models.py,sha256=LT1NaQVAw0VWXL-j3hdfNlXCA9HeiET_O3GDj3t9XC4,1883
18
+ aisbf-0.2.4.data/data/share/aisbf/aisbf/providers.py,sha256=gh3qe-bthAxb-hRPPpp_YLgGzbdTKRN5HKVjf-Yvnp0,19920
19
+ aisbf-0.2.4.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
20
+ aisbf-0.2.4.dist-info/METADATA,sha256=A_x1Mb5ryEHXXCKpdzfFz8Pgdn7FKgR4POzRKAmbdZU,4190
21
+ aisbf-0.2.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
22
+ aisbf-0.2.4.dist-info/entry_points.txt,sha256=dV_E5f6UvgSe9AoyPTzGxBK8IYaIeLR8yTe7EwBZ3F8,35
23
+ aisbf-0.2.4.dist-info/top_level.txt,sha256=odXp1LYymu31EdVSmMGCg3ZYAI5HeB8tZkaXh9Pw3kE,10
24
+ aisbf-0.2.4.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- cli.py,sha256=SpjVC1iBdDhQXuhJcjVFkODu4BH-nj_1hNFD_d8wPbw,2503
2
- aisbf/__init__.py,sha256=D3-tZRWCu31CltN_pjx8IikwPl0OGEJkvoASm8QjvcQ,2156
3
- aisbf/config.py,sha256=1Fo-9VakRmbBKvxyN1MwU-dG3DMFTlS81LcO5ny4DDo,8316
4
- aisbf/handlers.py,sha256=ageAWaTur6-bSrvVtYH03-8sw1flESmbTLbZ2RS2vLQ,19192
5
- aisbf/models.py,sha256=LT1NaQVAw0VWXL-j3hdfNlXCA9HeiET_O3GDj3t9XC4,1883
6
- aisbf/providers.py,sha256=7es-0EHYu9BIrgCk_3Y5ochQ4wNCCX7jIUh4IeIa3jg,17835
7
- aisbf-0.2.3.data/data/share/aisbf/aisbf.sh,sha256=ntI4UPefBtU2jrTwMR3hddHEPG_pDyJyO0J3SD7e5PA,4574
8
- aisbf-0.2.3.data/data/share/aisbf/autoselect.json,sha256=Anud0hTE1mehonmMmhOTPK2ANUxfruE2yMdLqiEkEUA,659
9
- aisbf-0.2.3.data/data/share/aisbf/autoselect.md,sha256=F8PilhaYBs0qdpIxIkokrjtIOdhAx5Bi1tA0hyfnqps,4301
10
- aisbf-0.2.3.data/data/share/aisbf/main.py,sha256=JCSEE2sBG73wWDQyjRWrhR7y06EuyWDd134mLnjF5KY,10075
11
- aisbf-0.2.3.data/data/share/aisbf/providers.json,sha256=9L5GO6sQ2Z6zndGed0AckvYNV1DMr9r7tSdZ9fJxYlA,3934
12
- aisbf-0.2.3.data/data/share/aisbf/requirements.txt,sha256=lp6cPakAO3lpTCwQ27THf-PNz_HIpzCELrtpdgo6-2o,133
13
- aisbf-0.2.3.data/data/share/aisbf/rotations.json,sha256=SzbmMeTRR0vVTrYTMwxSPxjXLVr8zxjaI4HYRxjyExQ,2123
14
- aisbf-0.2.3.data/data/share/aisbf/aisbf/__init__.py,sha256=D3-tZRWCu31CltN_pjx8IikwPl0OGEJkvoASm8QjvcQ,2156
15
- aisbf-0.2.3.data/data/share/aisbf/aisbf/config.py,sha256=1Fo-9VakRmbBKvxyN1MwU-dG3DMFTlS81LcO5ny4DDo,8316
16
- aisbf-0.2.3.data/data/share/aisbf/aisbf/handlers.py,sha256=ageAWaTur6-bSrvVtYH03-8sw1flESmbTLbZ2RS2vLQ,19192
17
- aisbf-0.2.3.data/data/share/aisbf/aisbf/models.py,sha256=LT1NaQVAw0VWXL-j3hdfNlXCA9HeiET_O3GDj3t9XC4,1883
18
- aisbf-0.2.3.data/data/share/aisbf/aisbf/providers.py,sha256=7es-0EHYu9BIrgCk_3Y5ochQ4wNCCX7jIUh4IeIa3jg,17835
19
- aisbf-0.2.3.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
20
- aisbf-0.2.3.dist-info/METADATA,sha256=9J11HLx3udA-pBzI7R4rnqbPNcT2BXyc7OxynuoJao4,4190
21
- aisbf-0.2.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
22
- aisbf-0.2.3.dist-info/entry_points.txt,sha256=dV_E5f6UvgSe9AoyPTzGxBK8IYaIeLR8yTe7EwBZ3F8,35
23
- aisbf-0.2.3.dist-info/top_level.txt,sha256=odXp1LYymu31EdVSmMGCg3ZYAI5HeB8tZkaXh9Pw3kE,10
24
- aisbf-0.2.3.dist-info/RECORD,,
File without changes