bahi-bh commited on
Commit
d2e502e
·
verified ·
1 Parent(s): 906da7a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +185 -325
app.py CHANGED
@@ -2,33 +2,27 @@ from fastapi import FastAPI, Request, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from fastapi.responses import StreamingResponse, JSONResponse
4
  from pydantic import BaseModel
5
- from typing import List, Optional, Dict
6
  import asyncio
7
  import json
8
  import time
9
  import uuid
10
  import logging
11
- from enum import Enum
12
 
13
  import g4f
14
  from g4f.client import Client
15
  from g4f.Provider import (
16
- BingCreateImage,
17
- OpenaiChat,
18
- Claude,
19
  Blackbox,
20
  DeepInfra,
21
- PerplexityLabs
 
22
  )
23
 
24
  # =====================================================
25
  # LOGGING
26
  # =====================================================
27
 
28
- logging.basicConfig(
29
- level=logging.INFO,
30
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
31
- )
32
  logger = logging.getLogger(__name__)
33
 
34
  # =====================================================
@@ -36,23 +30,21 @@ logger = logging.getLogger(__name__)
36
  # =====================================================
37
 
38
  API_KEY = "sk-your-secret-key"
39
- REQUEST_TIMEOUT = 120
40
- MAX_RETRIES = 3
41
 
42
  # =====================================================
43
- # PROVIDERS MAPPING
44
  # =====================================================
45
 
46
- PROVIDERS_MAP: Dict[str, type] = {
47
  "gpt-4o": Blackbox,
48
  "gpt-4o-mini": DeepInfra,
49
- "gpt-4": Claude,
50
- "claude-3-haiku": Claude,
51
- "claude-3-sonnet": Claude,
52
  "llama-3.1-70b": DeepInfra,
53
  "mixtral-8x7b": DeepInfra,
54
  "deepseek-chat": Blackbox,
55
- "perplexity-chat": PerplexityLabs,
56
  }
57
 
58
  # =====================================================
