Mark-Lasfar commited on
Commit
bd580e2
·
1 Parent(s): 5b49e79

Add telegram bot webhook handler

Browse files
Files changed (3) hide show
  1. api/endpoints.py +0 -6
  2. utils/generation.py +43 -99
  3. utils/telegram_bot.py +68 -54
api/endpoints.py CHANGED
@@ -41,12 +41,6 @@ BACKUP_HF_TOKEN = os.getenv("BACKUP_HF_TOKEN")
41
  if not BACKUP_HF_TOKEN:
42
  logger.warning("BACKUP_HF_TOKEN is not set. Fallback to secondary model will not work if primary token fails.")
43
 
44
-
45
- BACKUP_HF_TOKEN_2 = os.getenv("BACKUP_HF_TOKEN_2")
46
- if not BACKUP_HF_TOKEN_2:
47
- logger.warning("BACKUP_HF_TOKEN_2 is not set. Second backup token not available.")
48
-
49
-
50
  ROUTER_API_URL = os.getenv("ROUTER_API_URL", "https://router.huggingface.co")
51
  API_ENDPOINT = os.getenv("API_ENDPOINT", "https://router.huggingface.co/v1")
52
  FALLBACK_API_ENDPOINT = os.getenv("FALLBACK_API_ENDPOINT", "https://api-inference.huggingface.co/v1")
 
41
  if not BACKUP_HF_TOKEN:
42
  logger.warning("BACKUP_HF_TOKEN is not set. Fallback to secondary model will not work if primary token fails.")
43
 
 
 
 
 
 
 
44
  ROUTER_API_URL = os.getenv("ROUTER_API_URL", "https://router.huggingface.co")
45
  API_ENDPOINT = os.getenv("API_ENDPOINT", "https://router.huggingface.co/v1")
46
  FALLBACK_API_ENDPOINT = os.getenv("FALLBACK_API_ENDPOINT", "https://api-inference.huggingface.co/v1")
