Really-amin commited on
Commit
f4d95f6
1 Parent(s): 067d4fe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +201 -327
app.py CHANGED
@@ -1,5 +1,5 @@
1
  """
2
- دستیار هوشمند فارسی با قابلیت چت تلگرام و وب
3
  """
4
 
5
  import os
@@ -11,16 +11,30 @@ import asyncio
11
  import traceback
12
  from datetime import datetime
13
  from pathlib import Path
14
- from typing import Dict, Optional, Set, List, Any
15
- from collections import deque
16
  from contextlib import asynccontextmanager
17
- import tempfile
18
 
19
- # تنظیم کتابخانه‌های اصلی
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  import torch
21
  import transformers
22
- from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
23
- from fastapi import FastAPI, WebSocket, Request, HTTPException
24
  from fastapi.responses import HTMLResponse
25
  from fastapi.staticfiles import StaticFiles
26
  from fastapi.templating import Jinja2Templates
@@ -28,128 +42,123 @@ import uvicorn
28
  from telegram import Update, Bot, InlineKeyboardButton, InlineKeyboardMarkup, BotCommand
29
  from telegram.ext import Application, CommandHandler, MessageHandler, CallbackQueryHandler, filters
30
  import hazm
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  class Config:
33
  """تنظیمات اصلی برنامه"""
34
 
35
  # تنظیمات تلگرام
36
  TELEGRAM_TOKEN = "7437859619:AAGeGG3ZkLM0OVaw-Exx1uMRE55JtBCZZCY"
37
- CHAT_ID = "-1002228627548" # شناسه چت خودتون رو اینجا قرار بدید
38
- ADMIN_CHAT_ID = "-1002228627548" # شناسه ادمین رو اینجا قرار بدید
39
- ALLOWED_CHAT_IDS = {CHAT_ID, ADMIN_CHAT_ID} # چت‌های مجاز
40
 
41
  # تنظیمات مسیرها
42
- BASE_DIR = Path(__file__).parent
43
- CACHE_DIR = Path(tempfile.gettempdir()) / "ai_assistant_cache"
44
- LOG_DIR = Path(tempfile.gettempdir()) / "ai_assistant_logs"
 
 
45
 
46
  # تنظیمات مدل
47
- MODEL_NAME = "HooshvareLab/bert-fa-base-uncased"
48
- MAX_LENGTH = 512
49
  MIN_LENGTH = 20
50
  TEMPERATURE = 0.7
 
 
51
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
52
 
53
  # تنظیمات عمومی
54
  DEBUG = True
55
  HOST = "0.0.0.0"
56
  PORT = 8000
 
 
 
57
 
58
- @classmethod
59
- def setup(cls):
60
- """راه‌اندازی اولیه تنظیمات"""
61
- # ایجاد مسیرها
62
- cls.CACHE_DIR.mkdir(parents=True, exist_ok=True)
63
- cls.LOG_DIR.mkdir(parents=True, exist_ok=True)
64
-
65
- # تنظیم متغیرهای محیطی
66
- os.environ["TRANSFORMERS_CACHE"] = str(cls.CACHE_DIR)
67
- os.environ["TORCH_HOME"] = str(cls.CACHE_DIR / "torch")
68
-
69
- # تنظیم لاگینگ
70
- logging.basicConfig(
71
- level=logging.DEBUG if cls.DEBUG else logging.INFO,
72
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
73
- handlers=[
74
- logging.FileHandler(cls.LOG_DIR / "app.log", encoding='utf-8'),
75
- logging.StreamHandler(sys.stdout)
76
- ]
77
- )
78
-
79
- class AIModel:
80
  """کلاس مدیریت مدل هوش مصنوعی"""
81
 
82
  def __init__(self):
83
- self.device = Config.DEVICE
84
- self.model = None
85
- self.tokenizer = None
86
  self.pipeline = None
87
- self._initialize_model()
88
-
89
- def _initialize_model(self):
90
- """راه‌اندازی مدل"""
91
  try:
