bahi-bh commited on
Commit
06f5f87
·
verified ·
1 Parent(s): 8334502

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +480 -0
app.py ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ 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()
22
+
23
+ if raw_env:
24
+ try:
25
+ return json.loads(raw_env)
26
+ except Exception as e:
27
+ logger.warning(e)
28
+
29
+ try:
30
+ if os.path.exists("cookies.json"):
31
+ with open("cookies.json", "r", encoding="utf-8") as f:
32
+ return json.load(f)
33
+ except Exception as e:
34
+ logger.warning(e)
35
+
36
+ return {}
37
+
38
+ def load_cookies() -> str:
39
+ data = _load_cookies_raw()
40
+
41
+ if not data:
42
+ return "⚠️ No Cookies"
43
+
44
+ try:
45
+ from g4f.cookies import set_cookies
46
+ except Exception:
47
+ return "⚠️ Cookies Found"
48
+
49
+ for domain, vals in data.items():
50
+ try:
51
+ dom = domain if "." in domain else f".{domain}.com"
52
+
53
+ if isinstance(vals, list):
54
+ vals = {
55
+ x["name"]: x["value"]
56
+ for x in vals
57
+ if isinstance(x, dict)
58
+ }
59
+
60
+ if isinstance(vals, dict):
61
+ set_cookies(dom, vals)
62
+
63
+ except Exception as e:
64
+ logger.warning(e)
65
+
66
+ return "✅ Cookies Loaded"
67
+
68
+ COOKIE_STATUS = load_cookies()
69
+
70
+ # =====================================================
71
+ # PROVIDERS
72
+ # =====================================================
73
+ def get_provider(name: str):
74
+ try:
75
+ return getattr(g4f.Provider, name)
76
+ except:
77
+ return None
78
+
79
+ REAL_PROVIDERS: Dict[str, Any] = {
80
+ "Perplexity": get_provider("Perplexity") or get_provider("PerplexityAi"),
81
+ "Copilot": get_provider("Copilot"),
82
+ "Qwen": get_provider("Qwen"),
83
+ "Blackbox": get_provider("Blackbox"),
84
+ "DeepSeek": get_provider("DeepSeek"),
85
+ "HuggingFace": get_provider("HuggingFace"),
86
+ "Cloudflare": get_provider("Cloudflare"),
87
+ "You": get_provider("You"),
88
+ "Bing": get_provider("Bing"),
89
+ }
90
+
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"],
98
+ "Copilot": ["gpt-4o", "gpt-4", "turbo"],
99
+ "Qwen": ["qwen-max", "qwen-plus", "qwen-turbo", "qwen"],
100
+ "Blackbox": ["gpt-4o", "claude-3", "gemini-pro", "llama-3"],
101
+ "DeepSeek": ["deepseek-chat", "deepseek-coder"],
102
+ "HuggingFace": ["meta-llama", "mistral", "qwen", "gemma"],
103
+ "Cloudflare": ["llama-3", "mistral", "qwen"],
104
+ "You": ["gpt-4o", "claude", "llama-3"],
105
+ "Bing": ["gpt-4o", "gpt-4"],
106
+ }
107
+
108
+ # =====================================================
109
+ # MODEL DISCOVERY
110
+ # =====================================================
111
+ def _normalize_model_list(x: Any) -> List[str]:
112
+ out: List[str] = []
113
+
114
+ if x is None:
115
+ return out
116
+
117
+ if isinstance(x, dict):
118
+ out = [str(k) for k in x.keys()]
119
+ elif isinstance(x, (list, tuple, set)):
120
+ out = [str(i) for i in x]
121
+ else:
122
+ out = [str(x)]
123
+
124
+ out = [m.strip() for m in out if m]
125
+
126
+ seen = set()
127
+ uniq = []
128
+
129
+ for m in out:
130
+ if m not in seen:
131
+ uniq.append(m)
132
+ seen.add(m)
133
+
134
+ return uniq
135
+
136
+ def discover_provider_models(provider_obj: Any, provider_name: str) -> List[str]:
137
+
138
+ candidates = []
139
+
140
+ for attr in (
141
+ "models",
142
+ "model",
143
+ "default_model",
144
+ "available_models",
145
+ "supported_models"
146
+ ):
147
+ try:
148
+ if hasattr(provider_obj, attr):
149
+ candidates += _normalize_model_list(
150
+ getattr(provider_obj, attr)
151
+ )
152
+ except:
153
+ pass
154
+
155
+ for fn in ("get_models", "models_list", "list_models"):
156
+ try:
157
+ if hasattr(provider_obj, fn):
158
+ candidates += _normalize_model_list(
159
+ getattr(provider_obj, fn)()
160
+ )
161
+ except:
162
+ pass
163
+
164
+ if not candidates:
165
+ candidates = PROVIDER_MODELS_FALLBACK.get(
166
+ provider_name,
167
+ ["gpt-4o"]
168
+ )
169
+
170
+ seen = set()
171
+ uniq = []
172
+
173
+ for m in candidates:
174
+ if m not in seen:
175
+ uniq.append(m)
176
+ seen.add(m)
177
+
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:
211
+ return delta['content']
212
+ if 'content' in data:
213
+ return data['content']
214
+ if 'text' in data:
215
+ return data['text']
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):
269
+ CACHE[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
+
280
+ if not message:
281
+ yield ""
282
+ return
283
+
284
+ key = f"{provider_name}|{model_name}|{message}"
285
+
286
+ cached = _cache_get(key)
287
+
288
+ if cached is not None:
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")
305
+ if role and content:
306
+ msgs.append({
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]
316
+ a = item[1]
317
+ if u:
318
+ msgs.append({
319
+ "role": "user",
320
+ "content": str(u)
321
+ })
322
+ if a:
323
+ msgs.append({
324
+ "role": "assistant",
325
+ "content": str(a)
326
+ })
327
+ except Exception as e:
328
+ logger.warning(e)
329
+
330
+ msgs.append({
331
+ "role": "user",
332
+ "content": message
333
+ })
334
+
335
+ # =================================================
336
+ # تجربة المزودين مع fallback
337
+ # =================================================
338
+ fallback_providers = [
339
+ provider_name,
340
+ "Perplexity",
341
+ "Copilot",
342
+ "Blackbox",
343
+ "DeepSeek",
344
+ "Bing",
345
+ "You",
346
+ "Qwen"
347
+ ]
348
+
349
+ used = []
350
+
351
+ for pname in fallback_providers:
352
+
353
+ if pname in used:
354
+ continue
355
+
356
+ used.append(pname)
357
+
358
+ pobj = REAL_PROVIDERS.get(pname)
359
+
360
+ if not pobj:
361
+ continue
362
+
363
+ if pname not in _PROVIDER_MODEL_CACHE:
364
+ _PROVIDER_MODEL_CACHE[pname] = discover_provider_models(
365
+ pobj,
366
+ pname
367
+ )
368
+
369
+ model_candidates = [model_name] + [
370
+ x for x in _PROVIDER_MODEL_CACHE[pname]
371
+ if x != model_name
372
+ ]
373
+
374
+ for m in model_candidates[:12]:
375
+
376
+ try:
377
+ stream = g4f.ChatCompletion.create(
378
+ model=m,
379
+ provider=pobj,
380
+ messages=msgs,
381
+ stream=True
382
+ )
383
+
384
+ text = ""
385
+
386
+ for chunk in stream:
387
+ c = clean_stream(chunk)
388
+
389
+ if c is None or c == "":
390
+ continue
391
+
392
+ text += c
393
+ yield text
394
+
395
+ if text.strip():
396
+ _cache_set(key, text)
397
+ return
398
+
399
+ except Exception as e:
400
+ logger.warning(e)
401
+ continue
402
+
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
+ )