AhmadA82 commited on
Commit
d614e8c
·
verified ·
1 Parent(s): caceba9
Files changed (7) hide show
  1. Dockerfile +2 -4
  2. app.py +92 -71
  3. docker-compose.yml +0 -1
  4. main.py +17 -0
  5. memory.py +49 -30
  6. monitor.py +50 -10
  7. requirements.txt +3 -1
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
@@ -11,7 +11,6 @@ WORKDIR /home/user/app
11
  COPY --chown=user . .
12
 
13
  # مجلد التخزين المؤقت للنموذج
14
- RUN mkdir -p /home/user/app/data/cache && chown -R user:user /home/user/app/data
15
  RUN mkdir -p /home/user/app/data && chown -R user:user /home/user/app/data
16
 
17
  # إنشاء البيئة الافتراضية
@@ -24,7 +23,6 @@ ENV CMAKE_ARGS="-DGGML_CUDA=off"
24
  RUN pip install --upgrade pip
25
  RUN pip install --no-cache-dir -r requirements.txt
26
 
27
- # إضافة مكتبة SQLite (مضمنة في بايثون)
28
  USER user
29
 
30
- 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
 
11
  COPY --chown=user . .
12
 
13
  # مجلد التخزين المؤقت للنموذج
 
14
  RUN mkdir -p /home/user/app/data && chown -R user:user /home/user/app/data
15
 
16
  # إنشاء البيئة الافتراضية
 
23
  RUN pip install --upgrade pip
24
  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", "--log-config", "logging.conf"]
app.py CHANGED
@@ -1,28 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
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
8
  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.INFO,
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"
22
  MODEL_PATH = f"/home/user/app/data/cache/{MODEL_FILE}"
23
  HF_TOKEN = os.getenv("HF_TOKEN")
24
 
25
- # System Prompt محسن للنموذج الجديد
26
  SYSTEM_PROMPT = """<|im_start|>system
27
  You are Qwen2, created by Alibaba Cloud. You are a helpful assistant specialized in AI development.
28
  Follow these rules:
@@ -31,7 +34,32 @@ Follow these rules:
31
  3. Keep responses concise but complete
32
  4. For complex requests, ask clarifying questions<|im_end|>"""
33
 
34
- # تحميل النموذج
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  def load_model():
36
  if not os.path.exists(MODEL_PATH):
37
  os.makedirs("/home/user/app/data/cache", exist_ok=True)
@@ -48,30 +76,23 @@ def load_model():
48
  logger.error(f"❌ فشل تحميل النموذج: {str(e)}")
49
  raise RuntimeError("فشل تحميل النموذج") from e
50
 
51
- # تأكيد وجود النموذج
52
- if os.path.exists(MODEL_PATH):
53
- logger.info(f"✅ النموذج موجود: {MODEL_PATH}")
54
- else:
55
- logger.error(f"❌ النموذج غير موجود: {MODEL_PATH}")
56
  raise RuntimeError("النموذج غير موجود بعد التحميل")
57
 
58
- # تحميل النموذج مع إعدادات محسنة
59
  return Llama(
60
  model_path=MODEL_PATH,
61
- n_ctx=2048,
62
- n_threads=4,
63
  n_gpu_layers=0,
64
- n_batch=512,
65
- use_mlock=True,
66
  verbose=False
67
  )
68
 
69
- # بدء مراقبة الموارد
70
- start_monitoring_thread()
71
-
72
  app = FastAPI()
73
 
74
- # تحميل النموذج بشكل غير متزامن
75
  llm = None
76
  model_loading_thread = None
77
 
@@ -80,8 +101,6 @@ def load_model_in_background():
80
  try:
81
  llm = load_model()
82
  logger.info("✅ تم تحميل النموذج بنجاح")
83
-
84
- # اختبار النموذج مع System Prompt الجديد
85
  test_prompt = format_chat([], "مرحبا، كيف يمكنك المساعدة؟")
86
  test_output = llm(test_prompt, max_tokens=50)['choices'][0]['text'].strip()
87
  logger.info(f"✅ اختبار النموذج ناجح: {test_output[:100]}...")
@@ -89,6 +108,7 @@ def load_model_in_background():
89
  logger.error(f"❌ فشل تحميل النموذج: {str(e)}")
90
  logger.exception(e)
