1Egyb commited on
Commit
be3d7cd
·
verified ·
1 Parent(s): 120041a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -58
app.py CHANGED
@@ -8,24 +8,23 @@ import gradio as gr
8
  from huggingface_hub import InferenceClient
9
 
10
  # --------------------------- الإعدادات ---------------------------
11
- # قائمة شاملة لضمان قبول كل ملفات المشروع دون أخطاء
12
  ALLOWED_EXTENSIONS = {
13
  '.py', '.js', '.html', '.css', '.txt', '.json', '.md',
14
  '.php', '.xml', '.csv', '.zip', '.png', '.jpg', '.svg', '.ico'
15
  }
16
- MODEL_ID = os.environ.get('HF_MODEL_ID', 'moonshotai/Kimi-K2-Instruct')
 
17
  HF_TOKEN = os.environ.get('HF_TOKEN')
18
 
19
  class ProjectTank:
20
  def __init__(self):
21
  self.work_dir = tempfile.mkdtemp(prefix="tank_dev_")
22
- self.files_cache = {} # لتخزين محتوى الملفات: {الاسم: المحتوى}
 
23
 
24
  def sync_files(self, file_objs):
25
- """رفع الملفات وفكها وتجهيزها للمعالجة"""
26
  if not file_objs: return "⚠️ لم يتم اختيار ملفات.", []
27
 
28
- # تنظيف المجلد القديم
29
  if os.path.exists(self.work_dir):
30
  shutil.rmtree(self.work_dir)
31
  self.work_dir = tempfile.mkdtemp(prefix="tank_dev_")
@@ -41,16 +40,21 @@ class ProjectTank:
41
  shutil.copy(f.name, dest)
42
 
43
  self._reload_memory()
44
- return f"🚀 تم تحميل {len(self.files_cache)} ملف في السيرفر المحلي.", list(self.files_cache.keys())
45
 
46
  def _reload_memory(self):
47
  self.files_cache = {}
 
48
  for p in Path(self.work_dir).rglob('*'):
49
  if p.is_file():
50
  rel = str(p.relative_to(self.work_dir)).replace("\\", "/")
 
51
  try:
52
- self.files_cache[rel] = p.read_text(encoding='utf-8', errors='ignore')
 
 
53
  except: pass
 
54
 
55
  def update_file_content(self, filename, content):
56
  path = os.path.join(self.work_dir, filename)
@@ -60,118 +64,155 @@ class ProjectTank:
60
  self.files_cache[filename] = content
61
 
62
  def build_preview(self):
63
- """محرك المعاينة المحلي: حقن كل التبعيات في ملف واحد"""
64
- # البحث عن نقطة الدخول (Entry Point)
65
- entry = next((f for f in ["index.html", "main.html"] if f in self.files_cache), None)
66
- if not entry:
67
- entry = next((f for f in self.files_cache if f.endswith('.html')), None)
68
 
69
  if not entry:
70
- return "<div style='text-align:center; padding:50px;'>⚠️ لم يتم العثور على ملف HTML</div>"
71
 
72
  html = self.files_cache[entry]
73
 
74
- # حقن CSS
 
75
  for name, code in self.files_cache.items():
76
  if name.endswith('.css'):
77
- style = f"<style data-file='{name}'>\n{code}\n</style>"
78
- html = re.sub(f'<link.*?href=["\'].*?{name}["\'].*?>', style, html)
 
 
 
 
79
 
80
- # حقن JS
 
81
  for name, code in self.files_cache.items():
82
  if name.endswith('.js'):
83
- script = f"<script data-file='{name}'>\n{code}\n</script>"
84
- html = re.sub(f'<script.*?src=["\'].*?{name}["\'].*?></script>', script, html)
 
 
 
 
85
 
86
  return html
87
 
 
 
 
 
 
88
  tank = ProjectTank()
89
  client = InferenceClient(api_key=HF_TOKEN) if HF_TOKEN else None
90
 
91
  # --------------------------- منطق الذكاء الاصطناعي ---------------------------
92
 
93
- def clean_messages(history):
94
- """إصلاح خطأ Data Incompatible - تنظيف مصفوفة الرسائل"""
95
- return [{"role": m["role"], "content": str(m["content"])} for m in history if "role" in m and "content" in m]
96
-
97
  def chat_and_code(message, history):
98
  if not client:
99
  yield history + [{"role": "assistant", "content": "⚠️ مفقود HF_TOKEN"}]
100
  return
101
 
102
- # جمع السياق الكامل (وضع الدبابة)
103
- context = "### CURRENT PROJECT STRUCTURE & CONTENT ###\n"
 
