AhmedBenElsayedBenAhmed commited on
Commit
0429063
·
1 Parent(s): 283d83a
Files changed (1) hide show
  1. app.py +93 -300
app.py CHANGED
@@ -2,98 +2,26 @@
2
  # 1️⃣ استيراد المكتبات
3
  # ===============================
4
  import os
5
- os.environ["HF_HOME"] = "/tmp"
6
- os.environ["TRANSFORMERS_CACHE"] = "/tmp"
7
- os.environ["PEFT_HOME"] = "/tmp"
8
-
9
- from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments
10
- from peft import LoraConfig, get_peft_model, TaskType, PeftModel
11
  from flask import Flask, request, jsonify
12
  from flask_cors import CORS
13
- import threading, torch, json
14
  from datasets import Dataset
15
  import difflib
16
 
17
  # ===============================
18
  # 2️⃣ إعداد المتغيرات
19
  # ===============================
20
- MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
21
- FLASK_PORT = 7860
 
22
 
23
- SAVE_DIR = "/tmp/lora_model"
24
  DATA_DIR = "/tmp/data"
25
- os.makedirs(DATA_DIR, exist_ok=True)
26
- os.makedirs(SAVE_DIR, exist_ok=True)
27
-
28
  DATASET_FILE = os.path.join(DATA_DIR, "dataset.json")
 
29
 
30
- # ===============================
31
- # 3️⃣ تحديد الجهاز
32
- # ===============================
33
- device = "cuda" if torch.cuda.is_available() else "cpu"
34
- print("🔹 Using device:", device)
35
-
36
- # ===============================
37
- # 4️⃣ تحميل الموديل الأساسي
38
- # ===============================
39
- print("🔄 Loading base model...")
40
- tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
41
-
42
- try:
43
- model = AutoModelForCausalLM.from_pretrained(
44
- MODEL_NAME,
45
- device_map="auto" if device=="cuda" else {"": "cpu"},
46
- torch_dtype=torch.float16 if device=="cuda" else torch.float32,
47
- load_in_8bit=(device=="cuda"),
48
- trust_remote_code=True
49
- )
50
- model.to(device)
51
- print("✅ Base model loaded!")
52
- except Exception as e:
53
- print("⚠️ Failed to load base model:", e)
54
- raise e
55
-
56
- # ===============================
57
- # 5️⃣ إعداد LoRA
58
- # ===============================
59
- lora_config = LoraConfig(
60
- task_type=TaskType.CAUSAL_LM,
61
- r=2,
62
- lora_alpha=8,
63
- lora_dropout=0.05,
64
- target_modules=["q_proj", "v_proj"]
65
- )
66
- model = get_peft_model(model, lora_config)
67
- model.to(device)
68
- print("✅ LoRA model ready on device:", device)
69
-
70
- # ===============================
71
- # 6️⃣ Auto load LoRA weights
72
- # ===============================
73
- if os.path.exists(SAVE_DIR):
74
- try:
75
- base_model = AutoModelForCausalLM.from_pretrained(
76
- MODEL_NAME,
77
- device_map="auto" if device=="cuda" else {"": "cpu"},
78
- torch_dtype=torch.float16 if device=="cuda" else torch.float32,
79
- load_in_8bit=(device=="cuda"),
80
- trust_remote_code=True
81
- )
82
- model = PeftModel.from_pretrained(base_model, SAVE_DIR)
83
- model.to(device)
84
- print("✅ LoRA weights auto-loaded from disk!")
85
- except Exception as e:
86
- print("⚠️ Failed to auto-load LoRA weights:", e)
87
- else:
88
- print("ℹ️ No saved LoRA weights found, running with base model.")
89
-
90
- # ===============================
91
- # 7️⃣ إعداد Flask API
92
- # ===============================
93
- app = Flask(__name__)
94
- CORS(app)
95
-
96
- trainer = None
97
  current_dataset = None
98
 
99
  # Fake DB
@@ -104,137 +32,7 @@ users_db = {
104
  }
105
 
106
  # ===============================