91
 
 
92
  app.add_middleware(
93
  CORSMiddleware,
94
  allow_origins=["*"],
@@ -103,71 +123,47 @@ async def startup_event():
103
  logger.info("🚀 بدء تحميل النموذج في الخلفية...")
104
  model_loading_thread = threading.Thread(target=load_model_in_background, daemon=True)
105
  model_loading_thread.start()
 
 
 
106
 
 
107
  class ChatRequest(BaseModel):
108
- session_id: str # حقل جديد
109
  message: str
110
- # تمت إزالة history لأنه سيتم تخزينه على الخادم
111
 
112
  class ChatResponse(BaseModel):
113
  response: str
114
  updated_history: list[list[str]]
115
 
116
- def format_chat(history, new_message):
117
- """تنسيق المحادثة حسب التوصيات مع دعم Chat Template"""
118
- messages = []
119
- messages.append(("system", SYSTEM_PROMPT))
120
-
121
- # الحفاظ على آخر 8 تبادلات فقط (إدارة السياق)
122
- for user_msg, bot_msg in history[-8:]:
123
- messages.append(("user", user_msg))
124
- messages.append(("assistant", bot_msg))
125
-
126
- messages.append(("user", new_message))
127
-
128
- # بناء الـ prompt بتنسيق الدردشة الموصى به
129
- formatted = []
130
- for role, content in messages:
131
- if role == "system":
132
- formatted.append(f"<|im_start|>system\n{content}<|im_end|>")
133
- elif role == "user":
134
- formatted.append(f"<|im_start|>user\n{content}<|im_end|>")
135
- else:
136
- formatted.append(f"<|im_start|>assistant\n{content}<|im_end|>")
137
-
138
- # إضافة بداية رد المساعد
139
- formatted.append("<|im_start|>assistant\n")
140
- return "\n".join(formatted)
141
-
142
  @app.get("/model-status")
143
  def model_status():
144
  status = "loaded" if llm else "loading"
145
  thread_alive = model_loading_thread.is_alive() if model_loading_thread else False
146
  return {"status": status, "thread_alive": thread_alive}
147
 
 
148
  @app.get("/metrics")
149
  def read_metrics():
150
- logger.debug("📊 تم طلب حالة الموارد")
151
  return get_current_metrics()
152
 
 
153
  @app.post("/chat", response_model=ChatResponse)
154
  def chat(req: ChatRequest):
155
  global llm
156
-
157
  if not llm:
158
  if model_loading_thread and model_loading_thread.is_alive():
159
  raise HTTPException(status_code=503, detail="النموذج قيد التحميل، الرجاء المحاولة لاحقاً")
160
  else:
161
  raise HTTPException(status_code=500, detail="فشل تحميل النموذج")
162
-
163
  logger.info(f"📩 طلب جديد من جلسة {req.session_id}: {req.message}")
164
  try:
165
- # استرجاع تاريخ المحادثة من قاعدة البيانات
166
  history = get_history(req.session_id)
 
167
  prompt = format_chat(history, req.message)
168
- logger.debug(f"📝 prompt المُرسل:\n{prompt[:300]}...")
169
-
170
- # توليد الرد
171
  output = llm(
172
  prompt,
173
  max_tokens=512,
@@ -176,21 +172,46 @@ def chat(req: ChatRequest):
176
  stop=["<|im_end|>", "<|im_start|>"],
177
  echo=False
178
  )
179
-
180
  reply = output['choices'][0]['text'].strip()
181
  logger.info(f"🤖 رد النموذج: {reply[:100]}...")
182
-
183
- # تحديث التاريخ مع الحفاظ على الطول المعقول
184
  updated_history = (history + [[req.message, reply]])[-8:]
185
-
186
- # حفظ التاريخ الجديد
187
  save_history(req.session_id, updated_history)
188
-
189
  return ChatResponse(response=reply, updated_history=updated_history)
 
190
  except Exception as e:
191
  logger.error(f"❌ خطأ أثناء المعالجة: {str(e)}")
 
192
  raise HTTPException(status_code=500, detail="حدث خطأ أثناء توليد الرد")
193
 
 
194
  @app.get("/")
195
  def root():
196
- return {"message": "الخادم يعمل", "status": "ok"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
19
  from monitor import get_current_metrics, start_monitoring_thread
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"
26
  MODEL_PATH = f"/home/user/app/data/cache/{MODEL_FILE}"
27
  HF_TOKEN = os.getenv("HF_TOKEN")
28
 
 
29
  SYSTEM_PROMPT = """<|im_start|>system
30
  You are Qwen2, created by Alibaba Cloud. You are a helpful assistant specialized in AI development.
31
  Follow these rules:
 
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):
65
  os.makedirs("/home/user/app/data/cache", exist_ok=True)
 
76
  logger.error(f"❌ فشل تحميل النموذج: {str(e)}")
77
  raise RuntimeError("فشل تحميل النموذج") from e
78
 
79
+ if not os.path.exists(MODEL_PATH):
 
 
 
 
80
  raise RuntimeError("النموذج غير موجود بعد التحميل")
81
 
 
82
  return Llama(
83
  model_path=MODEL_PATH,
84
+ n_ctx=1024,
85
+ n_threads=2,
86
  n_gpu_layers=0,
87
+ n_batch=64,
88
+ use_mlock=False,
89
  verbose=False
90
  )
91
 
92
+ # إنشاء تطبيق FastAPI
 
 
93
  app = FastAPI()
94
 
95
+ # تحميل النموذج في الخلفية
96
  llm = None
97
  model_loading_thread = None
98
 
 
101
  try:
102
  llm = load_model()
103
  logger.info("✅ تم تحميل النموذج بنجاح")
 
 
104
  test_prompt = format_chat([], "مرحبا، كيف يمكنك المساعدة؟")
105
  test_output = llm(test_prompt, max_tokens=50)['choices'][0]['text'].strip()
106
  logger.info(f"✅ اختبار النموذج ناجح: {test_output[:100]}...")
 
108
  logger.error(f"❌ فشل تحميل النموذج: {str(e)}")
109
  logger.exception(e)
110
 
111
+ # تمكين CORS
112
  app.add_middleware(
113
  CORSMiddleware,
114
  allow_origins=["*"],
 
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):
132
+ session_id: str
133
  message: str
 
134
 
135
  class ChatResponse(BaseModel):
136
  response: str
137
  updated_history: list[list[str]]
138
 
139
+ # التحقق من حالة النموذج
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  @app.get("/model-status")
141
  def model_status():
142
  status = "loaded" if llm else "loading"
143
  thread_alive = model_loading_thread.is_alive() if model_loading_thread else False
144
  return {"status": status, "thread_alive": thread_alive}
145
 
146
+ # endpoint عرض المقاييس
147
  @app.get("/metrics")
148
  def read_metrics():
 
149
  return get_current_metrics()
150
 
151
+ # الدردشة الرئيسية
152
  @app.post("/chat", response_model=ChatResponse)
153
  def chat(req: ChatRequest):
154
  global llm
 
155
  if not llm:
156
  if model_loading_thread and model_loading_thread.is_alive():
157
  raise HTTPException(status_code=503, detail="النموذج قيد التحميل، الرجاء المحاولة لاحقاً")
158
  else:
159
  raise HTTPException(status_code=500, detail="فشل تحميل النموذج")
160
+
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(
168
  prompt,
169
  max_tokens=512,
 
172
  stop=["<|im_end|>", "<|im_start|>"],
173
  echo=False
174
  )
175
+
176
  reply = output['choices'][0]['text'].strip()
177
  logger.info(f"🤖 رد النموذج: {reply[:100]}...")
178
+
 
179
  updated_history = (history + [[req.message, reply]])[-8:]
 
 
180
  save_history(req.session_id, updated_history)
181
+
182
  return ChatResponse(response=reply, updated_history=updated_history)
183
+
184
  except Exception as e:
185
  logger.error(f"❌ خطأ أثناء المعالجة: {str(e)}")
186
+ logger.exception(e)
187
  raise HTTPException(status_code=500, detail="حدث خطأ أثناء توليد الرد")
188
 
189
+ # root
190
  @app.get("/")
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
+ )
docker-compose.yml CHANGED
@@ -10,7 +10,6 @@ services:
10
  - "7860:7860"
11
  volumes:
12
  - ./data:/home/user/app/data
13
- - ./db:/home/user/app
14
  environment:
15
  - HF_TOKEN=${HF_TOKEN}
16
  restart: unless-stopped
 
10
  - "7860:7860"
11
  volumes:
12
  - ./data:/home/user/app/data
 
13
  environment:
14
  - HF_TOKEN=${HF_TOKEN}
15
  restart: unless-stopped
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
memory.py CHANGED
@@ -1,42 +1,61 @@
1
- # memory.py
2
  import sqlite3
3
  import json
4
  import os
 
5
 
6
- DB_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "data")
7
- DB_PATH = os.path.join(DB_DIR, "chat_memory.db") # مسار جديد
8
 
9
- # إنشاء المجلد إذا لم يكن موجوداً
10
- os.makedirs(DB_DIR, exist_ok=True)
 
11
 
12
- # إنشاء قاعدة البيانات إذا لم تكن موجودة
13
- conn = sqlite3.connect(DB_PATH)
14
- c = conn.cursor()
15
- c.execute('''
16
- CREATE TABLE IF NOT EXISTS memory (
17
- session_id TEXT PRIMARY KEY,
18
- history TEXT
19
- )
20
- ''')
21
- conn.commit()
22
- conn.close()
23
 
24
- def get_history(session_id: str):
25
- """استرجاع تاريخ المحادثة من قاعدة البيانات"""
26
  conn = sqlite3.connect(DB_PATH)
27
  c = conn.cursor()
28
- c.execute("SELECT history FROM memory WHERE session_id = ?", (session_id,))
29
- row = c.fetchone()
 
 
 
 
 
30
  conn.close()
31
- return json.loads(row[0]) if row else []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  def save_history(session_id: str, history: list):
34
- """حفظ تاريخ المحادثة في قاعدة البيانات"""
35
- conn = sqlite3.connect(DB_PATH)
36
- c = conn.cursor()
37
- c.execute(
38
- "REPLACE INTO memory (session_id, history) VALUES (?, ?)",
39
- (session_id, json.dumps(history))
40
- )
41
- conn.commit()
42
- conn.close()
 
 
 
 
1
  import sqlite3
2
  import json
3
  import os
4
+ import logging
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)
 
 
 
 
 
 
 
 
 
 
13
 
14
+ try:
 
15
  conn = sqlite3.connect(DB_PATH)
16
  c = conn.cursor()
17
+ c.execute('''
18
+ CREATE TABLE IF NOT EXISTS memory (
19
+ session_id TEXT PRIMARY KEY,
20
+ history TEXT
21
+ )
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
+
29
+ def get_history(session_id: str):
30
+ try:
31
+ conn = sqlite3.connect(DB_PATH)
32
+ c = conn.cursor()
33
+ c.execute("SELECT history FROM memory WHERE session_id = ?", (session_id,))
34
+ row = c.fetchone()
35
+ conn.close()
36
+
37
+ if not row or not isinstance(row[0], str):
38
+ return []
39
+
40
+ data = json.loads(row[0])
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)}")
48
+ return []
49
 
50
  def save_history(session_id: str, history: list):
51
+ try:
52
+ conn = sqlite3.connect(DB_PATH)
53
+ c = conn.cursor()
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)}")
monitor.py CHANGED
@@ -4,31 +4,72 @@ import psutil
4
  import logging
5
  from collections import deque
6
  import os
 
7
 
 
8
  logger = logging.getLogger("monitor")
 
9
 
10
- # تخزين آخر 60 قياس (60 ثانية)
11
- cpu_history = deque(maxlen=60)
12
- mem_history = deque(maxlen=60)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  current_metrics = {'cpu': 0, 'memory': 0}
14
 
15
  def monitor_resources():
16
- """تراقب استخدام وحدة المعالجة المركزية والذاكرة كل ثانية"""
 
17
  while True:
18
  try:
19
- cpu_percent = psutil.cpu_percent(interval=None)
 
 
20
  mem_percent = psutil.virtual_memory().percent
 
21
  current_metrics['cpu'] = cpu_percent
22
  current_metrics['memory'] = mem_percent
23
  cpu_history.append(cpu_percent)
24
  mem_history.append(mem_percent)
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  except Exception as e:
26
- logger.error(f"خطأ في مراقبة الموارد: {str(e)}")
27
-
28
- time.sleep(1)
29
 
30
  def get_current_metrics():
31
- """ترجع القيم الحالية للموارد"""
32
  return {
33
  'cpu': current_metrics['cpu'],
34
  'memory': current_metrics['memory'],
@@ -37,7 +78,6 @@ def get_current_metrics():
37
  }
38
 
39
  def start_monitoring_thread():
40
- """تشغيل خيط المراقبة الخلفي"""
41
  thread = threading.Thread(target=monitor_resources, daemon=True)
42
  thread.start()
43
  logger.info("✅ تم بدء مراقبة الموارد في خيط منفصل")
 
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'],
 
78
  }
79
 
80
  def start_monitoring_thread():
 
81
  thread = threading.Thread(target=monitor_resources, daemon=True)
82
  thread.start()
83
  logger.info("✅ تم بدء مراقبة الموارد في خيط منفصل")
requirements.txt CHANGED
@@ -2,4 +2,6 @@ fastapi==0.115.1
2
  uvicorn==0.35.0
3
  huggingface_hub==0.23.0
4
  llama-cpp-python==0.2.77
5
- psutil==5.9.8
 
 
 
2
  uvicorn==0.35.0
3
  huggingface_hub==0.23.0
4
  llama-cpp-python==0.2.77
5
+ psutil==5.9.8
6
+ aiosqlite==0.20.0
7
+ python-multipart==0.0.9