92
- logging.info(f"بارگذاری مدل {Config.MODEL_NAME}...")
93
-
94
- # بارگذاری توکنایزر
95
- self.tokenizer = AutoTokenizer.from_pretrained(
96
- Config.MODEL_NAME,
97
- cache_dir=Config.CACHE_DIR,
98
- use_fast=True
99
- )
100
-
101
- # بارگذاری مدل
102
- self.model = AutoModelForCausalLM.from_pretrained(
103
- Config.MODEL_NAME,
104
- cache_dir=Config.CACHE_DIR,
105
- torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
106
- low_cpu_mem_usage=True
107
- )
108
-
109
- # انتقال مدل به GPU در صورت وجود
110
- self.model.to(self.device)
111
-
112
- # ایجاد پایپلاین
113
- self.pipeline = pipeline(
114
- "text-generation",
115
- model=self.model,
116
- tokenizer=self.tokenizer,
117
- device=0 if self.device == "cuda" else -1
118
- )
119
-
120
- logging.info("مدل با موفقیت بارگذاری شد")
121
- return True
122
-
123
  except Exception as e:
124
- logging.error(f"خطا در بارگذاری مدل: {e}")
 
125
  return False
126
-
127
  async def generate_response(self, text: str) -> str:
128
- """تولید پاسخ برای متن ورودی"""
129
  try:
130
- # نرمال‌سازی متن
131
- normalizer = hazm.Normalizer()
132
- text = normalizer.normalize(text)
133
-
134
- # تولید پاسخ
135
- response = self.pipeline(
136
  text,
137
  max_length=Config.MAX_LENGTH,
138
  min_length=Config.MIN_LENGTH,
139
- temperature=Config.TEMPERATURE,
140
  do_sample=True,
141
- no_repeat_ngram_size=3
 
 
 
 
142
  )
143
 
144
- # استخراج و پاکسازی پاسخ
145
- generated_text = response[0]["generated_text"]
146
- cleaned_response = generated_text.replace(text, "").strip()
147
 
148
- return cleaned_response
 
 
 
149
 
150
  except Exception as e:
151
- logging.error(f"خطا در تولید پاسخ: {e}")
152
- return "متأسفانه در تولید پاسخ مشکلی پیش آمد."
 
 
 
 
 
 
 
 
 
 
 
153
 
154
  class TelegramUI:
155
  """کلاس مدیریت رابط کاربری تلگرام"""
@@ -191,28 +200,10 @@ class TelegramUI:
191
  ]
192
  return InlineKeyboardMarkup(keyboard)
193
 
194
- @staticmethod
195
- def get_settings_menu() -> InlineKeyboardMarkup:
196
- """منوی تنظیمات"""
197
- keyboard = [
198
- [
199
- InlineKeyboardButton("🔊 تنظیمات زبان", callback_data="lang_settings"),
200
- InlineKeyboardButton("⚡️ حالت پاسخگویی", callback_data="response_mode")
201
- ],
202
- [
203
- InlineKeyboardButton("📊 آمار عملکرد", callback_data="stats"),
204
- InlineKeyboardButton("👤 پروفایل", callback_data="profile")
205
- ],
206
- [
207
- InlineKeyboardButton("🏠 بازگشت به ��نوی اصلی", callback_data="main_menu")
208
- ]
209
- ]
210
- return InlineKeyboardMarkup(keyboard)
211
-
212
  class TelegramBot:
213
  """کلاس اصلی ربات تلگرام"""
214
 
215
- def __init__(self, ai_model: AIModel):
216
  self.model = ai_model
217
  self.ui = TelegramUI()
218
  self.application = (
@@ -221,24 +212,10 @@ class TelegramBot:
221
  .build()
222
  )
223
  self.normalizer = hazm.Normalizer()
 
224
  self._setup_handlers()
225
- self.setup_commands()
226
-
227
- def setup_commands(self):
228
- """تنظیم دستورات ربات"""
229
- commands = [
230
- BotCommand("start", "شروع گفتگو با ربات"),
231
- BotCommand("help", "راهنمای استفاده"),
232
- BotCommand("menu", "نمایش منوی اصلی"),
233
- BotCommand("settings", "تنظیمات"),
234
- BotCommand("stats", "آمار عملکرد"),
235
- ]
236
-
237
- async def set_commands():
238
- bot = Bot(Config.TELEGRAM_TOKEN)
239
- await bot.set_my_commands(commands)
240
-
241
- asyncio.create_task(set_commands())
242
 
243
  def _setup_handlers(self):
244
  """تنظیم هندلرهای ربات"""
@@ -246,8 +223,7 @@ class TelegramBot:
246
  self.application.add_handler(CommandHandler("start", self.start_command))
247
  self.application.add_handler(CommandHandler("help", self.help_command))
248
  self.application.add_handler(CommandHandler("menu", self.menu_command))