107
- # 7a️⃣ توليد نصوص
108
- # ===============================
109
- @app.route("/generate", methods=["POST"])
110
- def generate():
111
- try:
112
- data = request.json or {}
113
- prompt = str(data.get("prompt", "")).strip()
114
- user_id = str(data.get("userId", "")).strip()
115
- focus_level = float(data.get("focus_level", 0.5))
116
- n_examples = int(data.get("n_examples", 2))
117
-
118
- user_info = users_db.get(user_id)
119
- user_name = user_info["name"] if user_info else "مستخدم"
120
-
121
- if not prompt:
122
- return jsonify({"messages": [{"role": "assistant", "content": "⚠️ الرجاء ارسال سؤال أو استفسار صحيح."}]}), 400
123
-
124
- # =====================
125
- # حالة: لا يوجد داتا تدريبية
126
- # =====================
127
- if current_dataset is None:
128
- base_prompt = (
129
- f"أنت مساعد قانوني ذكي اسمه زين 👨‍⚖️.\n"
130
- f"مهمة زين: الإجابة على الأسئلة القانونية بأسلوب واضح ومبسط "
131
- f"مع الحفاظ على الدقة القانونية.\n\n"
132
- f"السؤال:\n{prompt}\n\nالإجابة:"
133
- )
134
-
135
- inputs = tokenizer(base_prompt, return_tensors="pt", truncation=True, max_length=1024)
136
- inputs = {k: v.to(model.device) for k, v in inputs.items()}
137
- input_len = inputs["input_ids"].shape[-1]
138
-
139
- outputs = model.generate(
140
- **inputs,
141
- max_new_tokens=200,
142
- temperature=0.7,
143
- top_p=0.9,
144
- do_sample=True,
145
- pad_token_id=tokenizer.eos_token_id,
146
- repetition_penalty=1.1
147
- )
148
-
149
- reply = tokenizer.decode(outputs[0][input_len:], skip_special_tokens=True).strip()
150
-
151
- return jsonify({
152
- "messages": [
153
- {"role": "assistant", "content": f"أهلاً {user_name} 👋"},
154
- {"role": "assistant", "content": reply}
155
- ],
156
- "meta": {"mode": "base"}
157
- })
158
-
159
- # =====================
160
- # حالة: يوجد داتا تدريبية
161
- # =====================
162
- candidates = []
163
- for item in current_dataset:
164
- text = item.get("text", "")
165
- ratio = difflib.SequenceMatcher(None, prompt.lower(), text.lower()).ratio()
166
- candidates.append((ratio, text))
167
-
168
- candidates.sort(reverse=True, key=lambda x: x[0])
169
- top_texts = [t for r, t in candidates[:n_examples] if r > 0.05]
170
-
171
- context_parts = []
172
- for t in top_texts:
173
- lines = t.split("\n")
174
- u_lines, a_lines = [], []
175
- for l in lines:
176
- if ":" in l:
177
- role, content = l.split(":", 1)
178
- role = role.strip().lower()
179
- if role.startswith("user"):
180
- u_lines.append(content.strip())
181
- elif role.startswith("assistant") or role.startswith("ai") or role.startswith("system"):
182
- a_lines.append(content.strip())
183
- if u_lines or a_lines:
184
- context_parts.append("User: " + " ".join(u_lines) + "\nAssistant: " + " ".join(a_lines))
185
-
186
- context_text = "\n\n---\n\n".join(context_parts)
187
-
188
- instruction = (
189
- "التعليمات: استعن بالأمثلة التدريبية كمرجع أساسي، "
190
- "لكن أجب بأسلوبك الخاص وبكلمات مختلفة. "
191
- "حافظ على صحة المعلومة القانونية ولا تنسخ النصوص كما هي."
192
- )
193
-
194
- if context_text and focus_level > 0.0:
195
- emphasis = "ركز بشكل كبير على الأمثلة" if focus_level > 0.7 else "خذ الأمثلة كمرجع فقط"
196
- final_prompt = (
197
- f"{instruction}\n\n{emphasis}\n\n"
198
- f"أمثلة تدريبية (مستوى التركيز={focus_level}):\n{context_text}\n\n"
199
- f"السؤال:\n{prompt}\n\nالإجابة:"
200
- )
201
- else:
202
- final_prompt = f"{instruction}\n\nالسؤال:\n{prompt}\n\nالإجابة:"
203
-
204
- # ==== توليد
205
- inputs = tokenizer(final_prompt, return_tensors="pt", truncation=True, max_length=1024)
206
- inputs = {k: v.to(model.device) for k, v in inputs.items()}
207
- input_len = inputs["input_ids"].shape[-1]
208
-
209
- outputs = model.generate(
210
- **inputs,
211
- max_new_tokens=200,
212
- temperature=0.6,
213
- top_p=0.9,
214
- do_sample=True,
215
- pad_token_id=tokenizer.eos_token_id,
216
- repetition_penalty=1.1
217
- )
218
-
219
- reply = tokenizer.decode(outputs[0][input_len:], skip_special_tokens=True).strip()
220
-
221
- return jsonify({
222
- "messages": [
223
- {"role": "assistant", "content": f"أهلاً {user_name} 👋"},
224
- {"role": "assistant", "content": reply}
225
- ],
226
- "meta": {
227
- "mode": "trained",
228
- "used_examples": len(context_parts),
229
- "focus_level": focus_level
230
- }
231
- })
232
-
233
- except Exception as e:
234
- return jsonify({"messages": [{"role": "assistant", "content": f"❌ خطأ: {str(e)}"}]})
235
-
236
- # ===============================
237
- # 7b️⃣ تحميل الداتا
238
  # ===============================
