bahi-bh commited on
Commit
fdbfb13
·
verified ·
1 Parent(s): 8dede1c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -537
app.py CHANGED
@@ -1,6 +1,6 @@
1
  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
 
@@ -8,8 +8,8 @@ import asyncio
8
  import json
9
  import time
10
  import uuid
11
- import logging
12
  import random
 
13
  import httpx
14
  import g4f
15
 
@@ -30,170 +30,118 @@ logger = logging.getLogger(__name__)
30
 
31
 
32
  # =====================================================
33
- # CONFIG
34
  # =====================================================
35
 
36
- API_KEY = "sk-your-secret-key"
 
 
 
37
 
38
 
39
  # =====================================================
40
- # PROXY CONFIG
41
  # =====================================================
42
 
43
- # قائمة البروكسيات - أضف بروكسياتك هنا
44
- PROXY_LIST = [
45
- # "http://user:pass@proxy1:port",
46
- # "http://user:pass@proxy2:port",
47
- # "socks5://user:pass@proxy3:port",
48
- ]
 
 
 
 
 
 
 
 
 
 
49
 
50
- # بروكسي واحد إذا كان لديك
51
  SINGLE_PROXY = None
52
- # مثال:
53
- # "http://user:pass@host:port"
54
 
55
 
56
  # =====================================================
57
- # PROVIDER MAP - ربط النماذج بمزوديها
58
  # =====================================================
59
 
60
- MODEL_PROVIDER_MAP: Dict[str, list] = {
 
 
 
61
 
62
- # GPT Models
63
- "gpt-4o": [
64
- Provider.Blackbox,
65
- Provider.Liaobots,
66
- Provider.OpenaiChat,
67
- ],
68
-
69
- "gpt-4o-mini": [
70
- Provider.Blackbox,
71
- Provider.DDG,
72
- Provider.Liaobots,
73
- Provider.OpenaiChat,
74
- ],
75
-
76
- "gpt-4": [
77
- Provider.Blackbox,
78
- Provider.Liaobots,
79
- Provider.OpenaiChat,
80
- ],
81
-
82
- "gpt-3.5-turbo": [
83
- Provider.DDG,
84
- Provider.Blackbox,
85
- Provider.OpenaiChat,
86
- ],
87
-
88
- # Claude Models
89
- "claude-3-haiku": [
90
- Provider.DDG,
91
- Provider.Blackbox,
92
- ],
93
-
94
- "claude-3-5-sonnet": [
95
- Provider.Blackbox,
96
- Provider.Liaobots,
97
- ],
98
-
99
- "claude-3-opus": [
100
- Provider.Liaobots,
101
- Provider.Blackbox,
102
- ],
103
-
104
- # Llama Models
105
- "llama-3.1-70b": [
106
- Provider.Blackbox,
107
- Provider.DeepInfra,
108
- Provider.Cloudflare,
109
- ],
110
-
111
- "llama-3.1-8b": [
112
- Provider.Cloudflare,
113
- Provider.DeepInfra,
114
- ],
115
-
116
- "llama-3.2-90b": [
117
- Provider.Blackbox,
118
- Provider.DeepInfra,
119
- ],
120
-
121
- # Mixtral Models
122
- "mixtral-8x7b": [
123
- Provider.DeepInfra,
124
- Provider.Blackbox,
125
- ],
126
-
127
- # Deepseek Models
128
- "deepseek-chat": [
129
- Provider.DeepInfra,
130
- Provider.Blackbox,
131
- ],
132
-
133
- "deepseek-v3": [
134
- Provider.Blackbox,
135
- Provider.DeepInfra,
136
- ],
137
-
138
- # Gemini Models
139
- "gemini-pro": [
140
- Provider.Blackbox,
141
- Provider.Liaobots,
142
- ],
143
-
144
- "gemini-1.5-pro": [
145
- Provider.Blackbox,
146
- Provider.Liaobots,
147
- ],
148
-
149
- # Qwen Models
150
- "qwen-2.5-72b": [
151
- Provider.Blackbox,
152
- Provider.DeepInfra,
153
- ],
154
-
155
- # Default fallback
156
- "default": [
157
- Provider.Blackbox,
158
- Provider.DDG,
159
- Provider.DeepInfra,
160
- ],
161
- }
162
 
163
 
164
  # =====================================================
165
- # FALLBACK MODELS - إذا فشل النموذج المطلوب
166
  # =====================================================
167
 
168
- FALLBACK_MODELS = [
169
- "gpt-4o-mini",
170
- "gpt-3.5-turbo",
171
- "llama-3.1-70b",
 
 
 
 
 
 
 
172
  ]
