bahi-bh commited on
Commit
a7f2531
·
verified ·
1 Parent(s): 49ee4d5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +190 -1033
app.py CHANGED
@@ -1,1119 +1,276 @@
1
- """
2
- ╔══════════════════════════════════════════════════════════════════╗
3
- ║ SmartG4F API - محسّن بالكامل ║
4
- ║ بدون أي قيود ║
5
- ╚══════════════════════════════════════════════════════════════════╝
6
- """
7
-
8
- # ============================================================
9
- #Imports
10
- # ============================================================
11
- import os
12
- import sys
13
  import time
14
  import random
15
- import asyncio
16
  import traceback
17
- import json
18
  import uuid
19
- import logging
20
- import threading
21
- import hashlib
22
- from datetime import datetime, timedelta
23
- from collections import deque
24
- from typing import Optional, List, Dict, Any, Union
25
- from contextlib import asynccontextmanager
26
- from functools import wraps
27
- import re
28
-
29
- # FastAPI & Dependencies
30
- from fastapi import FastAPI, HTTPException, Depends, Header, Request, Response
31
- from fastapi.middleware.cors import CORSMiddleware
32
- from fastapi.responses import StreamingResponse, JSONResponse
33
- from fastapi.staticfiles import StaticFiles
34
- from pydantic import BaseModel, Field, validator
35
  import uvicorn
36
 
37
- # g4f Dependencies
38
  from g4f.client import Client
39
  from g4f.Provider import __providers__
40
 
41
- # ============================================================
42
- # إعدادات الـ Logging المحسّن
43
- # ============================================================
44
- class ColoredFormatter(logging.Formatter):
45
- """تنسيق Logs ملون للتمييز"""
46
- COLORS = {
47
- 'DEBUG': '\033[36m', # Cyan
48
- 'INFO': '\033[32m', # Green
49
- 'WARNING': '\033[33m', # Yellow
50
- 'ERROR': '\033[31m', # Red
51
- 'CRITICAL': '\033[35m', # Magenta
52
- 'RESET': '\033[0m'
53
- }
54
-
55
- def format(self, record):
56
- color = self.COLORS.get(record.levelname, self.COLORS['RESET'])
57
- record.levelname = f"{color}{record.levelname}{self.COLORS['RESET']}"
58
- return super().format(record)
59
-
60
- # إعداد الـ Logger
61
- logger = logging.getLogger("SmartG4F")
62
- logger.setLevel(logging.DEBUG)
63
-
64
- # Console Handler
65
- console_handler = logging.StreamHandler()
66
- console_handler.setLevel(logging.DEBUG)
67
- console_handler.setFormatter(ColoredFormatter(
68
- '%(asctime)s │ %(levelname)s │ %(message)s',
69
- datefmt='%H:%M:%S'
70
- ))
71
- logger.addHandler(console_handler)
72
-
73
- # ============================================================
74
- # إعداد نظام البروكسي (Proxies) - بدون قيود
75
- # ============================================================
76
- PROXIES_POOL: List[str] = []
77
-
78
- def load_proxies():
79
- """تحميل البروكسيات من مصادر متعددة"""
80
- global PROXIES_POOL
81
-
82
- # 1. من Environment Variable
83
- env_proxies = os.environ.get("PROXIES_LIST", "")
84
- if env_proxies:
85
- PROXIES_POOL.extend([p.strip() for p in env_proxies.split(",") if p.strip()])
86
-
87
- # 2. من ملف proxies.txt
88
- if os.path.exists("proxies.txt"):
89
- with open("proxies.txt", "r") as f:
90
- PROXIES_POOL.extend([line.strip() for line in f if line.strip()])
91
-
92
- # 3. البروكسي الفردي
93
- single_proxy = os.environ.get("STRONG_PROXY_URL") or os.environ.get("HTTP_PROXY")
94
- if single_proxy and single_proxy not in PROXIES_POOL:
95
- PROXIES_POOL.append(single_proxy)
96
-
97
- # إزالة المكررات
98
- PROXIES_POOL = list(dict.fromkeys(PROXIES_POOL))
99
-
100
- logger.info(f"📡 تم تحميل {len(PROXIES_POOL)} بروكسي")
101
-
102
- def get_random_proxy() -> Optional[str]:
103
- """جلب بروكسي عشوائي"""
104
- if not PROXIES_POOL:
105
- # محاولة تحميل البروكسيات
106
- load_proxies()
107
-
108
- if PROXIES_POOL:
109
- proxy = random.choice(PROXIES_POOL)
110
- # التحقق من صحة البروكسي
111
- if _validate_proxy(proxy):
112
- return proxy
113
-
114
- return None
115
-
116
- def _validate_proxy(proxy: str) -> bool:
117
- """التحقق من صحة صيغة البروكسي"""
118
- if not proxy:
119
- return False
120
- # صيغ مقبولة:
121
- # http://host:port
122
- # http://user:pass@host:port
123
- # socks5://host:port
124
- patterns = [
125
- r'^https?://[\w\-\.]+:\d+$',
126
- r'^https?://[\w\-\.]+:\d+@[\w\-\.]+:\d+$',
127
- r'^socks5?://[\w\-\.]+:\d+$',
128
- ]
129
- return any(re.match(p, proxy) for p in patterns)
130
-
131
- # ============================================================
132
- # تصفية وتجهيز المزودات (Safe Providers)
133
- # ============================================================
134
- MODEL = os.environ.get("DEFAULT_MODEL", "gpt-4o-mini")
135
- MAX_RETRIES = int(os.environ.get("MAX_RETRIES", "15"))
136
- TIMEOUT = int(os.environ.get("TIMEOUT", "120"))
137
- MAX_PROVIDERS_IN_MEMORY = 100
138
 
139
  SAFE_PROVIDERS = []
 
140
  for provider in __providers__:
 
141
  try:
 
142
  name = provider.__name__.lower()
143
-
144
- # تصفية المزودات غير العاملة
145
  if not getattr(provider, "working", False):