249
- self.application.add_handler(CommandHandler("settings", self.settings_command))
250
- self.application.add_handler(CommandHandler("stats", self.stats_command))
251
 
252
  # هندلر دکمه‌ها
253
  self.application.add_handler(CallbackQueryHandler(self.handle_callback))
@@ -283,17 +259,12 @@ class TelegramBot:
283
  /start - شروع مجدد ربات
284
  /menu - نمایش منوی اصلی
285
  /help - نمایش این راهنما
286
- /settings - تنظیمات ربات
287
- /stats - مشاهده آمار
288
 
289
  🔹 نحوه استفاده:
290
  1. از منوی اصلی بخش مورد نظر را انتخاب کنید
291
  2. در بخش چت، سوال یا متن خود را وارد کنید
292
  3. منتظر پاسخ بمانید
293
-
294
- 🔹 نکات مهم:
295
- • سوالات خود را واضح و دقیق مطرح کنید
296
- • برای بازگشت به منو از دکمه "🏠 بازگشت" استفاده کنید
297
  """
298
  await update.message.reply_text(
299
  help_text,
@@ -307,27 +278,21 @@ class TelegramBot:
307
  reply_markup=self.ui.get_main_menu()
308
  )
309
 
310
- async def settings_command(self, update: Update, context):
311
- """نمایش منوی تنظیمات"""
 
 
 
 
312
  await update.message.reply_text(
313
- "⚙️ تنظیمات:",
314
- reply_markup=self.ui.get_settings_menu()
315
  )
316
 
317
- async def stats_command(self, update: Update, context):
318
- """نمایش آمار"""
319
- stats_text = """
320
- 📊 آمار استفاده از ربات:
321
- • تعداد پیام‌های دریافتی: 0
322
- • تعداد پاسخ‌های ارسالی: 0
323
- • زمان آنلاین بودن: 0 ساعت
324
- """
325
- await update.message.reply_text(stats_text)
326
-
327
  async def handle_callback(self, update: Update, context):
328
  """پردازش دکمه‌های فشرده شده"""
329
  query = update.callback_query
330
- await query.answer() # پاسخ به کلیک دکمه
331
 
332
  if query.data == "main_menu":
333
  await query.message.edit_text(
@@ -341,64 +306,36 @@ class TelegramBot:
341
  reply_markup=self.ui.get_chat_menu()
342
  )
343
 
344
- elif query.data == "settings":
345
- await query.message.edit_text(
346
- "⚙️ تنظیمات:",
347
- reply_markup=self.ui.get_settings_menu()
348
- )
349
-
350
- elif query.data == "help":
351
- help_text = """
352
- 📚 راهنمای استفاده از ربات:
353
-
354
- 🔹 دستورات اصلی:
355
- /start - شروع مجدد ربات
356
- /menu - نمایش منوی اصلی
357
- /help - نمایش این راهنما
358
- /settings - تنظیمات ربات
359
- /stats - مشاهده آمار
360
-
361
- 🔹 نحوه استفاده:
362
- 1. از منوی اصلی بخش مورد نظر را انتخاب کنید
363
- 2. در بخش چت، سوال یا متن خود را وارد کنید
364
- 3. منتظر پاسخ بمانید
365
- """
366
- await query.message.edit_text(
367
- help_text,
368
- reply_markup=self.ui.get_main_menu()
369
- )
370
-
371
  elif query.data in ["general_chat", "qa_chat"]:
372
  chat_type = "عمومی" if query.data == "general_chat" else "سوال و جواب"
373
  await query.message.edit_text(
374
  f"💬 وارد بخش چت {chat_type} شدید.\nلطفاً پیام خود را بنویسید..."
375
  )
376
-
377
- elif query.data == "translate":
378
- await query.message.edit_text(
379
- "📝 لطفاً متن مورد نظر برای ترجمه را وارد کنید..."
380
- )
381
-
382
- elif query.data == "correct_text":
383
- await query.message.edit_text(
384
- "🔄 لطفاً متن مورد نظر برای تصحیح را وارد کنید..."
385
- )
386
 
387
  async def handle_message(self, update: Update, context):
388
  """پردازش پیام‌های دریافتی"""
389
  try:
390
- # بررسی دسترسی کاربر
391
- if str(update.message.chat_id) not in Config.ALLOWED_CHAT_IDS:
392
- await update.message.reply_text(
393
- "⚠️ شما مجوز استفاده از این ربات را ندارید."
394
- )
395
- return
396
-
397
  # نرمال‌سازی متن
398
  text = self.normalizer.normalize(update.message.text)
399
 
400
- # دریافت پاسخ از مدل
401
- response = await self.model.generate_response(text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
 
403
  # ارسال پاسخ با دکمه برگشت به منو
404
  keyboard = [[InlineKeyboardButton("🏠 بازگشت به منو", callback_data="main_menu")]]
@@ -410,173 +347,117 @@ class TelegramBot:
410
  )
411
 
412
  except Exception as e:
413
- logging.error(f"خطا در پردازش پیام: {e}")
 
414
  await update.message.reply_text(
415
  "❌ متأسفانه در پردازش پیام مشکلی پیش آمد. لطفاً دوباره تلاش کنید."
416
  )
417
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  async def run(self):
419
  """اجرای ربات"""
420
  try:
 
 
 
 
421
  await self.application.initialize()
422
  await self.application.start()
423
- await self.application.run_polling()
 
 
 
424
 
425
  except Exception as e:
426
- logging.error(f"خطا در اجرای ربات: {e}")
 
427
  raise
428
 
429
  @asynccontextmanager
430
  async def lifespan(app: FastAPI):
431
  """مدیریت چرخه حیات برنامه"""
432
  # راه‌اندازی
433
- Config.setup()
434
 
435
  # ایجاد نمونه از مدل
436
- app.state.model = AIModel()
437
- app.state.is_ready = asyncio.Event()
 
 
438
 
439
  # راه‌اندازی ربات تلگرام
440
  app.state.bot = TelegramBot(app.state.model)
441
  asyncio.create_task(app.state.bot.run())
442
 
443
  # آماده‌سازی کامل شد
444
- app.state.is_ready.set()
445
 
446
  yield
447
 
448
  # خاتمه برنامه
 
 
449
  tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
450
  for task in tasks:
451
  task.cancel()
 
452
  await asyncio.gather(*tasks, return_exceptions=True)
453
 
454
  if hasattr(app.state, "bot"):
455
  await app.state.bot.application.stop()
 
456
 
457
- logging.info("برنامه با موفقیت خاتمه یافت")
458
 
459
  # ساخت نمونه FastAPI
460
  app = FastAPI(
461
  title="دستیار هوشمند",
462
- description="API دستیار هوشمند مبتنی بر هوش مصنوعی",
463
  version="1.0.0",
464
  lifespan=lifespan
465
  )
466
 
467
- # محتوای پیش‌فرض HTML
468
- DEFAULT_HTML = """
469
- <!DOCTYPE html>
470
- <html dir="rtl" lang="fa">
471
- <head>
472
- <meta charset="UTF-8">
473
- <title>دستیار هوشمند</title>
474
- <style>
475
- body {
476
- font-family: Tahoma, Arial;
477
- background-color: #f5f5f5;
478
- margin: 0;
479
- padding: 20px;
480
- }
481
- .container {
482
- max-width: 800px;
483
- margin: 0 auto;
484
- background: white;
485
- padding: 20px;
486
- border-radius: 10px;
487
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
488
- }
489
- h1 { color: #1a73e8; }
490
- .chat-container {
491
- margin-top: 20px;
492
- border: 1px solid #e0e0e0;
493
- border-radius: 8px;
494
- padding: 10px;
495
- min-height: 300px;
496
- }
497
- </style>
498
- </head>
499
- <body>
500
- <div class="container">
501
- <h1>دستیار هوشمند</h1>
502
- <div id="status"></div>
503
- <div class="chat-container" id="chat-container"></div>
504
- <input
505
- type="text"
506
- id="message-input"
507
- placeholder="پیام خود را بنویسید..."
508
- style="width: 100%; padding: 10px; margin-top: 10px;">
509
- <button
510
- onclick="sendMessage()"
511
- style="margin-top: 10px; padding: 10px 20px;">
512
- ارسال
513
- </button>
514
- </div>
515
-
516
- <script>
517
- const ws = new WebSocket(`ws://${window.location.host}/ws`);
518
- const chatContainer = document.getElementById('chat-container');
519
- const messageInput = document.getElementById('message-input');
520
- const statusDiv = document.getElementById('status');
521
-
522
- ws.onopen = () => {
523
- statusDiv.textContent = 'وضعیت: متصل';
524
- statusDiv.style.color = 'green';
525
- };
526
-
527
- ws.onclose = () => {
528
- statusDiv.textContent = 'وضعیت: قطع شده';
529
- statusDiv.style.color = 'red';
530
- };
531
-
532
- ws.onmessage = (event) => {
533
- const response = JSON.parse(event.data);
534
- appendMessage('bot', response.response);
535
- };
536
-
537
- function appendMessage(type, text) {
538
- const messageDiv = document.createElement('div');
539
- messageDiv.className = `message ${type}`;
540
- messageDiv.style.padding = '10px';
541
- messageDiv.style.margin = '5px';
542
- messageDiv.style.borderRadius = '5px';
543
-
544
- if (type === 'user') {
545
- messageDiv.style.backgroundColor = '#e3f2fd';
546
- messageDiv.style.marginLeft = '20%';
547
- } else {
548
- messageDiv.style.backgroundColor = '#f5f5f5';
549
- messageDiv.style.marginRight = '20%';
550
- }
551
-
552
- messageDiv.textContent = text;
553
- chatContainer.appendChild(messageDiv);
554
- chatContainer.scrollTop = chatContainer.scrollHeight;
555
- }
556
-
557
- function sendMessage() {
558
- const message = messageInput.value.trim();
559
- if (message) {
560
- appendMessage('user', message);
561
- ws.send(message);
562
- messageInput.value = '';
563
- }
564
- }
565
-
566
- messageInput.addEventListener('keypress', function(e) {
567
- if (e.key === 'Enter') {
568
- sendMessage();
569
- }
570
- });
571
- </script>
572
- </body>
573
- </html>
574
- """
575
 
 
576
  @app.get("/", response_class=HTMLResponse)
577
  async def home(request: Request):
578
  """صفحه اصلی"""
579
- return HTMLResponse(content=DEFAULT_HTML)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
 
581
  @app.websocket("/ws")
582
  async def websocket_endpoint(websocket: WebSocket):
@@ -592,22 +473,15 @@ async def websocket_endpoint(websocket: WebSocket):
592
  response = await app.state.model.generate_response(text)
593
 
594
  # ارسال پاسخ
595
- await websocket.send_json({"response": response})
 
 
 
596
 
597
  except Exception as e:
598
- logging.error(f"خطا در WebSocket: {e}")
599
  await websocket.close()
600
 
601
- @app.get("/health")
602
- async def health_check():
603
- """بررسی سلامت برنامه"""
604
- return {
605
- "status": "ok",
606
- "timestamp": datetime.utcnow().isoformat(),
607
- "model_loaded": hasattr(app.state, "model"),
608
- "bot_running": hasattr(app.state, "bot")
609
- }
610
-
611
  if __name__ == "__main__":
612
  # اجرای برنامه
613
  uvicorn.run(
 
1
  """
2
+ دستیار هوشمند فارسی با قابلیت چت تلگرام
3
  """
4
 
5
  import os
 
11
  import traceback
12
  from datetime import datetime
13
  from pathlib import Path
14
+ from typing import Dict, Optional
 
15
  from contextlib import asynccontextmanager
 
16
 
17
+ # تنظیم مسیرها
18
+ BASE_DIR = Path(__file__).parent
19
+ CACHE_DIR = BASE_DIR / "cache"
20
+ LOG_DIR = BASE_DIR / "logs"
21
+ STATIC_DIR = BASE_DIR / "static"
22
+ TEMPLATES_DIR = BASE_DIR / "templates"
23
+
24
+ # ایجاد مسیرها
25
+ for path in [CACHE_DIR, LOG_DIR, STATIC_DIR, TEMPLATES_DIR]:
26
+ path.mkdir(exist_ok=True, parents=True)
27
+
28
+ # تنظیم متغیرهای محیطی
29
+ os.environ["TRANSFORMERS_CACHE"] = str(CACHE_DIR)
30
+ os.environ["HF_HOME"] = str(CACHE_DIR)
31
+ os.environ["TORCH_HOME"] = str(CACHE_DIR / "torch")
32
+
33
+ # کتابخانه‌های اصلی
34
  import torch
35
  import transformers
36
+ from transformers import pipeline
37
+ from fastapi import FastAPI, WebSocket, Request
38
  from fastapi.responses import HTMLResponse
39
  from fastapi.staticfiles import StaticFiles
40
  from fastapi.templating import Jinja2Templates
 
42
  from telegram import Update, Bot, InlineKeyboardButton, InlineKeyboardMarkup, BotCommand
43
  from telegram.ext import Application, CommandHandler, MessageHandler, CallbackQueryHandler, filters
44
  import hazm
45
+ import httpx
46
+
47
+ # تنظیم لاگینگ
48
+ logging.basicConfig(
49
+ level=logging.INFO,
50
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
51
+ handlers=[
52
+ logging.FileHandler(LOG_DIR / "app.log", encoding='utf-8'),
53
+ logging.StreamHandler(sys.stdout)
54
+ ]
55
+ )
56
+ logger = logging.getLogger(__name__)
57
 
58
  class Config:
59
  """تنظیمات اصلی برنامه"""
60
 
61
  # تنظیمات تلگرام
62
  TELEGRAM_TOKEN = "7437859619:AAGeGG3ZkLM0OVaw-Exx1uMRE55JtBCZZCY"
63
+ CHAT_ID = "-1002228627548"
 
 
64
 
65
  # تنظیمات مسیرها
66
+ BASE_DIR = BASE_DIR
67
+ CACHE_DIR = CACHE_DIR
68
+ LOG_DIR = LOG_DIR
69
+ STATIC_DIR = STATIC_DIR
70
+ TEMPLATES_DIR = TEMPLATES_DIR
71
 
72
  # تنظیمات مدل
73
+ MODEL_NAME = "bigscience/bloom-1b1"
74
+ MAX_LENGTH = 200
75
  MIN_LENGTH = 20
76
  TEMPERATURE = 0.7
77
+ TOP_K = 50
78
+ TOP_P = 0.95
79
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
80
 
81
  # تنظیمات عمومی
82
  DEBUG = True
83
  HOST = "0.0.0.0"
84
  PORT = 8000
85
+ MAX_RETRIES = 3
86
+ TIMEOUT = 30
87
+ CLEANUP_INTERVAL = 3600 # یک ساعت
88
 
89
+ class BloomAI:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  """کلاس مدیریت مدل هوش مصنوعی"""
91
 
92
  def __init__(self):
 
 
 
93
  self.pipeline = None
94
+ self.device = Config.DEVICE
95
+
96
+ async def initialize(self) -> bool:
 
97
  try:
98
+ logger.info(f"Loading AI model: {Config.MODEL_NAME}")
99
+ for attempt in range(Config.MAX_RETRIES):
100
+ try:
101
+ self.pipeline = pipeline(
102
+ "text-generation",
103
+ model=Config.MODEL_NAME,
104
+ device=self.device,
105
+ trust_remote_code=True
106
+ )
107
+ logger.info("AI model loaded successfully")
108
+ return True
109
+ except Exception as e:
110
+ if attempt < Config.MAX_RETRIES - 1:
111
+ logger.warning(f"Attempt {attempt + 1} failed, retrying in 5 seconds...")
112
+ await asyncio.sleep(5)
113
+ else:
114
+ raise e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  except Exception as e:
116
+ logger.error(f"Failed to load model: {str(e)}")
117
+ logger.error(traceback.format_exc())
118
  return False
119
+
120
  async def generate_response(self, text: str) -> str:
121
+ """تولید پاسخ با استفاده از مدل"""
122
  try:
123
+ if not self.pipeline:
124
+ logger.error("Model not initialized")
125
+ return "مدل در دسترس نیست."
126
+
127
+ outputs = self.pipeline(
 
128
  text,
129
  max_length=Config.MAX_LENGTH,
130
  min_length=Config.MIN_LENGTH,
 
131
  do_sample=True,
132
+ top_k=Config.TOP_K,
133
+ top_p=Config.TOP_P,
134
+ temperature=Config.TEMPERATURE,
135
+ num_return_sequences=1,
136
+ pad_token_id=self.pipeline.tokenizer.eos_token_id
137
  )
138
 
139
+ generated_text = outputs[0]['generated_text']
140
+ # حذف متن ورودی از پاسخ
141
+ response = generated_text.replace(text, "").strip()
142
 
143
+ if not response:
144
+ return "متأسفم، نتوانستم پاسخ مناسبی تولید کنم."
145
+
146
+ return response
147
 
148
  except Exception as e:
149
+ logger.error(f"Error generating response: {e}")
150
+ logger.error(traceback.format_exc())
151
+ return "خطا در تولید پاسخ."
152
+
153
+ async def cleanup(self):
154
+ """پاکسازی حافظه و کش مدل"""
155
+ try:
156
+ if torch.cuda.is_available():
157
+ torch.cuda.empty_cache()
158
+ gc.collect()
159
+ logger.info("Model cleanup completed")
160
+ except Exception as e:
161
+ logger.error(f"Error in cleanup: {e}")
162
 
163
  class TelegramUI:
164
  """کلاس مدیریت رابط کاربری تلگرام"""
 
200
  ]
201
  return InlineKeyboardMarkup(keyboard)
202
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  class TelegramBot:
204
  """کلاس اصلی ربات تلگرام"""
205
 
206
+ def __init__(self, ai_model: BloomAI):
207
  self.model = ai_model
208
  self.ui = TelegramUI()
209
  self.application = (
 
212
  .build()
213
  )
214
  self.normalizer = hazm.Normalizer()
215
+ self.client = httpx.AsyncClient(timeout=Config.TIMEOUT)
216
  self._setup_handlers()
217
+ self.message_queue = asyncio.Queue()
218
+ self.response_cache = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
  def _setup_handlers(self):
221
  """تنظیم هندلرهای ربات"""
 
223
  self.application.add_handler(CommandHandler("start", self.start_command))
224
  self.application.add_handler(CommandHandler("help", self.help_command))
225
  self.application.add_handler(CommandHandler("menu", self.menu_command))
226
+ self.application.add_handler(CommandHandler("cancel", self.cancel_command))
 
227
 
228
  # هندلر دکمه‌ها
229
  self.application.add_handler(CallbackQueryHandler(self.handle_callback))
 
259
  /start - شروع مجدد ربات
260
  /menu - نمایش منوی اصلی
261
  /help - نمایش این راهنما
262
+ /cancel - لغو عملیات فعلی
 
263
 
264
  🔹 نحوه استفاده:
265
  1. از منوی اصلی بخش مورد نظر را انتخاب کنید
266
  2. در بخش چت، سوال یا متن خود را وارد کنید
267
  3. منتظر پاسخ بمانید
 
 
 
 
268
  """
269
  await update.message.reply_text(
270
  help_text,
 
278
  reply_markup=self.ui.get_main_menu()
279
  )
280
 
281
+ async def cancel_command(self, update: Update, context):
282
+ """لغو عملیات فعلی"""
283
+ # پاک کردن صف پیام‌ها
284
+ while not self.message_queue.empty():
285
+ self.message_queue.get_nowait()
286
+
287
  await update.message.reply_text(
288
+ " عملیات فعلی لغو شد.",
289
+ reply_markup=self.ui.get_main_menu()
290
  )
291
 
 
 
 
 
 
 
 
 
 
 
292
  async def handle_callback(self, update: Update, context):
293
  """پردازش دکمه‌های فشرده شده"""
294
  query = update.callback_query
295
+ await query.answer()
296
 
297
  if query.data == "main_menu":
298
  await query.message.edit_text(
 
306
  reply_markup=self.ui.get_chat_menu()
307
  )
308
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  elif query.data in ["general_chat", "qa_chat"]:
310
  chat_type = "عمومی" if query.data == "general_chat" else "سوال و جواب"
311
  await query.message.edit_text(
312
  f"💬 وارد بخش چت {chat_type} شدید.\nلطفاً پیام خود را بنویسید..."
313
  )
 
 
 
 
 
 
 
 
 
 
314
 
315
  async def handle_message(self, update: Update, context):
316
  """پردازش پیام‌های دریافتی"""
317
  try:
 
 
 
 
 
 
 
318
  # نرمال‌سازی متن
319
  text = self.normalizer.normalize(update.message.text)
320
 
321
+ # بررسی کش
322
+ if text in self.response_cache:
323
+ response = self.response_cache[text]
324
+ logger.info("Using cached response")
325
+ else:
326
+ # ارسال پیام در حال پردازش
327
+ processing_message = await update.message.reply_text(
328
+ "🤔 در حال پردازش پیام شما..."
329
+ )
330
+
331
+ # دریافت پاسخ از مدل
332
+ response = await self.model.generate_response(text)
333
+
334
+ # ذخیره در کش
335
+ self.response_cache[text] = response
336
+
337
+ # حذف پیام در حال پردازش
338
+ await processing_message.delete()
339
 
340
  # ارسال پاسخ با دکمه برگشت به منو
341
  keyboard = [[InlineKeyboardButton("🏠 بازگشت به منو", callback_data="main_menu")]]
 
347
  )