173
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  # =====================================================
176
- # FASTAPI
177
  # =====================================================
178
 
179
- app = FastAPI(
180
- title="Universal AI Gateway",
181
- description="بوابة ذكاء اصطناعي شاملة مع دعم البروكسي",
182
- version="5.0.0"
183
- )
 
 
 
 
 
 
 
184
 
185
 
186
  # =====================================================
187
- # CORS
188
  # =====================================================
189
 
190
- app.add_middleware(
191
- CORSMiddleware,
192
- allow_origins=["*"],
193
- allow_credentials=True,
194
- allow_methods=["*"],
195
- allow_headers=["*"],
196
- )
197
 
198
 
199
  # =====================================================
@@ -214,75 +162,46 @@ class ChatRequest(BaseModel):
214
 
215
 
216
  # =====================================================
217
- # PROXY HELPER
218
  # =====================================================
219
 
220
- def get_proxy() -> Optional[str]:
221
- """الحصول على بروكسي من القائمة"""
222
 
223
- # إذا كان هناك بروكسي واحد محدد
224
  if SINGLE_PROXY:
225
- logger.info(f"Using single proxy: {SINGLE_PROXY[:20]}...")
226
  return SINGLE_PROXY
227
 
228
- # اختيار عشوائي من القائمة
229
  if PROXY_LIST:
230
- proxy = random.choice(PROXY_LIST)
231
- logger.info(f"Using proxy from list: {proxy[:20]}...")
232
- return proxy
233
 
234
  return None
235
 
236
 
237
- def get_g4f_proxy_config() -> dict:
238
- """إعداد البروكسي لـ g4f"""
239
-
240
- proxy = get_proxy()
241
-
242
- if proxy:
243
- return {"proxy": proxy}
244
-
245
- return {}
246
-
247
-
248
- # =====================================================
249
- # CLIENT BUILDER
250
- # =====================================================
251
-
252
- def build_client(proxy: Optional[str] = None) -> Client:
253
- """بناء عميل g4f مع البروكسي"""
254
 
255
- proxy_url = proxy or get_proxy()
 
 
256
 
257
- if proxy_url:
258
- try:
259
- client = Client(proxies=proxy_url)
260
- logger.info("Client created with proxy")
261
- return client
262
 
263
- except Exception as e:
264
- logger.warning(f"Failed to create client with proxy: {e}")
265
 
266
- return Client()
267
 
 
268
 
269
- # =====================================================
270
- # AUTH
271
- # =====================================================
272
 
273
  def verify_api_key(req: Request):
274
- """التحقق من مفتاح API"""
275
 
276
  auth = req.headers.get("Authorization")
277
 
278
- # السماح بدون مفتاح للاختبار
279
  if not auth:
280
  return True
281
 
282
  if not auth.startswith("Bearer "):
283
  raise HTTPException(
284
  status_code=401,
285
- detail="Invalid Authorization Format - يجب أن يبدأ بـ Bearer"
286
  )
287
 
288
  token = auth.replace("Bearer ", "").strip()
@@ -290,176 +209,112 @@ def verify_api_key(req: Request):
290
  if token != API_KEY:
291
  raise HTTPException(
292
  status_code=403,
293
- detail="Invalid API Key - مفتاح API غير صحيح"
294
  )
295
 
296
  return True
297
 
298
 
299
  # =====================================================
300
- # CORE: SMART COMPLETION WITH RETRY + FALLBACK
301
  # =====================================================
302
 
303
  async def smart_completion(
304
  model: str,
305
  messages: list,
306
  stream: bool = False,
307
- max_retries: int = 3,
308
- proxy: Optional[str] = None
309
  ):
310
- """
311
- محاولة ذكية مع:
312
- - تجربة عدة مزودين
313
- - إعادة المحاولة عند الفشل
314
- - التحويل لنموذج بديل
315
- """
316
 
317
- # الحصول على قائمة المزودين للنموذج
318
  providers = MODEL_PROVIDER_MAP.get(
319
  model,
320
  MODEL_PROVIDER_MAP["default"]
321
  )
322
 
323
- # خلط المزودين عشوائياً لتوزيع الحمل
324
- providers_shuffled = providers.copy()
325
- random.shuffle(providers_shuffled)
 
 
326
 
327
- last_error = None
328
 
329
- # -----------------------------------------------
330
- # المحاولة مع كل مزود
331
- # -----------------------------------------------
332
 
333
- for attempt in range(max_retries):
334
 
335
- for provider in providers_shuffled:
336
 
337
  try:
 
338
  logger.info(
339
- f"Attempt {attempt + 1} | Model: {model} | Provider: {provider.__name__}"
340
  )