146
  continue
147
-
148
- # تخطي المزودات التي تحتاج تسجيل
149
  if getattr(provider, "needs_auth", False):
150
  continue
151
-
152
- # تخطي المزودات التي تحتاج nodriver
153
  if getattr(provider, "use_nodriver", False):
154
  continue
155
-
156
- # منع المزودات المحظورة
157
- blocked = ["openai", "qwen", "copilot", "gemini", "claude"]
 
 
 
 
 
 
158
  if any(x in name for x in blocked):
159
  continue
160
-
161
  SAFE_PROVIDERS.append(provider)
 
162
  except:
163
  pass
164
 
165
  random.shuffle(SAFE_PROVIDERS)
166
- logger.info(f"✅ SAFE PROVIDERS: {len(SAFE_PROVIDERS)}")
167
-
168
- # ============================================================
169
- # إنشاء ملف config.json تلقائياً لـ Context7 MCP
170
- # ============================================================
171
- CONFIG_PATH = "config.json"
172
- if not os.path.exists(CONFIG_PATH):
173
- context7_key = os.environ.get("CONTEXT7_API_KEY", "")
174
- if context7_key:
175
- config_data = {
176
- "mcpServers": {
177
- "context7": {
178
- "url": "https://mcp.context7.com/mcp",
179
- "headers": {
180
- "Authorization": f"Bearer {context7_key}"
181
- }
182
- }
183
- }
184
- }
185
- with open(CONFIG_PATH, "w", encoding="utf-8") as f:
186
- json.dump(config_data, f, indent=2, ensure_ascii=False)
187
- logger.info("[+] config.json auto-generated for Context7 MCP")
188
- else:
189
- logger.warning("[!] CONTEXT7_API_KEY not set; skipping config.json creation")
190
-
191
- # ============================================================
192
- # نظام إدارة الـ Client المحسّن
193
- # ============================================================
194
- class ClientManager:
195
- """مدير الـ Clients للاتصال بـ g4f"""
196
- _instances: Dict[str, 'Client'] = {}
197
- _lock = threading.Lock()
198
- _max_instances = 50
199
-
200
- @classmethod
201
- def get_client(cls, proxy: Optional[str] = None) -> 'Client':
202
- """الحصول على Client (مع أو بدون بروكسي)"""
203
- key = proxy or "default"
204
-
205
- with cls._lock:
206
- if key not in cls._instances:
207
- if len(cls._instances) >= cls._max_instances:
208
- # إزالة أقدم client
209
- oldest_key = next(iter(cls._instances))
210
- del cls._instances[oldest_key]
211
-
212
- cls._instances[key] = Client(proxy=proxy)
213
- logger.debug(f"🆕 New client created: {key[:20]}...")
214
-
215
- return cls._instances[key]
216
-
217
- # ============================================================
218
- # نظام SmartG4F المحسّن بالكامل
219
- # ============================================================
220
  class SmartG4F:
221
- """
222
- نظام ذكي لإدارة Providers
223
- - تتبع Providers الناجحة والفاشلة
224
- - إدارة ذاكرة محسّنة
225
- - إحصائيات مفصلة
226
- - Thread-Safe
227
- """
228
-
229
- def __init__(self, max_good_providers: int = MAX_PROVIDERS_IN_MEMORY):
230
- # Good Providers - محدود بحجم ثابت
231
- self.good = deque(maxlen=max_good_providers)
232
-
233
- # Bad Providers - مع وقت انتهاء
234
- self.bad: Dict[str, float] = {}
235
-
236
- # قفل للأمان في تعدد الخيوط
237
- self.lock = threading.Lock()
238
-
239
- # إحصائيات شاملة
240
- self.stats = {
241
- "total_requests": 0,
242
- "successful_requests": 0,
243
- "failed_requests": 0,
244
- "total_tokens_used": 0,
245
- "avg_response_time": 0,
246
- "start_time": time.time()
247
- }
248
-
249
- # سجل الأخطاء الأخير
250
- self.error_log: deque = deque(maxlen=100)
251
-
252
- # Provider Stats
253
- self.provider_stats: Dict[str, Dict] = {}
254
-
255
- def mark_good(self, provider) -> None:
256
- """وضع Provider في قائمة الناجحين"""
257
- with self.lock:
258
- provider_name = provider.__name__
259
- if provider not in self.good:
260
- self.good.append(provider)
261
-
262
- # تحديث الإحصائيات
263
- if provider_name not in self.provider_stats:
264
- self.provider_stats[provider_name] = {
265
- "success": 0, "failed": 0, "avg_time": 0
266
- }
267
- self.provider_stats[provider_name]["success"] += 1
268
-
269
- def mark_bad(self, provider, cooldown: int = 300) -> None:
270
- """وضع Provider في قائمة الفاشلين"""
271
- with self.lock:
272
- provider_name = provider.__name__
273
- self.bad[provider_name] = time.time() + cooldown
274
-
275
- # تحديث الإحصائيات
276
- if provider_name not in self.provider_stats:
277
- self.provider_stats[provider_name] = {
278
- "success": 0, "failed": 0, "avg_time": 0
279
- }
280
- self.provider_stats[provider_name]["failed"] += 1
281
-
282
- logger.warning(f"⚠️ Provider marked bad: {provider_name} (cooldown: {cooldown}s)")
283
-
284
- def is_bad(self, provider) -> bool:
285
- """فحص إذا كان Provider في قائمة الحظر"""
286
- with self.lock:
287
- provider_name = provider.__name__
288
- expire = self.bad.get(provider_name)
289
-
290
- if not expire:
291
- return False
292
-
293
- if time.time() > expire:
294
- del self.bad[provider_name]
295
- logger.info(f"✅ Provider recovered: {provider_name}")
296
- return False
297
-
298
- return True
299
-
300
- def provider_pool(self) -> List:
301
- """إنشاء pool من Providers المتاحة"""
302
- with self.lock:
303
- providers = list(self.good)
304
-
305
- for p in SAFE_PROVIDERS:
306
- if p not in providers and not self.is_bad(p):
307
- providers.append(p)
308
-
309
- random.shuffle(providers)
310
- return providers
311
-
312
- def get_stats(self) -> Dict[str, Any]:
313
- """الحصول على الإحصائيات"""
314
- with self.lock:
315
- uptime = time.time() - self.stats["start_time"]
316
- success_rate = (
317
- self.stats["successful_requests"] / self.stats["total_requests"] * 100
318
- if self.stats["total_requests"] > 0 else 0
319
- )
320
-
321
- return {
322
- **self.stats,
323
- "uptime_seconds": uptime,
324
- "uptime_human": str(timedelta(seconds=int(uptime))),
325
- "success_rate": round(success_rate, 2),
326
- "good_providers": len(self.good),
327
- "bad_providers": len(self.bad),
328
- "available_providers": len(self.provider_pool()),
329
- "provider_stats": dict(self.provider_stats),
330
- "recent_errors": list(self.error_log)
331
- }
332
-
333
- def ask(
334
- self,
335
- prompt: str,
336
- temperature: float = 0.7,
337
- max_tokens: Optional[int] = None,
338
- stream: bool = False
339
- ) -> Dict[str, Any]:
340
- """إرسال طلب ونيل الرد"""
341
- request_id = str(uuid.uuid4())[:8]
342
  errors = []
