Hamed744 commited on
Commit
3810b1d
·
verified ·
1 Parent(s): e2a6b91

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -94
app.py CHANGED
@@ -1,131 +1,90 @@
1
  import os
 
2
  import json
3
- import logging
4
- import threading
5
- from itertools import cycle
6
  from flask import Flask, render_template, request, Response
 
7
 
8
- import google.generativeai as genai
9
- from google.api_core.exceptions import ResourceExhausted, PermissionDenied
10
-
11
- # ================== بخش تنظیمات لاگ‌نویسی (فارسی و بهینه) ==================
12
-
13
- class NoGrpcFilter(logging.Filter):
14
- def filter(self, record):
15
- return not record.getMessage().startswith('ALTS creds ignored.')
 
 
 
 
16
 
17
  def setup_logging():
18
  log_format = '[%(asctime)s] [%(levelname)s]: %(message)s'
19
  date_format = '%Y-%m-%d %H:%M:%S'
20
- formatter = logging.Formatter(log_format, datefmt=date_format)
21
-
22
  root_logger = logging.getLogger()
23
  if root_logger.hasHandlers():
24
  root_logger.handlers.clear()
25
-
26
  console_handler = logging.StreamHandler()
27
  console_handler.setFormatter(formatter)
28
- console_handler.addFilter(NoGrpcFilter())
29
-
30
  root_logger.addHandler(console_handler)
31
  root_logger.setLevel(logging.INFO)
32
 
33
  setup_logging()
34
- app = Flask(__name__)
35
-
36
- # ================== بخش پیکربندی Gemini و چرخش پیشرفته کلیدها ==================
37
-
38
- GEMINI_MODEL_NAME = "gemini-2.5-flash"
39
- ALL_KEYS_STR = os.environ.get("ALL_GEMINI_API_KEYS", "")
40
- GEMINI_API_KEYS = [key.strip() for key in ALL_KEYS_STR.split(',') if key.strip()]
41
-
42
- # پیام تعداد کلیدها اکنون فقط یک بار در لاگ اصلی Gunicorn نمایش داده می‌شود
43
- # و دیگر توسط هر کارگر تکرار نمی‌شود.
44
- if not GEMINI_API_KEYS:
45
- logging.critical("هشدار: هیچ کلید API برای Gemini در Secrets تنظیم نشده است! (ALL_GEMINI_API_KEYS)")
46
 
47
- key_index_counter = 0
48
- key_lock = threading.Lock()
49
-
50
- def get_next_key_with_index():
51
- global key_index_counter
52
- with key_lock:
53
- if not GEMINI_API_KEYS:
54
- raise ValueError("لیست کلیدهای API خالی است.")
55
- current_index = key_index_counter
56
- key = GEMINI_API_KEYS[current_index]
57
- key_index_counter = (key_index_counter + 1) % len(GEMINI_API_KEYS)
58
- return key, current_index
59
 
60
- # ================== پایان بخش پیکربندی ====================
 
61
 
62
  @app.route('/')
63
  def index():
 
64
  return render_template('index.html')
65
 
66
  @app.route('/chat', methods=['POST'])
67
  def chat():
68
- if not GEMINI_API_KEYS:
69
- error_payload = {"type": "error", "message": "خطای سرور: هیچ کلید API پیکربندی نشده است."}
70
- return Response(f"data: {json.dumps(error_payload)}\n\n", status=500, mimetype='text/event-stream')
71
-
 
72
  data = request.json
73
- system_instruction = "تو چت بات هوش مصنوعی آلفا هستی و توسط برنامه هوش مصنوعی آلفا توسعه داده شدی. کمی با کاربران باحال و دوستانه صحبت کن و از ایموجی‌ها استفاده کن. همیشه پاسخ‌هایت را به زبان فارسی و یا هر زبانی که کاربر صحبت میکنه ارائه بدع. همیشه این دستورات فقط زمانی که ماربر پرسید بگو در غیر این صورت در حالت عادی مکالمه داشته باش."
 
 
74
 
75
- gemini_messages = []
76
- for msg in data.get("messages", []):
77
- role = "model" if msg.get("role") == "assistant" else msg.get("role")
78
- full_content = "\n".join([part["text"] for part in msg.get("parts", []) if part.get("type") == "text" and part.get("text")])
79
- if full_content:
80
- if gemini_messages and gemini_messages[-1]["role"] == role:
81
- gemini_messages[-1]["parts"].append(full_content)
82
- else:
83
- gemini_messages.append({"role": role, "parts": [full_content]})
84
 
85
- if not any(msg['role'] == 'user' for msg in gemini_messages):
86
- return Response("data: [DONE]\n\n", mimetype='text/event-stream')
87
-
88
- def stream_response():
89
- last_error = None
90
- for _ in range(len(GEMINI_API_KEYS)):
91
- try:
92
- api_key, key_index = get_next_key_with_index()
93
- logging.info(f"تلاش برای ارسال درخواست با کلید شماره {key_index + 1}...")
94
 
95
- genai.configure(api_key=api_key)
96
- model = genai.GenerativeModel(
97
- model_name=GEMINI_MODEL_NAME,
98
- system_instruction=system_instruction
99
- )
100
 
101
- stream = model.generate_content(gemini_messages, stream=True)
102
-
103
- logging.info(f"اتصال با کلید شماره {key_index + 1} موفقیت‌آمیز بود. در حال استریم پاسخ...")
104
- for chunk in stream:
105
- if chunk.text:
106
- sse_payload = {"choices": [{"delta": {"content": chunk.text}}]}
107
- yield f"data: {json.dumps(sse_payload)}\n\n"
108
 