239
  def load_dataset():
240
  global current_dataset
@@ -264,115 +62,110 @@ def load_dataset():
264
  load_dataset()
265
 
266
  # ===============================
267
- # 7c️⃣ تدريب LoRA (Async)
268
  # ===============================
269
- @app.route("/train", methods=["POST"])
270
- def train():
271
- global trainer, current_dataset, model
272
- try:
273
- data = []
274
- epochs = 1
275
- batch_size = 1
276
 
277
- if request.is_json:
278
- body = request.get_json()
279
- if isinstance(body, list):
280
- data = body
281
- elif isinstance(body, dict) and "data" in body:
282
- data = body["data"]
283
- else:
284
- return jsonify({"error": "JSON format not recognized"}), 400
 
285
 
286
- epochs = int(body.get("epochs", 1)) if isinstance(body, dict) else 1
287
- batch_size = int(body.get("batch_size", 1)) if isinstance(body, dict) else 1
288
- else:
289
- file = request.files.get("file")
290
- if file:
291
- data = json.load(file)
292
- epochs = int(request.form.get("epochs", 1))
293
- batch_size = int(request.form.get("batch_size", 1))
294
- else:
295
- return jsonify({"error": "No file uploaded or JSON provided"}), 400
296
 
297
- old_data = []
298
- if os.path.exists(DATASET_FILE):
299
- with open(DATASET_FILE, "r", encoding="utf-8") as f:
300
- try:
301
- old_data = json.load(f)
302
- except:
303
- old_data = []
304
 
305
- combined = old_data + data
306
- unique_data, seen_texts = [], set()
307
- for item in combined:
308
- if "messages" in item:
309
- text_key = "\n".join([f"{m['role'].strip().lower()}:{m['content'].strip()}" for m in item["messages"]])
310
- elif "text" in item:
311
- text_key = item["text"].strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  else:
313
- continue
314
- if text_key not in seen_texts:
315
- seen_texts.add(text_key)
316
- unique_data.append(item)
317
-
318
- with open(DATASET_FILE, "w", encoding="utf-8") as f:
319
- json.dump(unique_data, f, ensure_ascii=False, indent=2)
320
-
321
- formatted = []
322
- for item in unique_data:
323
- if "messages" in item:
324
- conversation = ""
325
- for msg in item["messages"]:
326
- conversation += f"{msg['role'].capitalize()}: {msg['content']}\n"
327
- formatted.append({"text": conversation.strip()})
328
- elif "text" in item:
329
- formatted.append({"text": item["text"]})
330
- current_dataset = Dataset.from_list(formatted)
331
 