@@ -60,10 +52,8 @@ PROVIDERS_MAP: Dict[str, type] = {
60
  # =====================================================
61
 
62
  app = FastAPI(
63
- title="Universal AI Gateway Pro",
64
- version="5.0.0",
65
- docs_url="/docs",
66
- openapi_url="/openapi.json"
67
  )
68
 
69
  # =====================================================
@@ -91,269 +81,196 @@ class ChatRequest(BaseModel):
91
  messages: List[Message]
92
  stream: bool = False
93
  temperature: Optional[float] = 0.7
94
- max_tokens: Optional[int] = 2048
95
- top_p: Optional[float] = 1.0
96
-
97
- class ErrorResponse(BaseModel):
98
- error: Dict[str, str]
99
 
100
  # =====================================================
101
  # AUTH
102
  # =====================================================
103
 
104
- def verify_api_key(req: Request) -> bool:
105
- """التحقق من مفتاح API"""
106
-
107
  auth = req.headers.get("Authorization")
108
-
109
- # السماح للاختبار بدون مفتاح
110
  if not auth:
111
- logger.warning("Request without API key detected")
112
  return True
113
-
114
  if not auth.startswith("Bearer "):
115
  raise HTTPException(
116
  status_code=401,
117
- detail="Invalid Authorization Format. Use: Bearer <token>"
118
  )
119
-
120
  token = auth.replace("Bearer ", "").strip()
121
-
122
  if token != API_KEY:
123
  raise HTTPException(
124
  status_code=403,
125
  detail="Invalid API Key"
126
  )
127
-
128
- return True
129
-
130
- # =====================================================
131
- # HELPER FUNCTIONS
132
- # =====================================================
133
 
134
- def get_provider_for_model(model: str) -> type:
135
- """الحصول على المزود المناسب للنموذج"""
136
-
137
- provider = PROVIDERS_MAP.get(model, DeepInfra)
138
- logger.info(f"Using provider {provider.__name__} for model {model}")
139
- return provider
140
-
141
- async def create_chat_client(model: str) -> Client:
142
- """إنشاء عميل محسّن مع المزود المناسب"""
143
-
144
- provider = get_provider_for_model(model)
145
-
146
- try:
147
- client = Client(
148
- provider=provider,
149
- timeout=REQUEST_TIMEOUT,
150
- max_retries=MAX_RETRIES
151
- )
152
- return client
153
- except Exception as e:
154
- logger.error(f"Failed to create client for {model}: {e}")
155
- # fallback to default client
156
- return Client()
157
-
158
- def format_messages(messages: List[Message]) -> List[Dict]:
159
- """تنسيق الرسائل للإرسال"""
160
-
161
- return [
162
- {
163
- "role": m.role,
164
- "content": m.content
165
- }
166
- for m in messages
167
- ]
168
-
169
- async def create_completion_with_retry(
170
- client: Client,
171
- model: str,
172
- messages: List[Dict],
173
- stream: bool = False,
174
- temperature: float = 0.7,
175
- max_tokens: int = 2048
176
- ) -> any:
177
- """إنشاء استجابة مع إعادة محاولة"""
178
-
179
- retries = 0
180
- last_error = None
181
-
182
- while retries < MAX_RETRIES:
183
- try:
184
- response = await asyncio.to_thread(
185
- client.chat.completions.create,
186
- model=model,
187
- messages=messages,
188
- stream=stream,
189
- temperature=temperature,
190
- max_tokens=max_tokens
191
- )
192
- return response
193
-
194
- except Exception as e:
195
- last_error = e
196
- retries += 1
197
- logger.warning(
198
- f"Attempt {retries}/{MAX_RETRIES} failed: {e}"
199
- )
200
-
201
- if retries < MAX_RETRIES:
202
- await asyncio.sleep(2 ** retries) # exponential backoff
203
- else:
204
- raise last_error
205
 
206
  # =====================================================
207
- # ENDPOINTS
208
  # =====================================================
209
 
210
  @app.get("/")
211
  async def root():
212
- """جذر API - معلومات الخدمة"""
213
-
214
  return {
215
  "status": "online",
216
- "service": "Universal AI Gateway Pro",
217
- "version": "5.0.0",
218
- "providers": list(PROVIDERS_MAP.keys()),
219
- "documentation": "/docs"
220
  }
221
 
 
 
 
 
222
  @app.get("/v1/models")
223
  async def get_models():
224
- """قائمة النماذج المتاحة"""
225
-
226
  models_data = []
227
-
228
- # النماذج المدعومة مع مزودين حقيقيين
229
- supported_models = {
230
- "gpt-4o": "Blackbox",
231
- "gpt-4o-mini": "DeepInfra",
232
- "gpt-4": "Claude",
233
- "claude-3-haiku": "Claude",
234
- "claude-3-sonnet": "Claude",
235
- "llama-3.1-70b": "DeepInfra",
236
- "mixtral-8x7b": "DeepInfra",
237
- "deepseek-chat": "Blackbox",
238
- "perplexity-chat": "PerplexityLabs",
239
- }
240
-
241
- for model_id, provider_name in supported_models.items():
242
- models_data.append({
243
- "id": model_id,
244
- "object": "model",
245
- "created": int(time.time()),
246
- "owned_by": provider_name,
247
- "provider": provider_name,
248
- "active": True
249
- })
250
-
251
- logger.info(f"Returning {len(models_data)} available models")
252
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  return {
254
  "object": "list",
255
  "data": models_data
256
  }
257
 
258
- @app.get("/v1/models/{model_id}")
259
- async def get_model_info(model_id: str):
260
- """معلومات نموذج محدد"""
261
-
262
- if model_id not in PROVIDERS_MAP:
263
- raise HTTPException(
264
- status_code=404,
265
- detail=f"Model {model_id} not found"
266
- )
267
-
268
- provider = PROVIDERS_MAP[model_id]
269
-
270
- return {
271
- "id": model_id,
272
- "object": "model",
273
- "created": int(time.time()),
274
- "owned_by": provider.__name__,
275
- "provider": provider.__name__,
276
- "active": True
277
- }
278
 
279
- @app.post("/v1/chat/completions", response_class=StreamingResponse)
280
- async def chat_completions(req: Request, body: ChatRequest):
281
- """استكمال المحادثات - متوافق مع OpenAI API"""
282
-
283
- # التحقق من المفتاح
284
  verify_api_key(req)
285
-
286
- # تنسيق الرسائل
287
- messages = format_messages(body.messages)
288
-
 
 
 
 
 
289
  logger.info(
290
- f"Chat request - Model: {body.model}, "
291
- f"Stream: {body.stream}, "
292
- f"Messages: {len(messages)}"
293
  )
294
-
295
- # إنشاء العميل
296
- client = await create_chat_client(body.model)
297
-
298
- # =========================================================
299
- # STREAMING RESPONSE
300
- # =========================================================
301
-
302
  if body.stream:
303
-
304
  async def generate_stream():
305
- """مولّد الدفق المتدفق"""
306
-
307
- chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
308
-
309
  try:
310
- # الحصول على الاستجابة المتدفقة
311
- response = await create_completion_with_retry(
312
- client=client,
 
 
 
313
  model=body.model,
314
  messages=messages,
315
- stream=True,
316
- temperature=body.temperature,
317
- max_tokens=body.max_tokens
318
  )
319
-
320
- # إرسال الأجزاء
 
321
  for chunk in response:
 
322
  try:
 
323
  content = ""
324
- finish_reason = None
325
-
326
- # استخراج المحتوى من الجزء
327
- if hasattr(chunk, 'choices') and chunk.choices:
328
- if hasattr(chunk.choices[0], 'delta'):
329
- if chunk.choices[0].delta.content:
330
- content = chunk.choices[0].delta.content
331
- if hasattr(chunk.choices[0], 'finish_reason'):
332
- finish_reason = chunk.choices[0].finish_reason
333
-
334
- # إنشاء payload
335
- payload = {
336
- "id": chunk_id,
337
- "object": "chat.completion.chunk",
338
- "created": int(time.time()),
339
- "model": body.model,
340
- "choices": [
341
- {
342
- "index": 0,
343
- "delta": {"content": content} if content else {},
344
- "finish_reason": finish_reason
345
- }
346
- ]
347
- }
348
-
349
- yield f"data: {json.dumps(payload)}\n\n"
350
- await asyncio.sleep(0.01) # تحسين الاستجابة
351
-
 
 
352
  except Exception as chunk_error:
353
- logger.error(f"Chunk processing error: {chunk_error}")
354
- continue
355
-
356
- # إرسال الحزمة النهائية
 
357
  final_payload = {
358
  "id": chunk_id,
359
  "object": "chat.completion.chunk",
@@ -367,23 +284,16 @@ async def chat_completions(req: Request, body: ChatRequest):
367
  }
368
  ]
