bahi-bh commited on
Commit
e2587ac
·
verified ·
1 Parent(s): 22ae976

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -78
app.py CHANGED
@@ -1,68 +1,42 @@
1
  import random
 
 
 
 
 
 
 
 
2
  import asyncio
3
  import json
4
  import time
5
  import uuid
6
  import logging
7
 
8
- from typing import List, Optional
9
-
10
  import g4f
11
  from g4f.client import Client
12
 
13
- from fastapi import FastAPI, Request, HTTPException
14
- from fastapi.middleware.cors import CORSMiddleware
15
- from fastapi.responses import StreamingResponse, JSONResponse
16
- from pydantic import BaseModel
17
-
18
- # =====================================================
19
- # SAFE PROVIDERS
20
- # =====================================================
21
-
22
- SAFE_PROVIDER_NAMES = [
23
- "DeepInfra",
24
- "DDG",
25
- "Cloudflare",
26
- ]
27
 
28
  # =====================================================
29
  # LOGGING
30
  # =====================================================
31
 
32
- logging.basicConfig(
33
- level=logging.INFO,
34
- format="%(asctime)s [%(levelname)s] %(message)s"
35
- )
36
-
37
  logger = logging.getLogger(__name__)
38
 
 
39
  # =====================================================
40
  # CONFIG
41
  # =====================================================
42
 
43
  API_KEY = "sk-your-secret-key"
44
 
45
- # =====================================================
46
- # LOAD SAFE PROVIDERS
47
- # =====================================================
48
-
49
- AVAILABLE_PROVIDERS = []
50
-
51
- for provider_name in SAFE_PROVIDER_NAMES:
52
-
53
- try:
54
-
55
- provider = getattr(g4f.Provider, provider_name)
56
-
57
- AVAILABLE_PROVIDERS.append(provider)
58
 
59
- logger.info(f"Loaded provider: {provider_name}")
 
60
 
61
- except Exception:
62
-
63
- logger.warning(
64
- f"Provider not available: {provider_name}"
65
- )
66
 
67
  # =====================================================
68
  # FASTAPI
@@ -70,9 +44,10 @@ for provider_name in SAFE_PROVIDER_NAMES:
70
 
71
  app = FastAPI(
72
  title="Universal AI Gateway",
73
- version="5.0.0"
74
  )
75
 
 
76
  # =====================================================
77
  # CORS
78
  # =====================================================
@@ -85,6 +60,7 @@ app.add_middleware(
85
  allow_headers=["*"],
86
  )
87
 
 
88
  # =====================================================
89
  # MODELS
90
  # =====================================================
@@ -101,6 +77,7 @@ class ChatRequest(BaseModel):
101
  temperature: Optional[float] = 0.7
102
  max_tokens: Optional[int] = 4096
103
 
 
104
  # =====================================================
105
  # AUTH
106
  # =====================================================
@@ -109,6 +86,7 @@ def verify_api_key(req: Request):
109
 
110
  auth = req.headers.get("Authorization")
111
 
 
112
  if not auth:
113
  return True
114
 
@@ -128,6 +106,7 @@ def verify_api_key(req: Request):
128
 
129
  return True
130
 
 
131
  # =====================================================
132
  # ROOT
133
  # =====================================================
@@ -138,13 +117,10 @@ async def root():
138
  return {
139
  "status": "online",
140
  "service": "Universal AI Gateway",
141
- "version": "5.0.0",
142
- "providers": [
143
- p.__name__
144
- for p in AVAILABLE_PROVIDERS
145
- ]
146
  }
147
 
 
148
  # =====================================================
149
  # MODELS
150
  # =====================================================
@@ -185,6 +161,7 @@ async def get_models():
185
 
186
  logger.error(f"Models error: {e}")
187
 
 
188
  if not models_data:
189
 
190
  for model in fallback_models:
@@ -201,22 +178,12 @@ async def get_models():
201
  "data": models_data
202
  }
203
 
204
- # =====================================================
205
- # PROVIDER PICKER
206
- # =====================================================
207
-
208
- def pick_provider():
209
-
210
- if not AVAILABLE_PROVIDERS:
211
- return None
212
-
213
- return random.choice(AVAILABLE_PROVIDERS)
214
 
215
  # =====================================================
216
- # COMPLETION HELPER
217
  # =====================================================
218
 