343
- start_time = time.time()
344
-
345
- with self.lock:
346
- self.stats["total_requests"] += 1
347
-
348
- logger.info(f"🎯 [{request_id}] New request started")
349
- logger.debug(f"📝 Prompt length: {len(prompt)} chars")
350
-
351
  for attempt in range(MAX_RETRIES):
 
352
  pool = self.provider_pool()
353
-
354
  if not pool:
355
- error_msg = "❌ No providers available"
356
- logger.error(f"🚫 [{request_id}] {error_msg}")
357
- with self.lock:
358
- self.stats["failed_requests"] += 1
359
- self.error_log.append({
360
- "time": datetime.now().isoformat(),
361
- "error": error_msg,
362
- "request_id": request_id
363
- })
364
  return {
365
- "success": False,
366
- "error": error_msg,
367
- "request_id": request_id
368
  }
369
-
370
  provider = random.choice(pool)
371
- current_proxy = get_random_proxy()
372
-
373
- logger.info(
374
- f"🔄 [{request_id}] Attempt {attempt + 1}/{MAX_RETRIES} | "
375
- f"Provider: {provider.__name__} | "
376
- f"Proxy: {'Yes ✅' if current_proxy else 'No ❌'}"
377
- )
378
-
379
  try:
380
- # الحصول على Client
381
- client = ClientManager.get_client(proxy=current_proxy)
382
-
383
- # تجهيز الرسائل
384
- messages = [{"role": "user", "content": prompt}]
385
-
386
- # إرسال الطلب
387
- request_start = time.time()
388
-
389
  response = client.chat.completions.create(
390
  model=MODEL,
391
  provider=provider,
392
  messages=messages,
393
- temperature=temperature,
394
- max_tokens=max_tokens,
395
- timeout=TIMEOUT
396
  )
397
-
398
- request_time = time.time() - request_start
399
-
400
- # استخراج النص
401
  text = response.choices[0].message.content
402
-
403
  if not text:
404
- raise Exception("Empty response received")
405
-
406
- # تحديث الإحصائيات
407
- with self.lock:
408
- self.stats["successful_requests"] += 1
409
- self.stats["total_tokens_used"] += len(text.split())
410
-
411
- # حساب متوسط وقت الاستجابة
412
- old_avg = self.stats["avg_response_time"]
413
- total = self.stats["successful_requests"]
414
- self.stats["avg_response_time"] = (
415
- (old_avg * (total - 1) + request_time) / total
416
- )
417
-
418
- # وضع Provider في قائمة الناجحين
419
- self.mark_good(provider)
420
-
421
- response_data = {
422
  "success": True,
423
- "request_id": request_id,
424
  "provider": provider.__name__,
425
- "response": text,
426
- "response_time": round(request_time, 3),
427
- "tokens_used": len(text.split()),
428
- "proxy_used": bool(current_proxy)
429
  }
430
-
431
- logger.info(
432
- f"✅ [{request_id}] Success! | "
433
- f"Provider: {provider.__name__} | "
434
- f"Time: {request_time:.2f}s | "
435
- f"Length: {len(text)} chars"
436
- )
437
-
438
- return response_data
439
-
440
  except Exception as e:
441
- error_str = str(e)
442
- error_lower = error_str.lower()
443
- request_time = time.time() - start_time
444
-
445
- # تسجيل الخطأ
446
- error_entry = {
447
- "time": datetime.now().isoformat(),
448
  "provider": provider.__name__,
449
- "error": error_str,
450
- "attempt": attempt + 1,
451
- "request_id": request_id
452
- }
453
-
454
- with self.lock:
455
- self.error_log.append(error_entry)
456
-
457
- logger.error(
458
- f"❌ [{request_id}] Error: {error_str[:100]} | "
459
- f"Provider: {provider.__name__}"
460
- )
461
-
462
- if logger.level == logging.DEBUG:
463
- traceback.print_exc()
464
-
465
- errors.append(error_entry)
466
-
467
- # تحديد وقت الانتظار حسب نوع الخطأ
468
- if "429" in error_lower or "rate limit" in error_lower:
469
- cooldown = random.randint(600, 1200)
470
- sleep_time = random.randint(15, 30)
471
- logger.warning(f"⏳ Rate limited - sleeping {sleep_time}s")
472
- time.sleep(sleep_time)
473
-
474
- elif "cloudflare" in error_lower or "cf" in error_lower:
475
- cooldown = random.randint(1200, 2400)
476
- sleep_time = 30
477
- logger.warning(f"🛡️ Cloudflare blocked - sleeping {sleep_time}s")
478
- time.sleep(sleep_time)
479
-
480
- elif "timeout" in error_lower:
481
- cooldown = random.randint(300, 600)
482
- sleep_time = random.randint(5, 15)
483
- logger.warning(f"⏰ Timeout - sleeping {sleep_time}s")
484
- time.sleep(sleep_time)
485
-
486
- elif "connection" in error_lower:
487
- cooldown = random.randint(60, 180)
488
- sleep_time = random.randint(3, 10)
489
- logger.warning(f"🔗 Connection error - sleeping {sleep_time}s")
490
- time.sleep(sleep_time)
491
-
492
  else:
493
- cooldown = random.randint(180, 300)
494
- sleep_time = random.randint(2, 5)
495
- time.sleep(sleep_time)
496
-
497
- self.mark_bad(provider, cooldown)
498
  continue
499
-
500
- # فشل جميع المحاولات
501
- with self.lock:
502
- self.stats["failed_requests"] += 1
503
-
504
- logger.error(f"🚫 [{request_id}] All {MAX_RETRIES} attempts failed")
505
-
506
  return {
507
  "success": False,
508
- "request_id": request_id,
509
- "error": f"Failed after {MAX_RETRIES} attempts",
510
- "errors": errors,
511
- "total_time": round(time.time() - start_time, 3)
512
  }
513
 
514
- # ============================================================
515
- # نماذج Pydantic للتحقق من البيانات
516
- # ============================================================
 
 
 
 
 
 
 
517
  class Message(BaseModel):
518
- """نموذج رسالة واحدة"""
519
- role: str = Field(..., pattern="^(system|user|assistant)$")
520
  content: str
521
-
522
- class Config:
523
- json_schema_extra = {
524
- "example": {
525
- "role": "user",
526
- "content": "مرحباً، كيف حالك؟"
527
- }
528
- }
529
 
530
- class ChatCompletionRequest(BaseModel):
531
- """نموذج طلب الإكمال - متوافق مع OpenAI"""
532
- model: str = Field(default=MODEL, description="نموذج الذكاء الاصطناعي")
533
- messages: List[Message] = Field(
534
- ...,
535
- description="قائمة الرسائل",
536
- min_length=1
537
- )
538
- temperature: float = Field(
539
- default=0.7,
540
- ge=0.0,
541
- le=2.0,
542
- description="درجة الإبداع (0-2)"
543
- )
544
- max_tokens: Optional[int] = Field(
545
- default=4096,
546
- ge=1,
547
- le=32000,
548
- description="الحد الأقصى للرموز"
549
- )
550
- stream: bool = Field(
551
- default=False,
552
- description="تفعيل البث المباشر"
553
- )
554
- top_p: Optional[float] = Field(
555
- default=1.0,
556
- ge=0.0,
557
- le=1.0,
558
- description="نسبة أخذ العينات"
559
- )
560
- frequency_penalty: Optional[float] = Field(
561
- default=0.0,
562
- ge=-2.0,
563
- le=2.0
564
- )
565
- presence_penalty: Optional[float] = Field(
566
- default=0.0,
567
- ge=-2.0,
568
- le=2.0
569
- )
570
- stop: Optional[List[str]] = Field(
571
- default=None,
572
- description="كلمات الإيقاف"
573
- )
574
-
575
- class Config:
576
- json_schema_extra = {
577
- "example": {
578
- "model": "gpt-4o-mini",
579
- "messages": [
580
- {"role": "user", "content": "اكتب قصة قصيرة"}
581
- ],
582
- "temperature": 0.7,
583
- "max_tokens": 1000,
584
- "stream": False
585
- }
586
- }
587
 