369
  }
370
-
371
- yield f for {body.model}")
372
-
373
- except Exception as e:
374
- logger.error(f"Streaming error: {e}", exc_info=True)
375
-
376
- error_payload = {
377
  "error": {
378
  "message": str(e),
379
- "type": "server_error",
380
- "param": None,
381
- "code": "server_error"
382
  }
383
  }
384
-
385
  yield f"data: {json.dumps(error_payload)}\n\n"
386
-
387
  return StreamingResponse(
388
  generate_stream(),
389
  media_type="text/event-stream",
@@ -393,45 +303,30 @@ async def chat_completions(req: Request, body: ChatRequest):
393
  "X-Accel-Buffering": "no"
394
  }
395
  )
396
-
397
- # =========================================================
398
- # NORMAL (NON-STREAMING) RESPONSE
399
- # =========================================================
400
-
401
  try:
402
- response = await create_completion_with_retry(
403
- client=client,
 
 
 
 
 
404
  model=body.model,
405
- messages=messages,
406
- stream=False,
407
- temperature=body.temperature,
408
- max_tokens=body.max_tokens
409
  )
410
-
411
  assistant_message = ""
412
- completion_tokens = 0
413
-
414
  try:
415
- if hasattr(response, 'choices') and response.choices:
416
- if hasattr(response.choices[0], 'message'):
417
- assistant_message = response.choices[0].message.content
418
- else:
419
- assistant_message = str(response)
420
- else:
421
- assistant_message = str(response)
422
- except Exception as parse_error:
423
- logger.error(f"Response parsing error: {parse_error}")
424
  assistant_message = str(response)
