bahi-bh commited on
Commit
154a194
ยท
verified ยท
1 Parent(s): 1b0f789

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +146 -29
app.py CHANGED
@@ -1,8 +1,9 @@
1
-
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
@@ -10,11 +11,12 @@ import uuid
10
  import logging
11
  import random
12
  import httpx
13
-
14
  import g4f
 
15
  from g4f.client import Client
16
  from g4f import Provider
17
 
 
18
  # =====================================================
19
  # LOGGING
20
  # =====================================================
@@ -23,14 +25,17 @@ logging.basicConfig(
23
  level=logging.INFO,
24
  format="%(asctime)s [%(levelname)s] %(message)s"
25
  )
 
26
  logger = logging.getLogger(__name__)
27
 
 
28
  # =====================================================
29
  # CONFIG
30
  # =====================================================
31
 
32
  API_KEY = "sk-your-secret-key"
33
 
 
34
  # =====================================================
35
  # PROXY CONFIG
36
  # =====================================================
@@ -43,90 +48,110 @@ PROXY_LIST = [
43
  ]
44
 
45
  # ุจุฑูˆูƒุณูŠ ูˆุงุญุฏ ุฅุฐุง ูƒุงู† ู„ุฏูŠูƒ
46
- SINGLE_PROXY = None # ู…ุซุงู„: "http://user:pass@host:port"
 
 
 
47
 
48
  # =====================================================
49
  # PROVIDER MAP - ุฑุจุท ุงู„ู†ู…ุงุฐุฌ ุจู…ุฒูˆุฏูŠู‡ุง
50
  # =====================================================
51
 