588
- class CompletionRequest(BaseModel):
589
- """نموذج طلب الإكمال للنصوص"""
590
- model: str = Field(default=MODEL)
591
- prompt: str = Field(..., min_length=1)
592
- temperature: float = Field(default=0.7, ge=0.0, le=2.0)
593
- max_tokens: Optional[int] = Field(default=4096, ge=1)
594
- stream: bool = Field(default=False)
595
- n: int = Field(default=1, ge=1, le=10)
596
-
597
- class EmbeddingRequest(BaseModel):
598
- """نموذج طلب التضميد"""
599
- model: str = Field(default="text-embedding-3-small")
600
- input: Union[str, List[str]]
601
-
602
- # ============================================================
603
- # نظام الحماية - بدون Rate Limiting
604
- # ============================================================
605
- def verify_api_key(authorization: Optional[str] = Header(None)) -> bool:
606
- """التحقق من مفتاح API"""
607
- expected_key = os.environ.get("MY_PROJECT_API_KEY")
608
-
609
- if not expected_key:
610
- # إذا لم يتم تعيين مفتاح، نسمح بالوصول مع تح��ير
611
- logger.warning("⚠️ WARNING: No API key configured - running in open mode")
612
- return True
613
-
614
- if not authorization:
615
- raise HTTPException(
616
- status_code=403,
617
- detail="❌ Missing Authorization header"
618
- )
619
-
620
- if not authorization.startswith("Bearer "):
621
- raise HTTPException(
622
- status_code=403,
623
- detail="❌ Invalid authorization header format. Use: Bearer <token>"
624
- )
625
-
626
- token = authorization.split(" ", 1)[1]
627
-
628
- if token != expected_key:
629
- raise HTTPException(
630
- status_code=403,
631
- detail="❌ Invalid API key"
632
- )
633
-
634
- return True
635
-
636
- def get_client_ip(request: Request) -> str:
637
- """الحصول على IP العميل"""
638
- forwarded = request.headers.get("X-Forwarded-For")
639
- if forwarded:
640
- return forwarded.split(",")[0].strip()
641
- return request.client.host if request.client else "unknown"
642
-
643
- # ============================================================
644
- # إعداد التطبيق
645
- # ============================================================
646
- @asynccontextmanager
647
- async def lifespan(app: FastAPI):
648
- """إدارة دورة حياة التطبيق"""
649
- # Startup
650
- logger.info("=" * 60)
651
- logger.info("🚀 SmartG4F API Starting...")
652
- logger.info("=" * 60)
653
-
654
- # تحميل البروكسيات
655
- load_proxies()
656
-
657
- # إنشاء مجلدات إذا لم تكن موجودة
658
- os.makedirs("logs", exist_ok=True)
659
-
660
- app.state.start_time = time.time()
661
- app.state.request_count = 0
662
- app.state.ai = SmartG4F()
663
-
664
- logger.info(f"✅ Loaded {len(SAFE_PROVIDERS)} safe providers")
665
- logger.info(f"✅ Model: {MODEL}")
666
- logger.info(f"✅ Max retries: {MAX_RETRIES}")
667
- logger.info(f"✅ Timeout: {TIMEOUT}s")
668
- logger.info("=" * 60)
669
-
670
- yield
671
-
672
- # Shutdown
673
- logger.info("🛑 Shutting down SmartG4F API...")
674
-
675
- app = FastAPI(
676
- title="SmartG4F API",
677
- description="API خادم شامل لـ g4f بدون قيود",
678
- version="2.0.0",
679
- docs_url="/docs",
680
- redoc_url="/redoc",
681
- lifespan=lifespan
682
- )
683
-
684
- # ============================================================
685
- # CORS - مفتوح بالكامل
686
- # ============================================================
687
- app.add_middleware(
688
- CORSMiddleware,
689
- allow_origins=["*"], # كلOrigins مسموحة
690
- allow_credentials=True,
691
- allow_methods=["*"], # كل Methods مسموحة
692
- allow_headers=["*"], # كل Headers مسموحة
693
- expose_headers=["*"]
694
- )
695
-
696
- # ============================================================
697
- # Middleware للتتبع والـ Logging
698
- # ============================================================
699
- @app.middleware("http")
700
- async def log_requests(request: Request, call_next):
701
- """تسجيل كل طلب"""
702
- request_id = str(uuid.uuid4())[:8]
703
- start_time = time.time()
704
-
705
- # إضافة Request ID للـ Headers
706
- request.state.request_id = request_id
707
-
708
- client_ip = get_client_ip(request)
709
-
710
- logger.info(
711
- f"📥 [{request_id}] {request.method} {request.url.path} | "
712
- f"IP: {client_ip}"
713
- )
714
-
715
- try:
716
- response = await call_next(request)
717
- process_time = time.time() - start_time
718
-
719
- logger.info(
720
- f"📤 [{request_id}] Status: {response.status_code} | "
721
- f"Time: {process_time:.3f}s"
722
- )
723
-
724
- # إضافة Headers للاستجابة
725
- response.headers["X-Request-ID"] = request_id
726
- response.headers["X-Process-Time"] = str(round(process_time, 3))
727
-
728
- return response
729
-
730
- except Exception as e:
731
- process_time = time.time() - start_time
732
- logger.error(
733
- f"💥 [{request_id}] Error: {str(e)[:100]} | "
734
- f"Time: {process_time:.3f}s"
735
- )
736
- raise
737
 
738
- # ============================================================
739
- # نقاط النهاية (Endpoints)
740
- # ============================================================
741
 
742
  @app.get("/")
743
  async def home():
744
- """الصفحة الرئيسية"""
745
- stats = app.state.ai.get_stats()
746
- return {
747
- "name": "SmartG4F API",
748
- "version": "2.0.0",
749
- "status": "running",
750
- "uptime": stats["uptime_human"],
751
- "providers": {
752
- "safe": len(SAFE_PROVIDERS),
753
- "available": stats["available_providers"],
754
- "good": stats["good_providers"],
755
- "bad": stats["bad_providers"]
756
- },
757
- "statistics": {
758
- "total_requests": stats["total_requests"],
759
- "success_rate": f"{stats['success_rate']}%",
760
- "avg_response_time": f"{stats['avg_response_time']:.3f}s"
761
- },
762
- "proxy_configured": get_random_proxy() is not None,
763
- "documentation": "/docs",
764
- "health": "/health",
765
- "metrics": "/metrics"
766
- }
767
 
768
- @app.get("/health")
769
- async def health_check():
770
- """فحص حالة الخادم"""
771
- stats = app.state.ai.get_stats()
772
-
773
- status = "healthy"
774
- if stats["bad_providers"] > len(SAFE_PROVIDERS) * 0.5:
775
- status = "degraded"
776
- if stats["success_rate"] < 30:
777
- status = "unhealthy"
778
-
779
  return {
780
- "status": status,
781
- "timestamp": datetime.now().isoformat(),
782
- "uptime": stats["uptime_human"],
783
- "providers": {
784
- "total_safe": len(SAFE_PROVIDERS),
785
- "available": stats["available_providers"],
786
- "good": stats["good_providers"],
787
- "bad": stats["bad_providers"]
788
- },
789
- "requests": {
790
- "total": stats["total_requests"],
791
- "successful": stats["successful_requests"],
792
- "failed": stats["failed_requests"],
793
- "success_rate": stats["success_rate"]
794
- }
795
  }
796
 
797
- @app.get("/metrics")
798
- async def get_metrics():
799
- """الحصول على الإحصائيات المفصلة"""
800
- stats = app.state.ai.get_stats()
801
- return stats
802
 
803
- @app.get("/providers")
804
- async def list_providers():
805
- """قائمة جميع المزودات المتاحين"""
806
- return {
807
- "safe_providers": [p.__name__ for p in SAFE_PROVIDERS],
808
- "good_providers": [p.__name__ for p in app.state.ai.good],
809
- "bad_providers": list(app.state.ai.bad.keys()),
810
- "provider_stats": app.state.ai.provider_stats
811
- }
812
 
813
  @app.post("/v1/chat/completions")
814
  async def chat_completions(
815
- request: ChatCompletionRequest,
816
- authorization: Optional[str] = Header(None)
817
  ):