332
- def tokenize_function(examples):
333
- return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=256)
 
334
 
335
- tokenized_dataset = current_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
 
336
 
337
- training_args = TrainingArguments(
338
- output_dir=SAVE_DIR,
339
- per_device_train_batch_size=batch_size,
340
- num_train_epochs=epochs,
341
- logging_steps=2,
342
- save_strategy="epoch",
343
- fp16=torch.cuda.is_available(),
344
- report_to=None
345
- )
346
-
347
- trainer = Trainer(model=model, args=training_args, train_dataset=tokenized_dataset)
348
-
349
- def run_training():
350
- print("🔄 Training started...")
351
- trainer.train()
352
- trainer.save_model(SAVE_DIR)
353
- print("✅ Training completed & model saved!")
354
-
355
- threading.Thread(target=run_training, daemon=True).start()
356
 
357
  return jsonify({
358
- "status": "training started",
359
- "epochs": epochs,
360
- "batch_size": batch_size,
361
- "records": len(unique_data)
 
362
  })
363
 
364
  except Exception as e:
365
- return jsonify({"error": str(e)})
366
 
367
- # ===============================
368
- # 8️⃣ تشغيل Flask
369
- # ===============================
370
  @app.route("/", methods=["GET"])
371
  def home():
372
  return jsonify({
373
  "status": "ok",
374
  "message": "Graduation Project API is running 🚀",
375
- "endpoints": ["/generate", "/train"]
376
  })
377
 
378
  if __name__ == "__main__":
 
2
  # 1️⃣ استيراد المكتبات
3
  # ===============================
4
  import os
5
+ import requests
6
+ import threading
7
+ import json
 
 
 
8
  from flask import Flask, request, jsonify
9
  from flask_cors import CORS
 
10
  from datasets import Dataset
11
  import difflib
12
 
13
  # ===============================
14
  # 2️⃣ إعداد المتغيرات
15
  # ===============================
16
+ HF_API_URL = "https://api-inference.huggingface.co/models/AhmedBenElsayed/Graduation_Project"
17
+ HF_API_TOKEN = "YOUR_HUGGINGFACE_API_TOKEN" # ضع التوكن هنا
18
+ HEADERS = {"Authorization": f"Bearer {HF_API_TOKEN}"}
19
 
20
+ FLASK_PORT = 7860
21
  DATA_DIR = "/tmp/data"
 
 
 
22
  DATASET_FILE = os.path.join(DATA_DIR, "dataset.json")
23
+ os.makedirs(DATA_DIR, exist_ok=True)
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  current_dataset = None
26
 
27
  # Fake DB
 
32
  }
33
 
34
  # ===============================
35
+ # 3️⃣ تحميل الداتا
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  # ===============================
37
  def load_dataset():
38
  global current_dataset
 
62
  load_dataset()
63
 
64
  # ===============================
65
+ # 4️⃣ Flask API
66
  # ===============================
67
+ app = Flask(__name__)
68
+ CORS(app)
 
 
 
 
 
69
 
70
+ # ----- توليد النصوص -----
71
+ @app.route("/generate", methods=["POST"])
72
+ def generate():
73
+ try:
74
+ data = request.json or {}
75
+ prompt = str(data.get("prompt", "")).strip()
76
+ user_id = str(data.get("userId", "")).strip()
77
+ focus_level = float(data.get("focus_level", 0.5))
78
+ n_examples = int(data.get("n_examples", 2))
79
 
80
+ user_info = users_db.get(user_id)
81
+ user_name = user_info["name"] if user_info else "مستخدم"
 
 
 
 
 
 
 
 
82
 
83
+ if not prompt:
84
+ return jsonify({"messages": [{"role": "assistant", "content": "⚠️ الرجاء ارسال سؤال أو استفسار صحيح."}]}), 400
 
 
 
 
 
85
 