52
  MODEL_PROVIDER_MAP: Dict[str, list] = {
 
53
  # GPT Models
54
  "gpt-4o": [
55
  Provider.Blackbox,
56
  Provider.Liaobots,
57
  Provider.OpenaiChat,
58
  ],
 
59
  "gpt-4o-mini": [
60
  Provider.Blackbox,
61
  Provider.DDG,
62
  Provider.Liaobots,
63
  Provider.OpenaiChat,
64
  ],
 
65
  "gpt-4": [
66
  Provider.Blackbox,
67
  Provider.Liaobots,
68
  Provider.OpenaiChat,
69
  ],
 
70
  "gpt-3.5-turbo": [
71
  Provider.DDG,
72
  Provider.Blackbox,
73
  Provider.OpenaiChat,
74
  ],
 
75
  # Claude Models
76
  "claude-3-haiku": [
77
  Provider.DDG,
78
  Provider.Blackbox,
79
  ],
 
80
  "claude-3-5-sonnet": [
81
  Provider.Blackbox,
82
  Provider.Liaobots,
83
  ],
 
84
  "claude-3-opus": [
85
  Provider.Liaobots,
86
  Provider.Blackbox,
87
  ],
 
88
  # Llama Models
89
  "llama-3.1-70b": [
90
  Provider.Blackbox,
91
  Provider.DeepInfra,
92
  Provider.Cloudflare,
93
  ],
 
94
  "llama-3.1-8b": [
95
  Provider.Cloudflare,
96
  Provider.DeepInfra,
97
  ],
 
98
  "llama-3.2-90b": [
99
  Provider.Blackbox,
100
  Provider.DeepInfra,
101
  ],
 
102
  # Mixtral Models
103
  "mixtral-8x7b": [
104
  Provider.DeepInfra,
105
  Provider.Blackbox,
106
  ],
 
107
  # Deepseek Models
108
  "deepseek-chat": [
109
  Provider.DeepInfra,
110
  Provider.Blackbox,
111
  ],
 
112
  "deepseek-v3": [
113
  Provider.Blackbox,
114
  Provider.DeepInfra,
115
  ],
 
116
  # Gemini Models
117
  "gemini-pro": [
118
  Provider.Blackbox,
119
  Provider.Liaobots,
120
  ],
 
121
  "gemini-1.5-pro": [
122
  Provider.Blackbox,
123
  Provider.Liaobots,
124
  ],
 
125
  # Qwen Models
126
  "qwen-2.5-72b": [
127
  Provider.Blackbox,
128
  Provider.DeepInfra,
129
  ],
 
130
  # Default fallback
131
  "default": [
132
  Provider.Blackbox,
@@ -135,6 +160,7 @@ MODEL_PROVIDER_MAP: Dict[str, list] = {
135
  ],
136
  }
137
 
 
138
  # =====================================================
139
  # FALLBACK MODELS - ุฅุฐุง ูุดู„ ุงู„ู†ู…ูˆุฐุฌ ุงู„ู…ุทู„ูˆุจ
140
  # =====================================================
@@ -145,6 +171,7 @@ FALLBACK_MODELS = [
145
  "llama-3.1-70b",
146
  ]
147
 
 
148
  # =====================================================
149
  # FASTAPI
150
  # =====================================================
@@ -155,6 +182,7 @@ app = FastAPI(
155
  version="5.0.0"
156
  )
157
 
 
158
  # =====================================================
159
  # CORS
160
  # =====================================================
@@ -167,6 +195,7 @@ app.add_middleware(
167
  allow_headers=["*"],
168
  )
169
 
 
170
  # =====================================================
171
  # MODELS
172
  # =====================================================
@@ -175,6 +204,7 @@ class Message(BaseModel):
175
  role: str
176
  content: str
177
 
 
178
  class ChatRequest(BaseModel):
179
  model: str
180
  messages: List[Message]
@@ -182,6 +212,7 @@ class ChatRequest(BaseModel):
182
  temperature: Optional[float] = 0.7
183
  max_tokens: Optional[int] = 4096
184
 
 
185
  # =====================================================
186
  # PROXY HELPER
187
  # =====================================================
@@ -202,19 +233,25 @@ def get_proxy() -> Optional[str]:
202
 
203
  return None
204
 
 
205
  def get_g4f_proxy_config() -> dict:
206
  """ุฅุนุฏุงุฏ ุงู„ุจุฑูˆูƒุณูŠ ู„ู€ g4f"""
 
207
  proxy = get_proxy()
 
208
  if proxy:
209
  return {"proxy": proxy}
 
210
  return {}
211
 
 
212
  # =====================================================
213
  # CLIENT BUILDER
214
  # =====================================================
215
 
216
  def build_client(proxy: Optional[str] = None) -> Client:
217
  """ุจู†ุงุก ุนู…ูŠู„ g4f ู…ุน ุงู„ุจุฑูˆูƒุณูŠ"""
 
218
  proxy_url = proxy or get_proxy()
219
 
220
  if proxy_url:
@@ -222,17 +259,20 @@ def build_client(proxy: Optional[str] = None) -> Client:
222
  client = Client(proxies=proxy_url)
223
  logger.info("Client created with proxy")
224
  return client
 
225
  except Exception as e:
226
  logger.warning(f"Failed to create client with proxy: {e}")
227
 
228
  return Client()
229
 
 
230
  # =====================================================
231
  # AUTH
232
  # =====================================================
233
 
234
  def verify_api_key(req: Request):
235
  """ุงู„ุชุญู‚ู‚ ู…ู† ู…ูุชุงุญ API"""
 
236
  auth = req.headers.get("Authorization")
237
 
238
  # ุงู„ุณู…ุงุญ ุจุฏูˆู† ู…ูุชุงุญ ู„ู„ุงุฎุชุจุงุฑ
@@ -255,6 +295,7 @@ def verify_api_key(req: Request):
255
 
256
  return True
257
 
 
258
  # =====================================================
259
  # CORE: SMART COMPLETION WITH RETRY + FALLBACK
260
  # =====================================================
@@ -294,13 +335,11 @@ async def smart_completion(
294
  for provider in providers_shuffled:
295
 
296
  try:
297
-
298
  logger.info(
299
- f"Attempt {attempt+1} | Model: {model} | Provider: {provider.__name__}"
300
  )
301
 
302
  client = build_client(proxy)
303
-
304
  proxy_config = get_g4f_proxy_config()
305
 
306
  response = await asyncio.to_thread(
@@ -319,8 +358,8 @@ async def smart_completion(
319
  return response, model
320
 
321
  except Exception as e:
322
-
323
  last_error = e
 
324
  logger.warning(
325
  f"โŒ Failed | Model: {model} | Provider: {provider.__name__} | Error: {str(e)[:100]}"
326
  )
@@ -331,7 +370,11 @@ async def smart_completion(
331
  # ุงู†ุชุธุงุฑ ู‚ุจู„ ุฏูˆุฑุฉ ุงู„ู…ุญุงูˆู„ุงุช ุงู„ุฌุฏูŠุฏุฉ
332
  if attempt < max_retries - 1:
333
  wait_time = (attempt + 1) * 1.5
334
- logger.info(f"Waiting {wait_time}s before retry {attempt+2}...")
 
 
 
 
335
  await asyncio.sleep(wait_time)
336
 
337
  # -----------------------------------------------
@@ -352,10 +395,9 @@ async def smart_completion(
352
  MODEL_PROVIDER_MAP["default"]
353
  )
354
 
355
- for fb_provider in fallback_providers[:2]: # ุฃูˆู„ ู…ุฒูˆุฏูŠู† ูู‚ุท
356
 
357
  try:
358
-
359
  logger.info(
360
  f"๐Ÿ”„ Fallback | Model: {fallback_model} | Provider: {fb_provider.__name__}"
361
  )
@@ -383,6 +425,7 @@ async def smart_completion(
383
  logger.warning(
384
  f"โŒ Fallback Failed | Model: {fallback_model} | Error: {str(e)[:100]}"
385
  )
 
386
  await asyncio.sleep(0.3)
387
 
388
  # -----------------------------------------------
@@ -407,6 +450,7 @@ async def smart_completion(
407
  except Exception as e:
408
 
409
  last_error = e
 
410
  logger.error(f"๐Ÿ’€ All attempts failed: {str(e)}")
411
 
412
  raise HTTPException(
@@ -419,12 +463,14 @@ async def smart_completion(
419
  }
420
  )
421
 
 
422
  # =====================================================
423
  # ROOT
424
  # =====================================================
425
 
426
  @app.get("/")
427
  async def root():
 
428
  return {
429
  "status": "online",
430
  "service": "Universal AI Gateway",
@@ -438,6 +484,7 @@ async def root():
438
  }
439
  }
440
 
 
441
  # =====================================================
442
  # HEALTH CHECK
443
  # =====================================================
@@ -449,19 +496,29 @@ async def health_check():
449
  proxy_status = "disabled"
450
 
451
  if SINGLE_PROXY or PROXY_LIST:
 
452
  proxy_status = "enabled"
453
 
454
  # ุงุฎุชุจุงุฑ ุงู„ุจุฑูˆูƒุณูŠ
455
  proxy = get_proxy()
 
456
  if proxy:
457
  try:
458
  async with httpx.AsyncClient(
459
  proxies=proxy,
460
  timeout=10
461
  ) as client:
462
- resp = await client.get("https://api.ipify.org?format=json")
 
 
 
 
463
  ip_data = resp.json()
464
- proxy_status = f"working - IP: {ip_data.get('ip', 'unknown')}"
 
 
 
 
465
  except Exception as e:
466
  proxy_status = f"error - {str(e)[:50]}"
467
 
@@ -473,6 +530,7 @@ async def health_check():
473
  "fallback_models": FALLBACK_MODELS
474
  }
475
 
 
476
  # =====================================================
477
  # MODELS LIST
478
  # =====================================================
@@ -485,6 +543,7 @@ async def get_models():
485
 
486
  # ุงู„ู†ู…ุงุฐุฌ ู…ู† ุงู„ุฎุฑูŠุทุฉ
487
  for model_id in MODEL_PROVIDER_MAP.keys():
 
488
  if model_id == "default":
489
  continue
490
 
@@ -503,13 +562,21 @@ async def get_models():
503
 
504
  # ุฅุถุงูุฉ ู†ู…ุงุฐุฌ ุฅุถุงููŠ๏ฟฝ๏ฟฝ ู…ู† g4f
505
  try:
 
506
  if hasattr(g4f.models, "_all_models"):
 
507
  all_models = list(g4f.models._all_models)
508
- existing_ids = {m["id"] for m in models_data}
 
 
 
509
 
510
  for model in all_models[:30]:
 
511
  model_str = str(model)
 
512
  if model_str not in existing_ids:
 
513
  models_data.append({
514
  "id": model_str,
515
  "object": "model",
@@ -520,6 +587,7 @@ async def get_models():
520
  "root": model_str,
521
  "parent": None
522
  })
 
523
  existing_ids.add(model_str)
524
 
525
  except Exception as e:
@@ -531,6 +599,7 @@ async def get_models():
531
  "total": len(models_data)
532
  }
533
 
 
534
  # =====================================================
535
  # CHAT COMPLETIONS - MAIN ENDPOINT
536
  # =====================================================
@@ -546,7 +615,10 @@ async def chat_completions(
546
 
547
  # ุชุญูˆูŠู„ ุงู„ุฑุณุงุฆู„
548
  messages = [
549
- {"role": m.role, "content": m.content}
 
 
 
550
  for m in body.messages
551
  ]
552
 
@@ -566,6 +638,7 @@ async def chat_completions(
566
 
567
  # ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ุจุฑูˆูƒุณูŠ
568
  proxy = get_proxy()
 
569
  chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
570
  actual_model = body.model
571
 
@@ -585,7 +658,6 @@ async def chat_completions(
585
  for provider in providers_shuffled:
586
 
587
  try:
588
-
589
  logger.info(
590
  f"Stream attempt | Provider: {provider.__name__}"
591
  )
@@ -605,13 +677,17 @@ async def chat_completions(
605
  logger.info(
606
  f"โœ… Stream connected | Provider: {provider.__name__}"
607
  )
 
608
  break
609
 
610
  except Exception as e:
 
611
  last_err = e
 
612
  logger.warning(
613
  f"โŒ Stream provider failed: {provider.__name__} | {str(e)[:80]}"
614
  )
 
615
  await asyncio.sleep(0.3)
616
 
617
  # ุฅุฐุง ูุดู„ ูƒู„ ุดูŠุกุŒ ุฌุฑุจ ุจุฏูˆู† ู…ุฒูˆุฏ ู…ุญุฏุฏ
@@ -619,17 +695,25 @@ async def chat_completions(
619
 
620
  try:
621
  client = build_client(proxy)
 
622
  response = await asyncio.to_thread(
623
  client.chat.completions.create,
624
  model=body.model,
625
  messages=messages,
626
  stream=True
627
  )
628
- logger.info("โœ… Stream connected without specific provider")
 
 
 
629
 
630
  except Exception as e:
 
631
  last_err = e
632
- logger.error(f"๐Ÿ’€ Stream completely failed: {e}")
 
 
 
633
 
634
  error_payload = {
635
  "error": {
@@ -638,6 +722,7 @@ async def chat_completions(
638
  "code": 503
639
  }
640
  }
 
641
  yield f"data: {json.dumps(error_payload)}\n\n"
642
  return
643
 
@@ -647,7 +732,6 @@ async def chat_completions(
647
  for chunk in response:
648
 
649
  try:
650
-
651
  content = ""
652
 
653
  if (
@@ -679,11 +763,18 @@ async def chat_completions(
679
  ]
680
  }
681
 
682
- yield f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
 
 
 
 
683
  await asyncio.sleep(0)
684
 
685
  except Exception as chunk_error:
686
- logger.error(f"Chunk processing error: {chunk_error}")
 
 
 
687
  continue
688
 
689
  # ุฅุฐุง ู„ู… ูŠูƒู† ู‡ู†ุงูƒ ู…ุญุชูˆู‰
@@ -705,7 +796,11 @@ async def chat_completions(
705
  ]
706
  }
707
 
708
- yield f"data: {json.dumps(final_payload)}\n\n"
 
 
 
 
709
  yield "data: [DONE]\n\n"
710
 
711
  except Exception as e:
@@ -752,16 +847,27 @@ async def chat_completions(
752
  assistant_message = ""
753
 
754
  try:
755
- assistant_message = response.choices[0].message.content or ""
 
 
 
756
  except AttributeError:
 
757
  try:
758
  assistant_message = str(response)
 
759
  except Exception:
760
  assistant_message = "Error extracting response"
761
 
762
  # ุญุณุงุจ ุงู„ุฑู…ูˆุฒ ุชู‚ุฑูŠุจูŠุงู‹
763
- prompt_tokens = sum(len(m["content"].split()) for m in messages)
764
- completion_tokens = len(assistant_message.split())
 
 
 
 
 
 
765
 
766
  return JSONResponse({
767
  "id": f"chatcmpl-{uuid.uuid4().hex}",
@@ -781,7 +887,9 @@ async def chat_completions(
781
  "usage": {
782
  "prompt_tokens": prompt_tokens,
783
  "completion_tokens": completion_tokens,
784
- "total_tokens": prompt_tokens + completion_tokens
 
 
785
  }
786
  })
787
 
@@ -801,6 +909,7 @@ async def chat_completions(
801
  }
802
  )
803
 
 
804
  # =====================================================
805
  # RUN
806
  # =====================================================
@@ -809,13 +918,21 @@ if __name__ == "__main__":
809
 
810
  import uvicorn
811
 
812
- logger.info("๐Ÿš€ Starting Universal AI Gateway v5.0.0")
813
- logger.info(f"๐Ÿ”Œ Proxy enabled: {bool(PROXY_LIST or SINGLE_PROXY)}")
814
- logger.info(f"๐Ÿ“ฆ Models configured: {len(MODEL_PROVIDER_MAP)}")
 
 
 
 
 
 
 
 
815
 
816
  uvicorn.run(
817
  app,
818
  host="0.0.0.0",
819
  port=7860,
820
  log_level="info"
821
- )
 
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
+
7
  import asyncio
8
  import json
9
  import time
 
11
  import logging
12
  import random
13
  import httpx
 
14
  import g4f
15
+
16
  from g4f.client import Client
17
  from g4f import Provider
18
 
19
+
20
  # =====================================================
21
  # LOGGING
22
  # =====================================================
 
25
  level=logging.INFO,
26
  format="%(asctime)s [%(levelname)s] %(message)s"
27
  )
28
+
29
  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
  # =====================================================
 
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,
 
160
  ],
161
  }
162
 
163
+
164
  # =====================================================
165
  # FALLBACK MODELS - ุฅุฐุง ูุดู„ ุงู„ู†ู…ูˆุฐุฌ ุงู„ู…ุทู„ูˆุจ
166
  # =====================================================
 
171
  "llama-3.1-70b",
172
  ]
173
 
174
+
175
  # =====================================================
176
  # FASTAPI
177
  # =====================================================
 
182
  version="5.0.0"
183
  )
184
 
185
+
186
  # =====================================================
187
  # CORS
188
  # =====================================================
 
195
  allow_headers=["*"],
196
  )
197
 
198
+
199
  # =====================================================
200
  # MODELS
201
  # =====================================================
 
204
  role: str
205
  content: str
206
 
207
+
208
  class ChatRequest(BaseModel):
209
  model: str
210
  messages: List[Message]
 
212
  temperature: Optional[float] = 0.7
213
  max_tokens: Optional[int] = 4096
214
 
215
+
216
  # =====================================================
217
  # PROXY HELPER
218
  # =====================================================
 
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:
 
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
  # ุงู„ุณู…ุงุญ ุจุฏูˆู† ู…ูุชุงุญ ู„ู„ุงุฎุชุจุงุฑ
 
295
 
296
  return True
297
 
298
+
299
  # =====================================================
300
  # CORE: SMART COMPLETION WITH RETRY + FALLBACK
301
  # =====================================================
 
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(
 
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
  )
 
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
  # -----------------------------------------------
 
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
  )
 
425
  logger.warning(
426
  f"โŒ Fallback Failed | Model: {fallback_model} | Error: {str(e)[:100]}"
427
  )
428
+
429
  await asyncio.sleep(0.3)
430
 
431
  # -----------------------------------------------
 
450
  except Exception as e:
451
 
452
  last_error = e
453
+
454
  logger.error(f"๐Ÿ’€ All attempts failed: {str(e)}")
455
 
456
  raise HTTPException(
 
463
  }
464
  )
465
 
466
+
467
  # =====================================================
468
  # ROOT
469
  # =====================================================
470
 
471
  @app.get("/")
472
  async def root():
473
+
474
  return {
475
  "status": "online",
476
  "service": "Universal AI Gateway",
 
484
  }
485
  }
486
 
487
+
488
  # =====================================================
489
  # HEALTH CHECK
490
  # =====================================================
 
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
 
 
530
  "fallback_models": FALLBACK_MODELS
531
  }
532
 
533
+
534
  # =====================================================
535
  # MODELS LIST
536
  # =====================================================
 
543
 
544
  # ุงู„ู†ู…ุงุฐุฌ ู…ู† ุงู„ุฎุฑูŠุทุฉ
545
  for model_id in MODEL_PROVIDER_MAP.keys():
546
+
547
  if model_id == "default":
548
  continue
549
 
 
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",
 
587
  "root": model_str,
588
  "parent": None
589
  })