341
 
 
 
342
  client = build_client(proxy)
343
- proxy_config = get_g4f_proxy_config()
344
 
345
  response = await asyncio.to_thread(
346
  client.chat.completions.create,
347
  model=model,
348
  messages=messages,
349
  provider=provider,
350
- stream=stream,
351
- **proxy_config
352
  )
353
 
354
  logger.info(
355
- f" Success | Model: {model} | Provider: {provider.__name__}"
356
  )
357
 
358
  return response, model
359
 
360
  except Exception as e:
 
361
  last_error = e
362
 
363
  logger.warning(
364
- f" Failed | Model: {model} | Provider: {provider.__name__} | Error: {str(e)[:100]}"
365
  )
366
 
367
- # انتظار قصير قبل المحاولة التالية
368
- await asyncio.sleep(0.5)
369
-
370
- # انتظار قبل دورة المحاولات الجديدة
371
- if attempt < max_retries - 1:
372
- wait_time = (attempt + 1) * 1.5
373
-
374
- logger.info(
375
- f"Waiting {wait_time}s before retry {attempt + 2}..."
376
- )
377
-
378
- await asyncio.sleep(wait_time)
379
 
380
- # -----------------------------------------------
381
- # Fallback: تجربة نماذج بديلة
382
- # -----------------------------------------------
383
-
384
- logger.warning(
385
- f"All providers failed for {model}. Trying fallback models..."
386
- )
387
 
388
  for fallback_model in FALLBACK_MODELS:
389
 
390
- if fallback_model == model:
391
- continue
392
-
393
- fallback_providers = MODEL_PROVIDER_MAP.get(
394
- fallback_model,
395
- MODEL_PROVIDER_MAP["default"]
396
- )
397
-
398
- for fb_provider in fallback_providers[:2]:
399
-
400
- try:
401
- logger.info(
402
- f"🔄 Fallback | Model: {fallback_model} | Provider: {fb_provider.__name__}"
403
- )
404
-
405
- client = build_client(proxy)
406
- proxy_config = get_g4f_proxy_config()
407
-
408
- response = await asyncio.to_thread(
409
- client.chat.completions.create,
410
- model=fallback_model,
411
- messages=messages,
412
- provider=fb_provider,
413
- stream=stream,
414
- **proxy_config
415
- )
416
-
417
- logger.info(
418
- f"✅ Fallback Success | Model: {fallback_model} | Provider: {fb_provider.__name__}"
419
- )
420
-
421
- return response, fallback_model
422
-
423
- except Exception as e:
424
-
425
- logger.warning(
426
- f"❌ Fallback Failed | Model: {fallback_model} | Error: {str(e)[:100]}"
427
- )
428
-
429
- await asyncio.sleep(0.3)
430
-
431
- # -----------------------------------------------
432
- # آخر محاولة: بدون تحديد مزود
433
- # -----------------------------------------------
434
-
435
- try:
436
 
437
- logger.info(f"🆘 Last resort attempt for model: {model}")
 
 
438
 
439
- client = build_client(proxy)
440
 
441
- response = await asyncio.to_thread(
442
- client.chat.completions.create,
443
- model=model,
444
- messages=messages,
445
- stream=stream
446
- )
447
 
448
- return response, model
 
 
 
 
 
449
 
450
- except Exception as e:
451
 
452
- last_error = e
453
 
454
- logger.error(f"💀 All attempts failed: {str(e)}")
 
 
455
 
456
  raise HTTPException(
457
  status_code=503,
458
  detail={
459
- "error": "All providers failed",
460
- "message": str(last_error),
461
- "model_requested": model,
462
- "suggestion": "Try again later or use a different model"
463
  }
464
  )
465
 
@@ -473,135 +328,82 @@ async def root():
473
 
474
  return {
475
  "status": "online",
476
- "service": "Universal AI Gateway",
477
- "version": "5.0.0",
478
- "proxy_enabled": bool(PROXY_LIST or SINGLE_PROXY),
479
- "supported_models": list(MODEL_PROVIDER_MAP.keys()),
480
- "endpoints": {
481
- "chat": "/v1/chat/completions",
482
- "models": "/v1/models",
483
- "health": "/health"
484
- }
485
  }
486
 
487
 
488
  # =====================================================
489
- # HEALTH CHECK
490
  # =====================================================
491
 
492
  @app.get("/health")
493
- async def health_check():
494
- """فحص صحة الخدمة"""
495
 
496
  proxy_status = "disabled"
497
 
498
- if SINGLE_PROXY or PROXY_LIST:
499
 
500
- proxy_status = "enabled"
501
 