425
-
426
- # حساب التقريبي للـ tokens
427
- completion_tokens = len(assistant_message.split()) * 1.3
428
- prompt_tokens = sum(len(m["content"].split()) * 1.3 for m in messages)
429
-
430
- logger.info(
431
- f"Completion successful - Model: {body.model}, "
432
- f"Tokens: {prompt_tokens + completion_tokens}"
433
- )
434
-
435
  return JSONResponse({
436
  "id": f"chatcmpl-{uuid.uuid4().hex}",
437
  "object": "chat.completion",
@@ -448,66 +343,31 @@ async def chat_completions(req: Request, body: ChatRequest):
448
  }
449
  ],
450
  "usage": {
451
- "prompt_tokens": int(prompt_tokens),
452
- "completion_tokens": int(completion_tokens),
453
- "total_tokens": int(prompt_tokens + completion_tokens)
454
  }
455
  })
456
-
457
  except Exception as e:
458
- logger.error(f"Chat completion error: {e}", exc_info=True)
459
-
 
460
  raise HTTPException(
461
  status_code=500,
462
- detail={
463
- "message": str(e),
464
- "type": "server_error",
465
- "model": body.model
466
- }
467
  )
468
 
469
- @app.get("/v1/health")
470
- async def health_check():
471
- """فحص صحة الخدمة"""
472
-
473
- return {
474
- "status": "healthy",
475
- "timestamp": int(time.time()),
476
- "version": "5.0.0"
477
- }
478
-
479
- # =====================================================
480
- # ERROR HANDLERS
481
- # =====================================================
482
-
483
- @app.exception_handler(HTTPException)
484
- async def http_exception_handler(request: Request, exc: HTTPException):
485
- """معالج استثناءات HTTP"""
486
-
487
- return JSONResponse(
488
- status_code=exc.status_code,
489
- content={
490
- "error": {
491
- "message": exc.detail,
492
- "type": "http_error",
493
- "status_code": exc.status_code
494
- }
495
- }
496
- )
497
-
498
  # =====================================================
499
  # RUN
500
  # =====================================================
501
 
502
  if __name__ == "__main__":
 
503
  import uvicorn
504
-
505
- logger.info("Starting Universal AI Gateway Pro v5.0.0")
506
- logger.info(f"Available providers: {list(PROVIDERS_MAP.keys())}")
507
-
508
  uvicorn.run(
509
  app,
510
  host="0.0.0.0",
511
- port=7860,
512
- log_level="info"
513
  )
 
2
  from fastapi.middleware.cors import CORSMiddleware
3
  from fastapi.responses import StreamingResponse, JSONResponse
4
  from pydantic import BaseModel
5
+ from typing import List, Optional
6
  import asyncio
7
  import json
8
  import time
9
  import uuid
10
  import logging
 
11
 
12
  import g4f
13
  from g4f.client import Client
14
  from g4f.Provider import (
 
 
 
15
  Blackbox,
16
  DeepInfra,
17
+ PerplexityLabs,
18
+ HuggingChat
19
  )
20
 
21
  # =====================================================
22
  # LOGGING
23
  # =====================================================
24
 
25
+ logging.basicConfig(level=logging.INFO)
 
 
 
26
  logger = logging.getLogger(__name__)
27
 
28
  # =====================================================
 
30
  # =====================================================
31
 
32
  API_KEY = "sk-your-secret-key"
 
 
33
 
34
  # =====================================================
35
+ # PROVIDER MAPPING - التعديل الدقيق #1
36
  # =====================================================
37
 