590
+
591
  existing_ids.add(model_str)
592
 
593
  except Exception as e:
 
599
  "total": len(models_data)
600
  }
601
 
602
+
603
  # =====================================================
604
  # CHAT COMPLETIONS - MAIN ENDPOINT
605
  # =====================================================
 
615
 
616
  # ุชุญูˆูŠู„ ุงู„ุฑุณุงุฆู„
617
  messages = [
618
+ {
619
+ "role": m.role,
620
+ "content": m.content
621
+ }
622
  for m in body.messages
623
  ]
624
 
 
638
 
639
  # ุงู„ุญุตูˆู„ ุนู„ู‰ ุงู„ุจุฑูˆูƒุณูŠ
640
  proxy = get_proxy()
641
+
642
  chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
643
  actual_model = body.model
644
 
 
658
  for provider in providers_shuffled:
659
 
660
  try:
 
661
  logger.info(
662
  f"Stream attempt | Provider: {provider.__name__}"
663
  )
 
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
  # ุฅุฐุง ูุดู„ ูƒู„ ุดูŠุกุŒ ุฌุฑุจ ุจุฏูˆู† ู…ุฒูˆุฏ ู…ุญุฏุฏ
 
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": {
 
722
  "code": 503
723
  }
724
  }
725
+
726
  yield f"data: {json.dumps(error_payload)}\n\n"
727
  return
728
 
 
732
  for chunk in response:
733
 
734
  try:
 
735
  content = ""
736
 
737
  if (
 
763
  ]
764
  }
765
 
766
+ yield (
767
+ f"data: "
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
  # ุฅุฐุง ู„ู… ูŠูƒู† ู‡ู†ุงูƒ ู…ุญุชูˆู‰
 
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:
 
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
866
+ )
867
+
868
+ completion_tokens = len(
869
+ assistant_message.split()
870
+ )
871
 
872
  return JSONResponse({
873
  "id": f"chatcmpl-{uuid.uuid4().hex}",
 
887
  "usage": {
888
  "prompt_tokens": prompt_tokens,
889
  "completion_tokens": completion_tokens,
890
+ "total_tokens": (
891
+ prompt_tokens + completion_tokens
892
+ )
893
  }
894
  })
895
 
 
909
  }
910
  )
911
 
912
+
913
  # =====================================================
914
  # RUN
915
  # =====================================================
 
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(
934
  app,
935
  host="0.0.0.0",
936
  port=7860,
937
  log_level="info"
938
+ )