bahi-bh commited on
Commit
b8f9b0d
·
verified ·
1 Parent(s): 39d53e0

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +464 -0
app.py ADDED
@@ -0,0 +1,464 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ logging.basicConfig(level=logging.INFO)
11
+ logger = logging.getLogger("g4f-smart-router")
12
+
13
+ # =====================================================
14
+ # COOKIES
15
+ # =====================================================
16
+ def _load_cookies_raw() -> Dict[str, Any]:
17
+ raw_env = (os.getenv("COOKIES_JSON") or "").strip()
18
+
19
+ if raw_env:
20
+ try:
21
+ return json.loads(raw_env)
22
+ except Exception as e:
23
+ logger.warning(e)
24
+
25
+ try:
26
+ if os.path.exists("cookies.json"):
27
+ with open("cookies.json", "r", encoding="utf-8") as f:
28
+ return json.load(f)
29
+ except Exception as e:
30
+ logger.warning(e)
31
+
32
+ return {}
33
+
34
+ def load_cookies() -> str:
35
+ data = _load_cookies_raw()
36
+
37
+ if not data:
38
+ return "⚠️ No Cookies"
39
+
40
+ try:
41
+ from g4f.cookies import set_cookies
42
+ except Exception:
43
+ return "⚠️ Cookies Found"
44
+
45
+ for domain, vals in data.items():
46
+ try:
47
+ dom = domain if "." in domain else f".{domain}.com"
48
+
49
+ if isinstance(vals, list):
50
+ vals = {
51
+ x["name"]: x["value"]
52
+ for x in vals
53
+ if isinstance(x, dict)
54
+ }
55
+
56
+ if isinstance(vals, dict):
57
+ set_cookies(dom, vals)
58
+
59
+ except Exception as e:
60
+ logger.warning(e)
61
+
62
+ return "✅ Cookies Loaded"
63
+
64
+ COOKIE_STATUS = load_cookies()
65
+
66
+ # =====================================================
67
+ # PROVIDERS
68
+ # =====================================================
69
+ def get_provider(name: str):
70
+ try:
71
+ return getattr(g4f.Provider, name)
72
+ except:
73
+ return None
74
+
75
+ REAL_PROVIDERS: Dict[str, Any] = {
76
+ "Perplexity": get_provider("Perplexity") or get_provider("PerplexityAi"),
77
+ "Copilot": get_provider("Copilot"),
78
+ "Qwen": get_provider("Qwen"),
79
+ "Blackbox": get_provider("Blackbox"),
80
+ "DeepSeek": get_provider("DeepSeek"),
81
+ "HuggingFace": get_provider("HuggingFace"),
82
+ "Cloudflare": get_provider("Cloudflare"),
83
+ "You": get_provider("You"),
84
+ "Bing": get_provider("Bing"),
85
+ }
86
+
87
+ REAL_PROVIDERS = {k: v for k, v in REAL_PROVIDERS.items() if v}
88
+
89
+ # =====================================================
90
+ # MODELS
91
+ # =====================================================
92
+ PROVIDER_MODELS_FALLBACK: Dict[str, List[str]] = {
93
+ "Perplexity": ["sonar", "sonar-pro", "gpt-4o", "llama-3"],
94
+ "Copilot": ["gpt-4o", "gpt-4", "turbo"],
95
+ "Qwen": ["qwen-max", "qwen-plus", "qwen-turbo", "qwen"],
96
+ "Blackbox": ["gpt-4o", "claude-3", "gemini-pro", "llama-3"],
97
+ "DeepSeek": ["deepseek-chat", "deepseek-coder"],
98
+ "HuggingFace": ["meta-llama", "mistral", "qwen", "gemma"],
99
+ "Cloudflare": ["llama-3", "mistral", "qwen"],
100
+ "You": ["gpt-4o", "claude", "llama-3"],
101
+ "Bing": ["gpt-4o", "gpt-4"],
102
+ }
103
+
104
+ # =====================================================
105
+ # MODEL DISCOVERY
106
+ # =====================================================
107
+ def _normalize_model_list(x: Any) -> List[str]:
108
+ out: List[str] = []
109
+
110
+ if x is None:
111
+ return out
112
+
113
+ if isinstance(x, dict):
114
+ out = [str(k) for k in x.keys()]
115
+ elif isinstance(x, (list, tuple, set)):
116
+ out = [str(i) for i in x]
117
+ else:
118
+ out = [str(x)]
119
+
120
+ out = [m.strip() for m in out if m]
121
+
122
+ seen = set()
123
+ uniq = []
124
+
125
+ for m in out:
126
+ if m not in seen:
127
+ uniq.append(m)
128
+ seen.add(m)
129
+
130
+ return uniq
131
+
132
+ def discover_provider_models(provider_obj: Any, provider_name: str) -> List[str]:
133
+
134
+ candidates = []
135
+
136
+ for attr in (
137
+ "models",
138
+ "model",
139
+ "default_model",
140
+ "available_models",
141
+ "supported_models"
142
+ ):
143
+ try:
144
+ if hasattr(provider_obj, attr):
145
+ candidates += _normalize_model_list(
146
+ getattr(provider_obj, attr)
147
+ )
148
+ except:
149
+ pass
150
+
151
+ for fn in ("get_models", "models_list", "list_models"):
152
+ try:
153
+ if hasattr(provider_obj, fn):
154
+ candidates += _normalize_model_list(
155
+ getattr(provider_obj, fn)()
156
+ )
157
+ except:
158
+ pass
159
+
160
+ if not candidates:
161
+ candidates = PROVIDER_MODELS_FALLBACK.get(
162
+ provider_name,
163
+ ["gpt-4o"]
164
+ )
165
+
166
+ seen = set()
167
+ uniq = []
168
+
169
+ for m in candidates:
170
+ if m not in seen:
171
+ uniq.append(m)
172
+ seen.add(m)
173
+
174
+ return uniq
175
+
176
+ # =====================================================
177
+ # STREAM CLEANER (محسّنة جداً - تحل مشاكل JSON والأكواد)
178
+ # =====================================================
179
+ def clean_stream(chunk):
180
+ try:
181
+ # إذا كانت القطعة قاموساً (dict) مثل JSON
182
+ if isinstance(chunk, dict):
183
+ # استخرا�� النص من تنسيق Perplexity
184
+ if 'choices' in chunk and chunk['choices']:
185
+ delta = chunk['choices'][0].get('delta', {})
186
+ if 'content' in delta:
187
+ return delta['content']
188
+ if 'text' in delta:
189
+ return delta['text']
190
+ # تنسيقات أخرى
191
+ if 'content' in chunk:
192
+ return chunk['content']
193
+ if 'text' in chunk:
194
+ return chunk['text']
195
+ return ""
196
+
197
+ # إذا كانت القطعة نصاً
198
+ if isinstance(chunk, str):
199
+ # محاولة تفسيرها كـ JSON
200
+ if chunk.strip().startswith("{") and chunk.strip().endswith("}"):
201
+ try:
202
+ data = json.loads(chunk)
203
+ # استخراج النص من JSON
204
+ if 'choices' in data and data['choices']:
205
+ delta = data['choices'][0].get('delta', {})
206
+ if 'content' in delta:
207
+ return delta['content']
208
+ if 'content' in data:
209
+ return data['content']
210
+ if 'text' in data:
211
+ return data['text']
212
+ except:
213
+ pass
214
+
215
+ # تنظيف علامات الترميز (للكود)
216
+ chunk = chunk.replace('\\n', '\n').replace('\\r', '\r').replace('\\t', ' ')
217
+ chunk = chunk.replace('\\"', '"').replace("\\'", "'")
218
+ return chunk
219
+
220
+ # إذا كانت القطعة شيئاً آخر
221
+ return str(chunk)
222
+ except Exception as e:
223
+ logger.warning(f"خطأ في clean_stream: {e}")
224
+ return ""
225
+
226
+ # =====================================================
227
+ # UI update
228
+ # =====================================================
229
+ _PROVIDER_MODEL_CACHE = {}
230
+
231
+ def update_models(provider_name):
232
+
233
+ pobj = REAL_PROVIDERS.get(provider_name)
234
+
235
+ if not pobj:
236
+ arr = ["gpt-4o"]
237
+ else:
238
+ if provider_name not in _PROVIDER_MODEL_CACHE:
239
+ _PROVIDER_MODEL_CACHE[provider_name] = discover_provider_models(
240
+ pobj,
241
+ provider_name
242
+ )
243
+
244
+ arr = _PROVIDER_MODEL_CACHE[provider_name]
245
+
246
+ return gr.update(
247
+ choices=arr,
248
+ value=arr[0]
249
+ )
250
+
251
+ # =====================================================
252
+ # CACHE
253
+ # =====================================================
254
+ CACHE = {}
255
+ CACHE_TS = {}
256
+
257
+ def _cache_get(key):
258
+
259
+ if key not in CACHE:
260
+ return None
261
+
262
+ return CACHE[key]
263
+
264
+ def _cache_set(key, val):
265
+ CACHE[key] = val
266
+ CACHE_TS[key] = time.time()
267
+
268
+ # =====================================================
269
+ # CHAT (محسّنة - ذاكرة أطول وfallback أفضل)
270
+ # =====================================================
271
+ def ask(message: str, history, provider_name: str, model_name: str):
272
+
273
+ history = history or []
274
+ message = (message or "").strip()
275
+
276
+ if not message:
277
+ yield ""
278
+ return
279
+
280
+ key = f"{provider_name}|{model_name}|{message}"
281
+
282
+ cached = _cache_get(key)
283
+
284
+ if cached is not None:
285
+ yield cached
286
+ return
287
+
288
+ # =================================================
289
+ # بناء الذاكرة (محسّن - يحفظ آخر 40/20 بدلاً من 16/8)
290
+ # =================================================
291
+ msgs = []
292
+
293
+ try:
294
+ if history:
295
+ # format جديد
296
+ if isinstance(history[0], dict):
297
+ # حفظ آخر 40 رسالة (بدلاً من 16)
298
+ for item in history[-40:]:
299
+ role = item.get("role")
300
+ content = item.get("content")
301
+ if role and content:
302
+ msgs.append({
303
+ "role": str(role),
304
+ "content": str(content)
305
+ })
306
+ # format قديم
307
+ else:
308
+ # حفظ آخر 20 محادثة (بدلاً من 8)
309
+ for item in history[-20:]:
310
+ if isinstance(item, (list, tuple)) and len(item) == 2:
311
+ u = item[0]
312
+ a = item[1]
313
+ if u:
314
+ msgs.append({
315
+ "role": "user",
316
+ "content": str(u)
317
+ })
318
+ if a:
319
+ msgs.append({
320
+ "role": "assistant",
321
+ "content": str(a)
322
+ })
323
+ except Exception as e:
324
+ logger.warning(e)
325
+
326
+ msgs.append({
327
+ "role": "user",
328
+ "content": message
329
+ })
330
+
331
+ # =================================================
332
+ # تجربة المزودين مع fallback
333
+ # =================================================
334
+ fallback_providers = [
335
+ provider_name,
336
+ "Perplexity",
337
+ "Copilot",
338
+ "Blackbox",
339
+ "DeepSeek",
340
+ "Bing",
341
+ "You",
342
+ "Qwen"
343
+ ]
344
+
345
+ used = []
346
+
347
+ for pname in fallback_providers:
348
+
349
+ if pname in used:
350
+ continue
351
+
352
+ used.append(pname)
353
+
354
+ pobj = REAL_PROVIDERS.get(pname)
355
+
356
+ if not pobj:
357
+ continue
358
+
359
+ if pname not in _PROVIDER_MODEL_CACHE:
360
+ _PROVIDER_MODEL_CACHE[pname] = discover_provider_models(
361
+ pobj,
362
+ pname
363
+ )
364
+
365
+ model_candidates = [model_name] + [
366
+ x for x in _PROVIDER_MODEL_CACHE[pname]
367
+ if x != model_name
368
+ ]
369
+
370
+ for m in model_candidates[:12]:
371
+
372
+ try:
373
+ stream = g4f.ChatCompletion.create(
374
+ model=m,
375
+ provider=pobj,
376
+ messages=msgs,
377
+ stream=True
378
+ )
379
+
380
+ text = ""
381
+
382
+ for chunk in stream:
383
+ c = clean_stream(chunk)
384
+
385
+ if c is None or c == "":
386
+ continue
387
+
388
+ text += c
389
+ yield text
390
+
391
+ if text.strip():
392
+ _cache_set(key, text)
393
+ return
394
+
395
+ except Exception as e:
396
+ logger.warning(e)
397
+ continue
398
+
399
+ yield "❌ Failed with all providers."
400
+
401
+ # =====================================================
402
+ # UI
403
+ # =====================================================
404
+ css = """
405
+ .gradio-container{
406
+ max-width:1200px!important;
407
+ }
408
+ """
409
+
410
+ providers = list(REAL_PROVIDERS.keys())
411
+
412
+ default_provider = (
413
+ "Perplexity"
414
+ if "Perplexity" in providers
415
+ else providers[0]
416
+ )
417
+
418
+ default_models = discover_provider_models(
419
+ REAL_PROVIDERS[default_provider],
420
+ default_provider
421
+ )
422
+
423
+ with gr.Blocks(
424
+ theme=gr.themes.Soft(),
425
+ css=css
426
+ ) as demo:
427
+
428
+ gr.Markdown("# 🤖 G4F Smart Router FINAL")
429
+ gr.Markdown(COOKIE_STATUS)
430
+
431
+ with gr.Row():
432
+
433
+ provider_dd = gr.Dropdown(
434
+ choices=providers,
435
+ value=default_provider,
436
+ label="Provider"
437
+ )
438
+
439
+ model_dd = gr.Dropdown(
440
+ choices=default_models,
441
+ value=default_models[0],
442
+ label="Model"
443
+ )
444
+
445
+ provider_dd.change(
446
+ update_models,
447
+ inputs=provider_dd,
448
+ outputs=model_dd
449
+ )
450
+
451
+ gr.ChatInterface(
452
+ fn=ask,
453
+ additional_inputs=[
454
+ provider_dd,
455
+ model_dd
456
+ ],
457
+ title="",
458
+ description="Choose provider → models update automatically."
459
+ )
460
+
461
+ demo.queue().launch(
462
+ server_name="0.0.0.0",
463
+ server_port=int(os.getenv("PORT", "7860"))
464
+ )