38
+ REAL_PROVIDERS = {
39
  "gpt-4o": Blackbox,
40
  "gpt-4o-mini": DeepInfra,
41
+ "gpt-4": Blackbox,
42
+ "gpt-3.5-turbo": DeepInfra,
43
+ "claude-3-haiku": HuggingChat,
44
  "llama-3.1-70b": DeepInfra,
45
  "mixtral-8x7b": DeepInfra,
46
  "deepseek-chat": Blackbox,
47
+ "gemini-pro": PerplexityLabs
48
  }
49
 
50
  # =====================================================
 
52
  # =====================================================
53
 
54
  app = FastAPI(
55
+ title="Universal AI Gateway",
56
+ version="4.0.0"
 
 
57
  )
58
 
59
  # =====================================================
 
81
  messages: List[Message]
82
  stream: bool = False
83
  temperature: Optional[float] = 0.7
84
+ max_tokens: Optional[int] = 4096
 
 
 
 
85
 
86
  # =====================================================
87
  # AUTH
88
  # =====================================================
89
 
90
+ def verify_api_key(req: Request):
91
+
 
92
  auth = req.headers.get("Authorization")
93
+
94
+ # السماح للاختبار
95
  if not auth:
 
96
  return True
97
+
98
  if not auth.startswith("Bearer "):
99
  raise HTTPException(
100
  status_code=401,
101
+ detail="Invalid Authorization Format"
102
  )
103
+
104
  token = auth.replace("Bearer ", "").strip()
105
+
106
  if token != API_KEY:
107
  raise HTTPException(
108
  status_code=403,
109
  detail="Invalid API Key"
110
  )
 
 
 
 
 
 
111
 
112
+ return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  # =====================================================
115
+ # ROOT
116
  # =====================================================
117
 
118
  @app.get("/")
119
  async def root():
120
+
 
121
  return {
122
  "status": "online",
123
+ "service": "Universal AI Gateway",
124
+ "version": "4.0.0"
 
 
125
  }
126
 
127
+ # =====================================================
128
+ # MODELS
129
+ # =====================================================
130
+
131
  @app.get("/v1/models")
132
  async def get_models():
133
+
 
134
  models_data = []
135
+
136
+ fallback_models = [
137
+ "gpt-4o-mini",
138
+ "gpt-4o",
139
+ "gpt-4",
140
+ "gpt-3.5-turbo",
141
+ "claude-3-haiku",
142
+ "llama-3.1-70b",
143
+ "mixtral-8x7b",
144
+ "deepseek-chat",
145
+ "gemini-pro"
146
+ ]
147
+
148
+ try:
149
+
150
+ if hasattr(g4f.models, "_all_models"):
151
+
152
+ all_models = list(g4f.models._all_models)
153
+
154
+ for model in all_models[:50]:
155
+
156
+ models_data.append({
157
+ "id": str(model),
158
+ "object": "model",
159
+ "created": int(time.time()),
160
+ "owned_by": "g4f"
161
+ })
162
+
163
+ except Exception as e:
164
+
165
+ logger.error(f"Models error: {e}")
166
+
167
+ # fallback - التعديل الدقيق #2
168
+ if not models_data:
169
+
170
+ for model in fallback_models:
171
+ provider_name = "g4f"
172
+ if model in REAL_PROVIDERS:
173
+ provider_name = REAL_PROVIDERS[model].__name__
174
+
175
+ models_data.append({
176
+ "id": model,
177
+ "object": "model",
178
+ "created": int(time.time()),
179
+ "owned_by": provider_name
180
+ })
181
+
182
  return {
183
  "object": "list",
184
  "data": models_data
185
  }
186
 
187
+ # =====================================================
188
+ # CHAT COMPLETIONS
189
+ # =====================================================
190
+
191
+ @app.post("/v1/chat/completions")
192
+ async def chat_completions(
193
+ req: Request,
194
+ body: ChatRequest
195
+ ):
 
 
 
 
 
 
 
 
 
 
 
196
 
 
 
 
 
 
197
  verify_api_key(req)
198
+
199
+ messages = [
200
+ {
201
+ "role": m.role,
202
+ "content": m.content
203
+ }
204
+ for m in body.messages
205
+ ]
206
+
207
  logger.info(
208
+ f"Request model={body.model} stream={body.stream}"
 
 
209
  )