502
- # اختبار البروكسي
503
- proxy = get_proxy()
504
 
505
- if proxy:
506
- try:
507
- async with httpx.AsyncClient(
508
- proxies=proxy,
509
- timeout=10
510
- ) as client:
511
 
512
- resp = await client.get(
513
- "https://api.ipify.org?format=json"
514
- )
515
 
516
- ip_data = resp.json()
517
 
518
- proxy_status = (
519
- f"working - IP: {ip_data.get('ip', 'unknown')}"
520
- )
521
 
522
- except Exception as e:
523
- proxy_status = f"error - {str(e)[:50]}"
 
524
 
525
  return {
526
  "status": "healthy",
527
- "timestamp": int(time.time()),
528
  "proxy": proxy_status,
529
- "providers_count": len(MODEL_PROVIDER_MAP),
530
- "fallback_models": FALLBACK_MODELS
531
  }
532
 
533
 
534
  # =====================================================
535
- # MODELS LIST
536
  # =====================================================
537
 
538
  @app.get("/v1/models")
539
- async def get_models():
540
- """قائمة النماذج المتاحة"""
541
 
542
- models_data = []
543
 
544
- # النماذج من الخريطة
545
  for model_id in MODEL_PROVIDER_MAP.keys():
546
 
547
  if model_id == "default":
548
  continue
549
 
550
- providers_count = len(MODEL_PROVIDER_MAP[model_id])
551
-
552
- models_data.append({
553
  "id": model_id,
554
  "object": "model",
555
  "created": int(time.time()),
556
- "owned_by": "g4f",
557
- "providers_available": providers_count,
558
- "permission": [],
559
- "root": model_id,
560
- "parent": None
561
  })
562
 
563
- # إضافة نماذج إضافية من g4f
564
- try:
565
-
566
- if hasattr(g4f.models, "_all_models"):
567
-
568
- all_models = list(g4f.models._all_models)
569
-
570
- existing_ids = {
571
- m["id"] for m in models_data
572
- }
573
-
574
- for model in all_models[:30]:
575
-
576
- model_str = str(model)
577
-
578
- if model_str not in existing_ids:
579
-
580
- models_data.append({
581
- "id": model_str,
582
- "object": "model",
583
- "created": int(time.time()),
584
- "owned_by": "g4f",
585
- "providers_available": 1,
586
- "permission": [],
587
- "root": model_str,
588
- "parent": None
589
- })
590
-
591
- existing_ids.add(model_str)
592
-
593
- except Exception as e:
594
- logger.warning(f"Could not fetch g4f models: {e}")
595
-
596
  return {
597
  "object": "list",
598
- "data": models_data,
599
- "total": len(models_data)
600
  }
601
 
602
 
603
  # =====================================================
604
- # CHAT COMPLETIONS - MAIN ENDPOINT
605
  # =====================================================
606
 
607
  @app.post("/v1/chat/completions")
@@ -609,11 +411,9 @@ async def chat_completions(
609
  req: Request,
610
  body: ChatRequest
611
  ):
612
- """نقطة نهاية المحادثة الرئيسية"""
613
 
614
  verify_api_key(req)
615
 
