bahi-bh commited on
Commit
4803fcb
·
verified ·
1 Parent(s): abe929c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -131
app.py CHANGED
@@ -3,19 +3,16 @@ import json
3
  import time
4
  import logging
5
  from typing import Any, Dict, List, Optional, Tuple
6
-
7
- import gradio as gr
 
8
  import g4f
9
 
10
- # ========== إضافة FastAPI (3 سطر فقط) ==========
11
- from fastapi import FastAPI, Request, HTTPException
12
- from fastapi.middleware.wsgi import WSGIMiddleware
13
-
14
  logging.basicConfig(level=logging.INFO)
15
  logger = logging.getLogger("g4f-smart-router")
16
 
17
  # =====================================================
18
- # COOKIES
19
  # =====================================================
20
  def _load_cookies_raw() -> Dict[str, Any]:
21
  raw_env = (os.getenv("COOKIES_JSON") or "").strip()
@@ -68,7 +65,7 @@ def load_cookies() -> str:
68
  COOKIE_STATUS = load_cookies()
69
 
70
  # =====================================================
71
- # PROVIDERS
72
  # =====================================================
73
  def get_provider(name: str):
74
  try:
@@ -91,7 +88,7 @@ REAL_PROVIDERS: Dict[str, Any] = {
91
  REAL_PROVIDERS = {k: v for k, v in REAL_PROVIDERS.items() if v}
92
 
93
  # =====================================================
94
- # MODELS
95
  # =====================================================
96
  PROVIDER_MODELS_FALLBACK: Dict[str, List[str]] = {
97
  "Perplexity": ["sonar", "sonar-pro", "gpt-4o", "llama-3"],
@@ -106,7 +103,7 @@ PROVIDER_MODELS_FALLBACK: Dict[str, List[str]] = {
106
  }
107
 
108
  # =====================================================
109
- # MODEL DISCOVERY
110
  # =====================================================
111
  def _normalize_model_list(x: Any) -> List[str]:
112
  out: List[str] = []
@@ -178,33 +175,27 @@ def discover_provider_models(provider_obj: Any, provider_name: str) -> List[str]
178
  return uniq
179
 
180
  # =====================================================
181
- # STREAM CLEANER (محسّنة جداً - تحل مشاكل JSON والأكواد)
182
  # =====================================================
183
  def clean_stream(chunk):
184
  try:
185
- # إذا كانت القطعة قاموساً (dict) مثل JSON
186
  if isinstance(chunk, dict):
187
- # استخراج النص من تنسيق Perplexity
188
  if 'choices' in chunk and chunk['choices']:
189
  delta = chunk['choices'][0].get('delta', {})
190
  if 'content' in delta:
191
  return delta['content']
192
  if 'text' in delta:
193
  return delta['text']
194
- # تنسيقات أخرى
195
  if 'content' in chunk:
196
  return chunk['content']
197
  if 'text' in chunk:
198
  return chunk['text']
199
  return ""
200
 
201
- # إذا كانت القطعة نصاً
202
  if isinstance(chunk, str):
203
- # محاولة تفسيرها كـ JSON
204
  if chunk.strip().startswith("{") and chunk.strip().endswith("}"):
205
  try:
206
  data = json.loads(chunk)
207
- # استخراج النص من JSON
208
  if 'choices' in data and data['choices']:
209
  delta = data['choices'][0].get('delta', {})
210
  if 'content' in delta:
@@ -216,53 +207,24 @@ def clean_stream(chunk):
216
  except:
217
  pass
218
 
219
- # تنظيف علامات الترميز (للكود)
220
  chunk = chunk.replace('\\n', '\n').replace('\\r', '\r').replace('\\t', ' ')
221
  chunk = chunk.replace('\\"', '"').replace("\\'", "'")
222
  return chunk
223
 
224
- # إذا كانت القطعة شيئاً آخر
225
  return str(chunk)
226
  except Exception as e:
227
  logger.warning(f"خطأ في clean_stream: {e}")
228
  return ""
229
 
230
  # =====================================================
231
- # UI update
232
- # =====================================================
233
- _PROVIDER_MODEL_CACHE = {}
234
-
235
- def update_models(provider_name):
236
-
237
- pobj = REAL_PROVIDERS.get(provider_name)
238
-
239
- if not pobj:
240
- arr = ["gpt-4o"]
241
- else:
242
- if provider_name not in _PROVIDER_MODEL_CACHE:
243
- _PROVIDER_MODEL_CACHE[provider_name] = discover_provider_models(
244
- pobj,
245
- provider_name
246
- )
247
-
248
- arr = _PROVIDER_MODEL_CACHE[provider_name]
249
-
250
- return gr.update(
251
- choices=arr,
252
- value=arr[0]
253
- )
254
-
255
- # =====================================================
256
- # CACHE
257
  # =====================================================
258
  CACHE = {}
259
  CACHE_TS = {}
260
 
261
  def _cache_get(key):
262
-
263
  if key not in CACHE:
264
  return None
265
-
266
  return CACHE[key]
267
 
268
  def _cache_set(key, val):
@@ -270,10 +232,11 @@ def _cache_set(key, val):
270
  CACHE_TS[key] = time.time()
271
 
272
  # =====================================================
273
- # CHAT (محسّنة - ذاكرة أطول وfallback أفضل)
274
  # =====================================================
275
- def ask(message: str, history, provider_name: str, model_name: str):
276
 
 
277
  history = history or []
278
  message = (message or "").strip()
279
 
@@ -289,16 +252,11 @@ def ask(message: str, history, provider_name: str, model_name: str):
289
  yield cached
290
  return
291
 
292
- # =================================================
293
- # بناء الذاكرة (محسّن - يحفظ آخر 40/20 بدلاً من 16/8)
294
- # =================================================
295
  msgs = []
296
 
297
  try:
298
  if history:
299
- # format جديد
300
  if isinstance(history[0], dict):
301
- # حفظ آخر 40 رسالة (بدلاً من 16)
302
  for item in history[-40:]:
303
  role = item.get("role")
304
  content = item.get("content")
@@ -307,9 +265,7 @@ def ask(message: str, history, provider_name: str, model_name: str):
307
  "role": str(role),
308
  "content": str(content)
309
  })
