AhmadA82 commited on
Commit
caceba9
·
verified ·
1 Parent(s): 46e2f45
Files changed (5) hide show
  1. data/Dockerfile +2 -2
  2. data/app.py +57 -42
  3. data/main.py +17 -0
  4. data/memory.py +6 -4
  5. data/monitor.py +83 -71
data/Dockerfile CHANGED
@@ -2,7 +2,7 @@ FROM python:3.10-slim
2
 
3
  # إضافة حزم أساسية فقط
4
  RUN apt-get update && apt-get install -y \
5
- build-essential pkg-config \
6
  && rm -rf /var/lib/apt/lists/*
7
 
8
  # إضافة مستخدم غير root
@@ -25,4 +25,4 @@ RUN pip install --no-cache-dir -r requirements.txt
25
 
26
  USER user
27
 
28
- CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
2
 
3
  # إضافة حزم أساسية فقط
4
  RUN apt-get update && apt-get install -y \
5
+ build-essential pkg-config curl \
6
  && rm -rf /var/lib/apt/lists/*
7
 
8
  # إضافة مستخدم غير root
 
25
 
26
  USER user
27
 
28
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--log-config", "logging.conf"]
data/app.py CHANGED
@@ -1,7 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
  from fastapi import FastAPI, HTTPException
2
  from pydantic import BaseModel
3
  from llama_cpp import Llama
4
- import logging
5
  import os
6
  import threading
7
  from fastapi.middleware.cors import CORSMiddleware
@@ -9,13 +20,6 @@ from monitor import get_current_metrics, start_monitoring_thread
9
  from huggingface_hub import hf_hub_download
10
  from memory import get_history, save_history
11
 
12
- # إعداد السجل العام للتطبيق
13
- logging.basicConfig(
14
- level=logging.DEBUG,
15
- format="🪵 [%(asctime)s] [%(levelname)s] %(message)s",
16
- )
17
- logger = logging.getLogger(__name__)
18
-
19
  # إعدادات النموذج
20
  MODEL_REPO = "Qwen/Qwen2-1.5B-Instruct-GGUF"
21
  MODEL_FILE = "qwen2-1_5b-instruct-q6_k.gguf"
@@ -30,6 +34,31 @@ Follow these rules:
30
  3. Keep responses concise but complete
31
  4. For complex requests, ask clarifying questions<|im_end|>"""
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  # تحميل النموذج إذا لم يكن موجوداً
34
  def load_model():
35
  if not os.path.exists(MODEL_PATH):
@@ -60,9 +89,6 @@ def load_model():
60
  verbose=False
61
  )
62
 
63
- # بدء مراقبة الموارد
64
- start_monitoring_thread()
65
-
66
  # إنشاء تطبيق FastAPI
67
  app = FastAPI()
68
 
@@ -97,6 +123,9 @@ async def startup_event():
97
  logger.info("🚀 بدء تحميل النموذج في الخلفية...")
98
  model_loading_thread = threading.Thread(target=load_model_in_background, daemon=True)
99
  model_loading_thread.start()
 
 
 
100
 
101
  # نموذج الطلب والرد
102
  class ChatRequest(BaseModel):
@@ -107,31 +136,6 @@ class ChatResponse(BaseModel):
107
  response: str
108
  updated_history: list[list[str]]
109
 
110
- # تنسيق prompt الدردشة
111
- def format_chat(history, new_message):
112
- messages = [("system", SYSTEM_PROMPT)]
113
-
114
- for item in history[-8:]:
115
- if isinstance(item, list) and len(item) == 2:
116
- user_msg, bot_msg = item
117
- messages.append(("user", user_msg))
118
- messages.append(("assistant", bot_msg))
119
- else:
120
- logger.warning(f"⚠️ عنصر غير متوافق في التاريخ: {item}")
121
-
122
- messages.append(("user", new_message))
123
-
124
- formatted = []
125
- for role, content in messages:
126
- if role == "system":
127
- formatted.append(f"<|im_start|>system\n{content}<|im_end|>")
128
- elif role == "user":
129
- formatted.append(f"<|im_start|>user\n{content}<|im_end|>")
130
- else:
131
- formatted.append(f"<|im_start|>assistant\n{content}<|im_end|>")
132
- formatted.append("<|im_start|>assistant\n")
133
- return "\n".join(formatted)
134
-
135
  # التحقق من حالة النموذج
136
  @app.get("/model-status")
137
  def model_status():
@@ -157,7 +161,7 @@ def chat(req: ChatRequest):
157
  logger.info(f"📩 طلب جديد من جلسة {req.session_id}: {req.message}")
158
  try:
159
  history = get_history(req.session_id)
160
- logger.info(f"📜 التاريخ الحالي:\n{history}")
161
  prompt = format_chat(history, req.message)
162
 
163
  output = llm(
@@ -187,16 +191,27 @@ def chat(req: ChatRequest):
187
  def root():
188
  return {"message": "الخادم يعمل", "status": "ok"}
189
 
190
- # endpoint جديد: قراءة سجل مراقبة الموارد
191
  @app.get("/monitor-log")
192
  def read_monitor_log():
193
- log_path = "data/monitor.log"
 
 
 
 
194
  if not os.path.exists(log_path):
195
- raise HTTPException(status_code=404, detail="لم يتم العثور على ملف السجل")
 
 
 
 
196
  try:
197
- with open(log_path, encoding="utf-8") as f:
198
  content = f.read()
199
  return {"log": content}
200
  except Exception as e:
201
  logger.error(f"❌ فشل قراءة سجل المراقبة: {str(e)}")
202
- raise HTTPException(status_code=500, detail="حدث خطأ أثناء قراءة السجل")
 
 
 
 
1
+ # إعداد السجل العام للتطبيق
2
+ import logging
3
+ logging.basicConfig(
4
+ level=logging.DEBUG,
5
+ format="🪵 [%(asctime)s] [%(levelname)s] %(message)s",
6
+ handlers=[
7
+ logging.StreamHandler(),
8
+ logging.FileHandler("app.log")
9
+ ]
10
+ )
11
+ logger = logging.getLogger(__name__)
12
+
13
  from fastapi import FastAPI, HTTPException
14
  from pydantic import BaseModel
15
  from llama_cpp import Llama
 
16
  import os
17
  import threading
18
  from fastapi.middleware.cors import CORSMiddleware
 
20
  from huggingface_hub import hf_hub_download
21
  from memory import get_history, save_history
22
 
 
 
 
 
 
 
 
23
  # إعدادات النموذج
24
  MODEL_REPO = "Qwen/Qwen2-1.5B-Instruct-GGUF"
25
  MODEL_FILE = "qwen2-1_5b-instruct-q6_k.gguf"
 
34
  3. Keep responses concise but complete
35
  4. For complex requests, ask clarifying questions<|im_end|>"""
36
 
37
+ # تنسيق prompt الدردشة
38
+ def format_chat(history, new_message):
39
+ messages = [("system", SYSTEM_PROMPT)]
40
+
41
+ for item in history[-8:]:
42
+ if isinstance(item, list) and len(item) == 2:
43
+ user_msg, bot_msg = item
44
+ messages.append(("user", user_msg))
45
+ messages.append(("assistant", bot_msg))
46
+ else:
47
+ logger.warning(f"⚠️ عنصر غير متوافق في التاريخ: {item}")
48
+
49
+ messages.append(("user", new_message))
50
+
51
+ formatted = []
52
+ for role, content in messages:
53
+ if role == "system":
54
+ formatted.append(f"<|im_start|>system\n{content}<|im_end|>")
55
+ elif role == "user":
56
+ formatted.append(f"<|im_start|>user\n{content}<|im_end|>")
57
+ else:
58
+ formatted.append(f"<|im_start|>assistant\n{content}<|im_end|>")
59
+ formatted.append("<|im_start|>assistant\n")
60
+ return "\n".join(formatted)
61
+
62
  # تحميل النموذج إذا لم يكن موجوداً
63
  def load_model():
64
  if not os.path.exists(MODEL_PATH):
 
89
  verbose=False
90
  )
91
 
 
 
 
92
  # إنشاء تطبيق FastAPI
93
  app = FastAPI()
94
 
 
123
  logger.info("🚀 بدء تحميل النموذج في الخلفية...")
124
  model_loading_thread = threading.Thread(target=load_model_in_background, daemon=True)
125
  model_loading_thread.start()
126
+
127
+ # بدء مراقبة الموارد بعد إنشاء التطبيق
128
+ start_monitoring_thread()
129
 
130
  # نموذج الطلب والرد
131
  class ChatRequest(BaseModel):
 
136
  response: str
137
  updated_history: list[list[str]]
138
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  # التحقق من حالة النموذج
140
  @app.get("/model-status")
141
  def model_status():
 
161
  logger.info(f"📩 طلب جديد من جلسة {req.session_id}: {req.message}")
162
  try:
163
  history = get_history(req.session_id)
164
+ logger.info(f"📜 التاريخ الحالي: {len(history)} رسائل")
165
  prompt = format_chat(history, req.message)
166
 
167
  output = llm(
 
191
  def root():
192
  return {"message": "الخادم يعمل", "status": "ok"}
193
 
194
+ # endpoint قراءة سجل مراقبة الموارد
195
  @app.get("/monitor-log")
196
  def read_monitor_log():
197
+ import os
198
+ # استخدام المسار المطلق للتأكد من العثور على الملف
199
+ base_dir = os.path.dirname(os.path.abspath(__file__))
200
+ log_path = os.path.join(base_dir, "data", "monitor.log")
201
+
202
  if not os.path.exists(log_path):
203
+ raise HTTPException(
204
+ status_code=404,
205
+ detail=f"لم يتم العثور على ملف السجل في المسار: {log_path}"
206
+ )
207
+
208
  try:
209
+ with open(log_path, "r", encoding="utf-8") as f:
210
  content = f.read()
211
  return {"log": content}
212
  except Exception as e:
213
  logger.error(f"❌ فشل قراءة سجل المراقبة: {str(e)}")
214
+ raise HTTPException(
215
+ status_code=500,
216
+ detail=f"حدث خطأ أثناء قراءة السجل: {str(e)}"
217
+ )
data/main.py CHANGED
@@ -1,9 +1,26 @@
1
  import logging
 
 
 
 
 
 
 
 
 
 
 
2
  logger = logging.getLogger(__name__)
3
 
4
  try:
5
  from app import app
6
  logger.info("✅ استيراد app من app.py ناجح")
 
 
 
 
 
 
7
  except ImportError as e:
8
  logger.error(f"❌ فشل استيراد app من app.py: {str(e)}")
9
  raise
 
1
  import logging
2
+ import os
3
+
4
+ # إعداد السجل قبل الاستيرادات الأخرى
5
+ logging.basicConfig(
6
+ level=logging.DEBUG,
7
+ format="🪵 [%(asctime)s] [%(levelname)s] %(message)s",
8
+ handlers=[
9
+ logging.StreamHandler(),
10
+ logging.FileHandler("app.log")
11
+ ]
12
+ )
13
  logger = logging.getLogger(__name__)
14
 
15
  try:
16
  from app import app
17
  logger.info("✅ استيراد app من app.py ناجح")
18
+
19
+ # التأكد من وجود مجلد البيانات
20
+ data_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
21
+ os.makedirs(data_dir, exist_ok=True)
22
+ logger.info(f"📁 مجلد البيانات جاهز: {data_dir}")
23
+
24
  except ImportError as e:
25
  logger.error(f"❌ فشل استيراد app من app.py: {str(e)}")
26
  raise
data/memory.py CHANGED
@@ -5,7 +5,8 @@ import logging
5
 
6
  logger = logging.getLogger(__name__)
7
 
8
- DB_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
 
9
  DB_PATH = os.path.join(DB_DIR, "chat_memory.db")
10
 
11
  os.makedirs(DB_DIR, exist_ok=True)
@@ -21,6 +22,7 @@ try:
21
  ''')
22
  conn.commit()
23
  conn.close()
 
24
  except sqlite3.Error as e:
25
  logger.error(f"❌ خطأ في إنشاء قاعدة البيانات: {str(e)}")
26
 
@@ -39,7 +41,7 @@ def get_history(session_id: str):
39
  if isinstance(data, list) and all(isinstance(pair, list) and len(pair) == 2 for pair in data):
40
  return data
41
  else:
42
- logger.warning(f"⚠️ تم تجاهل تاريخ غير متوافق: {data}")
43
  return []
44
  except Exception as e:
45
  logger.error(f"❌ خطأ أثناء استرجاع التاريخ: {str(e)}")
@@ -52,8 +54,8 @@ def save_history(session_id: str, history: list):
52
  c.execute(
53
  "REPLACE INTO memory (session_id, history) VALUES (?, ?)",
54
  (session_id, json.dumps(history))
55
- )
56
  conn.commit()
57
  conn.close()
 
58
  except Exception as e:
59
- logger.error(f"❌ خطأ أثناء حفظ التاريخ: {str(e)}")
 
5
 
6
  logger = logging.getLogger(__name__)
7
 
8
+ base_dir = os.path.dirname(os.path.abspath(__file__))
9
+ DB_DIR = os.path.join(base_dir, "data")
10
  DB_PATH = os.path.join(DB_DIR, "chat_memory.db")
11
 
12
  os.makedirs(DB_DIR, exist_ok=True)
 
22
  ''')
23
  conn.commit()
24
  conn.close()
25
+ logger.info(f"✅ تم إنشاء/فتح قاعدة البيانات في: {DB_PATH}")
26
  except sqlite3.Error as e:
27
  logger.error(f"❌ خطأ في إنشاء قاعدة البيانات: {str(e)}")
28
 
 
41
  if isinstance(data, list) and all(isinstance(pair, list) and len(pair) == 2 for pair in data):
42
  return data
43
  else:
44
+ logger.warning(f"⚠️ تم تجاهل تاريخ غير متوافق: {type(data)}")
45
  return []
46
  except Exception as e:
47
  logger.error(f"❌ خطأ أثناء استرجاع التاريخ: {str(e)}")
 
54
  c.execute(
55
  "REPLACE INTO memory (session_id, history) VALUES (?, ?)",
56
  (session_id, json.dumps(history))
 
57
  conn.commit()
58
  conn.close()
59
+ logger.debug(f"💾 تم حفظ التاريخ للجلسة: {session_id}")
60
  except Exception as e:
61
+ logger.error(f"❌ خطأ أثناء حفظ التاريخ: {str(e)}")
data/monitor.py CHANGED
@@ -1,71 +1,83 @@
1
- import threading
2
- import time
3
- import psutil
4
- import logging
5
- from collections import deque
6
- import os
7
- import tracemalloc
8
-
9
- logger = logging.getLogger("monitor")
10
-
11
- # إعداد ملف السجل
12
- log_path = "data/monitor.log"
13
- os.makedirs("data", exist_ok=True)
14
- file_handler = logging.FileHandler(log_path)
15
- file_handler.setLevel(logging.DEBUG)
16
- formatter = logging.Formatter("📁 [%(asctime)s] [%(levelname)s] %(message)s")
17
- file_handler.setFormatter(formatter)
18
- logger.addHandler(file_handler)
19
-
20
- # تفعيل تتبع الذاكرة
21
- tracemalloc.start()
22
-
23
- # تخزين آخر 10 قياسات
24
- cpu_history = deque(maxlen=10)
25
- mem_history = deque(maxlen=10)
26
- current_metrics = {'cpu': 0, 'memory': 0}
27
-
28
- def monitor_resources():
29
- counter = 0
30
- logger.info("🔁 بدأ تشغيل monitor_resources()")
31
- while True:
32
- try:
33
- process = psutil.Process(os.getpid())
34
- mem_info = process.memory_info()
35
- cpu_percent = psutil.cpu_percent(interval=None)
36
- mem_percent = psutil.virtual_memory().percent
37
-
38
- current_metrics['cpu'] = cpu_percent
39
- current_metrics['memory'] = mem_percent
40
- cpu_history.append(cpu_percent)
41
- mem_history.append(mem_percent)
42
-
43
- logger.info(f"🧠 RSS: {mem_info.rss / 1024**2:.2f} MB | VMS: {mem_info.vms / 1024**2:.2f} MB")
44
- current, peak = tracemalloc.get_traced_memory()
45
- logger.info(f"📊 tracemalloc: Current = {current / 1024**2:.2f} MB | Peak = {peak / 1024**2:.2f} MB")
46
-
47
- counter += 1
48
- if counter % 12 == 0:
49
- snapshot = tracemalloc.take_snapshot()
50
- top_stats = snapshot.statistics('lineno')
51
- logger.info("📌 أعلى 5 أسطر استهلاكًا للذاكرة:")
52
- for stat in top_stats[:5]:
53
- logger.info(stat)
54
-
55
- except Exception as e:
56
- logger.exception(f"❌ استثناء في مراقبة الموارد: {str(e)}")
57
-
58
- time.sleep(5)
59
-
60
- def get_current_metrics():
61
- return {
62
- 'cpu': current_metrics['cpu'],
63
- 'memory': current_metrics['memory'],
64
- 'cpu_history': list(cpu_history),
65
- 'mem_history': list(mem_history)
66
- }
67
-
68
- def start_monitoring_thread():
69
- thread = threading.Thread(target=monitor_resources, daemon=True)
70
- thread.start()
71
- logger.info("✅ تم بدء مراقبة الموارد في خيط منفصل")
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+ import time
3
+ import psutil
4
+ import logging
5
+ from collections import deque
6
+ import os
7
+ import tracemalloc
8
+
9
+ # إنشاء logger خاص لهذه الوحدة
10
+ logger = logging.getLogger("monitor")
11
+ logger.setLevel(logging.DEBUG)
12
+
13
+ # إعداد ملف السجل
14
+ base_dir = os.path.dirname(os.path.abspath(__file__))
15
+ log_dir = os.path.join(base_dir, "data")
16
+ os.makedirs(log_dir, exist_ok=True)
17
+ log_path = os.path.join(log_dir, "monitor.log")
18
+
19
+ # إعداد handler لملف السجل
20
+ file_handler = logging.FileHandler(log_path)
21
+ file_handler.setLevel(logging.DEBUG)
22
+ formatter = logging.Formatter("📁 [%(asctime)s] [%(levelname)s] %(message)s")
23
+ file_handler.setFormatter(formatter)
24
+ logger.addHandler(file_handler)
25
+
26
+ # إعداد handler للوحدة الأساسية (اختياري)
27
+ stream_handler = logging.StreamHandler()
28
+ stream_handler.setFormatter(logging.Formatter("🪵 [%(asctime)s] [%(levelname)s] %(message)s"))
29
+ logger.addHandler(stream_handler)
30
+
31
+ # تفعيل تتبع الذاكرة
32
+ tracemalloc.start()
33
+ logger.info("🔍 بدأ تتبع الذاكرة")
34
+
35
+ # تخزين آخر 10 قياسات
36
+ cpu_history = deque(maxlen=10)
37
+ mem_history = deque(maxlen=10)
38
+ current_metrics = {'cpu': 0, 'memory': 0}
39
+
40
+ def monitor_resources():
41
+ counter = 0
42
+ logger.info("🔁 بدأ تشغيل مراقبة الموارد")
43
+ while True:
44
+ try:
45
+ process = psutil.Process(os.getpid())
46
+ mem_info = process.memory_info()
47
+ cpu_percent = psutil.cpu_percent(interval=0.5)
48
+ mem_percent = psutil.virtual_memory().percent
49
+
50
+ current_metrics['cpu'] = cpu_percent
51
+ current_metrics['memory'] = mem_percent
52
+ cpu_history.append(cpu_percent)
53
+ mem_history.append(mem_percent)
54
+
55
+ logger.info(f"🧠 الذاكرة - RSS: {mem_info.rss / 1024**2:.2f} MB | VMS: {mem_info.vms / 1024**2:.2f} MB")
56
+ current, peak = tracemalloc.get_traced_memory()
57
+ logger.info(f"📊 tracemalloc - الحالي: {current / 1024**2:.2f} MB | الأعلى: {peak / 1024**2:.2f} MB")
58
+
59
+ counter += 1
60
+ if counter % 12 == 0:
61
+ snapshot = tracemalloc.take_snapshot()
62
+ top_stats = snapshot.statistics('lineno')
63
+ logger.info("📌 أعلى 5 أسطر استهلاكًا للذاكرة:")
64
+ for stat in top_stats[:5]:
65
+ logger.info(f" {stat}")
66
+
67
+ except Exception as e:
68
+ logger.exception(f"❌ استثناء في مراقبة الموارد: {str(e)}")
69
+
70
+ time.sleep(5)
71
+
72
+ def get_current_metrics():
73
+ return {
74
+ 'cpu': current_metrics['cpu'],
75
+ 'memory': current_metrics['memory'],
76
+ 'cpu_history': list(cpu_history),
77
+ 'mem_history': list(mem_history)
78
+ }
79
+
80
+ def start_monitoring_thread():
81
+ thread = threading.Thread(target=monitor_resources, daemon=True)
82
+ thread.start()
83
+ logger.info("✅ تم بدء مراقبة الموارد في خيط منفصل")