818
- """
819
- نقطة نهاية الإكمال المحادث - متوافق مع OpenAI API
820
- """
821
- # التحقق من المفتاح (اختياري)
822
- if os.environ.get("MY_PROJECT_API_KEY"):
823
- verify_api_key(authorization)
824
-
825
- # تحويل الرسائل إلى prompt واحد
826
- prompt = "\n".join([
827
- f"{msg.role}: {msg.content}"
828
- for msg in request.messages
829
- ])
830
-
831
- logger.info(f"💬 Chat request | Model: {request.model} | Messages: {len(request.messages)}")
832
-
833
- # إرسال الطلب
834
- result = app.state.ai.ask(
835
- prompt=prompt,
836
- temperature=request.temperature,
837
- max_tokens=request.max_tokens,
838
- stream=request.stream
839
  )
840
-
841
- if result.get("success"):
842
- return {
843
- "id": f"chatcmpl-{result['request_id']}",
844
- "object": "chat.completion",
845
- "created": int(time.time()),
846
- "model": request.model,
847
- "choices": [
848
- {
849
- "index": 0,
850
- "message": {
851
- "role": "assistant",
852
- "content": result.get("response")
853
- },
854
- "finish_reason": "stop"
855
- }
856
- ],
857
- "usage": {
858
- "prompt_tokens": len(prompt.split()),
859
- "completion_tokens": result.get("tokens_used", 0),
860
- "total_tokens": len(prompt.split()) + result.get("tokens_used", 0)
861
- },
862
- "provider": result.get("provider"),
863
- "request_id": result.get("request_id"),
864
- "response_time": result.get("response_time")
865
- }
866
- else:
867
  raise HTTPException(
868
  status_code=500,
869
- detail={
870
- "error": {
871
- "message": result.get("error", "Failed to generate response"),
872
- "type": "api_error",
873
- "code": "GENERATION_FAILED",
874
- "request_id": result.get("request_id")
875
- }
876
- }
877
  )
878
 
879
- @app.post("/v1/completions")
880
- async def completions(
881
- request: CompletionRequest,
882
- authorization: Optional[str] = Header(None)
883
- ):
884
- """نقطة نهاية الإكمال النصي"""
885
- if os.environ.get("MY_PROJECT_API_KEY"):
886
- verify_api_key(authorization)
887
-
888
- logger.info(f"✍️ Completion request | Model: {request.model}")
889
-
890
- results = []
891
-
892
- for i in range(request.n):
893
- result = app.state.ai.ask(
894
- prompt=request.prompt,
895
- temperature=request.temperature,
896
- max_tokens=request.max_tokens
897
- )
898
-
899
- if result.get("success"):
900
- results.append({
901
- "text": result.get("response"),
902
- "index": i,
903
- "finish_reason": "stop"
904
- })
905
- else:
906
- results.append({
907
- "text": "",
908
- "index": i,
909
- "finish_reason": "error",
910
- "error": result.get("error")
911
- })
912
-
913
- return {
914
- "id": f"comp-{str(uuid.uuid4())[:8]}",
915
- "object": "text_completion",
916
- "created": int(time.time()),
917
- "model": request.model,
918
- "choices": results,
919
- "usage": {
920
- "prompt_tokens": len(request.prompt.split()),
921
- "total_tokens": sum(len(r.get("text", "").split()) for r in results)
922
- }
923
- }
924
 
925
- @app.post("/v1/embeddings")
926
- async def embeddings(
927
- request: EmbeddingRequest,
928
- authorization: Optional[str] = Header(None)
929
- ):
930
- """نقطة نهاية التضميد"""
931
- if os.environ.get("MY_PROJECT_API_KEY"):
932
- verify_api_key(authorization)
933
-
934
- inputs = [request.input] if isinstance(request.input, str) else request.input
935
-
936
- embeddings = []
937
- for i, text in enumerate(inputs):
938
- # توليد embedding وهمي (لأن g4f لا يدعم embeddings رسمياً)
939
- embedding = [
940
- random.uniform(-1, 1) for _ in range(1536)
941
- ]
942
- embeddings.append({
943
- "object": "embedding",
944
- "embedding": embedding,
945
- "index": i
946
- })
947
-
948
  return {
949
- "object": "list",
950
- "data": embeddings,
 
951
  "model": request.model,
952
- "usage": {
953
- "prompt_tokens": sum(len(t.split()) for t in inputs),
954
- "total_tokens": sum(len(t.split()) for t in inputs)
955
- }
956
- }
957
-
958
- @app.get("/models")
959
- async def list_models():
960
- """قائمة النماذج المتاحة"""
961
- return {
962
- "object": "list",
963
- "data": [
964
- {
965
- "id": "gpt-4o-mini",
966
- "object": "model",
967
- "created": 20240101,
968
- "owned_by": "openai"
969
- },
970
- {
971
- "id": "gpt-4o",
972
- "object": "model",
973
- "created": 20240101,
974
- "owned_by": "openai"
975
- },
976
- {
977
- "id": "gpt-3.5-turbo",
978
- "object": "model",
979
- "created": 20240101,
980
- "owned_by": "openai"
981
- },
982
  {
983
- "id": "claude-3-opus",
984
- "object": "model",
985
- "created": 20240101,
986
- "owned_by": "anthropic"
987
- },
988
- {
989
- "id": "claude-3-sonnet",
990
- "object": "model",
991
- "created": 20240101,
992
- "owned_by": "anthropic"
993
  }
994
- ]
 
 
 
 
 
995
  }
996
 