104
  for name, content in tank.files_cache.items():
105
- context += f"\nFILE: {name}\n{content}\n--- END ---\n"
106
 
107
  system_instr = (
108
- "You are an AI IDE. Provide short explanations then output the FULL modified files using this format:\n"
109
- "[TARGET: filename]\n```\nUpdated Code\n```"
 
 
 
110
  )
111
 
112
- clean_hist = clean_messages(history)
113
- payload = [{"role": "system", "content": system_instr}]
114
- payload.append({"role": "user", "content": f"{context}\n\nUser Request: {message}"})
 
 
115
 
116
  response = ""
117
  history.append({"role": "user", "content": message})
118
  history.append({"role": "assistant", "content": ""})
119
 
120
- stream = client.chat_completion(model=MODEL_ID, messages=payload, max_tokens=4000, stream=True)
121
- for chunk in stream:
122
- token = chunk.choices[0].delta.content
123
- if token:
124
- response += token
125
- history[-1]["content"] = response
126
- yield history
 
 
 
 
 
127
 
128
  def apply_and_preview(history):
129
- """استخراج الكود وتحديث السيرفر المحلي"""
130
- if not history: return "لا توجد بيانات", None
131
 
132
  content = history[-1]["content"]
133
- matches = re.findall(r"\[TARGET:\s*(.*?)\]\s*```.*?\n(.*?)```", content, re.DOTALL)
 
 
 
134
 
135
  for filename, code in matches:
136
  tank.update_file_content(filename.strip(), code.strip())
137
 
138
- return "✅ تم التعديل والمعاينة بنجاح", tank.build_preview()
139
 
140
  # --------------------------- الواجهة ---------------------------
141
 
142
-
143
-
144
- with gr.Blocks(theme=gr.themes.Soft(), title="AI Tank IDE") as demo:
145
- gr.Markdown("# 🛡️ AI Tank IDE Pro\n**سيرفر محلي + رؤية كاملة للملفات**")
146
 
 
 
147
  with gr.Row():
148
  with gr.Column(scale=1):
149
- upload = gr.UploadButton("📁 ارفع مشروعك (ZIP/Files)", file_count="multiple")
150
- file_list = gr.Dropdown(label="ملفات المشروع المكتشفة", choices=[])
151
  status = gr.Markdown("🟢 جاهز للعمل")
152
 
153
  with gr.Group():
154
- chat = gr.Chatbot(type="messages", height=400)
155
- msg = gr.Textbox(placeholder="اطلب تعديل المشروع...", show_label=False)
156
  with gr.Row():
157
  send = gr.Button("إرسال", variant="primary")
158
- apply = gr.Button("⚡ تطبيق المعاينة", variant="stop")
 
 
159
 
160
  with gr.Column(scale=2):
161
  with gr.Tabs():
162
- with gr.TabItem("🌐 المعاينة الفورية (Local Server)"):
163
- preview = gr.HTML(value="<div style='text-align:center; padding:50px;'>ارفع مشروعك لرؤية النتيجة</div>")
164
  with gr.TabItem("📝 محرر الكود"):
165
- editor = gr.Code(label="كود الملف المختار", lines=25, interactive=True)
 
166
 
167
- # ربط الأحداث
168
- upload.upload(tank.sync_files, upload, [status, file_list]).then(tank.build_preview, None, preview)
 
 
 
 
 
169
 
170
  send.click(chat_and_code, [msg, chat], chat).then(lambda: "", None, msg)
171
 
172
- apply.click(apply_and_preview, chat, [status, preview])
 
 
 
 
 
 
 
 
173
 
174
- file_list.change(lambda n: tank.files_cache.get(n, ""), file_list, editor)
175
 
176
  if __name__ == "__main__":
177
  demo.queue().launch()
 
8
  from huggingface_hub import InferenceClient
9
 
10
  # --------------------------- الإعدادات ---------------------------
 
11
  ALLOWED_EXTENSIONS = {
12
  '.py', '.js', '.html', '.css', '.txt', '.json', '.md',
13
  '.php', '.xml', '.csv', '.zip', '.png', '.jpg', '.svg', '.ico'
14
  }
15
+ # نستخدم نموذج Qwen2.5-Coder لقوته الفائقة في فهم المشاريع البرمجية كاملة
16
+ MODEL_ID = os.environ.get('HF_MODEL_ID', 'Qwen/Qwen2.5-Coder-32B-Instruct')
17
  HF_TOKEN = os.environ.get('HF_TOKEN')
18
 
19
  class ProjectTank:
20
  def __init__(self):
21
  self.work_dir = tempfile.mkdtemp(prefix="tank_dev_")