348
 
349
  except Exception as e:
350
+ logger.error(f"خطا در پردازش پیام: {e}")
351
+ logger.error(traceback.format_exc())
352
  await update.message.reply_text(
353
  "❌ متأسفانه در پردازش پیام مشکلی پیش آمد. لطفاً دوباره تلاش کنید."
354
  )
355
 
356
+ async def periodic_cleanup(self):
357
+ """پاکسازی دوره‌ای حافظه و کش"""
358
+ while True:
359
+ await asyncio.sleep(Config.CLEANUP_INTERVAL)
360
+ logger.info("Starting periodic cleanup...")
361
+
362
+ # پاکسازی کش پاسخ‌ها
363
+ self.response_cache.clear()
364
+
365
+ # پاکسازی حافظه
366
+ await self.model.cleanup()
367
+
368
+ logger.info("Periodic cleanup completed")
369
+
370
  async def run(self):
371
  """اجرای ربات"""
372
  try:
373
+ # شروع پاکسازی دوره‌ای
374
+ asyncio.create_task(self.periodic_cleanup())
375
+
376
+ # راه‌اندازی ربات
377
  await self.application.initialize()
378
  await self.application.start()
379
+ await self.application.run_polling(
380
+ allowed_updates=Update.ALL_TYPES,
381
+ drop_pending_updates=True
382
+ )
383
 
