AhmadA82 commited on
Commit
b0a5199
·
verified ·
1 Parent(s): 7051ed8
Files changed (4) hide show
  1. .dockerignore +8 -0
  2. Dockerfile +16 -17
  3. app.py +48 -44
  4. docker-compose.yml +13 -33
.dockerignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ .env
2
+ *.pyc
3
+ __pycache__/
4
+ data/
5
+ .cache/
6
+ .vscode/
7
+ *.log
8
+ tmp/
Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- # صورة بايثون خفيفة الحجم
2
  FROM python:3.10-slim
3
 
4
  # تثبيت الأدوات الأساسية
@@ -7,35 +7,34 @@ RUN apt-get update && apt-get install -y \
7
  cmake \
8
  gcc \
9
  g++ \
 
10
  && rm -rf /var/lib/apt/lists/*
11
 
12
- # إنشاء مستخدم غير root لتجنب التحذيرات
13
  RUN useradd -m -u 1000 user
14
 
15
- # مجلد العمل الافتراضي
16
  WORKDIR /home/user/app
17
-
18
- # نسخ الملفات إلى الحاوية وتغيير الملكية
19
  COPY --chown=user . .
20
 
21
- # إنشاء مجلد التخزين المؤقت للنموذج
22
- RUN mkdir -p /home/user/app/data/cache && chown -R user:user /home/user/app/data/cache
23
-
24
- # إنشاء مجلد البيانات وتغيير صلاحياته
25
- RUN mkdir -p /home/user/app/data && chown -R user:user /home/user/app/data
26
 
27
- # تصحيح صلاحيات مجلد /tmp
28
- RUN chown -R user:user /tmp
29
-
30
- # إضافة هذه الخطوة قبل تثبيت المتطلبات
31
  RUN python -m venv /home/user/venv
32
  ENV PATH="/home/user/venv/bin:$PATH"
33
 
34
- # تحديث pip وتثبيت المتطلبات مع التحقق
35
  RUN pip install --upgrade pip && \
36
  pip install -r requirements.txt && \
37
  echo "✅ تثبيت المتطلبات ناجح" > /tmp/requirements_install.log || \
38
  echo "❌ فشل تثبيت المتطلبات" > /tmp/requirements_install.log
39
 
40
- # تشغيل التطبيق باستخدام Uvicorn من ملف main.py
41
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
 
 
1
+ # استخدام صورة بايثون خفيفة
2
  FROM python:3.10-slim
3
 
4
  # تثبيت الأدوات الأساسية
 
7
  cmake \
8
  gcc \
9
  g++ \
10
+ python3-dev \
11
  && rm -rf /var/lib/apt/lists/*
12
 
13
+ # إنشاء مستخدم غير root
14
  RUN useradd -m -u 1000 user
15
 
16
+ # إنشاء مجلد العمل وتعيين الصلاحيات
17
  WORKDIR /home/user/app
 
 
18
  COPY --chown=user . .
19
 
20
+ # إنشاء المجلدات المطلوبة وتعيين الصلاحيات
21
+ RUN mkdir -p /home/user/app/data/cache && \
22
+ mkdir -p /home/user/app/data && \
23
+ chown -R user:user /home/user/app/data && \
24
+ chown -R user:user /tmp
25
 
26
+ # تفعيل بيئة افتراضية في home وليس داخل المشروع
 
 
 
27
  RUN python -m venv /home/user/venv
28
  ENV PATH="/home/user/venv/bin:$PATH"
29
 
30
+ # تثبيت المتطلبات
31
  RUN pip install --upgrade pip && \
32
  pip install -r requirements.txt && \
33
  echo "✅ تثبيت المتطلبات ناجح" > /tmp/requirements_install.log || \
34
  echo "❌ فشل تثبيت المتطلبات" > /tmp/requirements_install.log
35
 
36
+ # استخدام المستخدم غير root
37
+ USER user
38
+
39
+ # تشغيل التطبيق
40
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
app.py CHANGED
@@ -1,15 +1,18 @@
1
- from fastapi import FastAPI
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
-
9
- # استيراد وحدة المراقبة المعدلة
10
  from monitor import get_current_metrics, start_monitoring_thread
 
 
 
 
 
11
 
12
- # 🔧 إعداد السجل
13
  logging.basicConfig(
14
  level=logging.DEBUG,
15
  format="🪵 [%(asctime)s] [%(levelname)s] %(message)s",
@@ -19,60 +22,65 @@ logger = logging.getLogger(__name__)
19
  MODEL_REPO = "QuantFactory/Qwen2.5-7B-Instruct-GGUF"
20
  MODEL_FILE = "Qwen2.5-7B-Instruct.Q4_K_M.gguf"
21
  MODEL_PATH = f"/home/user/app/data/cache/{MODEL_FILE}"
 
22
 
 
23
  if not os.path.exists(MODEL_PATH):
24
- token=os.getenv("HF_TOKEN")
25
- from huggingface_hub import hf_hub_download
26
  os.makedirs("/home/user/app/data/cache", exist_ok=True)
27
  logger.info("📦 تحميل النموذج من Hugging Face Hub...")
28
- hf_hub_download(
29
- repo_id=MODEL_REPO,
30
- filename=MODEL_FILE,
31
- local_dir="/home/user/app/data/cache",
32
- )
 
 
 
 
 
33
 
 
34
  if os.path.exists(MODEL_PATH):
35
  logger.info(f"✅ النموذج موجود: {MODEL_PATH}")
36
  else:
37
  logger.error(f"❌ النموذج غير موجود: {MODEL_PATH}")
 
38
 
39
  # تحميل النموذج
40
  llm = Llama(
41
  model_path=MODEL_PATH,
42
- n_ctx=2048, # تقليل السياق
43
- n_threads=4, # تقليل عدد الخيوط
44
  n_gpu_layers=0,
45
- n_batch=512, # حجم الدُفعة الأمثل
46
- use_mlock=True, # منع تبديل الذاكرة
47
  verbose=False
48
  )
49
 
50
- # 🔍 اختبار النموذج بعد التحميل مباشرة
51
  try:
52
- # اختبار النموذج بطلب بسيط
53
- logger.info("🔍 يجري اختبار النموذج...")
54
  test_output = llm("مرحبا", max_tokens=10)
55
  logger.info(f"✅ اختبار النموذج ناجح: {test_output}")
56
  except Exception as e:
57
  logger.error(f"❌ فشل اختبار النموذج: {str(e)}")
58
- raise RuntimeError("فشل تحميل النموذج") from e
59
 
60
  SYSTEM_PROMPT = """<|im_start|>system
61
  You are Qwen, created by Alibaba Cloud. You are an AI development assistant. Follow these rules:
62
  1. If request is simple (single file, <50 lines), handle it directly
63
- 2. For complex requests (multiple files, >50 lines), just respond with \"CODER\"
64
  3. Always check code for errors before sending
65
  4. Never execute unsafe code<|im_end|>"""
66
 
67
- # بدء خيط المراقبة
68
  start_monitoring_thread()
69
 
70
- # API setup
71
  app = FastAPI()
72
 
73
  app.add_middleware(
74
  CORSMiddleware,
75
- allow_origins=["*"], # يمكنك تخصيص الدومين لاحقًا
76
  allow_credentials=True,
77
  allow_methods=["*"],
78
  allow_headers=["*"],
@@ -84,11 +92,11 @@ async def startup_event():
84
 
85
  class ChatRequest(BaseModel):
86
  message: str
87
- history: list[tuple[str, str]] = [] # تغيير إلى tuple
88
 
89
  class ChatResponse(BaseModel):
90
  response: str
91
- updated_history: list[tuple[str, str]]
92
 
93
  def format_prompt(messages):
94
  formatted = []
@@ -99,7 +107,7 @@ def format_prompt(messages):
99
  formatted.append(f"<|im_start|>user\n{content}<|im_end|>")
100
  else:
101
  formatted.append(f"<|im_start|>assistant\n{content}<|im_end|>")
102
- formatted.append("<|im_start|>assistant\n") # إضافة بداية رد المساعد
103
  return "\n".join(formatted)
104
 
105
  @app.get("/metrics")
@@ -110,20 +118,18 @@ def read_metrics():
110
  @app.post("/chat", response_model=ChatResponse)
111
  def chat(req: ChatRequest):
112
  logger.info(f"📩 طلب جديد: {req.message}")
113
-
114
- # بناء الرسائل بشكل صحيح
115
- messages = [("system", SYSTEM_PROMPT)]
116
- for user_msg, bot_msg in req.history:
117
- messages.append(("user", user_msg))
118
- messages.append(("assistant", bot_msg))
119
- messages.append(("user", req.message))
120
 
121
- prompt = format_prompt(messages)
122
- logger.debug(f"📝 prompt المُرسل للنموذج:\n{prompt[:500]}...")
123
 
124
- try:
125
  import gc
126
- gc.collect() # تفعيل جامع القمامة
127
  output = llm(
128
  prompt,
129
  max_tokens=1024,
@@ -134,14 +140,12 @@ def chat(req: ChatRequest):
134
  )
135
  reply = output["choices"][0]["text"].strip()
136
  logger.info(f"🤖 رد النموذج: {reply}")
 
 
137
  except Exception as e:
138
- logger.error(f"حدث خطأ: {str(e)}")
139
- raise
140
-
141
- # إصلاح تحديث السجل
142
- updated_history = req.history + [(req.message, reply)]
143
- return ChatResponse(response=reply, updated_history=updated_history)
144
 
145
  @app.get("/")
146
  def root():
147
- return {"message": "الخادم يعمل", "status": "ok"}
 
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 dotenv import load_dotenv
11
+
12
+ # تحميل متغيرات البيئة
13
+ load_dotenv()
14
 
15
+ # إعداد السجل
16
  logging.basicConfig(
17
  level=logging.DEBUG,
18
  format="🪵 [%(asctime)s] [%(levelname)s] %(message)s",
 
22
  MODEL_REPO = "QuantFactory/Qwen2.5-7B-Instruct-GGUF"
23
  MODEL_FILE = "Qwen2.5-7B-Instruct.Q4_K_M.gguf"
24
  MODEL_PATH = f"/home/user/app/data/cache/{MODEL_FILE}"
25
+ HF_TOKEN = os.getenv("HF_TOKEN")
26
 
27
+ # تحميل النموذج إذا لم يكن موجودًا
28
  if not os.path.exists(MODEL_PATH):
 
 
29
  os.makedirs("/home/user/app/data/cache", exist_ok=True)
30
  logger.info("📦 تحميل النموذج من Hugging Face Hub...")
31
+ try:
32
+ hf_hub_download(
33
+ repo_id=MODEL_REPO,
34
+ filename=MODEL_FILE,
35
+ local_dir="/home/user/app/data/cache",
36
+ token=HF_TOKEN,
37
+ )
38
+ except Exception as e:
39
+ logger.error(f"❌ فشل تحميل النموذج: {str(e)}")
40
+ raise RuntimeError("فشل تحميل النموذج") from e
41
 
42
+ # تأكيد وجود النموذج
43
  if os.path.exists(MODEL_PATH):
44
  logger.info(f"✅ النموذج موجود: {MODEL_PATH}")
45
  else:
46
  logger.error(f"❌ النموذج غير موجود: {MODEL_PATH}")
47
+ raise RuntimeError("النموذج غير موجود بعد التحميل")
48
 
49
  # تحميل النموذج
50
  llm = Llama(
51
  model_path=MODEL_PATH,
52
+ n_ctx=2048,
53
+ n_threads=4,
54
  n_gpu_layers=0,
55
+ n_batch=512,
56
+ use_mlock=True,
57
  verbose=False
58
  )
59
 
60
+ # اختبار النموذج مباشرة
61
  try:
62
+ logger.info("🔍 اختبار النموذج...")
 
63
  test_output = llm("مرحبا", max_tokens=10)
64
  logger.info(f"✅ اختبار النموذج ناجح: {test_output}")
65
  except Exception as e:
66
  logger.error(f"❌ فشل اختبار النموذج: {str(e)}")
67
+ raise RuntimeError("فشل اختبار النموذج") from e
68
 
69
  SYSTEM_PROMPT = """<|im_start|>system
70
  You are Qwen, created by Alibaba Cloud. You are an AI development assistant. Follow these rules:
71
  1. If request is simple (single file, <50 lines), handle it directly
72
+ 2. For complex requests (multiple files, >50 lines), just respond with "CODER"
73
  3. Always check code for errors before sending
74
  4. Never execute unsafe code<|im_end|>"""
75
 
76
+ # بدء مراقبة الموارد
77
  start_monitoring_thread()
78
 
 
79
  app = FastAPI()
80
 
81
  app.add_middleware(
82
  CORSMiddleware,
83
+ allow_origins=["*"], # يمكن تخصيصه لاحقًا
84
  allow_credentials=True,
85
  allow_methods=["*"],
86
  allow_headers=["*"],
 
92
 
93
  class ChatRequest(BaseModel):
94
  message: str
95
+ history: list[list[str]] = [] # يجب أن تكون قائمة من القوائم لتمثيل JSON
96
 
97
  class ChatResponse(BaseModel):
98
  response: str
99
+ updated_history: list[list[str]]
100
 
101
  def format_prompt(messages):
102
  formatted = []
 
107
  formatted.append(f"<|im_start|>user\n{content}<|im_end|>")
108
  else:
109
  formatted.append(f"<|im_start|>assistant\n{content}<|im_end|>")
110
+ formatted.append("<|im_start|>assistant\n")
111
  return "\n".join(formatted)
112
 
113
  @app.get("/metrics")
 
118
  @app.post("/chat", response_model=ChatResponse)
119
  def chat(req: ChatRequest):
120
  logger.info(f"📩 طلب جديد: {req.message}")
121
+ try:
122
+ messages = [("system", SYSTEM_PROMPT)]
123
+ for user_msg, bot_msg in req.history:
124
+ messages.append(("user", user_msg))
125
+ messages.append(("assistant", bot_msg))
126
+ messages.append(("user", req.message))
 
127
 
128
+ prompt = format_prompt(messages)
129
+ logger.debug(f"📝 prompt المُرسل:\n{prompt[:300]}...")
130
 
 
131
  import gc
132
+ gc.collect()
133
  output = llm(
134
  prompt,
135
  max_tokens=1024,
 
140
  )
141
  reply = output["choices"][0]["text"].strip()
142
  logger.info(f"🤖 رد النموذج: {reply}")
143
+ updated_history = req.history + [[req.message, reply]]
144
+ return ChatResponse(response=reply, updated_history=updated_history)
145
  except Exception as e:
146
+ logger.error(f" خطأ أثناء المعالجة: {str(e)}")
147
+ raise HTTPException(status_code=500, detail="حدث خطأ أثناء توليد الرد")
 
 
 
 
148
 
149
  @app.get("/")
150
  def root():
151
+ return {"message": "الخادم يعمل", "status": "ok"}
docker-compose.yml CHANGED
@@ -1,43 +1,23 @@
1
- version: '3.8'
2
 
3
  services:
4
  ai-assistant:
5
- build: .
 
 
 
6
  ports:
7
  - "7860:7860"
 
 
8
  environment:
9
  - HF_TOKEN=${HF_TOKEN}
10
  - GOOGLE_DRIVE_FOLDER_ID=${GOOGLE_DRIVE_FOLDER_ID}
11
  - GITHUB_REPO=${GITHUB_REPO}
12
  - GITHUB_TOKEN=${GITHUB_TOKEN}
13
- - GOOGLE_SERVICE_ACCOUNT_JSON=${GOOGLE_SERVICE_ACCOUNT_JSON}
14
- volumes:
15
- - ./data:/home/user/app/data/cache # تصحيح المسار ليتوافق مع Dockerfile
16
- deploy:
17
- resources:
18
- limits:
19
- memory: 8G
20
- reservations:
21
- memory: 6G
22
- command: ["sh", "-c", "echo '✅ تم بدء الخدمة مع استهلاك موارد: $(cat /sys/fs/cgroup/memory/memory.usage_in_bytes)' > /tmp/resource_check.log && uvicorn main:app --host 0.0.0.0 --port 7860"]
23
-
24
- prometheus:
25
- image: prom/prometheus
26
- volumes:
27
- - ./prometheus.yml:/etc/prometheus/prometheus.yml
28
- ports:
29
- - "9090:9090"
30
- command: ["--config.file=/etc/prometheus/prometheus.yml", "--web.enable-lifecycle"]
31
-
32
- grafana:
33
- image: grafana/grafana
34
- ports:
35
- - "3000:3000"
36
- environment:
37
- - GF_SECURITY_ADMIN_PASSWORD=admin
38
- volumes:
39
- - grafana-data:/var/lib/grafana
40
- command: ["--homepath=/usr/share/grafana"]
41
-
42
- volumes:
43
- grafana-data:
 
1
+ version: '3.9'
2
 
3
  services:
4
  ai-assistant:
5
+ build:
6
+ context: .
7
+ dockerfile: Dockerfile
8
+ container_name: ai-dev-assistant
9
  ports:
10
  - "7860:7860"
11
+ volumes:
12
+ - ./data:/home/user/app/data # حفظ البيانات والنموذج خارجيًا
13
  environment:
14
  - HF_TOKEN=${HF_TOKEN}
15
  - GOOGLE_DRIVE_FOLDER_ID=${GOOGLE_DRIVE_FOLDER_ID}
16
  - GITHUB_REPO=${GITHUB_REPO}
17
  - GITHUB_TOKEN=${GITHUB_TOKEN}
18
+ restart: unless-stopped
19
+ healthcheck:
20
+ test: ["CMD", "curl", "-f", "http://localhost:7860/"]
21
+ interval: 30s
22
+ timeout: 5s
23
+ retries: 3