210
+
211
+ # =================================================
212
+ # STREAMING
213
+ # =================================================
214
+
 
 
 
215
  if body.stream:
216
+
217
  async def generate_stream():
218
+
 
 
 
219
  try:
220
+
221
+ # التعديل الدقيق #3 - استخدام المزود الحقيقي
222
+ provider = REAL_PROVIDERS.get(body.model, None)
223
+ client = Client(provider=provider) if provider else Client()
224
+
225
+ response = client.chat.completions.create(
226
  model=body.model,
227
  messages=messages,
228
+ stream=True
 
 
229
  )
230
+
231
+ chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
232
+
233
  for chunk in response:
234
+
235
  try:
236
+
237
  content = ""
238
+
239
+ if (
240
+ chunk.choices
241
+ and chunk.choices[0].delta
242
+ and chunk.choices[0].delta.content
243
+ ):
244
+ content = chunk.choices[0].delta.content
245
+
246
+ if content:
247
+
248
+ payload = {
249
+ "id": chunk_id,
250
+ "object": "chat.completion.chunk",
251
+ "created": int(time.time()),
252
+ "model": body.model,
253
+ "choices": [
254
+ {
255
+ "index": 0,
256
+ "delta": {
257
+ "content": content
258
+ },
259
+ "finish_reason": None
260
+ }
261
+ ]
262
+ }
263
+
264
+ yield f"data: {json.dumps(payload)}\n\n"
265
+
266
+ await asyncio.sleep(0)
267
+
268
  except Exception as chunk_error:
269
+
270
+ logger.error(
271
+ f"Chunk error: {chunk_error}"
272
+ )
273
+
274
  final_payload = {
275
  "id": chunk_id,
276
  "object": "chat.completion.chunk",
 
284
  }
285
  ]
286
  }
287
+
288
+ yield f"data: {json.dumps(final_payload)}\n\error_payload = {
 
 
 
 
 
289
  "error": {
290
  "message": str(e),
291
+ "type": "server_error"
 
 
292
  }
293
  }
294
+
295
  yield f"data: {json.dumps(error_payload)}\n\n"
296
+
297
  return StreamingResponse(
298
  generate_stream(),
299
  media_type="text/event-stream",
 
303
  "X-Accel-Buffering": "no"
304
  }
305
  )
306
+
307
+ # =================================================
308
+ # NORMAL RESPONSE
309
+ # =================================================
310
+
311
  try:
312
+
313
+ # التعديل الدقيق #4 - استخدام المزود الحقيقي
314
+ provider = REAL_PROVIDERS.get(body.model, None)
315
+ client = Client(provider=provider) if provider else Client()
316
+
317
+ response = await asyncio.to_thread(
318
+ client.chat.completions.create,
319
  model=body.model,
320
+ messages=messages
 
 
 
321
  )
322
+
323
  assistant_message = ""
324
+
 
325
  try:
326
+ assistant_message = response.choices[0].message.content
327
+ except:
 
 
 
 
 
 
 
328
  assistant_message = str(response)
329
+
 
 
 
 
 
 
 
 
 
330
  return JSONResponse({
331
  "id": f"chatcmpl-{uuid.uuid4().hex}",
332
  "object": "chat.completion",
 
343
  }
344
  ],
345
  "usage": {
346
+ "prompt_tokens": 0,
347
+ "completion_tokens": 0,
348
+ "total_tokens": 0
349
  }
350
  })
351
+
352
  except Exception as e:
353
+
354
+ logger.error(f"Chat error: {e}")
355
+
356
  raise HTTPException(
357
  status_code=500,
358
+ detail=str(e)
 
 
 
 
359
  )
360
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  # =====================================================
362
  # RUN
363
  # =====================================================
364
 
365
  if __name__ == "__main__":
366
+
367
  import uvicorn
368
+
 
 
 
369
  uvicorn.run(
370
  app,
371
  host="0.0.0.0",
372
+ port=7860
 
373
  )