Really-amin commited on
Commit
3b2e348
1 Parent(s): d325d22

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +272 -45
app.py CHANGED
@@ -1,13 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import torch
2
  import transformers
3
  from transformers import pipeline
 
 
 
4
  from fastapi import FastAPI, WebSocket, Request
5
  from fastapi.responses import HTMLResponse
6
  from fastapi.staticfiles import StaticFiles
7
  from fastapi.templating import Jinja2Templates
8
- import uvicorn
9
  from telegram import Update, Bot, InlineKeyboardButton, InlineKeyboardMarkup, BotCommand
10
  from telegram.ext import Application, CommandHandler, MessageHandler, CallbackQueryHandler, filters
 
 
11
  import hazm
12
  import httpx
13
 
@@ -24,18 +58,18 @@ logger = logging.getLogger(__name__)
24
 
25
  class Config:
26
  """تنظیمات اصلی برنامه"""
27
-
28
  # تنظیمات تلگرام
29
- TELEGRAM_TOKEN = "7437859619:AAGeGG3ZkLM0OVaw-Exx1uMRE55JtBCZZCY"
30
- CHAT_ID = "-1002228627548"
31
-
32
  # تنظیمات مسیرها
33
  BASE_DIR = BASE_DIR
34
  CACHE_DIR = CACHE_DIR
35
  LOG_DIR = LOG_DIR
36
  STATIC_DIR = STATIC_DIR
37
  TEMPLATES_DIR = TEMPLATES_DIR
38
-
39
  # تنظیمات مدل
40
  MODEL_NAME = "bigscience/bloom-1b1"
41
  MAX_LENGTH = 200
@@ -44,7 +78,7 @@ class Config:
44
  TOP_K = 50
45
  TOP_P = 0.95
46
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
47
-
48
  # تنظیمات عمومی
49
  DEBUG = True
50
  HOST = "0.0.0.0"
@@ -52,14 +86,17 @@ class Config:
52
  MAX_RETRIES = 3
53
  TIMEOUT = 30
54
  CLEANUP_INTERVAL = 3600 # یک ساعت
 
 
 
55
 
56
  class BloomAI:
57
  """کلاس مدیریت مدل هوش مصنوعی"""
58
-
59
  def __init__(self):
60
  self.pipeline = None
61
  self.device = Config.DEVICE
62
-
63
  async def initialize(self) -> bool:
64
  try:
65
  logger.info(f"Loading AI model: {Config.MODEL_NAME}")
@@ -90,7 +127,7 @@ class BloomAI:
90
  if not self.pipeline:
91
  logger.error("Model not initialized")
92
  return "مدل در دسترس نیست."
93
-
94
  outputs = self.pipeline(
95
  text,
96
  max_length=Config.MAX_LENGTH,
@@ -102,16 +139,16 @@ class BloomAI:
102
  num_return_sequences=1,
103
  pad_token_id=self.pipeline.tokenizer.eos_token_id
104
  )
105
-
106
  generated_text = outputs[0]['generated_text']
107
  # حذف متن ورودی از پاسخ
108
  response = generated_text.replace(text, "").strip()
109
-
110
  if not response:
111
  return "متأسفم، نتوانستم پاسخ مناسبی تولید کنم."
112
-
113
  return response
114
-
115
  except Exception as e:
116
  logger.error(f"Error generating response: {e}")
117
  logger.error(traceback.format_exc())
@@ -129,7 +166,7 @@ class BloomAI:
129
 
130
  class TelegramUI:
131
  """کلاس مدیریت رابط کاربری تلگرام"""
132
-
133
  @staticmethod
134
  def get_main_menu() -> InlineKeyboardMarkup:
135
  """منوی اصلی با دکمه‌های تخت"""
@@ -169,7 +206,7 @@ class TelegramUI:
169
 
170
  class TelegramBot:
171
  """کلاس اصلی ربات تلگرام"""
172
-
173
  def __init__(self, ai_model: BloomAI):
174
  self.model = ai_model
175
  self.ui = TelegramUI()
@@ -191,10 +228,10 @@ class TelegramBot:
191
  self.application.add_handler(CommandHandler("help", self.help_command))
192
  self.application.add_handler(CommandHandler("menu", self.menu_command))