22
+ self.files_cache = {} # {filename: content}
23
+ self.structure = "" # خريطة المشروع
24
 
25
  def sync_files(self, file_objs):
 
26
  if not file_objs: return "⚠️ لم يتم اختيار ملفات.", []
27
 
 
28
  if os.path.exists(self.work_dir):
29
  shutil.rmtree(self.work_dir)
30
  self.work_dir = tempfile.mkdtemp(prefix="tank_dev_")
 
40
  shutil.copy(f.name, dest)
41
 
42
  self._reload_memory()
43
+ return f"🚀 تم تحميل {len(self.files_cache)} ملف بنجاح.", list(self.files_cache.keys())
44
 
45
  def _reload_memory(self):
46
  self.files_cache = {}
47
+ structure_list = []
48
  for p in Path(self.work_dir).rglob('*'):
49
  if p.is_file():
50
  rel = str(p.relative_to(self.work_dir)).replace("\\", "/")
51
+ structure_list.append(f"- {rel}")
52
  try:
53
+ # نقرأ فقط ملفات الكود في الذاكرة لتوفير الـ Tokens
54
+ if p.suffix in ['.html', '.css', '.js', '.json', '.txt', '.md', '.py']:
55
+ self.files_cache[rel] = p.read_text(encoding='utf-8', errors='ignore')
56
  except: pass
57
+ self.structure = "\n".join(structure_list)
58
 
59
  def update_file_content(self, filename, content):
60
  path = os.path.join(self.work_dir, filename)
 
64
  self.files_cache[filename] = content
65
 
66
  def build_preview(self):
67
+ entry = next((f for f in ["index.html", "main.html"] if f in self.files_cache),
68
+ next((f for f in self.files_cache if f.endswith('.html')), None))
 
 
 
69
 
70
  if not entry:
71
+ return "<div style='text-align:center; padding:50px;'>⚠️ لم يتم العثور على ملف HTML للمعاينة</div>"
72
 
73
  html = self.files_cache[entry]
74
 
75
+ # 1. حقن CSS (بذكاء: استبدال أو إضافة في الـ Head)
76
+ css_content = ""
77
  for name, code in self.files_cache.items():
78
  if name.endswith('.css'):
79
+ css_content += f"\n/* --- {name} --- */\n{code}\n"
80
+
81
+ if "</head>" in html:
82
+ html = html.replace("</head>", f"<style>{css_content}</style>\n</head>")
83
+ else:
84
+ html = f"<style>{css_content}</style>" + html
85
 
86
+ # 2. حقن JS (في نهاية الـ Body لضمان عمل الـ DOM)
87
+ js_content = ""
88
  for name, code in self.files_cache.items():
89
  if name.endswith('.js'):
90
+ js_content += f"\n// --- {name} ---\n{code}\n"
91
+
92
+ if "</body>" in html:
93
+ html = html.replace("</body>", f"<script>{js_content}</script>\n</body>")
94
+ else:
95
+ html += f"<script>{js_content}</script>"
96
 
97
  return html
98
 
99
+ def export_project(self):
100
+ out_path = tempfile.mktemp()
101
+ shutil.make_archive(out_path, 'zip', self.work_dir)
102
+ return out_path + ".zip"
103
+
104
  tank = ProjectTank()
105
  client = InferenceClient(api_key=HF_TOKEN) if HF_TOKEN else None
106
 
107
  # --------------------------- منطق الذكاء الاصطناعي ---------------------------
108
 
 
 
 
 
109
  def chat_and_code(message, history):
110
  if not client:
111
  yield history + [{"role": "assistant", "content": "⚠️ مفقود HF_TOKEN"}]
112
  return
113
 
114
+ # بناء سياق قوي: الهيكل + المحتوى
115
+ context = f"### PROJECT STRUCTURE ###\n{tank.structure}\n\n"
116
+ context += "### FULL FILE CONTENTS ###\n"
117
  for name, content in tank.files_cache.items():
118
+ context += f"--- FILE: {name} ---\n{content}\n"
119
 
120
  system_instr = (
121
+ "You are an expert AI IDE. Your goal is to help the user build and debug their project.\n"
122
+ "1. Analyze the project structure to understand how files interact.\n"
123
+ "2. Provide clear explanations.\n"
124
+ "3. When modifying code, use the format: [TARGET: filename] followed by a code block.\n"
125
+ "4. Always output the FULL content of the file in the code block."
126
  )
127
 
128
+ messages = [{"role": "system", "content": system_instr}]
129
+ for m in history:
130
+ messages.append({"role": m["role"], "content": str(m["content"])})
131
+
132
+ messages.append({"role": "user", "content": f"{context}\n\nTask: {message}"})
133
 