utils/generation.py CHANGED
@@ -37,7 +37,6 @@ LATEX_DELIMS = [
37
  # إعداد العميل لـ Hugging Face API
38
  HF_TOKEN = os.getenv("HF_TOKEN")
39
  BACKUP_HF_TOKEN = os.getenv("BACKUP_HF_TOKEN")
40
- BACKUP_HF_TOKEN_2 = os.getenv("BACKUP_HF_TOKEN_2")
41
  ROUTER_API_URL = os.getenv("ROUTER_API_URL", "https://router.huggingface.co")
42
  API_ENDPOINT = os.getenv("API_ENDPOINT", "https://router.huggingface.co/v1")
43
  FALLBACK_API_ENDPOINT = os.getenv("FALLBACK_API_ENDPOINT", "https://api-inference.huggingface.co/v1")
@@ -61,7 +60,7 @@ PROVIDER_ENDPOINTS = {
61
  }
62
 
63
  def check_model_availability(model_name: str, api_key: str) -> tuple[bool, str, str]:
64
- """التحقق من توفر النموذج — مع استثناء لنماذج الصور ودعم عدة مفاتيح احتياطية."""
65
 
66
  # ✅ القائمة الشاملة لنماذج الصور (تحليل أو توليد)
67
  IMAGE_MODELS = [
@@ -78,54 +77,28 @@ def check_model_availability(model_name: str, api_key: str) -> tuple[bool, str,
78
  clean_model_name = model_name.split(":")[0] # عشان نشيل أي provider مثل :novita
79
  return True, api_key, f"{IMAGE_INFERENCE_API}/{clean_model_name}"
80
 
81
- # قائمة بكل المفاتيح المتاحة (بالترتيب)
82
- available_tokens = []
83
-
84
- # أضف المفتاح الأساسي إذا كان موجوداً
85
- if HF_TOKEN:
86
- available_tokens.append(HF_TOKEN)
87
-
88
- # أضف المفتاح الاحتياطي الأول إذا كان موجوداً
89
- if BACKUP_HF_TOKEN:
90
- available_tokens.append(BACKUP_HF_TOKEN)
91
-
92
- # أضف المفتاح الاحتياطي الثاني إذا كان موجوداً (الجديد)
93
- if BACKUP_HF_TOKEN_2:
94
- available_tokens.append(BACKUP_HF_TOKEN_2)
95
-
96
- # إذا لم يكن هناك أي مفتاح
97
- if not available_tokens:
98
- logger.error(" No HF tokens are set in environment variables!")
 
 
 
99
  return False, api_key, API_ENDPOINT
100
-
101
- # جرب كل المفاتيح بالترتيب
102
- for i, token in enumerate(available_tokens):
103
- try:
104
- logger.debug(f"📡 Checking model {model_name} with token #{i+1} of {len(available_tokens)}")
105
- response = requests.get(
106
- f"{ROUTER_API_URL}/v1/models/{model_name}",
107
- headers={"Authorization": f"Bearer {token}"},
108
- timeout=30
109
- )
110
- logger.debug(f"📡 Response status for token #{i+1}: {response.status_code}")
111
-
112
- if response.status_code == 200:
113
- logger.info(f"✅ Model {model_name} is available with token #{i+1}")
114
- return True, token, API_ENDPOINT
115
- elif response.status_code == 429:
116
- logger.warning(f"⚠️ Rate limit reached for token #{i+1}, trying next token...")
117
- continue # جرب المفتاح التالي
118
- else:
119
- logger.warning(f"⚠️ Token #{i+1} failed with status {response.status_code}, trying next...")
120
- continue
121
-
122
- except Exception as e:
123
- logger.warning(f"🔥 Token #{i+1} error: {e}, trying next...")
124
- continue
125
-
126
- # إذا فشلت جميع المفاتيح
127
- logger.error(f"❌ All {len(available_tokens)} tokens failed for model {model_name}")
128
- return False, api_key, API_ENDPOINT
129
 
130
  def select_model(query: str, input_type: str = "text", preferred_model: Optional[str] = None) -> tuple[str, str]:
131
  if preferred_model and preferred_model in MODEL_ALIASES:
@@ -506,56 +479,28 @@ def request_generation(
506
 
507
  except Exception as e:
508
  logger.error(f"[Gateway] Streaming failed for model {model_name}: {e}")
509
-
510
- # ============================================================
511
- # محاولة استخدام المفاتيح الاحتياطية بالترتيب
512
- # ============================================================
513
-
514
- # قائمة بكل المفاتيح المتاحة (بعد المفتاح الحالي)
515
- available_backup_tokens = []
516
- if BACKUP_HF_TOKEN:
517
- available_backup_tokens.append(BACKUP_HF_TOKEN)
518
- if BACKUP_HF_TOKEN_2:
519
- available_backup_tokens.append(BACKUP_HF_TOKEN_2)
520
-
521
- # جرب كل المفاتيح الاحتياطية بالترتيب
522
- backup_used = False
523
- for backup_token in available_backup_tokens:
524
- if selected_api_key != backup_token:
525
- logger.warning(f"🔄 Retrying with backup token for model {model_name}")
526
- try:
527
- for chunk in request_generation(
528
- api_key=backup_token,
529
- api_base=selected_endpoint,
530
- message=message,
531
- system_prompt=system_prompt,
532
- model_name=model_name,
533
- chat_history=chat_history,
534
- temperature=temperature,
535
- max_new_tokens=max_new_tokens,
536
- reasoning_effort=reasoning_effort,
537
- tools=tools,
538
- tool_choice=tool_choice,
539
- deep_search=deep_search,
540
- input_type=input_type,
541
- audio_data=audio_data,
542
- image_data=image_data,
543
- output_format=output_format,
544
- ):
545
- yield chunk
546
- backup_used = True
547
- return
548
- except Exception as backup_error:
549
- logger.warning(f"⚠️ Backup token also failed: {backup_error}, trying next...")
550
- continue # جرب المفتاح الاحتياطي التالي
551
-
552
- if backup_used:
553
  return
554
-
555
- # ============================================================
556
- # إذا فشلت جميع المفاتيح، جرب النماذج الاحتياطية
557
- # ============================================================
558
-
559
  if model_name == MODEL_NAME:
560
  fallback_model = SECONDARY_MODEL_NAME
561
  fallback_endpoint = FALLBACK_API_ENDPOINT
@@ -725,7 +670,6 @@ def request_generation(
725
  else:
726
  yield f"Error: Failed to load model {model_name}: {e}"
727
  return
728
-
729
  def format_final(analysis_text: str, visible_text: str) -> str:
730
  reasoning_safe = html.escape((analysis_text or "").strip())
731
  response = (visible_text or "").strip()
 
37
  # إعداد العميل لـ Hugging Face API
38
  HF_TOKEN = os.getenv("HF_TOKEN")
39
  BACKUP_HF_TOKEN = os.getenv("BACKUP_HF_TOKEN")
 
40
  ROUTER_API_URL = os.getenv("ROUTER_API_URL", "https://router.huggingface.co")
41
  API_ENDPOINT = os.getenv("API_ENDPOINT", "https://router.huggingface.co/v1")
42
  FALLBACK_API_ENDPOINT = os.getenv("FALLBACK_API_ENDPOINT", "https://api-inference.huggingface.co/v1")
 
60
  }
61
 
62
  def check_model_availability(model_name: str, api_key: str) -> tuple[bool, str, str]:
63
+ """التحقق من توفر النموذج — مع استثناء لنماذج الصور."""
64
 
65
  # ✅ القائمة الشاملة لنماذج الصور (تحليل أو توليد)
66
  IMAGE_MODELS = [
 
77
  clean_model_name = model_name.split(":")[0] # عشان نشيل أي provider مثل :novita
78
  return True, api_key, f"{IMAGE_INFERENCE_API}/{clean_model_name}"
79
 
80
+ # ✅ لو مش صورة نستخدم الطريقة العادية (للدردشة)
81
+ try:
82
+ response = requests.get(
83
+ f"{ROUTER_API_URL}/v1/models/{model_name}",
84
+ headers={"Authorization": f"Bearer {api_key}"},
85
+ timeout=30
86
+ )
87
+ logger.debug(f"📡 Checking model {model_name}: {response.status_code} - {response.text}")
88
+ if response.status_code == 200:
89
+ logger.info(f"✅ Model {model_name} is available at {API_ENDPOINT}")
90
+ return True, api_key, API_ENDPOINT
91
+ elif response.status_code == 429 and BACKUP_HF_TOKEN and api_key != BACKUP_HF_TOKEN:
92
+ logger.warning(f"⚠️ Rate limit reached for token {api_key}. Switching to backup token.")
93
+ return check_model_availability(model_name, BACKUP_HF_TOKEN)
94
+ logger.error(f"❌ Model {model_name} not available: {response.status_code} - {response.text}")
95
+ return False, api_key, API_ENDPOINT
96
+ except Exception as e:
97
+ logger.error(f"🔥 Failed to check model availability for {model_name}: {e}")
98
+ if BACKUP_HF_TOKEN and api_key != BACKUP_HF_TOKEN:
99
+ logger.warning(f"🔁 Retrying with backup token for {model_name}")
100
+ return check_model_availability(model_name, BACKUP_HF_TOKEN)
101
  return False, api_key, API_ENDPOINT
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
  def select_model(query: str, input_type: str = "text", preferred_model: Optional[str] = None) -> tuple[str, str]:
104
  if preferred_model and preferred_model in MODEL_ALIASES:
 
479
 
480
  except Exception as e:
481
  logger.error(f"[Gateway] Streaming failed for model {model_name}: {e}")
482
+ if selected_api_key != BACKUP_HF_TOKEN and BACKUP_HF_TOKEN:
483
+ logger.warning(f"Retrying with backup token for {model_name}")
484
+ for chunk in request_generation(
485
+ api_key=BACKUP_HF_TOKEN,
486
+ api_base=selected_endpoint,
487
+ message=message,
488
+ system_prompt=system_prompt,
489
+ model_name=model_name,
490
+ chat_history=chat_history,
491
+ temperature=temperature,
492
+ max_new_tokens=max_new_tokens,
493
+ reasoning_effort=reasoning_effort,
494
+ tools=tools,
495
+ tool_choice=tool_choice,
496
+ deep_search=deep_search,
497
+ input_type=input_type,
498
+ audio_data=audio_data,
499
+ image_data=image_data,
500
+ output_format=output_format,
501
+ ):
502
+ yield chunk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
  return
 
 
 
 
 
504
  if model_name == MODEL_NAME:
505
  fallback_model = SECONDARY_MODEL_NAME
506
  fallback_endpoint = FALLBACK_API_ENDPOINT
 
670
  else:
671
  yield f"Error: Failed to load model {model_name}: {e}"
672
  return
 
673
  def format_final(analysis_text: str, visible_text: str) -> str:
674
  reasoning_safe = html.escape((analysis_text or "").strip())
675
  response = (visible_text or "").strip()
utils/telegram_bot.py CHANGED
@@ -15,8 +15,7 @@ from fastapi import APIRouter, Request, HTTPException, Header
15
  from utils.generation import request_generation
16
  from api.endpoints import enhance_system_prompt
17
  from utils.constants import API_ENDPOINT, MODEL_NAME
18
- from utils.generation import HF_TOKEN, BACKUP_HF_TOKEN, BACKUP_HF_TOKEN_2
19
-
20
 
21
  logger = logging.getLogger(__name__)
22
  router = APIRouter()
@@ -428,61 +427,76 @@ def format_ai_response(raw_response: str) -> str:
428
  # ============================================================
429
 
430
  async def call_ai_with_fallback(user_message: str, enhanced_prompt: str) -> str:
431
- """Calls the AI, tries all available tokens (primary, backup1, backup2) in sequence."""
432
-
433
- # قائمة بكل المفاتيح المتاحة (بالترتيب)
434
- available_tokens = []
435
-
436
- if HF_TOKEN:
437
- available_tokens.append(HF_TOKEN)
438
- if BACKUP_HF_TOKEN:
439
- available_tokens.append(BACKUP_HF_TOKEN)
440
- if BACKUP_HF_TOKEN_2:
441
- available_tokens.append(BACKUP_HF_TOKEN_2)
442
-
443
- if not available_tokens:
444
- logger.error("No HF tokens are set!")
445
- return get_error_message("en")
446
-
447
- last_error = None
448
 
449
- for i, token in enumerate(available_tokens):
450
- try:
451
- logger.info(f"🔄 Trying token #{i+1} of {len(available_tokens)}...")
452
-
453
- response_chunks = []
454
- stream = request_generation(
455
- api_key=token,
456
- api_base=API_ENDPOINT,
457
- message=user_message,
458
- system_prompt=enhanced_prompt,
459
- model_name=MODEL_NAME,
460
- temperature=0.7,
461
- max_new_tokens=1500,
462
- deep_search=True,
463
- input_type="text",
464
- output_format="text"
465
- )
466
-
467
- for chunk in stream:
468
- if isinstance(chunk, str) and chunk not in ["analysis", "assistantfinal"]:
469
- response_chunks.append(chunk)
 
470
 
471
- bot_reply = "".join(response_chunks).strip()
472
- if bot_reply and len(bot_reply) >= 5:
473
- logger.info(f"✅ Token #{i+1} succeeded!")
474
- return bot_reply
475
- else:
476
- raise Exception("Empty or insufficient response")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
 
478
- except Exception as e:
479
- logger.warning(f"⚠️ Token #{i+1} failed: {e}")
480
- last_error = e
481
- continue # جرب المفتاح التالي
482
-
483
- # إذا فشلت جميع المفاتيح
484
- logger.error(f" All {len(available_tokens)} tokens failed. Last error: {last_error}")
485
- raise Exception("All tokens failed.")
 
 
 
 
 
 
 
 
 
486
 
487
 
488
  # ============================================================
 
15
  from utils.generation import request_generation
16
  from api.endpoints import enhance_system_prompt
17
  from utils.constants import API_ENDPOINT, MODEL_NAME
18
+ from utils.generation import HF_TOKEN, BACKUP_HF_TOKEN
 
19
 
20
  logger = logging.getLogger(__name__)
21
  router = APIRouter()
 
427
  # ============================================================
428
 
429
  async def call_ai_with_fallback(user_message: str, enhanced_prompt: str) -> str:
430
+ """Calls the AI, automatically falls back to BACKUP_HF_TOKEN if primary fails."""
431
+ # First try with primary token
432
+ primary_token = HF_TOKEN
433
+ if not primary_token:
434
+ logger.error("HF_TOKEN is not set!")
435
+ return get_error_message("en") # Fallback to error message
 
 
 
 
 
 
 
 
 
 
 
436
 
437
+ try:
438
+ response_chunks = []
439
+ stream = request_generation(
440
+ api_key=primary_token,
441
+ api_base=API_ENDPOINT,
442
+ message=user_message,
443
+ system_prompt=enhanced_prompt,
444
+ model_name=MODEL_NAME,
445
+ temperature=0.7,
446
+ max_new_tokens=1500,
447
+ deep_search=True,
448
+ input_type="text",
449
+ output_format="text"
450
+ )
451
+
452
+ for chunk in stream:
453
+ if isinstance(chunk, str) and chunk not in ["analysis", "assistantfinal"]:
454
+ response_chunks.append(chunk)
455
+
456
+ bot_reply = "".join(response_chunks).strip()
457
+ if bot_reply and len(bot_reply) >= 5:
458
+ return bot_reply
459
 
460
+ # If we get here, primary failed or returned empty
461
+ raise Exception("Primary token returned empty or insufficient response")
462
+
463
+ except Exception as primary_error:
464
+ logger.warning(f"Primary token failed: {primary_error}. Trying backup token...")
465
+
466
+ # If backup token exists, try it
467
+ if BACKUP_HF_TOKEN:
468
+ try:
469
+ response_chunks = []
470
+ stream = request_generation(
471
+ api_key=BACKUP_HF_TOKEN,
472
+ api_base=API_ENDPOINT,
473
+ message=user_message,
474
+ system_prompt=enhanced_prompt,
475
+ model_name=MODEL_NAME,
476
+ temperature=0.7,
477
+ max_new_tokens=1500,
478
+ deep_search=True,
479
+ input_type="text",
480
+ output_format="text"
481
+ )
482
 
483
+ for chunk in stream:
484
+ if isinstance(chunk, str) and chunk not in ["analysis", "assistantfinal"]:
485
+ response_chunks.append(chunk)
486
+
487
+ bot_reply = "".join(response_chunks).strip()
488
+ if bot_reply and len(bot_reply) >= 5:
489
+ logger.info("Backup token succeeded.")
490
+ return bot_reply
491
+ else:
492
+ raise Exception("Backup token returned empty response")
493
+
494
+ except Exception as backup_error:
495
+ logger.error(f"Backup token also failed: {backup_error}")
496
+ raise Exception("Both primary and backup tokens failed.")
497
+ else:
498
+ logger.error("No backup token configured.")
499
+ raise primary_error
500
 
501
 
502
  # ============================================================