193
  self.application.add_handler(CommandHandler("cancel", self.cancel_command))
194
-
195
  # هندلر دکمه‌ها
196
  self.application.add_handler(CallbackQueryHandler(self.handle_callback))
197
-
198
  # هندلر پیام‌های متنی
199
  self.application.add_handler(
200
  MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message)
@@ -250,7 +287,7 @@ class TelegramBot:
250
  # پاک کردن صف پیام‌ها
251
  while not self.message_queue.empty():
252
  self.message_queue.get_nowait()
253
-
254
  await update.message.reply_text(
255
  "❌ عملیات فعلی لغو شد.",
256
  reply_markup=self.ui.get_main_menu()
@@ -260,19 +297,19 @@ class TelegramBot:
260
  """پردازش دکمه‌های فشرده شده"""
261
  query = update.callback_query
262
  await query.answer()
263
-
264
  if query.data == "main_menu":
265
  await query.message.edit_text(
266
  "🏠 منوی اصلی:",
267
  reply_markup=self.ui.get_main_menu()
268
  )
269
-
270
  elif query.data == "new_chat":
271
  await query.message.edit_text(
272
  "💭 لطفاً نوع چت را انتخاب کنید:",
273
  reply_markup=self.ui.get_chat_menu()
274
  )
275
-
276
  elif query.data in ["general_chat", "qa_chat"]:
277
  chat_type = "عمومی" if query.data == "general_chat" else "سوال و جواب"
278
  await query.message.edit_text(
@@ -282,9 +319,19 @@ class TelegramBot:
282
  async def handle_message(self, update: Update, context):
283
  """پردازش پیام‌های دریافتی"""
284
  try:
 
 
 
 
 
 
 
 
 
 
285
  # نرمال‌سازی متن
286
  text = self.normalizer.normalize(update.message.text)
287
-
288
  # بررسی کش
289
  if text in self.response_cache:
290
  response = self.response_cache[text]
@@ -294,25 +341,32 @@ class TelegramBot:
294
  processing_message = await update.message.reply_text(
295
  "🤔 در حال پردازش پیام شما..."
296
  )
297
-
298
  # دریافت پاسخ از مدل
299
  response = await self.model.generate_response(text)
300
-
 
 
 
 
 
 
 
301
  # ذخیره در کش
302
  self.response_cache[text] = response
303
-
304
  # حذف پیام در حال پردازش
305
  await processing_message.delete()
306
-
307
  # ارسال پاسخ با دکمه برگشت به منو
308
  keyboard = [[InlineKeyboardButton("🏠 بازگشت به منو", callback_data="main_menu")]]
309
  reply_markup = InlineKeyboardMarkup(keyboard)
310
-
311
  await update.message.reply_text(
312
  f"🤖 {response}",
313
  reply_markup=reply_markup
314
  )
315
-
316
  except Exception as e:
317
  logger.error(f"خطا در پردازش پیام: {e}")
318
  logger.error(traceback.format_exc())
@@ -322,24 +376,28 @@ class TelegramBot:
322
 
323
  async def periodic_cleanup(self):
324
  """پاکسازی دوره‌ای حافظه و کش"""
325
- while True:
326
- await asyncio.sleep(Config.CLEANUP_INTERVAL)
327
- logger.info("Starting periodic cleanup...")
328
-
329
- # پاکسازی کش پاسخ‌ها
330
- self.response_cache.clear()
331
-
332
- # پاکسازی حافظه
333
- await self.model.cleanup()
334
-
335
- logger.info("Periodic cleanup completed")
 
 
 
 
336
 
337
  async def run(self):
338
  """اجرای ربات"""
339
  try:
340
  # شروع پاکسازی دوره‌ای
341
  asyncio.create_task(self.periodic_cleanup())
342
-
343
  # راه‌اندازی ربات
344
  await self.application.initialize()
345
  await self.application.start()
@@ -347,7 +405,7 @@ class TelegramBot:
347
  allowed_updates=Update.ALL_TYPES,
348
  drop_pending_updates=True
349
  )
350
-
351
  except Exception as e:
352
  logger.error(f"خطا در اجرای ربات: {e}")
353
  logger.error(traceback.format_exc())
@@ -358,11 +416,180 @@ async def lifespan(app: FastAPI):
358
  """مدیریت چرخه حیات برنامه"""
359
  # راه‌اندازی
360
  logger.info("Starting application...")
361
-
362
  # ایجاد نمونه از مدل
363
  app.state.model = BloomAI()
364
  if not await app.state.model.initialize():
365
  logger.error("Failed to initialize AI model")
366
  sys.exit(1)
367
-
368
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ import gc
4
+ import json
5
+ import logging
6
+ import asyncio
7
+ import traceback
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+ from typing import Dict, Optional
11
+ from contextlib import asynccontextmanager
12
+ import tempfile
13
+
14
+ # تنظیم مسیرها - استفاده از مسیر temp برای حل مشکل دسترسی
15
+ BASE_DIR = Path(tempfile.gettempdir()) / "ai_assistant"
16
+ CACHE_DIR = BASE_DIR / "cache"
17
+ LOG_DIR = BASE_DIR / "logs"
18
+ STATIC_DIR = BASE_DIR / "static"
19
+ TEMPLATES_DIR = BASE_DIR / "templates"
20
+
21
+ # ایجاد مسیرها
22
+ for path in [BASE_DIR, CACHE_DIR, LOG_DIR, STATIC_DIR, TEMPLATES_DIR]:
23
+ path.mkdir(exist_ok=True, parents=True)
24
+
25
+ # تنظیم متغیرهای محیطی
26
+ os.environ["TRANSFORMERS_CACHE"] = str(CACHE_DIR)
27
+ os.environ["HF_HOME"] = str(CACHE_DIR)
28
+ os.environ["TORCH_HOME"] = str(CACHE_DIR / "torch")
29
+
30
+ # کتابخانه‌های هوش مصنوعی
31
  import torch
32
  import transformers
33
  from transformers import pipeline
34
+
35
+ # کتابخانه‌های وب و تلگرام
36
+ import uvicorn
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
 
41
  from telegram import Update, Bot, InlineKeyboardButton, InlineKeyboardMarkup, BotCommand
42
  from telegram.ext import Application, CommandHandler, MessageHandler, CallbackQueryHandler, filters
43
+
44
+ # کتابخانه‌های دیگر
45
  import hazm
46
  import httpx
47
 
 
58
 
59
  class Config:
60
  """تنظیمات اصلی برنامه"""
61
+
62
  # تنظیمات تلگرام
63
+ TELEGRAM_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN"
64
+ CHAT_ID = "YOUR_CHAT_ID"
65
+
66
  # تنظیمات مسیرها
67
  BASE_DIR = BASE_DIR
68
  CACHE_DIR = CACHE_DIR
69
  LOG_DIR = LOG_DIR
70
  STATIC_DIR = STATIC_DIR
71
  TEMPLATES_DIR = TEMPLATES_DIR
72
+
73
  # تنظیمات مدل
74
  MODEL_NAME = "bigscience/bloom-1b1"
75
  MAX_LENGTH = 200
 
78
  TOP_K = 50
79
  TOP_P = 0.95
80
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
81
+
82
  # تنظیمات عمومی
83
  DEBUG = True
84
  HOST = "0.0.0.0"
 
86
  MAX_RETRIES = 3
87
  TIMEOUT = 30
88
  CLEANUP_INTERVAL = 3600 # یک ساعت
89
+ MAX_QUEUE_SIZE = 1000
90
+ MAX_CACHE_SIZE = 1000
91
+ MAX_MESSAGE_LENGTH = 500
92
 
93
  class BloomAI:
94
  """کلاس مدیریت مدل هوش مصنوعی"""
95
+
96
  def __init__(self):
97
  self.pipeline = None
98
  self.device = Config.DEVICE
99
+
100
  async def initialize(self) -> bool:
101
  try:
102
  logger.info(f"Loading AI model: {Config.MODEL_NAME}")
 
127
  if not self.pipeline:
128
  logger.error("Model not initialized")
129
  return "مدل در دسترس نیست."
130
+
131
  outputs = self.pipeline(
132
  text,
133
  max_length=Config.MAX_LENGTH,
 
139
  num_return_sequences=1,
140
  pad_token_id=self.pipeline.tokenizer.eos_token_id
141
  )
142
+
143
  generated_text = outputs[0]['generated_text']
144
  # حذف متن ورودی از پاسخ
145
  response = generated_text.replace(text, "").strip()
146
+
147
  if not response:
148
  return "متأسفم، نتوانستم پاسخ مناسبی تولید کنم."
149
+
150
  return response
151
+
152
  except Exception as e:
153
  logger.error(f"Error generating response: {e}")
154
  logger.error(traceback.format_exc())
 
166
 
167
  class TelegramUI:
168
  """کلاس مدیریت رابط کاربری تلگرام"""
169
+
170
  @staticmethod
171
  def get_main_menu() -> InlineKeyboardMarkup:
172
  """منوی اصلی با دکمه‌های تخت"""
 
206
 
207
  class TelegramBot:
208
  """کلاس اصلی ربات تلگرام"""
209
+
210
  def __init__(self, ai_model: BloomAI):
211
  self.model = ai_model
212
  self.ui = TelegramUI()
 
228
  self.application.add_handler(CommandHandler("help", self.help_command))
229
  self.application.add_handler(CommandHandler("menu", self.menu_command))
230
  self.application.add_handler(CommandHandler("cancel", self.cancel_command))
231
+
232
  # هندلر دکمه‌ها
233
  self.application.add_handler(CallbackQueryHandler(self.handle_callback))
234
+
235
  # هندلر پیام‌های متنی
236
  self.application.add_handler(
237
  MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message)
 
287
  # پاک کردن صف پیام‌ها
288
  while not self.message_queue.empty():
289
  self.message_queue.get_nowait()
290
+
291
  await update.message.reply_text(
292
  "❌ عملیات فعلی لغو شد.",
293
  reply_markup=self.ui.get_main_menu()
 
297
  """پردازش دکمه‌های فشرده شده"""
298
  query = update.callback_query
299
  await query.answer()
300
+
301
  if query.data == "main_menu":
302
  await query.message.edit_text(
303
  "🏠 منوی اصلی:",
304
  reply_markup=self.ui.get_main_menu()
305
  )
306
+
307
  elif query.data == "new_chat":
308
  await query.message.edit_text(
309
  "💭 لطفاً نوع چت را انتخاب کنید:",
310
  reply_markup=self.ui.get_chat_menu()
311
  )
312
+
313
  elif query.data in ["general_chat", "qa_chat"]:
314
  chat_type = "عمومی" if query.data == "general_chat" else "سوال و جواب"
315
  await query.message.edit_text(
 
319
  async def handle_message(self, update: Update, context):
320
  """پردازش پیام‌های دریافتی"""
321
  try:
322
+ # بررسی طول پیام
323
+ if len(update.message.text) > Config.MAX_MESSAGE_LENGTH:
324
+ await update.message.reply_text("⚠️ پیام شما بیش از حد طولانی است.")
325
+ return
326
+
327
+ # بررسی حجم صف پیام‌ها
328
+ if self.message_queue.qsize() >= Config.MAX_QUEUE_SIZE:
329
+ await update.message.reply_text("⚠️ سیستم در حال حاضر شلوغ است. لطفاً کمی صبر کنید.")
330
+ return
331
+
332
  # نرمال‌سازی متن
333
  text = self.normalizer.normalize(update.message.text)
334
+
335
  # بررسی کش
336
  if text in self.response_cache:
337
  response = self.response_cache[text]
 
341
  processing_message = await update.message.reply_text(
342
  "🤔 در حال پردازش پیام شما..."
343
  )
344
+
345
  # دریافت پاسخ از مدل
346
  response = await self.model.generate_response(text)
347
+
348
+ # بررسی حجم کش
349
+ if len(self.response_cache) >= Config.MAX_CACHE_SIZE:
350
+ # حذف قدیمی‌ترین پاسخ‌ها
351
+ oldest_keys = list(self.response_cache.keys())[:100]
352
+ for key in oldest_keys:
353
+ del self.response_cache[key]
354
+
355
  # ذخیره در کش
356
  self.response_cache[text] = response
357
+
358
  # حذف پیام در حال پردازش
359
  await processing_message.delete()
360
+
361
  # ارسال پاسخ با دکمه برگشت به منو
362
  keyboard = [[InlineKeyboardButton("🏠 بازگشت به منو", callback_data="main_menu")]]
363
  reply_markup = InlineKeyboardMarkup(keyboard)
364
+
365
  await update.message.reply_text(
366
  f"🤖 {response}",
367
  reply_markup=reply_markup
368
  )
369
+
370
  except Exception as e:
371
  logger.error(f"خطا در پردازش پیام: {e}")
372
  logger.error(traceback.format_exc())
 
376
 
377
  async def periodic_cleanup(self):
378
  """پاکسازی دوره‌ای حافظه و کش"""
379
+ try:
380
+ while True:
381
+ await asyncio.sleep(Config.CLEANUP_INTERVAL)
382
+ logger.info("Starting periodic cleanup...")
383
+
384
+ # پاکسازی کش پاسخ‌ها
385
+ self.response_cache.clear()
386
+
387
+ # پاکسازی حافظه
388
+ await self.model.cleanup()
389
+
390
+ logger.info("Periodic cleanup completed")
391
+ except Exception as e:
392
+ logger.error(f"Error in periodic cleanup: {e}")
393
+ logger.error(traceback.format_exc())
394
 
395
  async def run(self):
396
  """اجرای ربات"""
397
  try:
398
  # شروع پاکسازی دوره‌ای
399
  asyncio.create_task(self.periodic_cleanup())
400
+
401
  # راه‌اندازی ربات
402
  await self.application.initialize()
403
  await self.application.start()
 
405
  allowed_updates=Update.ALL_TYPES,
406
  drop_pending_updates=True
407
  )
408
+
409
  except Exception as e:
410
  logger.error(f"خطا در اجرای ربات: {e}")
411
  logger.error(traceback.format_exc())
 
416
  """مدیریت چرخه حیات برنامه"""
417
  # راه‌اندازی
418
  logger.info("Starting application...")
419
+
420
  # ایجاد نمونه از مدل
421
  app.state.model = BloomAI()
422
  if not await app.state.model.initialize():
423
  logger.error("Failed to initialize AI model")
424
  sys.exit(1)
425
+
426
+ # راه‌اندازی ربات تلگرام
427
+ app.state.bot = TelegramBot(app.state.model)
428
+ asyncio.create_task(app.state.bot.run())
429
+
430
+ yield
431
+
432
+ # خاتمه
433
+ tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
434
+ for task in tasks:
435
+ task.cancel()
436
+ await asyncio.gather(*tasks, return_exceptions=True)
437
+ await app.state.bot.application.shutdown()
438
+ logger.info("Application shutdown completed")
439
+
440
+ # برنامه FastAPI
441
+ app = FastAPI(title="AI Assistant", lifespan=lifespan)
442
+
443
+ # پیوست کردن فایل‌های استاتیک
444
+ app.mount("/static", StaticFiles(directory=Config.STATIC_DIR), name="static")
445
+ templates = Jinja2Templates(directory=Config.TEMPLATES_DIR)
446
+
447
+ # HTML پیش‌فرض
448
+ DEFAULT_HTML = """
449
+ <!DOCTYPE html>
450
+ <html dir="rtl" lang="fa">
451
+ <head>
452
+ <meta charset="UTF-8">
453
+ <title>دستیار هوشمند</title>
454
+ <style>
455
+ body {
456
+ font-family: Tahoma, Arial;
457
+ background-color: #f5f5f5;
458
+ margin: 0;
459
+ padding: 20px;
460
+ }
461
+ .container {
462
+ max-width: 800px;
463
+ margin: 0 auto;
464
+ background: white;
465
+ padding: 20px;
466
+ border-radius: 10px;
467
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
468
+ }
469
+ h1 { color: #1a73e8; }
470
+ .status { margin: 20px 0; }
471
+ .chat-container {
472
+ margin-top: 20px;
473
+ border: 1px solid #e0e0e0;
474
+ border-radius: 8px;
475
+ padding: 10px;
476
+ min-height: 300px;
477
+ }
478
+ </style>
479
+ </head>
480
+ <body>
481
+ <div class="container">
482
+ <h1>دستیار هوشمند</h1>
483
+ <div class="status">
484
+ <p>برای استفاده از دستیار هوشمند، به ربات تلگرام مراجعه کنید یا از رابط وب استفاده نمایید.</p>
485
+ </div>
486
+ <div class="chat-container" id="chat-container">
487
+ </div>
488
+ <input type="text" id="message-input" placeholder="پیام خود را بنویسید..." style="width: 100%; padding: 10px; margin-top: 10px;">
489
+ <button onclick="sendMessage()" style="margin-top: 10px; padding: 10px 20px;">ارسال</button>
490
+ </div>
491
+ <script>
492
+ const ws = new WebSocket(`ws://${window.location.host}/ws`);
493
+ const chatContainer = document.getElementById('chat-container');
494
+ const messageInput = document.getElementById('message-input');
495
+
496
+ ws.onmessage = function(event) {
497
+ const response = JSON.parse(event.data);
498
+ appendMessage('bot', response.response);
499
+ };
500
+
501
+ function appendMessage(type, text) {
502
+ const messageDiv = document.createElement('div');
503
+ messageDiv.style.padding = '10px';
504
+ messageDiv.style.margin = '5px';
505
+ messageDiv.style.borderRadius = '5px';
506
+
507
+ if (type === 'user') {
508
+ messageDiv.style.backgroundColor = '#e3f2fd';
509
+ messageDiv.style.marginLeft = '20%';
510
+ } else {
511
+ messageDiv.style.backgroundColor = '#f5f5f5';
512
+ messageDiv.style.marginRight = '20%';
513
+ }
514
+
515
+ messageDiv.textContent = text;
516
+ chatContainer.appendChild(messageDiv);
517
+ chatContainer.scrollTop = chatContainer.scrollHeight;
518
+ }
519
+
520
+ function sendMessage() {
521
+ const message = messageInput.value.trim();
522
+ if (message) {
523
+ appendMessage('user', message);
524
+ ws.send(message);
525
+ messageInput.value = '';
526
+ }
527
+ }
528
+
529
+ messageInput.addEventListener('keypress', function(e) {
530
+ if (e.key === 'Enter') {
531
+ sendMessage();
532
+ }
533
+ });
534
+ </script>
535
+ </body>
536
+ </html>
537
+ """
538
+
539
+ @app.get("/", response_class=HTMLResponse)
540
+ async def home(request: Request):
541
+ """صفحه اصلی"""
542
+ try:
543
+ # می‌توانید اطلاعات بیشتری را به تمپلیت ارسال کنید
544
+ return templates.TemplateResponse(
545
+ "index.html",
546
+ {
547
+ "request": request,
548
+ "bot_status": "فعال" if request.app.state.is_ready.is_set() else "در حال آماده‌سازی"
549
+ }
550
+ )
551
+ except Exception as e:
552
+ logger.error(f"خطا در نمایش صفحه ��صلی: {e}")
553
+ return HTMLResponse(content=DEFAULT_HTML)
554
+
555
+ @app.websocket("/ws")
556
+ async def websocket_endpoint(websocket: WebSocket):
557
+ """اتصال WebSocket"""
558
+ await websocket.accept()
559
+
560
+ try:
561
+ while True:
562
+ # دریافت پیام
563
+ text = await websocket.receive_text()
564
+
565
+ # تولید پاسخ
566
+ response = await app.state.model.generate_response(text)
567
+
568
+ # ارسال پاسخ
569
+ await websocket.send_json({
570
+ "response": response
571
+ })
572
+ except WebSocketDisconnect:
573
+ logger.info("اتصال WebSocket قطع شد")
574
+ except Exception as e:
575
+ logger.error(f"خطا در WebSocket: {e}")
576
+ await websocket.close()
577
+
578
+ @app.exception_handler(404)
579
+ async def not_found_handler(request: Request, exc: Exception):
580
+ """پردازش خطای 404"""
581
+ return HTMLResponse(content=DEFAULT_HTML, status_code=404)
582
+
583
+ @app.exception_handler(500)
584
+ async def server_error_handler(request: Request, exc: Exception):
585
+ """پردازش خطای 500"""
586
+ return HTMLResponse(content=DEFAULT_HTML, status_code=500)
587
+
588
+ if __name__ == "__main__":
589
+ # اجرای برنامه
590
+ uvicorn.run(
591
+ app,
592
+ host=Config.HOST,
593
+ port=Config.PORT,
594
+ log_level="info"
595
+ )