616
- # تحويل الرسائل
617
  messages = [
618
  {
619
  "role": m.role,
@@ -623,120 +423,36 @@ async def chat_completions(
623
  ]
624
 
625
  logger.info(
626
- f"📨 Request | model={body.model} | stream={body.stream} | messages={len(messages)}"
627
  )
628
 
629
  # =================================================
630
- # STREAMING
631
  # =================================================
632
 
633
  if body.stream:
634
 
635
- async def generate_stream():
636
 
637
  try:
638
 
639
- # الحصول على البروكسي
640
- proxy = get_proxy()
641
-
642
- chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
643
- actual_model = body.model
644
-
645
- # محاولة مع كل مزود
646
- providers = MODEL_PROVIDER_MAP.get(
647
- body.model,
648
- MODEL_PROVIDER_MAP["default"]
649
  )
650
 
651
- providers_shuffled = providers.copy()
652
- random.shuffle(providers_shuffled)
653
-
654
- response = None
655
- last_err = None
656
-
657
- # تجربة كل مزود
658
- for provider in providers_shuffled:
659
-
660
- try:
661
- logger.info(
662
- f"Stream attempt | Provider: {provider.__name__}"
663
- )
664
-
665
- client = build_client(proxy)
666
- proxy_config = get_g4f_proxy_config()
667
-
668
- response = await asyncio.to_thread(
669
- client.chat.completions.create,
670
- model=body.model,
671
- messages=messages,
672
- provider=provider,
673
- stream=True,
674
- **proxy_config
675
- )
676
-
677
- logger.info(
678
- f"✅ Stream connected | Provider: {provider.__name__}"
679
- )
680
-
681
- break
682
-
683
- except Exception as e:
684
-
685
- last_err = e
686
-
687
- logger.warning(
688
- f"❌ Stream provider failed: {provider.__name__} | {str(e)[:80]}"
689
- )
690
-
691
- await asyncio.sleep(0.3)
692
-
693
- # إذا فشل كل شيء، جرب بدون مزود محدد
694
- if response is None:
695
-
696
- try:
697
- client = build_client(proxy)
698
-
699
- response = await asyncio.to_thread(
700
- client.chat.completions.create,
701
- model=body.model,
702
- messages=messages,
703
- stream=True
704
- )
705
-
706
- logger.info(
707
- "✅ Stream connected without specific provider"
708
- )
709
-
710
- except Exception as e:
711
-
712
- last_err = e
713
-
714
- logger.error(
715
- f"💀 Stream completely failed: {e}"
716
- )
717
-
718
- error_payload = {
719
- "error": {
720
- "message": str(e),
721
- "type": "server_error",
722
- "code": 503
723
- }
724
- }
725
-
726
- yield f"data: {json.dumps(error_payload)}\n\n"
727
- return
728
-
729
- # إرسال البيانات
730
- has_content = False
731
 
732
  for chunk in response:
733
 
734
  try:
 
735
  content = ""
736
 
737
  if (
738
- chunk.choices
739
- and len(chunk.choices) > 0
740
  and chunk.choices[0].delta
741
  and chunk.choices[0].delta.content
742
  ):
@@ -744,8 +460,6 @@ async def chat_completions(
744
 
745
  if content:
746
 
747
- has_content = True
748
-
749
  payload = {
750
  "id": chunk_id,
751
  "object": "chat.completion.chunk",
@@ -768,63 +482,28 @@ async def chat_completions(
768
  f"{json.dumps(payload, ensure_ascii=False)}\n\n"
769
  )
770
 
771
- await asyncio.sleep(0)
772
 
773
- except Exception as chunk_error:
774
  logger.error(
775
- f"Chunk processing error: {chunk_error}"
776
  )
777
 
778
- continue
779
-
780
- # إذا لم يكن هناك محتوى
781
- if not has_content:
782
- logger.warning("Stream produced no content!")
783
-
784
- # إشارة الانتهاء
785
- final_payload = {
786
- "id": chunk_id,
787
- "object": "chat.completion.chunk",
788
- "created": int(time.time()),
789
- "model": actual_model,
790
- "choices": [
791
- {
792
- "index": 0,
793
- "delta": {},
794
- "finish_reason": "stop"
795
- }
796
- ]
797
- }
798
-
799
- yield (
800
- f"data: "
801
- f"{json.dumps(final_payload)}\n\n"
802
- )
803
-
804
  yield "data: [DONE]\n\n"
805
 
806
  except Exception as e:
807
 
808
- logger.error(f"Stream generation error: {e}")
809
-
810
  error_payload = {
811
- "error": {
812
- "message": str(e),
813
- "type": "server_error"
814
- }
815
  }
816
 
817
- yield f"data: {json.dumps(error_payload)}\n\n"
 
 
 
818
 
819
  return StreamingResponse(
820
- generate_stream(),
821
- media_type="text/event-stream",
822
- headers={
823
- "Cache-Control": "no-cache",
824
- "Connection": "keep-alive",
825
- "X-Accel-Buffering": "no",
826
- "Transfer-Encoding": "chunked"
827
- }
828
  )
829
 
830
  # =================================================
@@ -833,33 +512,24 @@ async def chat_completions(
833
 
834
  try:
835
 
836
- proxy = get_proxy()
837
-
838
  response, actual_model = await smart_completion(
839
  model=body.model,
840
  messages=messages,
841
- stream=False,
842
- max_retries=3,
843
- proxy=proxy
844
  )
845
 
846
- # استخراج المحتوى
847
  assistant_message = ""
848
 
849
  try:
 
850
  assistant_message = (
851
- response.choices[0].message.content or ""
852
  )
853
 
854
- except AttributeError:
855
-
856
- try:
857
- assistant_message = str(response)
858
 
859
- except Exception:
860
- assistant_message = "Error extracting response"
861
 
862
- # حساب الرموز تقريبياً
863
  prompt_tokens = sum(
864
  len(m["content"].split())
865
  for m in messages
@@ -898,15 +568,11 @@ async def chat_completions(
898
 
899
  except Exception as e:
900
 
901
- logger.error(f"Chat completion error: {e}")
902
 
903
  raise HTTPException(
904
  status_code=500,
905
- detail={
906
- "error": str(e),
907
- "model": body.model,
908
- "message": "Internal server error"
909
- }
910
  )
911
 
912
 
@@ -918,16 +584,10 @@ if __name__ == "__main__":
918
 
919
  import uvicorn
920
 
921
- logger.info(
922
- "🚀 Starting Universal AI Gateway v5.0.0"
923
- )
924
-
925
- logger.info(
926
- f"🔌 Proxy enabled: {bool(PROXY_LIST or SINGLE_PROXY)}"
927
- )
928
 
929
  logger.info(
930
- f"📦 Models configured: {len(MODEL_PROVIDER_MAP)}"
931
  )
932
 
933
  uvicorn.run(
 
1
  from fastapi import FastAPI, Request, HTTPException
2
  from fastapi.middleware.cors import CORSMiddleware
3
+ from fastapi.responses import JSONResponse, StreamingResponse
4
  from pydantic import BaseModel
5
  from typing import List, Optional, Dict
6
 
 
8
  import json
9
  import time
10
  import uuid
 
11
  import random
12
+ import logging
13
  import httpx
14
  import g4f
15
 
 
30
 
31
 
32
  # =====================================================
33
+ # APP
34
  # =====================================================
35
 
36
+ app = FastAPI(
37
+ title="Universal AI Gateway",
38
+ version="6.0.0"
39
+ )
40
 
41
 
42
  # =====================================================
43
+ # CORS
44
  # =====================================================
45
 
46
+ app.add_middleware(
47
+ CORSMiddleware,
48
+ allow_origins=["*"],
49
+ allow_credentials=True,
50
+ allow_methods=["*"],
51
+ allow_headers=["*"],
52
+ )
53
+
54
+
55
+ # =====================================================
56
+ # CONFIG
57
+ # =====================================================
58
+
59
+ API_KEY = "sk-your-secret-key"
60
+
61
+ PROXY_LIST = []
62
 
 
63
  SINGLE_PROXY = None
 
 
64
 
65
 
66
  # =====================================================
67
+ # SAFE PROVIDER GETTER
68
  # =====================================================
69
 
70
+ def provider_exists(name: str):
71
+ """
72
+ التحقق من وجود المزود داخل g4f
73
+ """
74
 
75
+ return hasattr(Provider, name)
76
+
77
+
78
+ def get_provider(name: str):
79
+ """
80
+ جلب المزود إذا كان موجود
81
+ """
82
+
83
+ if provider_exists(name):
84
+ return getattr(Provider, name)
85
+
86
+ return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
 
89
  # =====================================================
90
+ # AUTO DETECT PROVIDERS
91
  # =====================================================
92
 
93
+ AVAILABLE_PROVIDERS = []
94
+
95
+ PREFERRED_PROVIDERS = [
96
+ "Blackbox",
97
+ "BlackboxPro",
98
+ "Liaobots",
99
+ "DDG",
100
+ "OpenaiChat",
101
+ "DeepInfra",
102
+ "Cloudflare",
103
+ "RetryProvider",
104
  ]
105
 
106
+ for provider_name in PREFERRED_PROVIDERS:
107
+
108
+ provider = get_provider(provider_name)
109
+
110
+ if provider:
111
+ AVAILABLE_PROVIDERS.append(provider)
112
+ logger.info(f"Loaded provider: {provider_name}")
113
+
114
+ else:
115
+ logger.warning(f"Provider not found: {provider_name}")
116
+
117
 
118
  # =====================================================
119
+ # MODEL MAP
120
  # =====================================================
121
 
122
+ MODEL_PROVIDER_MAP: Dict[str, list] = {
123
+ "gpt-4o": AVAILABLE_PROVIDERS,
124
+ "gpt-4o-mini": AVAILABLE_PROVIDERS,
125
+ "gpt-4": AVAILABLE_PROVIDERS,
126
+ "gpt-3.5-turbo": AVAILABLE_PROVIDERS,
127
+ "claude-3-haiku": AVAILABLE_PROVIDERS,
128
+ "claude-3-sonnet": AVAILABLE_PROVIDERS,
129
+ "deepseek-chat": AVAILABLE_PROVIDERS,
130
+ "llama-3.1-70b": AVAILABLE_PROVIDERS,
131
+ "gemini-pro": AVAILABLE_PROVIDERS,
132
+ "default": AVAILABLE_PROVIDERS,
133
+ }
134
 
135
 
136
  # =====================================================
137
+ # FALLBACK MODELS
138
  # =====================================================
139
 
140
+ FALLBACK_MODELS = [
141
+ "gpt-4o-mini",
142
+ "gpt-3.5-turbo",
143
+ "deepseek-chat",
144
+ ]
 
 
145
 
146
 
147
  # =====================================================
 
162
 
163
 
164
  # =====================================================
165
+ # HELPERS
166
  # =====================================================
167
 
168
+ def get_proxy():
 
169
 
 
170
  if SINGLE_PROXY:
 
171
  return SINGLE_PROXY
172
 
 
173
  if PROXY_LIST:
174
+ return random.choice(PROXY_LIST)
 
 
175
 
176
  return None
177
 
178
 
179
+ def build_client(proxy=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
+ try:
182
+ if proxy:
183
+ return Client(proxies=proxy)
184
 
185
+ return Client()
 
 
 
 
186
 
187
+ except Exception as e:
 
188
 
189
+ logger.error(f"Client error: {e}")
190
 
191
+ return Client()
192
 
 
 
 
193
 
194
  def verify_api_key(req: Request):
 
195
 
196
  auth = req.headers.get("Authorization")
197
 
 
198
  if not auth:
199
  return True
200
 
201
  if not auth.startswith("Bearer "):
202
  raise HTTPException(
203
  status_code=401,
204
+ detail="Invalid Authorization format"
205
  )
206
 
207
  token = auth.replace("Bearer ", "").strip()
 
209
  if token != API_KEY:
210
  raise HTTPException(
211
  status_code=403,
212
+ detail="Invalid API key"
213
  )
214
 
215
  return True
216
 
217
 
218
  # =====================================================
219
+ # SMART COMPLETION
220
  # =====================================================
221
 
222
  async def smart_completion(
223
  model: str,
224
  messages: list,
225
  stream: bool = False,
226
+ retries: int = 3
 
227
  ):
 
 
 
 
 
 
228
 
 
229
  providers = MODEL_PROVIDER_MAP.get(
230
  model,
231
  MODEL_PROVIDER_MAP["default"]
232
  )
233
 
234
+ if not providers:
235
+ raise HTTPException(
236
+ status_code=500,
237
+ detail="No providers available"
238
+ )
239
 
240
+ random.shuffle(providers)
241
 
242
+ last_error = None
 
 
243
 
244
+ for attempt in range(retries):
245
 
246
+ for provider in providers:
247
 
248
  try:
249
+
250
  logger.info(
251
+ f"Attempt {attempt+1} | Provider: {provider.__name__}"
252
  )
253
 
254
+ proxy = get_proxy()
255
+
256
  client = build_client(proxy)
 
257
 
258
  response = await asyncio.to_thread(
259
  client.chat.completions.create,
260
  model=model,
261
  messages=messages,
262
  provider=provider,
263
+ stream=stream
 
264
  )
265
 
266
  logger.info(
267
+ f"SUCCESS => {provider.__name__}"
268
  )
269
 
270
  return response, model
271
 
272
  except Exception as e:
273
+
274
  last_error = e
275
 
276
  logger.warning(
277
+ f"FAILED => {provider.__name__} | {e}"
278
  )
279
 
280
+ await asyncio.sleep(1)
 
 
 
 
 
 
 
 
 
 
 
281
 
282
+ # ============================================
283
+ # FALLBACK MODELS
284
+ # ============================================
 
 
 
 
285
 
286
  for fallback_model in FALLBACK_MODELS:
287
 
288
+ try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
 
290
+ logger.info(
291
+ f"Trying fallback model: {fallback_model}"
292
+ )
293
 
294
+ proxy = get_proxy()
295
 
296
+ client = build_client(proxy)
 
 
 
 
 
297
 
298
+ response = await asyncio.to_thread(
299
+ client.chat.completions.create,
300
+ model=fallback_model,
301
+ messages=messages,
302
+ stream=stream
303
+ )
304
 
305
+ return response, fallback_model
306
 
307
+ except Exception as e:
308
 
309
+ logger.warning(
310
+ f"Fallback failed: {e}"
311
+ )
312
 
313
  raise HTTPException(
314
  status_code=503,
315
  detail={
316
+ "error": str(last_error),
317
+ "message": "All providers failed"
 
 
318
  }
319
  )
320
 
 
328
 
329
  return {
330
  "status": "online",
331
+ "version": "6.0.0",
332
+ "providers_loaded": [
333
+ p.__name__
334
+ for p in AVAILABLE_PROVIDERS
335
+ ]
 
 
 
 
336
  }
337
 
338
 
339
  # =====================================================
340
+ # HEALTH
341
  # =====================================================
342
 
343
  @app.get("/health")
344
+ async def health():
 
345
 
346
  proxy_status = "disabled"
347
 
348
+ proxy = get_proxy()
349
 
350
+ if proxy:
351
 
352
+ try:
 
353
 
354
+ async with httpx.AsyncClient(
355
+ proxies=proxy,
356
+ timeout=10
357
+ ) as client:
 
 
358
 
359
+ r = await client.get(
360
+ "https://api.ipify.org?format=json"
361
+ )
362
 
363
+ ip = r.json().get("ip")
364
 
365
+ proxy_status = f"working ({ip})"
 
 
366
 
367
+ except Exception as e:
368
+
369
+ proxy_status = f"error: {e}"
370
 
371
  return {
372
  "status": "healthy",
 
373
  "proxy": proxy_status,
374
+ "providers_count": len(AVAILABLE_PROVIDERS)
 
375
  }
376
 
377
 
378
  # =====================================================
379
+ # MODELS
380
  # =====================================================
381
 
382
  @app.get("/v1/models")
383
+ async def models():
 
384
 
385
+ data = []
386
 
 
387
  for model_id in MODEL_PROVIDER_MAP.keys():
388
 
389
  if model_id == "default":
390
  continue
391
 
392
+ data.append({
 
 
393
  "id": model_id,
394
  "object": "model",
395
  "created": int(time.time()),
396
+ "owned_by": "g4f"
 
 
 
 
397
  })
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
  return {
400
  "object": "list",
401
+ "data": data
 
402
  }
403
 
404
 
405
  # =====================================================
406
+ # CHAT COMPLETIONS
407
  # =====================================================
408
 
409
  @app.post("/v1/chat/completions")
 
411
  req: Request,
412
  body: ChatRequest
413
  ):
 
414
 
415
  verify_api_key(req)
416
 
 
417
  messages = [
418
  {
419
  "role": m.role,
 
423
  ]
424
 
425
  logger.info(
426
+ f"Request => model={body.model} stream={body.stream}"
427
  )
428
 
429
  # =================================================
430
+ # STREAM
431
  # =================================================
432
 
433
  if body.stream:
434
 
435
+ async def event_stream():
436
 
437
  try:
438
 
439
+ response, actual_model = await smart_completion(
440
+ model=body.model,
441
+ messages=messages,
442
+ stream=True
 
 
 
 
 
 
443
  )
444
 
445
+ chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
 
447
  for chunk in response:
448
 
449
  try:
450
+
451
  content = ""
452
 
453
  if (
454
+ hasattr(chunk, "choices")
455
+ and chunk.choices
456
  and chunk.choices[0].delta
457
  and chunk.choices[0].delta.content
458
  ):
 
460
 
461
  if content:
462
 
 
 
463
  payload = {
464
  "id": chunk_id,
465
  "object": "chat.completion.chunk",
 
482
  f"{json.dumps(payload, ensure_ascii=False)}\n\n"
483
  )
484
 
485
+ except Exception as e:
486
 
 
487
  logger.error(
488
+ f"Chunk error: {e}"
489
  )
490
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  yield "data: [DONE]\n\n"
492
 
493
  except Exception as e:
494
 
 
 
495
  error_payload = {
496
+ "error": str(e)
 
 
 
497
  }
498
 
499
+ yield (
500
+ f"data: "
501
+ f"{json.dumps(error_payload)}\n\n"
502
+ )
503
 
504
  return StreamingResponse(
505
+ event_stream(),
506
+ media_type="text/event-stream"
 
 
 
 
 
 
507
  )
508
 
509
  # =================================================
 
512
 
513
  try:
514
 
 
 
515
  response, actual_model = await smart_completion(
516
  model=body.model,
517
  messages=messages,
518
+ stream=False
 
 
519
  )
520
 
 
521
  assistant_message = ""
522
 
523
  try:
524
+
525
  assistant_message = (
526
+ response.choices[0].message.content
527
  )
528
 
529
+ except Exception:
 
 
 
530
 
531
+ assistant_message = str(response)
 
532
 
 
533
  prompt_tokens = sum(
534
  len(m["content"].split())
535
  for m in messages
 
568
 
569
  except Exception as e:
570
 
571
+ logger.error(f"Chat error: {e}")
572
 
573
  raise HTTPException(
574
  status_code=500,
575
+ detail=str(e)
 
 
 
 
576
  )
577
 
578
 
 
584
 
585
  import uvicorn
586
 
587
+ logger.info("Starting Universal AI Gateway v6")
 
 
 
 
 
 
588
 
589
  logger.info(
590
+ f"Providers loaded: {len(AVAILABLE_PROVIDERS)}"
591
  )
592
 
593
  uvicorn.run(