384
  except Exception as e:
385
+ logger.error(f"خطا در اجرای ربات: {e}")
386
+ logger.error(traceback.format_exc())
387
  raise
388
 
389
  @asynccontextmanager
390
  async def lifespan(app: FastAPI):
391
  """مدیریت چرخه حیات برنامه"""
392
  # راه‌اندازی
393
+ logger.info("Starting application...")
394
 
395
  # ایجاد نمونه از مدل
396
+ app.state.model = BloomAI()
397
+ if not await app.state.model.initialize():
398
+ logger.error("Failed to initialize AI model")
399
+ sys.exit(1)
400
 
401
  # راه‌اندازی ربات تلگرام
402
  app.state.bot = TelegramBot(app.state.model)
403
  asyncio.create_task(app.state.bot.run())
404
 
405
  # آماده‌سازی کامل شد
406
+ logger.info("Application startup completed")
407
 
408
  yield
409
 
410
  # خاتمه برنامه
411
+ logger.info("Shutting down application...")
412
+
413
  tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
414
  for task in tasks:
415
  task.cancel()
416
+
417
  await asyncio.gather(*tasks, return_exceptions=True)
418
 
419
  if hasattr(app.state, "bot"):
420
  await app.state.bot.application.stop()
421
+ await app.state.bot.client.aclose()
422
 
