#!/usr/bin/env python3 """ خادم واجهة إعداد النموذج — يعمل على المنفذ 7860 قبل Open WebUI يتيح تحميل النماذج من Hugging Face وتشغيلها دون إعادة البناء """ import http.server, threading, subprocess, os, json, sys MODELS_DIR = "/data/models" os.makedirs(MODELS_DIR, exist_ok=True) state = {"status": "waiting", "message": "في انتظار إدخال رابط النموذج"} httpd = None # يُعيَّن لاحقاً HTML = """ مدير النماذج

🤖 مدير النماذج

حمّل نموذجاً من Hugging Face ثم شغّله — لا شيء يُحمَّل تلقائياً

النماذج المحفوظة في /data/models
لا توجد نماذج بعد
تحميل نموذج جديد
""" class Handler(http.server.BaseHTTPRequestHandler): def log_message(self, *a): pass # إخفاء السجلات def do_GET(self): if self.path == '/api/status': self.json(state) elif self.path == '/api/models': files = sorted([f for f in os.listdir(MODELS_DIR) if f.endswith('.gguf')]) if os.path.exists(MODELS_DIR) else [] self.json({"files": files}) else: self.send_response(200) self.send_header('Content-Type', 'text/html; charset=utf-8') self.end_headers() self.wfile.write(HTML.encode('utf-8')) def do_POST(self): body = json.loads(self.rfile.read(int(self.headers.get('Content-Length', 0)))) if self.path == '/api/download': threading.Thread(target=do_download, args=(body,), daemon=True).start() self.json({"ok": True}) elif self.path == '/api/launch': threading.Thread(target=do_launch, args=(body,), daemon=True).start() self.json({"ok": True}) def json(self, data): self.send_response(200) self.send_header('Content-Type', 'application/json') self.end_headers() self.wfile.write(json.dumps(data).encode()) def do_download(body): state['status'] = 'downloading' state['message'] = f"جارٍ تحميل {body['file']} ..." os.makedirs(MODELS_DIR, exist_ok=True) files_to_dl = [body['file']] if body.get('mmproj'): files_to_dl.append(body['mmproj']) for fname in files_to_dl: state['message'] = f"جارٍ تحميل {fname} ..." r = subprocess.run( ['hf', 'download', body['repo'], fname, '--local-dir', MODELS_DIR], capture_output=True, text=True ) if r.returncode != 0: state['status'] = 'error' state['message'] = r.stderr.strip() or 'فشل التحميل' return state['status'] = 'done' state['message'] = f"اكتمل تحميل {body['file']} — اضغط تشغيل" def do_launch(body): state['status'] = 'launching' state['message'] = 'جارٍ تشغيل llama.cpp وOpen WebUI...' with open('/tmp/selected_model', 'w') as f: f.write(body.get('model', '')) with open('/tmp/selected_mmproj', 'w') as f: f.write(body.get('mmproj', '')) open('/tmp/launch_signal', 'w').close() # إيقاف الخادم بشكل آمن من thread مختلف threading.Thread(target=httpd.shutdown, daemon=True).start() if __name__ == '__main__': httpd = http.server.HTTPServer(('0.0.0.0', 7860), Handler) print(">>> واجهة إعداد النموذج تعمل على http://0.0.0.0:7860", flush=True) httpd.serve_forever() print(">>> واجهة الإعداد أُغلقت، جارٍ تسليم المنفذ لـ Open WebUI...", flush=True)