86
+ # =====================
87
+ # حالة: لا يوجد داتا تدريبية
88
+ # =====================
89
+ if current_dataset is None:
90
+ final_prompt = (
91
+ f"أنت مساعد قانوني ذكي اسمه زين 👨‍⚖️.\n"
92
+ f"مهمة زين: الإجابة على الأسئلة القانونية بأسلوب واضح ومبسط.\n\n"
93
+ f"السؤال:\n{prompt}\n\nالإجابة:"
94
+ )
95
+ else:
96
+ # =====================
97
+ # حالة: يوجد داتا تدريبية
98
+ # =====================
99
+ candidates = []
100
+ for item in current_dataset:
101
+ text = item.get("text", "")
102
+ ratio = difflib.SequenceMatcher(None, prompt.lower(), text.lower()).ratio()
103
+ candidates.append((ratio, text))
104
+ candidates.sort(reverse=True, key=lambda x: x[0])
105
+ top_texts = [t for r, t in candidates[:n_examples] if r > 0.05]
106
+
107
+ context_parts = []
108
+ for t in top_texts:
109
+ lines = t.split("\n")
110
+ u_lines, a_lines = [], []
111
+ for l in lines:
112
+ if ":" in l:
113
+ role, content = l.split(":", 1)
114
+ role = role.strip().lower()
115
+ if role.startswith("user"):
116
+ u_lines.append(content.strip())
117
+ elif role.startswith("assistant") or role.startswith("ai") or role.startswith("system"):
118
+ a_lines.append(content.strip())
119
+ if u_lines or a_lines:
120
+ context_parts.append("User: " + " ".join(u_lines) + "\nAssistant: " + " ".join(a_lines))
121
+
122
+ context_text = "\n\n---\n\n".join(context_parts)
123
+ instruction = (
124
+ "التعليمات: استعن بالأمثلة التدريبية كمرجع، "
125
+ "لكن أجب بأسلوبك الخاص وبكلمات مختلفة. "
126
+ "حافظ على صحة المعلومة القانونية ولا تنسخ النصوص كما هي."
127
+ )
128
+ if context_text and focus_level > 0.0:
129
+ emphasis = "ركز بشكل كبير على الأمثلة" if focus_level > 0.7 else "خذ الأمثلة كمرجع فقط"
130
+ final_prompt = (
131
+ f"{instruction}\n\n{emphasis}\n\n"
132
+ f"أمثلة تدريبية (مستوى التركيز={focus_level}):\n{context_text}\n\n"
133
+ f"السؤال:\n{prompt}\n\nالإجابة:"
134
+ )
135
  else:
136
+ final_prompt = f"{instruction}\n\nالسؤال:\n{prompt}\n\nالإجابة:"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
+ # ==== استدعاء API HuggingFace
139
+ payload = {"inputs": final_prompt}
140
+ response = requests.post(HF_API_URL, headers=HEADERS, json=payload, timeout=60)
141
 
142
+ if response.status_code != 200:
143
+ return jsonify({"messages": [{"role": "assistant", "content": f"❌ خطأ من HuggingFace API: {response.text}"}]})
144
 
145
+ result = response.json()
146
+ if isinstance(result, list) and "generated_text" in result[0]:
147
+ reply = result[0]["generated_text"]
148
+ else:
149
+ reply = str(result)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
  return jsonify({
152
+ "messages": [
153
+ {"role": "assistant", "content": f"أهلاً {user_name} 👋"},
154
+ {"role": "assistant", "content": reply}
155
+ ],
156
+ "meta": {"mode": "api", "focus_level": focus_level}
157
  })
158
 
159
  except Exception as e:
160
+ return jsonify({"messages": [{"role": "assistant", "content": f"❌ خطأ: {str(e)}"}]})
161
 
162
+ # ----- الصفحة الرئيسية -----
 
 
163
  @app.route("/", methods=["GET"])
164
  def home():
165
  return jsonify({
166
  "status": "ok",
167
  "message": "Graduation Project API is running 🚀",
168
+ "endpoints": ["/generate"]
169
  })
170
 
171
  if __name__ == "__main__":