423
+ logger.info("Application shutdown completed")
424
 
425
  # ساخت نمونه FastAPI
426
  app = FastAPI(
427
  title="دستیار هوشمند",
428
+ description="دستیار هوشمند فارسی با قابلیت چت تلگرام",
429
  version="1.0.0",
430
  lifespan=lifespan
431
  )
432
 
433
+ # اضافه کردن فایل‌های استاتیک
434
+ app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
435
+ templates = Jinja2Templates(directory=TEMPLATES_DIR)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
 
437
+ # مسیرهای وب
438
  @app.get("/", response_class=HTMLResponse)
439
  async def home(request: Request):
440
  """صفحه اصلی"""
441
+ return templates.TemplateResponse(
442
+ "index.html",
443
+ {
444
+ "request": request,
445
+ "bot_name": "دستیار هوشمند",
446
+ "model_name": Config.MODEL_NAME,
447
+ "is_gpu_available": torch.cuda.is_available()
448
+ }
449
+ )
450
+
451
+ @app.get("/health")
452
+ async def health_check():
453
+ """بررسی سلامت برنامه"""
454
+ return {
455
+ "status": "ok",
456
+ "timestamp": datetime.now().isoformat(),
457
+ "model_loaded": hasattr(app.state, "model"),
458
+ "bot_running": hasattr(app.state, "bot"),
459
+ "gpu_available": torch.cuda.is_available()
460
+ }
461
 
462
  @app.websocket("/ws")
463
  async def websocket_endpoint(websocket: WebSocket):
 
473
  response = await app.state.model.generate_response(text)
474
 
475
  # ارسال پاسخ
476
+ await websocket.send_json({
477
+ "response": response,
478
+ "timestamp": datetime.now().isoformat()
479
+ })
480
 
481
  except Exception as e:
482
+ logger.error(f"خطا در WebSocket: {e}")
483
  await websocket.close()
484
 
 
 
 
 
 
 
 
 
 
 
485
  if __name__ == "__main__":
486
  # اجرای برنامه
487
  uvicorn.run(