219
- async def create_completion(
220
  model,
221
  messages,
222
  stream=False
@@ -224,33 +191,25 @@ async def create_completion(
224
 
225
  last_error = None
226
 
227
- providers = AVAILABLE_PROVIDERS.copy()
228
-
229
- random.shuffle(providers)
230
-
231
- for provider in providers:
232
 
233
  try:
234
 
235
  logger.info(
236
- f"Trying provider: {provider.__name__}"
237
  )
238
 
239
  client = Client()
240
 
 
241
  response = await asyncio.wait_for(
242
  asyncio.to_thread(
243
  client.chat.completions.create,
244
  model=model,
245
  messages=messages,
246
- provider=provider,
247
  stream=stream
248
  ),
249
- timeout=45
250
- )
251
-
252
- logger.info(
253
- f"Success provider: {provider.__name__}"
254
  )
255
 
256
  return response
@@ -260,14 +219,13 @@ async def create_completion(
260
  last_error = e
261
 
262
  logger.warning(
263
- f"Provider failed: {provider.__name__} | {e}"
264
  )
265
 
266
  await asyncio.sleep(1)
267
 
268
- raise Exception(
269
- f"All providers failed: {last_error}"
270
- )
271
 
272
  # =====================================================
273
  # CHAT COMPLETIONS
@@ -303,7 +261,7 @@ async def chat_completions(
303
 
304
  try:
305
 
306
- response = await create_completion(
307
  model=body.model,
308
  messages=messages,
309
  stream=True
@@ -311,6 +269,8 @@ async def chat_completions(
311
 
312
  chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
313
 
 
 
314
  for chunk in response:
315
 
316
  try:
@@ -327,6 +287,8 @@ async def chat_completions(
327
 
328
  if content:
329
 
 
 
330
  payload = {
331
  "id": chunk_id,
332
  "object": "chat.completion.chunk",
@@ -356,6 +318,21 @@ async def chat_completions(
356
  f"Chunk error: {chunk_error}"
357
  )
358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  final_payload = {
360
  "id": chunk_id,
361
  "object": "chat.completion.chunk",
@@ -377,6 +354,22 @@ async def chat_completions(
377
 
378
  yield "data: [DONE]\n\n"
379
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
  except Exception as e:
381
 
382
  logger.error(f"Streaming error: {e}")
@@ -409,7 +402,7 @@ async def chat_completions(
409
 
410
  try:
411
 
412
- response = await create_completion(
413
  model=body.model,
414
  messages=messages,
415
  stream=False
@@ -449,6 +442,15 @@ async def chat_completions(
449
  }
450
  })
451
 
 
 
 
 
 
 
 
 
 
452
  except Exception as e:
453
 
454
  logger.error(f"Chat error: {e}")
@@ -458,6 +460,7 @@ async def chat_completions(
458
  detail=str(e)
459
  )
460
 
 
461
  # =====================================================
462
  # RUN
463
  # =====================================================
@@ -466,8 +469,6 @@ if __name__ == "__main__":
466
 
467
  import uvicorn
468
 
469
- logger.info("Starting Universal AI Gateway")
470
-
471
  uvicorn.run(
472
  app,
473
  host="0.0.0.0",
 
1
  import random
2
+ import httpx
3
+
4
+ from fastapi import FastAPI, Request, HTTPException
5
+ from fastapi.middleware.cors import CORSMiddleware
6
+ from fastapi.responses import StreamingResponse, JSONResponse
7
+ from pydantic import BaseModel
8
+ from typing import List, Optional
9
+
10
  import asyncio
11
  import json
12
  import time
13
  import uuid
14
  import logging
15
 
 
 
16
  import g4f
17
  from g4f.client import Client
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  # =====================================================
21
  # LOGGING
22
  # =====================================================
23
 
24
+ logging.basicConfig(level=logging.INFO)
 
 
 
 
25
  logger = logging.getLogger(__name__)
26
 
27
+
28
  # =====================================================
29
  # CONFIG
30
  # =====================================================
31
 
32
  API_KEY = "sk-your-secret-key"
33
 
34
+ # Timeout لمنع التعليق
35
+ REQUEST_TIMEOUT = 40
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ # عدد المحاولات
38
+ MAX_RETRIES = 3
39
 
 
 
 
 
 
40
 
41
  # =====================================================
42
  # FASTAPI
 
44
 
45
  app = FastAPI(
46
  title="Universal AI Gateway",
47
+ version="4.1.0"
48
  )
49
 
50
+
51
  # =====================================================
52
  # CORS
53
  # =====================================================
 
60
  allow_headers=["*"],
61
  )
62
 
63
+
64
  # =====================================================
65
  # MODELS
66
  # =====================================================
 
77
  temperature: Optional[float] = 0.7
78
  max_tokens: Optional[int] = 4096
79
 
80
+
81
  # =====================================================
82
  # AUTH
83
  # =====================================================
 
86
 
87
  auth = req.headers.get("Authorization")
88
 
89
+ # السماح للاختبار
90
  if not auth:
91
  return True
92
 
 
106
 
107
  return True
108
 
109
+
110
  # =====================================================
111
  # ROOT
112
  # =====================================================
 
117
  return {
118
  "status": "online",
119
  "service": "Universal AI Gateway",
120
+ "version": "4.1.0"
 
 
 
 
121
  }
122
 
123
+
124
  # =====================================================
125
  # MODELS
126
  # =====================================================
 
161
 
162
  logger.error(f"Models error: {e}")
163
 
164
+ # fallback
165
  if not models_data:
166
 
167
  for model in fallback_models:
 
178
  "data": models_data
179
  }
180
 
 
 
 
 
 
 
 
 
 
 
181
 
182
  # =====================================================
183
+ # SAFE COMPLETION
184
  # =====================================================
185
 
186
+ async def safe_completion(
187
  model,
188
  messages,
189
  stream=False
 
191
 
192
  last_error = None
193
 
194
+ for attempt in range(MAX_RETRIES):
 
 
 
 
195
 
196
  try:
197
 
198
  logger.info(
199
+ f"Attempt {attempt + 1} | model={model}"
200
  )
201
 
202
  client = Client()
203
 
204
+ # timeout لمنع التعليق
205
  response = await asyncio.wait_for(
206
  asyncio.to_thread(
207
  client.chat.completions.create,
208
  model=model,
209
  messages=messages,
 
210
  stream=stream
211
  ),
212
+ timeout=REQUEST_TIMEOUT
 
 
 
 
213
  )
214
 
215
  return response
 
219
  last_error = e
220
 
221
  logger.warning(
222
+ f"Attempt failed {attempt + 1}: {e}"
223
  )
224
 
225
  await asyncio.sleep(1)
226
 
227
+ raise Exception(last_error)
228
+
 
229
 
230
  # =====================================================
231
  # CHAT COMPLETIONS
 
261
 
262
  try:
263
 
264
+ response = await safe_completion(
265
  model=body.model,
266
  messages=messages,
267
  stream=True
 
269
 
270
  chunk_id = f"chatcmpl-{uuid.uuid4().hex}"
271
 
272
+ chunk_count = 0
273
+
274
  for chunk in response:
275
 
276
  try:
 
287
 
288
  if content:
289
 
290
+ chunk_count += 1
291
+
292
  payload = {
293
  "id": chunk_id,
294
  "object": "chat.completion.chunk",
 
318
  f"Chunk error: {chunk_error}"
319
  )
320
 
321
+ # إذا لم يصل أي chunk
322
+ if chunk_count == 0:
323
+
324
+ error_payload = {
325
+ "error": {
326
+ "message": "Provider returned empty stream",
327
+ "type": "empty_stream"
328
+ }
329
+ }
330
+
331
+ yield (
332
+ f"data: "
333
+ f"{json.dumps(error_payload)}\n\n"
334
+ )
335
+
336
  final_payload = {
337
  "id": chunk_id,
338
  "object": "chat.completion.chunk",
 
354
 
355
  yield "data: [DONE]\n\n"
356
 
357
+ except asyncio.TimeoutError:
358
+
359
+ logger.error("Streaming timeout")
360
+
361
+ error_payload = {
362
+ "error": {
363
+ "message": "Request timeout",
364
+ "type": "timeout"
365
+ }
366
+ }
367
+
368
+ yield (
369
+ f"data: "
370
+ f"{json.dumps(error_payload)}\n\n"
371
+ )
372
+
373
  except Exception as e:
374
 
375
  logger.error(f"Streaming error: {e}")
 
402
 
403
  try:
404
 
405
+ response = await safe_completion(
406
  model=body.model,
407
  messages=messages,
408
  stream=False
 
442
  }
443
  })
444
 
445
+ except asyncio.TimeoutError:
446
+
447
+ logger.error("Request timeout")
448
+
449
+ raise HTTPException(
450
+ status_code=408,
451
+ detail="Request timeout"
452
+ )
453
+
454
  except Exception as e:
455
 
456
  logger.error(f"Chat error: {e}")
 
460
  detail=str(e)
461
  )
462
 
463
+
464
  # =====================================================
465
  # RUN
466
  # =====================================================
 
469
 
470
  import uvicorn
471
 
 
 
472
  uvicorn.run(
473
  app,
474
  host="0.0.0.0",