310
- # format قديم
311
  else:
312
- # حفظ آخر 20 محادثة (بدلاً من 8)
313
  for item in history[-20:]:
314
  if isinstance(item, (list, tuple)) and len(item) == 2:
315
  u = item[0]
@@ -332,9 +288,6 @@ def ask(message: str, history, provider_name: str, model_name: str):
332
  "content": message
333
  })
334
 
335
- # =================================================
336
- # تجربة المزودين مع fallback
337
- # =================================================
338
  fallback_providers = [
339
  provider_name,
340
  "Perplexity",
@@ -349,7 +302,6 @@ def ask(message: str, history, provider_name: str, model_name: str):
349
  used = []
350
 
351
  for pname in fallback_providers:
352
-
353
  if pname in used:
354
  continue
355
 
@@ -372,7 +324,6 @@ def ask(message: str, history, provider_name: str, model_name: str):
372
  ]
373
 
374
  for m in model_candidates[:12]:
375
-
376
  try:
377
  stream = g4f.ChatCompletion.create(
378
  model=m,
@@ -403,78 +354,72 @@ def ask(message: str, history, provider_name: str, model_name: str):
403
  yield "❌ Failed with all providers."
404
 
405
  # =====================================================
406
- # UI
407
  # =====================================================
408
- css = """
409
- .gradio-container{
410
- max-width:1200px!important;
411
- }
412
- """
413
-
414
- providers = list(REAL_PROVIDERS.keys())
415
-
416
- default_provider = (
417
- "Perplexity"
418
- if "Perplexity" in providers
419
- else providers[0]
420
- )
421
-
422
- default_models = discover_provider_models(
423
- REAL_PROVIDERS[default_provider],
424
- default_provider
425
- )
426
-
427
- with gr.Blocks(
428
- theme=gr.themes.Soft(),
429
- css=css
430
- ) as demo:
431
 
432
- gr.Markdown("# 🤖 G4F Smart Router FINAL")
433
- gr.Markdown(COOKIE_STATUS)
434
 
435
- with gr.Row():
436
-
437
- provider_dd = gr.Dropdown(
438
- choices=providers,
439
- value=default_provider,
440
- label="Provider"
441
- )
442
-
443
- model_dd = gr.Dropdown(
444
- choices=default_models,
445
- value=default_models[0],
446
- label="Model"
447
- )
448
-
449
- provider_dd.change(
450
- update_models,
451
- inputs=provider_dd,
452
- outputs=model_dd
453
- )
454
-
455
- gr.ChatInterface(
456
- fn=ask,
457
- additional_inputs=[
458
- provider_dd,
459
- model_dd
460
- ],
461
- title="",
462
- description="Choose provider → models update automatically."
463
- )
464
-
465
- # ========== إضافة FastAPI + مفتاح (6 سطر فقط) ==========
466
- fastapp = FastAPI()
467
  API_KEY = os.getenv("API_KEY", "mysecretkey123")
468
 
469
- @fastapp.middleware("http")
470
- async def auth_middleware(request: Request, call_next):
471
- if request.headers.get("Authorization") != f"Bearer {API_KEY}":
472
- raise HTTPException(401, "Invalid or missing API key")
473
- return await call_next(request)
474
-
475
- fastapp.mount("/", WSGIMiddleware(demo))
476
-
477
- demo.queue().launch(
478
- server_name="0.0.0.0",
479
- server_port=int(os.getenv("PORT", "7860"))
480
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import time
4
  import logging
5
  from typing import Any, Dict, List, Optional, Tuple
6
+ from fastapi import FastAPI, HTTPException, Request
7
+ from fastapi.responses import StreamingResponse, JSONResponse
8
+ from pydantic import BaseModel
9
  import g4f
10
 
 
 
 
 
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger("g4f-smart-router")
13
 
14
  # =====================================================
15
+ # COOKIES (نفس الكود لم يتغير)
16
  # =====================================================
17
  def _load_cookies_raw() -> Dict[str, Any]:
18
  raw_env = (os.getenv("COOKIES_JSON") or "").strip()
 
65
  COOKIE_STATUS = load_cookies()
66
 
67
  # =====================================================
68
+ # PROVIDERS (نفس الكود لم يتغير)
69
  # =====================================================
70
  def get_provider(name: str):
71
  try:
 
88
  REAL_PROVIDERS = {k: v for k, v in REAL_PROVIDERS.items() if v}
89
 
90
  # =====================================================
91
+ # MODELS (نفس الكود لم يتغير)
92
  # =====================================================
93
  PROVIDER_MODELS_FALLBACK: Dict[str, List[str]] = {
94
  "Perplexity": ["sonar", "sonar-pro", "gpt-4o", "llama-3"],
 
103
  }
104
 
105
  # =====================================================
106
+ # MODEL DISCOVERY (نفس الكود لم يتغير)
107
  # =====================================================
108
  def _normalize_model_list(x: Any) -> List[str]:
109
  out: List[str] = []
 
175
  return uniq
176
 
177
  # =====================================================
178
+ # STREAM CLEANER (نفس الكود لم يتغير)
179
  # =====================================================
180
  def clean_stream(chunk):
181
  try:
 
182
  if isinstance(chunk, dict):
 
183
  if 'choices' in chunk and chunk['choices']:
184
  delta = chunk['choices'][0].get('delta', {})
185
  if 'content' in delta:
186
  return delta['content']
187
  if 'text' in delta:
188
  return delta['text']
 
189
  if 'content' in chunk:
190
  return chunk['content']
191
  if 'text' in chunk:
192
  return chunk['text']
193
  return ""
194
 
 
195
  if isinstance(chunk, str):
 
196
  if chunk.strip().startswith("{") and chunk.strip().endswith("}"):
197
  try:
198
  data = json.loads(chunk)
 
199
  if 'choices' in data and data['choices']:
200
  delta = data['choices'][0].get('delta', {})
201
  if 'content' in delta:
 
207
  except:
208
  pass
209
 
 
210
  chunk = chunk.replace('\\n', '\n').replace('\\r', '\r').replace('\\t', ' ')
211
  chunk = chunk.replace('\\"', '"').replace("\\'", "'")
212
  return chunk
213
 
 
214
  return str(chunk)
215
  except Exception as e:
216
  logger.warning(f"خطأ في clean_stream: {e}")
217
  return ""
218
 
219
  # =====================================================
220
+ # CACHE (نفس الكود لم يتغير)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  # =====================================================
222
  CACHE = {}
223
  CACHE_TS = {}
224
 
225
  def _cache_get(key):
 
226
  if key not in CACHE:
227
  return None
 
228
  return CACHE[key]
229
 
230
  def _cache_set(key, val):
 
232
  CACHE_TS[key] = time.time()
233
 
234
  # =====================================================
235
+ # CHAT LOGIC فس الكود لم يتغير - هذا هو قلب المشروع)
236
  # =====================================================
237
+ _PROVIDER_MODEL_CACHE = {}
238
 
239
+ def ask(message: str, history, provider_name: str, model_name: str):
240
  history = history or []
241
  message = (message or "").strip()
242
 
 
252
  yield cached
253
  return
254
 
 
 
 
255
  msgs = []
256
 
257
  try:
258
  if history:
 
259
  if isinstance(history[0], dict):
 
260
  for item in history[-40:]:
261
  role = item.get("role")
262
  content = item.get("content")
 
265
  "role": str(role),
266
  "content": str(content)
267
  })
 