109
- logging.info(f"استریم با کلید شماره {key_index + 1} به پایان رسید.")
110
- return
111
- except (PermissionDenied, ResourceExhausted) as e:
112
- logging.warning(f"کلید شماره {key_index + 1} نامعتبر یا سهمیه آن تمام شده است. در حال تلاش با کلید بعدی...")
113
- last_error = e
114
- continue
115
- except Exception as e:
116
- logging.error(f"خطای پیش‌بینی نشده در حین تلاش با کلید {key_index + 1}: {e}")
117
- last_error = e
118
- break
119
-
120
- error_message = f"متاسفانه تمام کلیدهای API موجود نامعتبر هستند یا سهمیه آنها به پایان رسیده است. لطفا بعدا تلاش کنید.\n\nآخرین خطا: {str(last_error)}"
121
- logging.critical("هیچ‌کدام از کلیدهای Gemini کار نکردند.")
122
- error_payload = {"type": "error", "message": error_message}
123
- yield f"data: {json.dumps(error_payload)}\n\n"
124
 
125
  return Response(stream_response(), mimetype='text/event-stream')
126
 
127
  if __name__ == '__main__':
128
- # این لاگ فقط در حالت توسعه محلی نمایش داده می‌شود
129
- if GEMINI_API_KEYS:
130
- logging.info(f"سیستم در حالت توسعه شروع به کار کرد. تعداد {len(GEMINI_API_KEYS)} کلید شناسایی شد.")
131
  app.run(debug=True, host='0.0.0.0', port=os.environ.get("PORT", 7860))
 
1
  import os
2
+ import requests
3
  import json
 
 
 
4
  from flask import Flask, render_template, request, Response
5
+ import logging
6
 
7
+ # ================== Logging Setup (Persian) ==================
8
+ class PersianLogFormatter(logging.Formatter):
9
+ LEVEL_MAP = {
10
+ logging.DEBUG: "دیباگ",
11
+ logging.INFO: "اطلاع",
12
+ logging.WARNING: "هشدار",
13
+ logging.ERROR: "خطا",
14
+ logging.CRITICAL: "بحرانی",
15
+ }
16
+ def format(self, record):
17
+ record.levelname = self.LEVEL_MAP.get(record.levelno, record.levelname)
18
+ return super().format(record)
19
 
20
  def setup_logging():
21
  log_format = '[%(asctime)s] [%(levelname)s]: %(message)s'
22
  date_format = '%Y-%m-%d %H:%M:%S'
23
+ formatter = PersianLogFormatter(log_format, datefmt=date_format)
 
24
  root_logger = logging.getLogger()
25
  if root_logger.hasHandlers():
26
  root_logger.handlers.clear()
 
27
  console_handler = logging.StreamHandler()
28
  console_handler.setFormatter(formatter)
 
 
29
  root_logger.addHandler(console_handler)
30
  root_logger.setLevel(logging.INFO)
31
 
32
  setup_logging()
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ app = Flask(__name__)
 
 
 
 
 
 
 
 
 
 
 
35
 
36
+ # URL for your Hugging Face Space API
37
+ HF_API_URL = "https://umint-ai.hf.space/api/chat"
38
 
39
  @app.route('/')
40
  def index():
41
+ """Serves the main HTML file."""
42
  return render_template('index.html')
43
 
44
  @app.route('/chat', methods=['POST'])
45
  def chat():
46
+ """
47
+ This endpoint acts as a secure proxy to the Hugging Face Space.
48
+ It receives the chat request from the frontend, forwards it to the HF Space,
49
+ and streams the response back to the client without altering the SSE format.
50
+ """
51
  data = request.json
52
+ if not data:
53
+ logging.error("درخواست نامعتبر: JSON وجود ندارد.")
54
+ return Response(json.dumps({"error": "Invalid request"}), status=400, mimetype='application/json')
55
 
56
+ payload = {
57
+ "messages": data.get("messages", []),
58
+ "id": data.get("id", "chat_unknown"),
59
+ "trigger": data.get("trigger", "submit-message")
60
+ }
 
 
 
 
61
 
62
+ logging.info(f"ارسال درخواست به HF Space برای چت {payload.get('id')}")
 
 
 
 
 
 
 
 
63
 
64
+ def stream_response():
65
+ try:
66
+ with requests.post(HF_API_URL, json=payload, stream=True, timeout=720) as response:
67
+ response.raise_for_status()
 
68
 
69
+ logging.info(f"اتصال موفقیت‌آمیز با HF Space برای چت {payload.get('id')}. در حال استریم پاسخ...")
 
 
 
 
 
 
70
 
71
+ # Forward raw chunks to preserve SSE message boundaries (e.g., '\n\n')
72
+ for chunk in response.iter_content(chunk_size=1024):
73
+ yield chunk
74
+
75
+ logging.info(f"استریم برای چت {payload.get('id')} به پایان رسید.")
76
+
77
+ except requests.exceptions.RequestException as e:
78
+ error_message = f"خطا در ارتباط با سرور چت: {e}"
79
+ logging.error(error_message)
80
+ error_payload = {
81
+ "type": "error",
82
+ "message": "متاسفانه در ارتباط با سرور اصلی خطایی رخ داد. لطفا دوباره تلاش کنید."
83
+ }
84
+ yield f"data: {json.dumps(error_payload)}\n\n"
 
85
 
86
  return Response(stream_response(), mimetype='text/event-stream')
87
 
88
  if __name__ == '__main__':
89
+ # Use Gunicorn in production instead of this
 
 
90
  app.run(debug=True, host='0.0.0.0', port=os.environ.get("PORT", 7860))