997
- @app.post("/reset-stats")
998
- async def reset_statistics(authorization: Optional[str] = Header(None)):
999
- """إعادة تعيين الإحصائيات"""
1000
- if os.environ.get("MY_PROJECT_API_KEY"):
1001
- verify_api_key(authorization)
1002
-
1003
- app.state.ai.stats = {
1004
- "total_requests": 0,
1005
- "successful_requests": 0,
1006
- "failed_requests": 0,
1007
- "total_tokens_used": 0,
1008
- "avg_response_time": 0,
1009
- "start_time": time.time()
1010
- }
1011
- app.state.ai.error_log.clear()
1012
-
1013
- return {"message": "Statistics reset successfully"}
1014
-
1015
- @app.post("/reset-providers")
1016
- async def reset_providers(authorization: Optional[str] = Header(None)):
1017
- """إعادة تعيين قائمة Providers"""
1018
- if os.environ.get("MY_PROJECT_API_KEY"):
1019
- verify_api_key(authorization)
1020
-
1021
- app.state.ai.good.clear()
1022
- app.state.ai.bad.clear()
1023
- app.state.ai.provider_stats.clear()
1024
-
1025
- return {"message": "Providers reset successfully"}
1026
-
1027
- # ============================================================
1028
- # نقطة النهاية للتدفق (Streaming)
1029
- # ============================================================
1030
- async def generate_stream_response(request_id: str, provider: str, text: str, response_time: float):
1031
- """توليد الاستجابة المتدفقة"""
1032
- import json
1033
-
1034
- # إرسال بداية الاستجابة
1035
- chunk_id = f"chatcmpl-{request_id}"
1036
- created = int(time.time())
1037
-
1038
- yield f"data: {json.dumps({'id': chunk_id, 'object': 'chat.completion.chunk', 'created': created, 'model': MODEL, 'choices': [{'index': 0, 'delta': {'role': 'assistant'}, 'finish_reason': None}]})}\n\n"
1039
-
1040
- # تقسيم النص لكلمات
1041
- words = text.split()
1042
- for i, word in enumerate(words):
1043
- delta = {'content': word + ' '}
1044
- yield f"data: {json.dumps({'id': chunk_id, 'object': 'chat.completion.chunk', 'created': created, 'model': MODEL, 'choices': [{'index': 0, 'delta': delta, 'finish_reason': None}]})}\n\n"
1045
- await asyncio.sleep(0.01) # تأخير بسيط للمحاكاة
1046
-
1047
- # إرسال نهاية الاستجابة
1048
- yield f"data: {json.dumps({'id': chunk_id, 'object': 'chat.completion.chunk', 'created': created, 'model': MODEL, 'choices': [{'index': 0, 'delta': {}, 'finish_reason': 'stop'}]})}\n\n"
1049
-
1050
- # إشعار النهاية
1051
- yield "data: [DONE]\n\n"
1052
-
1053
- @app.post("/v1/chat/completions/stream")
1054
- async def chat_completions_stream(
1055
- request: ChatCompletionRequest,
1056
- authorization: Optional[str] = Header(None)
1057
- ):
1058
- """نقطة نهاية الإكمال المتدفق"""
1059
- if os.environ.get("MY_PROJECT_API_KEY"):
1060
- verify_api_key(authorization)
1061
-
1062
- prompt = "\n".join([
1063
- f"{msg.role}: {msg.content}"
1064
- for msg in request.messages
1065
- ])
1066
-
1067
- logger.info(f"🌊 Stream request | Model: {request.model}")
1068
-
1069
- result = app.state.ai.ask(
1070
- prompt=prompt,
1071
- temperature=request.temperature,
1072
- max_tokens=request.max_tokens
1073
- )
1074
-
1075
- if result.get("success"):
1076
- return StreamingResponse(
1077
- generate_stream_response(
1078
- request_id=result['request_id'],
1079
- provider=result['provider'],
1080
- text=result['response'],
1081
- response_time=result['response_time']
1082
- ),
1083
- media_type="text/event-stream",
1084
- headers={
1085
- "Cache-Control": "no-cache",
1086
- "Connection": "keep-alive",
1087
- "X-Request-ID": result['request_id'],
1088
- "X-Provider": result['provider']
1089
- }
1090
- )
1091
- else:
1092
- raise HTTPException(
1093
- status_code=500,
1094
- detail=f"Failed to generate response: {result.get('error')}"
1095
- )
1096
 
1097
- # ============================================================
1098
- # تشغيل الخادم
1099
- # ============================================================
1100
  if __name__ == "__main__":