268
  else:
 
269
  for item in history[-20:]:
270
  if isinstance(item, (list, tuple)) and len(item) == 2:
271
  u = item[0]
 
288
  "content": message
289
  })
290
 
 
 
 
291
  fallback_providers = [
292
  provider_name,
293
  "Perplexity",
 
302
  used = []
303
 
304
  for pname in fallback_providers:
 
305
  if pname in used:
306
  continue
307
 
 
324
  ]
325
 
326
  for m in model_candidates[:12]:
 
327
  try:
328
  stream = g4f.ChatCompletion.create(
329
  model=m,
 
354
  yield "❌ Failed with all providers."
355
 
356
  # =====================================================
357
+ # FASTAPI (الجزء الجديد - بدلاً من Gradio)
358
  # =====================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
 
360
+ app = FastAPI(title="G4F API", description="G4F Smart Router API")
 
361
 
362
+ # المفتاح السري
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
  API_KEY = os.getenv("API_KEY", "mysecretkey123")
364
 
365
+ # نموذج البيانات للطلب
366
+ class ChatRequest(BaseModel):
367
+ message: str
368
+ provider: str = "Perplexity"
369
+ model: str = "sonar"
370
+ history: List[Any] = []
371
+
372
+ # التحقق من المفتاح
373
+ def verify_api_key(request: Request):
374
+ auth = request.headers.get("Authorization")
375
+ if not auth or not auth.startswith("Bearer "):
376
+ raise HTTPException(status_code=401, detail="Missing or invalid Authorization header. Use: Bearer YOUR_KEY")
377
+ key = auth.replace("Bearer ", "")
378
+ if key != API_KEY:
379
+ raise HTTPException(status_code=403, detail="Invalid API key")
380
+
381
+ # نقطة الصحة
382
+ @app.get("/health")
383
+ async def health():
384
+ return {"status": "ok", "cookies": COOKIE_STATUS, "providers": list(REAL_PROVIDERS.keys())}
385
+
386
+ # نقطة الدردشة (غير متدفق)
387
+ @app.post("/chat")
388
+ async def chat(request: Request, chat_req: ChatRequest):
389
+ verify_api_key(request)
390
+
391
+ full_response = ""
392
+ for chunk in ask(chat_req.message, chat_req.history, chat_req.provider, chat_req.model):
393
+ full_response = chunk
394
+
395
+ return JSONResponse(content={"response": full_response})
396
+
397
+ # نقطة الدردشة (متتدفق - Stream)
398
+ @app.post("/chat/stream")
399
+ async def chat_stream(request: Request, chat_req: ChatRequest):
400
+ verify_api_key(request)
401
+
402
+ async def generate():
403
+ for chunk in ask(chat_req.message, chat_req.history, chat_req.provider, chat_req.model):
404
+ yield f"data: {json.dumps({'chunk': chunk})}\n\n"
405
+ yield "data: [DONE]\n\n"
406
+
407
+ return StreamingResponse(generate(), media_type="text/event-stream")
408
+
409
+ # نقطة الحصول على المزودين والنماذج
410
+ @app.get("/providers")
411
+ async def get_providers(request: Request):
412
+ verify_api_key(request)
413
+
414
+ providers_info = {}
415
+ for pname, pobj in REAL_PROVIDERS.items():
416
+ if pname not in _PROVIDER_MODEL_CACHE:
417
+ _PROVIDER_MODEL_CACHE[pname] = discover_provider_models(pobj, pname)
418
+ providers_info[pname] = _PROVIDER_MODEL_CACHE[pname]
419
+
420
+ return JSONResponse(content={"providers": providers_info})
421
+
422
+ if __name__ == "__main__":
423
+ import uvicorn
424
+ port = int(os.getenv("PORT", "7860"))
425
+ uvicorn.run(app, host="0.0.0.0", port=port)