134
  response = ""
135
  history.append({"role": "user", "content": message})
136
  history.append({"role": "assistant", "content": ""})
137
 
138
+ try:
139
+ stream = client.chat_completion(model=MODEL_ID, messages=messages, max_tokens=6000, stream=True)
140
+ for chunk in stream:
141
+ if hasattr(chunk, 'choices') and chunk.choices and len(chunk.choices) > 0:
142
+ token = chunk.choices[0].delta.content
143
+ if token:
144
+ response += token
145
+ history[-1]["content"] = response
146
+ yield history
147
+ except Exception as e:
148
+ history[-1]["content"] = f"❌ خطأ في السيرفر: {str(e)}"
149
+ yield history
150
 
151
  def apply_and_preview(history):
152
+ if not history: return "لا توجد بيانات", None, []
 
153
 
154
  content = history[-1]["content"]
155
+ matches = re.findall(r"\[TARGET:\s*(.*?)\]\s*```.*?\n(.*?)```", content, re.DOTALL | re.IGNORECASE)
156
+
157
+ if not matches:
158
+ return "⚠️ لم يتم اكتشاف ملفات للتحديث.", tank.build_preview(), list(tank.files_cache.keys())
159
 
160
  for filename, code in matches:
161
  tank.update_file_content(filename.strip(), code.strip())
162
 
163
+ return "✅ تم تحديث الملفات وحقن المعاينة بنجاح!", tank.build_preview(), list(tank.files_cache.keys())
164
 
165
  # --------------------------- الواجهة ---------------------------
166
 
167
+ with gr.Blocks(theme=gr.themes.Default(primary_hue="blue", spacing_size="sm"), title="AI Tank IDE Power") as demo:
168
+ gr.Markdown("# 🛡️ AI Tank IDE Pro (V2)\n**بيئة تطوير ذكية مع رؤية كاملة للهيكل**")
 
 
169
 
170
+ active_file = gr.State()
171
+
172
  with gr.Row():
173
  with gr.Column(scale=1):
174
+ upload = gr.UploadButton("📁 ارفع المشروع (ZIP/Files)", file_count="multiple", variant="secondary")
175
+ file_list = gr.Dropdown(label="مستكشف الملفات", choices=[])
176
  status = gr.Markdown("🟢 جاهز للعمل")
177
 
178
  with gr.Group():
179
+ chat = gr.Chatbot(type="messages", height=450)
180
+ msg = gr.Textbox(placeholder="اطلب تعديلاً أو اسأل عن عمل المشروع...", show_label=False)
181
  with gr.Row():
182
  send = gr.Button("إرسال", variant="primary")
183
+ apply = gr.Button("⚡ تطبيق التعديلات", variant="stop")
184
+
185
+ export_btn = gr.DownloadButton("📥 تصدير المشروع النهائي", variant="secondary")
186
 
187
  with gr.Column(scale=2):
188
  with gr.Tabs():
189
+ with gr.TabItem("🌐 المعاينة المباشرة"):
190
+ preview = gr.HTML(value="<div style='text-align:center; padding:50px;'>ارفع مشروعك لبدء المعاينة</div>")
191
  with gr.TabItem("📝 محرر الكود"):
192
+ editor = gr.Code(label="محتوى الملف", lines=25, interactive=True)
193
+ save_manual = gr.Button("💾 حفظ التعديل اليدوي", size="sm")
194
 
195
+ # --- ربط الأحداث ---
196
+
197
+ def on_upload(files):
198
+ log, choices = tank.sync_files(files)
199
+ return log, gr.update(choices=choices), tank.build_preview()
200
+
201
+ upload.upload(on_upload, upload, [status, file_list, preview])
202
 
203
  send.click(chat_and_code, [msg, chat], chat).then(lambda: "", None, msg)
204
 
205
+ def on_apply(h):
206
+ log, pre, choices = apply_and_preview(h)
207
+ return log, pre, gr.update(choices=choices)
208
+
209
+ apply.click(on_apply, chat, [status, preview, file_list])
210
+
211
+ file_list.change(lambda n: (tank.files_cache.get(n, ""), n), file_list, [editor, active_file])
212
+
213
+ save_manual.click(lambda n, c: tank.update_file_content(n, c) or "✅ تم الحفظ يدوياً", [active_file, editor], status).then(tank.build_preview, None, preview)
214
 
215
+ export_btn.click(tank.export_project, None, export_btn)
216
 
217
  if __name__ == "__main__":
218
  demo.queue().launch()