1101
- # إعدادات Environment
1102
- port = int(os.environ.get("PORT", 7860))
1103
- host = os.environ.get("HOST", "0.0.0.0")
1104
- workers = int(os.environ.get("WORKERS", 1))
1105
-
1106
- logger.info("=" * 60)
1107
- logger.info(f"🌐 Starting SmartG4F API on {host}:{port}")
1108
- logger.info(f"📚 Documentation: http://localhost:{port}/docs")
1109
- logger.info("=" * 60)
1110
-
1111
  uvicorn.run(
1112
- "main:app",
1113
- host=host,
1114
- port=port,
1115
- workers=workers,
1116
- log_level="info",
1117
- access_log=True,
1118
- reload=False # تعطيل Reload في الإنتاج
1119
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import time
2
  import random
 
3
  import traceback
 
4
  import uuid
5
+
6
+ from fastapi import FastAPI, Header, HTTPException
7
+ from pydantic import BaseModel
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  import uvicorn
9
 
 
10
  from g4f.client import Client
11
  from g4f.Provider import __providers__
12
 
13
+ MODEL = "gpt-4o-mini"
14
+
15
+ MAX_RETRIES = 10
16
+ TIMEOUT = 60
17
+
18
+ # ضع المفتاح هنا
19
+ OPENAI_API_KEY = "sk-your-key"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  SAFE_PROVIDERS = []
22
+
23
  for provider in __providers__:
24
+
25
  try:
26
+
27
  name = provider.__name__.lower()
28
+
 
29
  if not getattr(provider, "working", False):
30
  continue
31
+
 
32
  if getattr(provider, "needs_auth", False):
33
  continue
34
+
 
35
  if getattr(provider, "use_nodriver", False):
36
  continue
37
+
38
+ blocked = [
39
+ "openai",
40
+ "qwen",
41
+ "copilot",
42
+ "gemini",
43
+ "claude",
44
+ ]
45
+
46
  if any(x in name for x in blocked):
47
  continue
48
+
49
  SAFE_PROVIDERS.append(provider)
50
+
51
  except:
52
  pass
53
 
54
  random.shuffle(SAFE_PROVIDERS)
55
+
56
+ print(f"[+] SAFE PROVIDERS: {len(SAFE_PROVIDERS)}")
57
+
58
+ client = Client()
59
+
60
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  class SmartG4F:
62
+
63
+ def __init__(self):
64
+
65
+ self.good = []
66
+ self.bad = {}
67
+
68
+ def mark_bad(self, provider, cooldown=300):
69
+
70
+ self.bad[provider.__name__] = time.time() + cooldown
71
+
72
+ def is_bad(self, provider):
73
+
74
+ expire = self.bad.get(provider.__name__)
75
+
76
+ if not expire:
77
+ return False
78
+
79
+ if time.time() > expire:
80
+ del self.bad[provider.__name__]
81
+ return False
82
+
83
+ return True
84
+
85
+ def provider_pool(self):
86
+
87
+ providers = []
88
+
89
+ providers.extend(self.good)
90
+
91
+ for p in SAFE_PROVIDERS:
92
+
93
+ if p not in providers and not self.is_bad(p):
94
+ providers.append(p)
95
+
96
+ random.shuffle(providers)
97
+
98
+ return providers
99
+
100
+ def ask(self, messages):
101
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  errors = []
103
+
 
 
 
 
 
 
 
104
  for attempt in range(MAX_RETRIES):
105
+
106
  pool = self.provider_pool()
107
+
108
  if not pool:
109
+
 
 
 
 
 
 
 
 
110
  return {
111
+ "success": False,
112
+ "error": "No providers available"
 
113
  }
114
+
115
  provider = random.choice(pool)
116
+
117
+ print(f"[TRY {attempt+1}] {provider.__name__}")
118
+
 
 
 
 
 
119
  try:
120
+
 
 
 
 
 
 
 
 
121
  response = client.chat.completions.create(
122
  model=MODEL,
123
  provider=provider,
124
  messages=messages,
125
+ timeout=TIMEOUT,
 
 
126
  )
127
+
 
 
 
128
  text = response.choices[0].message.content
129
+
130
  if not text:
131
+ raise Exception("Empty response")
132
+
133
+ if provider not in self.good:
134
+ self.good.append(provider)
135
+
136
+ return {
 
 
 
 
 
 
 
 
 
 
 
 
137
  "success": True,
 
138
  "provider": provider.__name__,
139
+ "response": text
 
 
 
140
  }
141
+
 
 
 
 
 
 
 
 
 
142
  except Exception as e:
143
+
144
+ err = str(e).lower()
145
+
146
+ traceback.print_exc()
147
+
148
+ errors.append({
 
149
  "provider": provider.__name__,
150
+ "error": str(e)
151
+ })
152
+
153
+ if "429" in err:
154
+
155
+ self.mark_bad(provider, 600)
156
+ time.sleep(random.randint(10, 20))
157
+
158
+ elif "cloudflare" in err:
159
+
160
+ self.mark_bad(provider, 1200)
161
+ time.sleep(20)
162
+
163
+ elif "timeout" in err:
164
+
165
+ self.mark_bad(provider, 300)
166
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  else:
168
+
169
+ self.mark_bad(provider, 180)
170
+
 
 
171
  continue
172
+
 
 
 
 
 
 
173
  return {
174
  "success": False,
175
+ "errors": errors
 
 
 
176
  }
177
 
178
+
179
+ app = FastAPI()
180
+
181
+ ai = SmartG4F()
182
+
183
+
184
+ # =========================
185
+ # OpenAI Compatible Models
186
+ # =========================
187
+
188
  class Message(BaseModel):
189
+ role: str
 
190
  content: str
 
 
 
 
 
 
 
 
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
+ class ChatRequest(BaseModel):
194
+ model: str = MODEL
195
+ messages: list[Message]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
 
 
 
197
 
198
  @app.get("/")
199
  async def home():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
 
 
 
 
 
 
 
 
 
 
 
 
201
  return {
202
+ "status": "running",
203
+ "providers": len(SAFE_PROVIDERS)
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  }
205
 
 
 
 
 
 
206
 
207
+ # =========================
208
+ # OpenAI Endpoint
209
+ # =========================
 
 
 
 
 
 
210
 
211
  @app.post("/v1/chat/completions")
212
  async def chat_completions(
213
+ request: ChatRequest,
214
+ authorization: str = Header(None)
215
  ):
216
+
217
+ # التحقق من المفتاح
218
+ if OPENAI_API_KEY:
219
+
220
+ if not authorization:
221
+ raise HTTPException(
222
+ status_code=401,
223
+ detail="Missing Authorization header"
224
+ )
225
+
226
+ token = authorization.replace("Bearer ", "")
227
+
228
+ if token != OPENAI_API_KEY:
229
+ raise HTTPException(
230
+ status_code=401,
231
+ detail="Invalid API key"
232
+ )
233
+
234
+ result = ai.ask(
235
+ [m.dict() for m in request.messages]
 
236
  )
237
+
238
+ if not result["success"]:
239
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  raise HTTPException(
241
  status_code=500,
242
+ detail=result
 
 
 
 
 
 
 
243
  )
244
 
245
+ response_text = result["response"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  return {
248
+ "id": f"chatcmpl-{uuid.uuid4().hex}",
249
+ "object": "chat.completion",
250
+ "created": int(time.time()),
251
  "model": request.model,
252
+ "choices": [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  {
254
+ "index": 0,
255
+ "message": {
256
+ "role": "assistant",
257
+ "content": response_text
258
+ },
259
+ "finish_reason": "stop"
 
 
 
 
260
  }
261
+ ],
262
+ "usage": {
263
+ "prompt_tokens": 0,
264
+ "completion_tokens": 0,
265
+ "total_tokens": 0
266
+ }
267
  }
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
 
 
 
 
270
  if __name__ == "__main__":
271
+
 
 
 
 
 
 
 
 
 
272
  uvicorn.run(
273
+ app,
274
+ host="0.0.0.0",
275
+ port=7860
276
+ )