Spaces:
Sleeping
Sleeping
| import os | |
| import shutil | |
| import uuid | |
| import zipfile | |
| import json | |
| import sys | |
| import traceback | |
| from typing import Dict, List | |
| from datetime import datetime | |
| from fastapi import FastAPI, UploadFile, File, WebSocket, WebSocketDisconnect, HTTPException, Request | |
| from fastapi.responses import HTMLResponse, JSONResponse, FileResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel | |
| import psutil | |
| import platform | |
| # محاولة استيراد CrewAI | |
| try: | |
| from crew_manager import ProjectManagerCrew | |
| CREWAI_AVAILABLE = True | |
| print("✅ CrewAI متوفر") | |
| except ImportError as e: | |
| print(f"⚠️ CrewAI غير متوفر: {e}") | |
| CREWAI_AVAILABLE = False | |
| app = FastAPI(title="مركز الوكلاء الذكي", description="نظام إدارة المشاريع بالذكاء الاصطناعي") | |
| # إعداد CORS | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| UPLOAD_DIR = "projects" | |
| os.makedirs(UPLOAD_DIR, exist_ok=True) | |
| # تخزين الجلسات | |
| sessions: Dict[str, Dict] = {} | |
| class UserRequest(BaseModel): | |
| project_id: str | |
| message: str | |
| # WebSocket للدردشة المباشرة | |
| class ConnectionManager: | |
| def __init__(self): | |
| self.active_connections: Dict[str, WebSocket] = {} | |
| async def connect(self, websocket: WebSocket, project_id: str): | |
| await websocket.accept() | |
| self.active_connections[project_id] = websocket | |
| def disconnect(self, project_id: str): | |
| if project_id in self.active_connections: | |
| del self.active_connections[project_id] | |
| async def send_message(self, message: str, project_id: str): | |
| if project_id in self.active_connections: | |
| await self.active_connections[project_id].send_text(json.dumps({ | |
| "type": "manager", | |
| "message": message | |
| })) | |
| manager = ConnectionManager() | |
| # ============ وظائف المساعدة ============ | |
| def create_simple_project_manager(project_path: str): | |
| """إنشاء مدير مشروع بسيط إذا كان CrewAI غير متوفر""" | |
| class SimpleManager: | |
| def __init__(self, path): | |
| self.path = path | |
| self.conversation = [] | |
| def chat_with_manager(self, message, conversation_history=None): | |
| return f"👋 مرحباً! أنا المدير البسيط. تم تحميل مشروعك في {self.path}. رسالتك: {message}" | |
| def create_development_plan(self, user_request): | |
| return f"📋 خطة تطوير بسيطة للمشروع في {self.path}. الطلب: {user_request}" | |
| return SimpleManager(project_path) | |
| def get_simple_response(project_name: str, message: str) -> str: | |
| """إنشاء رد بسيط بدون نماذج ذكاء اصطناعي""" | |
| responses = [ | |
| f"👋 مرحباً! أنا مدير مشروع {project_name}.", | |
| f"💬 رسالتك: '{message}'", | |
| "📊 أقوم بتحليل مشروعك...", | |
| "💡 اقتراح: رفع ملف ZIP لمشروعك للحصول على تحليل مفصل.", | |
| "🛠️ يمكنني مساعدتك في: تحليل الكود، خطط التطوير، النصائح التقنية.", | |
| "🚀 الميزات القادمة: تحليل ذكي، اقتراحات تلقائية، دعم متعدد اللغات.", | |
| "📝 لمزيد من المساعدة، ارفع مشروعك واطرح أسئلة محددة." | |
| ] | |
| return "\n\n".join(responses) | |
| def get_dir_size(path): | |
| """حساب حجم المجلد بالبايت""" | |
| if not os.path.exists(path): | |
| return 0 | |
| total = 0 | |
| for dirpath, dirnames, filenames in os.walk(path): | |
| for f in filenames: | |
| fp = os.path.join(dirpath, f) | |
| if os.path.exists(fp): | |
| total += os.path.getsize(fp) | |
| return total / (1024**2) # تحويل إلى ميجابايت | |
| def count_files(path): | |
| """عد عدد الملفات في المجلد""" | |
| if not os.path.exists(path): | |
| return 0 | |
| count = 0 | |
| for _, _, filenames in os.walk(path): | |
| count += len(filenames) | |
| return count | |
| def check_for_issues(upload_dir_info, sessions, env_vars): | |
| """فحص المشاكل المحتملة""" | |
| issues = [] | |
| # فحص مجلد الرفع | |
| if not upload_dir_info["exists"]: | |
| issues.append({ | |
| "level": "critical", | |
| "title": "مجلد الرفع غير موجود", | |
| "description": f"المجلد {UPLOAD_DIR} غير موجود. لن يتمكن المستخدمون من رفع المشاريع.", | |
| "solution": "إنشاء المجلد تلقائياً أو تعديل مساره في الإعدادات." | |
| }) | |
| elif not os.access(upload_dir_info["path"], os.W_OK): | |
| issues.append({ | |
| "level": "critical", | |
| "title": "صلاحيات كتابة غير كافية", | |
| "description": f"لا يمكن الكتابة في المجلد {UPLOAD_DIR}.", | |
| "solution": "تعديل صلاحيات المجلد إلى 755 أو 777." | |
| }) | |
| # فحص توكن Hugging Face | |
| if "HF_TOKEN" not in env_vars or "غير موجود" in env_vars.get("HF_TOKEN", ""): | |
| issues.append({ | |
| "level": "high", | |
| "title": "HF_TOKEN غير موجود", | |
| "description": "مفتاح Hugging Face غير موجود. لن تعمل النماذج اللغوية.", | |
| "solution": "إضافة HF_TOKEN إلى متغيرات البيئة أو إعدادات السرية." | |
| }) | |
| # فحص المساحة الحرة | |
| try: | |
| disk_usage = psutil.disk_usage('/') | |
| if disk_usage.percent > 90: | |
| issues.append({ | |
| "level": "high", | |
| "title": "مساحة التخزين منخفضة", | |
| "description": f"تبقى {100 - disk_usage.percent}% فقط من المساحة الحرة.", | |
| "solution": "حذف المشاريع القديمة أو توسيع مساحة التخزين." | |
| }) | |
| except: | |
| pass | |
| # فحص الذاكرة | |
| try: | |
| memory = psutil.virtual_memory() | |
| if memory.percent > 85: | |
| issues.append({ | |
| "level": "medium", | |
| "title": "استخدام عالي للذاكرة", | |
| "description": f"الذاكرة المستخدمة: {memory.percent}%", | |
| "solution": "تحسين كفاءة الكود أو إضافة المزيد من الذاكرة." | |
| }) | |
| except: | |
| pass | |
| # فحص الجلسات القديمة | |
| for pid, session in sessions.items(): | |
| if session.get("status") == "error": | |
| issues.append({ | |
| "level": "low", | |
| "title": "جلسة معطلة", | |
| "description": f"المشروع {pid} ({session.get('name')}) في حالة خطأ.", | |
| "solution": "حذف الجلسة أو إعادة معالجتها." | |
| }) | |
| return issues | |
| def generate_recommendations(upload_dir_info, system_info, env_vars): | |
| """توليد توصيات تحسين""" | |
| recommendations = [] | |
| # توصيات المساحة | |
| if upload_dir_info["size_mb"] > 100: | |
| recommendations.append({ | |
| "priority": "high", | |
| "title": "تنظيف المشاريع القديمة", | |
| "description": f"حجم مجلد المشاريع: {upload_dir_info['size_mb']:.2f} ميجابايت", | |
| "action": "تفعيل حذف تلقائي للمشاريع القديمة بعد فترة زمنية." | |
| }) | |
| # توصيات الذاكرة | |
| if system_info["memory_percent"] > 70: | |
| recommendations.append({ | |
| "priority": "medium", | |
| "title": "تحسين استخدام الذاكرة", | |
| "description": f"الذاكرة المستخدمة: {system_info['memory_percent']}%", | |
| "action": "تفعيل ضغط الذاكرة أو تقليل حجم التخزين المؤقت." | |
| }) | |
| # توصيات CPU | |
| if system_info["cpu_percent"] > 80: | |
| recommendations.append({ | |
| "priority": "medium", | |
| "title": "تحسين استخدام المعالج", | |
| "description": f"استخدام المعالج: {system_info['cpu_percent']}%", | |
| "action": "تفعيل معالجة غير متزامنة أو تقليل التعقيد الحسابي." | |
| }) | |
| # توصيات الأمان | |
| if "HF_TOKEN" in env_vars and "✅ موجود" in env_vars["HF_TOKEN"]: | |
| recommendations.append({ | |
| "priority": "low", | |
| "title": "تدوير المفاتيح الأمنية", | |
| "description": "HF_TOKEN قيد الاستخدام منذ فترة", | |
| "action": "تغيير مفتاح Hugging Face دورياً للأمان." | |
| }) | |
| # توصيات النسخ الاحتياطي | |
| if upload_dir_info["project_count"] > 0: | |
| recommendations.append({ | |
| "priority": "low", | |
| "title": "تفعيل النسخ الاحتياطي", | |
| "description": f"يوجد {upload_dir_info['project_count']} مشروع", | |
| "action": "تنفيذ نظام نسخ احتياطي تلقائي للمشاريع." | |
| }) | |
| return recommendations | |
| # تعريف وقت بدء التطبيق | |
| app_start_time = datetime.now() | |
| # ============ HTML الرئيسي ============ | |
| async def root(): | |
| html_content = """ | |
| <!DOCTYPE html> | |
| <html lang="ar" dir="rtl"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>مركز الوكلاء الذكي</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| * { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } | |
| ::-webkit-scrollbar { width: 8px; } | |
| ::-webkit-scrollbar-track { background: #f1f1f1; } | |
| ::-webkit-scrollbar-thumb { background: #888; } | |
| ::-webkit-scrollbar-thumb:hover { background: #555; } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50"> | |
| <div class="min-h-screen"> | |
| <!-- رأس الصفحة --> | |
| <nav class="bg-gradient-to-r from-blue-600 to-purple-600 text-white p-4 shadow-lg"> | |
| <div class="container mx-auto flex justify-between items-center"> | |
| <div class="flex items-center space-x-3"> | |
| <i class="fas fa-robot text-2xl"></i> | |
| <h1 class="text-2xl font-bold">مركز الوكلاء الذكي</h1> | |
| </div> | |
| <div class="space-x-4"> | |
| <button onclick="showUploadModal()" class="bg-white text-blue-600 px-4 py-2 rounded-lg font-semibold hover:bg-gray-100"> | |
| <i class="fas fa-upload mr-2"></i>رفع مشروع جديد | |
| </button> | |
| <button onclick="testConnection()" class="bg-green-600 text-white px-4 py-2 rounded-lg font-semibold hover:bg-green-700"> | |
| <i class="fas fa-vial mr-2"></i>اختبار الاتصال | |
| </button> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- المحتوى الرئيسي --> | |
| <div class="container mx-auto p-6"> | |
| <div class="bg-white rounded-xl shadow-lg p-8 text-center"> | |
| <div class="mb-8"> | |
| <i class="fas fa-robot text-6xl text-blue-500 mb-4"></i> | |
| <h2 class="text-3xl font-bold text-gray-800 mb-2">مرحباً بك في مركز الوكلاء الذكي</h2> | |
| <p class="text-gray-600">نظام متكامل لتحليل وتطوير المشاريع البرمجية باستخدام الذكاء الاصطناعي</p> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8"> | |
| <div class="bg-blue-50 p-6 rounded-xl"> | |
| <i class="fas fa-upload text-blue-500 text-3xl mb-4"></i> | |
| <h3 class="font-bold text-lg mb-2">رفع المشاريع</h3> | |
| <p class="text-gray-600">ارفع ملف ZIP لمشروعك ليقوم النظام بتحليله</p> | |
| </div> | |
| <div class="bg-green-50 p-6 rounded-xl"> | |
| <i class="fas fa-comments text-green-500 text-3xl mb-4"></i> | |
| <h3 class="font-bold text-lg mb-2">دردشة ذكية</h3> | |
| <p class="text-gray-600">تحدث مع المدير الذكي للحصول على نصائح وتحليلات</p> | |
| </div> | |
| <div class="bg-purple-50 p-6 rounded-xl"> | |
| <i class="fas fa-chart-line text-purple-500 text-3xl mb-4"></i> | |
| <h3 class="font-bold text-lg mb-2">تحليل متقدم</h3> | |
| <p class="text-gray-600">احصل على خطط تطوير وتحسينات لمشروعك</p> | |
| </div> | |
| </div> | |
| <button onclick="showUploadModal()" | |
| class="bg-gradient-to-r from-blue-600 to-purple-600 text-white px-8 py-4 rounded-xl font-bold text-lg hover:shadow-lg transition-shadow"> | |
| <i class="fas fa-rocket mr-2"></i>ابدأ الآن - ارفع مشروعك الأول | |
| </button> | |
| <div id="status" class="mt-6 p-4 bg-gray-100 rounded-xl hidden"></div> | |
| </div> | |
| <!-- قسم المشاريع --> | |
| <div id="projectsSection" class="hidden mt-8"> | |
| <div class="bg-white rounded-xl shadow-lg p-6"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-xl font-bold text-gray-800"> | |
| <i class="fas fa-folder-open text-blue-500 mr-2"></i> | |
| المشاريع المرفوعة | |
| </h2> | |
| <div class="flex items-center space-x-2"> | |
| <span class="bg-blue-100 text-blue-800 text-sm font-semibold px-3 py-1 rounded-full" id="projectsCount">0</span> | |
| <button onclick="loadProjects()" class="text-blue-600 hover:text-blue-800 p-1" title="تحديث القائمة"> | |
| <i class="fas fa-redo-alt"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <div id="projectsList" class="space-y-4"> | |
| <!-- المشاريع ستظهر هنا --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- نافذة رفع الملف --> | |
| <div id="uploadModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> | |
| <div class="bg-white rounded-xl shadow-2xl p-8 max-w-md w-full mx-4"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h3 class="text-xl font-bold text-gray-800">رفع مشروع جديد</h3> | |
| <button onclick="closeUploadModal()" class="text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times text-xl"></i> | |
| </button> | |
| </div> | |
| <div class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center mb-6 hover:border-blue-500 transition-colors cursor-pointer" | |
| id="dropZone" onclick="document.getElementById('zipFile').click()"> | |
| <i class="fas fa-cloud-upload-alt text-4xl text-gray-400 mb-4"></i> | |
| <p class="text-gray-600 mb-2">اسحب وأفلت ملف ZIP هنا</p> | |
| <p class="text-sm text-gray-500">أو انقر لاختيار ملف</p> | |
| <input type="file" id="zipFile" accept=".zip" class="hidden"> | |
| </div> | |
| <div id="fileInfo" class="hidden bg-gray-100 p-4 rounded-lg mb-6"> | |
| <div class="flex justify-between items-center"> | |
| <div> | |
| <p class="font-semibold" id="fileName"></p> | |
| <p class="text-sm text-gray-500" id="fileSize"></p> | |
| </div> | |
| <button onclick="removeFile()" class="text-red-500 hover:text-red-700"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <button id="uploadButton" onclick="uploadFile()" | |
| class="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed" | |
| disabled> | |
| <i class="fas fa-upload mr-2"></i>رفع المشروع | |
| </button> | |
| <div id="uploadStatus" class="mt-4 p-4 bg-gray-50 rounded-lg hidden"></div> | |
| </div> | |
| </div> | |
| <!-- نافذة الدردشة --> | |
| <div id="chatModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"> | |
| <div class="bg-white rounded-xl shadow-2xl w-full max-w-4xl h-3/4 flex flex-col mx-4"> | |
| <div class="bg-gradient-to-r from-blue-600 to-purple-600 text-white p-4 rounded-t-xl flex justify-between items-center"> | |
| <div> | |
| <h3 class="text-xl font-bold"> | |
| <i class="fas fa-comments mr-2"></i> | |
| دردشة مع المدير الذكي | |
| </h3> | |
| <p class="text-sm opacity-90" id="chatProjectName"></p> | |
| </div> | |
| <button onclick="closeChatModal()" class="text-white hover:text-gray-200"> | |
| <i class="fas fa-times text-xl"></i> | |
| </button> | |
| </div> | |
| <div id="chatMessages" class="flex-1 overflow-y-auto p-4 space-y-4"> | |
| <!-- الرسائل تظهر هنا --> | |
| </div> | |
| <div class="border-t p-4"> | |
| <div class="flex space-x-2"> | |
| <input type="text" id="chatInput" | |
| class="flex-1 border rounded-lg px-4 py-2 focus:outline-none focus:border-blue-500" | |
| placeholder="اكتب رسالتك هنا..." | |
| onkeypress="if(event.key === 'Enter') sendChatMessage()"> | |
| <button onclick="sendChatMessage()" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700"> | |
| <i class="fas fa-paper-plane"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // متغيرات عامة | |
| let currentProjectId = null; | |
| let chatSocket = null; | |
| let currentChatProjectId = null; | |
| // اختبار الاتصال | |
| async function testConnection() { | |
| const status = document.getElementById('status'); | |
| status.classList.remove('hidden'); | |
| status.innerHTML = '<div class="text-blue-600"><i class="fas fa-spinner fa-spin mr-2"></i>جاري اختبار الاتصال...</div>'; | |
| try { | |
| const response = await fetch('/api/test-connection'); | |
| const data = await response.json(); | |
| status.innerHTML = ` | |
| <div class="text-green-600"> | |
| <i class="fas fa-check-circle mr-2"></i> | |
| ✅ النظام يعمل بنجاح<br> | |
| <small class="text-gray-600">${data.message} | ${data.time}</small> | |
| </div> | |
| `; | |
| // إظهار قسم المشاريع بعد الاختبار الناجح | |
| document.getElementById('projectsSection').classList.remove('hidden'); | |
| loadProjects(); | |
| } catch (error) { | |
| status.innerHTML = ` | |
| <div class="text-red-600"> | |
| <i class="fas fa-exclamation-circle mr-2"></i> | |
| ❌ فشل الاتصال: ${error.message} | |
| </div> | |
| `; | |
| } | |
| } | |
| // وظائف رفع الملفات | |
| function showUploadModal() { | |
| document.getElementById('uploadModal').classList.remove('hidden'); | |
| } | |
| function closeUploadModal() { | |
| document.getElementById('uploadModal').classList.add('hidden'); | |
| removeFile(); | |
| document.getElementById('uploadStatus').innerHTML = ''; | |
| document.getElementById('uploadStatus').classList.add('hidden'); | |
| } | |
| function handleFileSelect(event) { | |
| const file = event.target.files[0]; | |
| if (file && file.name.endsWith('.zip')) { | |
| displayFileInfo(file); | |
| } else { | |
| showNotification('يرجى اختيار ملف بصيغة ZIP فقط', 'error'); | |
| removeFile(); | |
| } | |
| } | |
| function displayFileInfo(file) { | |
| const fileSizeMB = (file.size / (1024 * 1024)).toFixed(2); | |
| document.getElementById('fileName').textContent = file.name; | |
| document.getElementById('fileSize').textContent = `${fileSizeMB} MB`; | |
| document.getElementById('fileInfo').classList.remove('hidden'); | |
| document.getElementById('uploadButton').disabled = false; | |
| // إظهار حالة جديدة | |
| const statusDiv = document.getElementById('uploadStatus'); | |
| statusDiv.innerHTML = `<div class="text-green-600"><i class="fas fa-check mr-2"></i>تم اختيار الملف: ${file.name}</div>`; | |
| statusDiv.classList.remove('hidden'); | |
| } | |
| function removeFile() { | |
| document.getElementById('zipFile').value = ''; | |
| document.getElementById('fileInfo').classList.add('hidden'); | |
| document.getElementById('uploadButton').disabled = true; | |
| } | |
| async function uploadFile() { | |
| const fileInput = document.getElementById('zipFile'); | |
| if (!fileInput.files[0]) { | |
| showNotification('❌ يرجى اختيار ملف أولاً', 'error'); | |
| return; | |
| } | |
| const file = fileInput.files[0]; | |
| const maxSize = 100 * 1024 * 1024; // 100MB | |
| if (file.size > maxSize) { | |
| showNotification('❌ حجم الملف كبير جداً. الحد الأقصى 100 ميجابايت', 'error'); | |
| return; | |
| } | |
| const formData = new FormData(); | |
| formData.append('file', file); | |
| const uploadButton = document.getElementById('uploadButton'); | |
| const originalText = uploadButton.innerHTML; | |
| uploadButton.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>جاري الرفع...'; | |
| uploadButton.disabled = true; | |
| const statusDiv = document.getElementById('uploadStatus'); | |
| statusDiv.innerHTML = '<div class="text-blue-600"><i class="fas fa-spinner fa-spin mr-2"></i>جاري رفع الملف... قد يستغرق دقيقة</div>'; | |
| statusDiv.classList.remove('hidden'); | |
| try { | |
| const controller = new AbortController(); | |
| const timeoutId = setTimeout(() => controller.abort(), 300000); | |
| const response = await fetch('/api/upload-zip', { | |
| method: 'POST', | |
| body: formData, | |
| signal: controller.signal | |
| }); | |
| clearTimeout(timeoutId); | |
| if (!response.ok) { | |
| const errorText = await response.text(); | |
| throw new Error(`خطأ ${response.status}: ${errorText}`); | |
| } | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| statusDiv.innerHTML = ` | |
| <div class="text-green-600"> | |
| <i class="fas fa-check-circle mr-2"></i> | |
| ✅ ${data.message}<br> | |
| <small class="text-gray-600">رقم المشروع: ${data.project_id}</small> | |
| </div> | |
| `; | |
| // إظهار إشعار النجاح | |
| showNotification(`🎉 تم رفع المشروع "${data.project_name}" بنجاح!`, 'success'); | |
| // تحديث قائمة المشاريع | |
| setTimeout(async () => { | |
| closeUploadModal(); | |
| const projects = await loadProjects(); | |
| // إظهار المشروع الجديد في الأعلى | |
| if (projects && projects.length > 0) { | |
| console.log('تم إضافة مشروع جديد:', data.project_id); | |
| } | |
| }, 1500); | |
| } else { | |
| throw new Error(data.error || data.message || 'حدث خطأ غير معروف'); | |
| } | |
| } catch (error) { | |
| console.error('خطأ في الرفع:', error); | |
| let errorMessage = error.message; | |
| if (error.name === 'AbortError') { | |
| errorMessage = 'انتهت مهلة الطلب. قد يكون الملف كبيراً جداً أو هناك مشكلة في الاتصال.'; | |
| } | |
| statusDiv.innerHTML = ` | |
| <div class="text-red-600"> | |
| <i class="fas fa-exclamation-circle mr-2"></i> | |
| ❌ ${errorMessage} | |
| </div> | |
| `; | |
| showNotification(`❌ فشل رفع الملف: ${errorMessage}`, 'error'); | |
| } finally { | |
| uploadButton.innerHTML = originalText; | |
| uploadButton.disabled = false; | |
| } | |
| } | |
| // وظائف إدارة المشاريع | |
| async function loadProjects() { | |
| try { | |
| console.log('جاري تحميل المشاريع...'); | |
| const response = await fetch('/api/projects'); | |
| if (!response.ok) { | |
| throw new Error(`خطأ في الخادم: ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| console.log('تم تحميل المشاريع:', data); | |
| if (data.projects && data.projects.length > 0) { | |
| updateProjectsUI(data.projects); | |
| showProjectSection(); | |
| } else { | |
| console.log('لا توجد مشاريع'); | |
| updateProjectsUI([]); | |
| } | |
| return data.projects || []; | |
| } catch (error) { | |
| console.error('خطأ في تحميل المشاريع:', error); | |
| showNotification('خطأ في تحميل المشاريع: ' + error.message, 'error'); | |
| return []; | |
| } | |
| } | |
| function showProjectSection() { | |
| document.getElementById('projectsSection').classList.remove('hidden'); | |
| } | |
| function updateProjectsUI(projects) { | |
| const projectsList = document.getElementById('projectsList'); | |
| const projectsCount = document.getElementById('projectsCount'); | |
| projectsCount.textContent = projects.length; | |
| if (projects.length === 0) { | |
| projectsList.innerHTML = ` | |
| <div class="text-center py-8 text-gray-500"> | |
| <i class="fas fa-folder-open text-4xl mb-4"></i> | |
| <p>لا توجد مشاريع بعد. ابدأ برفع مشروعك الأول!</p> | |
| </div> | |
| `; | |
| return; | |
| } | |
| projectsList.innerHTML = projects.map(project => ` | |
| <div class="border border-gray-200 rounded-lg p-4 hover:bg-gray-50 transition-colors"> | |
| <div class="flex justify-between items-center mb-3"> | |
| <div> | |
| <h4 class="font-bold text-gray-800">${project.name}</h4> | |
| <p class="text-sm text-gray-500">ID: ${project.id}</p> | |
| </div> | |
| <span class="px-3 py-1 rounded-full text-xs font-semibold ${getStatusColor(project.status)}"> | |
| ${getStatusText(project.status)} | |
| </span> | |
| </div> | |
| <div class="flex justify-between items-center text-sm text-gray-600"> | |
| <span>${project.conversation_count || 0} رسالة</span> | |
| <div class="space-x-2"> | |
| <button onclick="viewProjectDetails('${project.id}')" class="text-blue-600 hover:text-blue-800 font-semibold"> | |
| <i class="fas fa-eye mr-1"></i>تفاصيل | |
| </button> | |
| <button onclick="startChatWithProject('${project.id}')" class="text-green-600 hover:text-green-800 font-semibold"> | |
| <i class="fas fa-comments mr-1"></i>دردشة | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| `).join(''); | |
| } | |
| function viewProjectDetails(projectId) { | |
| alert(`عرض تفاصيل المشروع ${projectId}. هذه ميزة قيد التطوير.`); | |
| } | |
| function startChatWithProject(projectId) { | |
| currentChatProjectId = projectId; | |
| showChatModal(projectId); | |
| } | |
| function getStatusColor(status) { | |
| switch(status) { | |
| case 'active': return 'bg-green-100 text-green-800'; | |
| case 'processing': return 'bg-blue-100 text-blue-800'; | |
| case 'error': return 'bg-red-100 text-red-800'; | |
| default: return 'bg-gray-100 text-gray-800'; | |
| } | |
| } | |
| function getStatusText(status) { | |
| switch(status) { | |
| case 'active': return 'نشط'; | |
| case 'processing': return 'قيد المعالجة'; | |
| case 'error': return 'خطأ'; | |
| default: return 'غير معروف'; | |
| } | |
| } | |
| // وظائف الدردشة | |
| function showChatModal(projectId) { | |
| currentChatProjectId = projectId; | |
| document.getElementById('chatModal').classList.remove('hidden'); | |
| document.getElementById('chatProjectName').textContent = `المشروع: ${projectId}`; | |
| loadChatHistory(projectId); | |
| } | |
| function closeChatModal() { | |
| document.getElementById('chatModal').classList.add('hidden'); | |
| currentChatProjectId = null; | |
| document.getElementById('chatMessages').innerHTML = ''; | |
| document.getElementById('chatInput').value = ''; | |
| } | |
| async function loadChatHistory(projectId) { | |
| try { | |
| const response = await fetch(`/api/conversation/${projectId}`); | |
| const data = await response.json(); | |
| const chatMessages = document.getElementById('chatMessages'); | |
| chatMessages.innerHTML = ''; | |
| if (data.conversation && data.conversation.length > 0) { | |
| data.conversation.forEach(msg => { | |
| addMessageToChat(msg.role, msg.content); | |
| }); | |
| } else { | |
| addMessageToChat('system', '👋 مرحباً! أنا مدير المشروع الذكي. كيف يمكنني مساعدتك اليوم؟'); | |
| } | |
| // التمرير إلى الأسفل | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| } catch (error) { | |
| console.error('خطأ في تحميل محادثة المشروع:', error); | |
| addMessageToChat('system', '❌ فشل تحميل محادثة المشروع.'); | |
| } | |
| } | |
| function addMessageToChat(role, content) { | |
| const chatMessages = document.getElementById('chatMessages'); | |
| const messageDiv = document.createElement('div'); | |
| if (role === 'user') { | |
| messageDiv.className = 'flex justify-end'; | |
| messageDiv.innerHTML = ` | |
| <div class="bg-blue-100 text-blue-800 rounded-lg p-3 max-w-xs"> | |
| <p>${content}</p> | |
| <div class="text-xs text-blue-600 mt-1 text-left">أنت</div> | |
| </div> | |
| `; | |
| } else { | |
| messageDiv.className = 'flex justify-start'; | |
| messageDiv.innerHTML = ` | |
| <div class="bg-gray-100 text-gray-800 rounded-lg p-3 max-w-xs"> | |
| <p>${content}</p> | |
| <div class="text-xs text-gray-600 mt-1 text-right">المدير الذكي</div> | |
| </div> | |
| `; | |
| } | |
| chatMessages.appendChild(messageDiv); | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| } | |
| async function sendChatMessage() { | |
| const input = document.getElementById('chatInput'); | |
| const message = input.value.trim(); | |
| if (!message || !currentChatProjectId) { | |
| return; | |
| } | |
| // إضافة رسالة المستخدم إلى الدردشة | |
| addMessageToChat('user', message); | |
| input.value = ''; | |
| try { | |
| const response = await fetch('/api/chat', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| project_id: currentChatProjectId, | |
| message: message | |
| }) | |
| }); | |
| const data = await response.json(); | |
| if (data.status === 'success') { | |
| addMessageToChat('manager', data.response); | |
| } else { | |
| addMessageToChat('system', `❌ خطأ: ${data.detail || 'حدث خطأ غير معروف'}`); | |
| } | |
| } catch (error) { | |
| console.error('خطأ في إرسال الرسالة:', error); | |
| addMessageToChat('system', '❌ فشل إرسال الرسالة. تحقق من اتصالك بالإنترنت.'); | |
| } | |
| } | |
| // وظائف الإشعارات | |
| function showNotification(message, type = 'info') { | |
| // إنشاء عنصر الإشعار | |
| const notification = document.createElement('div'); | |
| notification.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg z-50 transform transition-transform duration-300 ${ | |
| type === 'success' ? 'bg-green-100 text-green-800 border border-green-200' : | |
| type === 'error' ? 'bg-red-100 text-red-800 border border-red-200' : | |
| 'bg-blue-100 text-blue-800 border border-blue-200' | |
| }`; | |
| notification.innerHTML = ` | |
| <div class="flex items-center"> | |
| <i class="fas ${ | |
| type === 'success' ? 'fa-check-circle' : | |
| type === 'error' ? 'fa-exclamation-circle' : | |
| 'fa-info-circle' | |
| } mr-2"></i> | |
| <span>${message}</span> | |
| </div> | |
| `; | |
| document.body.appendChild(notification); | |
| // إخفاء الإشعار بعد 5 ثواني | |
| setTimeout(() => { | |
| notification.style.transform = 'translateX(100%)'; | |
| setTimeout(() => { | |
| if (notification.parentNode) { | |
| notification.parentNode.removeChild(notification); | |
| } | |
| }, 300); | |
| }, 5000); | |
| } | |
| // إعداد مستمع الحدث للملف | |
| document.getElementById('zipFile').addEventListener('change', handleFileSelect); | |
| // تفعيل إرسال الرسالة بالضغط على Enter | |
| document.getElementById('chatInput').addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| sendChatMessage(); | |
| } | |
| }); | |
| // تحميل المشاريع عند بدء التشغيل | |
| window.onload = function() { | |
| loadProjects(); | |
| showNotification('مرحباً بك في مركز الوكلاء الذكي!', 'info'); | |
| }; | |
| </script> | |
| </body> | |
| </html>""" | |
| return HTMLResponse(content=html_content) | |
| # ============ API Endpoints ============ | |
| async def test_api(): | |
| """اختبار API""" | |
| return { | |
| "status": "success", | |
| "message": "✅ نظام مركز الوكلاء يعمل بنجاح!", | |
| "timestamp": datetime.now().isoformat(), | |
| "crewai_available": CREWAI_AVAILABLE, | |
| "sessions_count": len(sessions) | |
| } | |
| async def test_connection(): | |
| """اختبار الاتصال مع الخادم""" | |
| return { | |
| "status": "success", | |
| "message": "✅ الاتصال ناجح", | |
| "time": datetime.now().isoformat(), | |
| "total_projects": len(sessions), | |
| "upload_dir": UPLOAD_DIR, | |
| "upload_dir_exists": os.path.exists(UPLOAD_DIR), | |
| "crewai_available": CREWAI_AVAILABLE | |
| } | |
| async def upload_zip(file: UploadFile = File(...)): | |
| """رفع ملف ZIP لمشروع جديد""" | |
| try: | |
| if not file.filename.endswith('.zip'): | |
| raise HTTPException(status_code=400, detail="يجب أن يكون الملف بصيغة ZIP") | |
| # إنشاء معرف فريد للمشروع | |
| project_id = str(uuid.uuid4()) | |
| project_dir = os.path.join(UPLOAD_DIR, project_id) | |
| os.makedirs(project_dir, exist_ok=True) | |
| # حفظ الملف المرفوع | |
| zip_path = os.path.join(project_dir, file.filename) | |
| with open(zip_path, "wb") as buffer: | |
| shutil.copyfileobj(file.file, buffer) | |
| # فك ضغط الملف | |
| extract_dir = os.path.join(project_dir, "extracted") | |
| os.makedirs(extract_dir, exist_ok=True) | |
| with zipfile.ZipFile(zip_path, 'r') as zip_ref: | |
| zip_ref.extractall(extract_dir) | |
| # إنشاء جلسة للمشروع | |
| project_name = file.filename.replace('.zip', '') | |
| sessions[project_id] = { | |
| "name": project_name, | |
| "path": extract_dir, | |
| "status": "active", | |
| "conversation": [ | |
| { | |
| "role": "system", | |
| "content": f"👋 تم إنشاء المشروع '{project_name}' بنجاح. جاهز للتحليل والدردشة.", | |
| "timestamp": datetime.now().isoformat() | |
| } | |
| ], | |
| "crew": None, | |
| "created_at": datetime.now().isoformat(), | |
| "updated_at": datetime.now().isoformat() | |
| } | |
| print(f"✅ تم رفع المشروع: {project_name} (ID: {project_id})") | |
| print(f"📁 المسار: {extract_dir}") | |
| print(f"📊 عدد الجلسات النشطة: {len(sessions)}") | |
| return { | |
| "status": "success", | |
| "message": f"تم رفع المشروع '{project_name}' بنجاح", | |
| "project_id": project_id, | |
| "project_name": project_name, | |
| "extracted_path": extract_dir, | |
| "session_created": True, | |
| "total_projects": len(sessions) | |
| } | |
| except Exception as e: | |
| print(f"❌ خطأ في رفع الملف: {e}") | |
| traceback.print_exc() | |
| raise HTTPException(status_code=500, detail=f"فشل رفع الملف: {str(e)}") | |
| async def get_projects(): | |
| """الحصول على قائمة المشاريع""" | |
| projects = [] | |
| for project_id, session in sessions.items(): | |
| projects.append({ | |
| "id": project_id, | |
| "name": session.get("name", "غير معروف"), | |
| "status": session.get("status", "unknown"), | |
| "conversation_count": len(session.get("conversation", [])), | |
| "created_at": session.get("created_at", ""), | |
| "has_crew": session.get("crew") is not None | |
| }) | |
| print(f"📋 إرجاع {len(projects)} مشروع") | |
| # ترتيب المشاريع من الأحدث إلى الأقدم | |
| projects.sort(key=lambda x: x.get("created_at", ""), reverse=True) | |
| return {"projects": projects} | |
| async def simple_chat(project_id: str, message: str): | |
| """دردشة مبسطة بدون CrewAI""" | |
| if project_id not in sessions: | |
| raise HTTPException(status_code=404, detail="المشروع غير موجود") | |
| project_name = sessions[project_id].get("name", "غير معروف") | |
| # إنشاء رد مبسط | |
| simple_response = f""" | |
| 👋 مرحباً! أنا مدير المشروع المبسط. | |
| **مشروعك:** {project_name} | |
| **رسالتك:** {message} | |
| ## 💡 كيف يمكنني مساعدتك: | |
| 1. **تحليل المشروع**: أستطيع تحليل ملفات مشروعك | |
| 2. **خطط التطوير**: تقديم اقتراحات للتحسين | |
| 3. **نصائح تقنية**: إرشادات برمجية | |
| 4. **هيكلة المشروع**: تنظيم الملفات والمجلدات | |
| ## 📋 للمساعدة الأفضل: | |
| - ارفع ملف ZIP لمشروعك | |
| - اطرح أسئلة محددة | |
| - حدد التقنيات المستخدمة | |
| *ملاحظة: النظام قيد التطوير، الميزات الكاملة قريباً!* | |
| """ | |
| # تحديث المحادثة | |
| sessions[project_id]["conversation"].append({ | |
| "role": "user", | |
| "content": message, | |
| "timestamp": datetime.now().isoformat() | |
| }) | |
| sessions[project_id]["conversation"].append({ | |
| "role": "manager", | |
| "content": simple_response, | |
| "timestamp": datetime.now().isoformat() | |
| }) | |
| return { | |
| "status": "success", | |
| "response": simple_response, | |
| "conversation_length": len(sessions[project_id]["conversation"]) | |
| } | |
| async def start_discussion(request: UserRequest): | |
| """بدء نقاش حول المشروع""" | |
| project_id = request.project_id | |
| if project_id not in sessions: | |
| raise HTTPException(status_code=404, detail="المشروع غير موجود") | |
| try: | |
| # إذا كان CrewAI غير متوفر، استخدم الرد المبسط | |
| if not CREWAI_AVAILABLE: | |
| return await simple_chat(project_id, request.message) | |
| project_path = sessions[project_id]["path"] | |
| crew = sessions[project_id]["crew"] | |
| if not crew: | |
| try: | |
| crew = ProjectManagerCrew(project_path) | |
| sessions[project_id]["crew"] = crew | |
| except Exception as e: | |
| print(f"⚠️ خطأ في إنشاء Crew: {e}") | |
| return await simple_chat(project_id, request.message) | |
| # بدء عملية التطوير | |
| try: | |
| result = crew.create_development_plan(request.message) | |
| except Exception as e: | |
| print(f"⚠️ خطأ في create_development_plan: {e}") | |
| return await simple_chat(project_id, request.message) | |
| # حفظ في المحادثة | |
| sessions[project_id]["conversation"].append({ | |
| "role": "user", | |
| "content": request.message, | |
| "timestamp": datetime.now().isoformat() | |
| }) | |
| sessions[project_id]["conversation"].append({ | |
| "role": "system", | |
| "content": str(result), | |
| "timestamp": datetime.now().isoformat() | |
| }) | |
| # تحديث الحالة والوقت | |
| sessions[project_id]["status"] = "processing_complete" | |
| sessions[project_id]["updated_at"] = datetime.now().isoformat() | |
| return { | |
| "status": "success", | |
| "result": str(result), | |
| "message": "تمت معالجة طلبك بنجاح" | |
| } | |
| except Exception as e: | |
| print(f"❌ خطأ في start_discussion: {e}") | |
| traceback.print_exc() | |
| # استخدم الرد المبسط في حالة الخطأ | |
| return await simple_chat(project_id, request.message) | |
| async def chat_with_manager(request: UserRequest): | |
| """الدردشة مع المدير""" | |
| project_id = request.project_id | |
| if project_id not in sessions: | |
| raise HTTPException(status_code=404, detail="المشروع غير موجود") | |
| try: | |
| # إذا كان CrewAI غير متوفر، استخدم الرد المبسط | |
| if not CREWAI_AVAILABLE: | |
| return await simple_chat(project_id, request.message) | |
| if sessions[project_id]["crew"] is None: | |
| project_path = sessions[project_id]["path"] | |
| try: | |
| crew = ProjectManagerCrew(project_path) | |
| sessions[project_id]["crew"] = crew | |
| except Exception as e: | |
| print(f"⚠️ خطأ في إنشاء Crew: {e}") | |
| return await simple_chat(project_id, request.message) | |
| else: | |
| crew = sessions[project_id]["crew"] | |
| conversation = sessions[project_id]["conversation"] | |
| # الحصول على رد من المدير | |
| try: | |
| response = crew.chat_with_manager(request.message, conversation) | |
| except Exception as e: | |
| print(f"⚠️ خطأ في الدردشة مع Crew: {e}") | |
| return await simple_chat(project_id, request.message) | |
| # تحديث المحادثة | |
| sessions[project_id]["conversation"].append({ | |
| "role": "user", | |
| "content": request.message, | |
| "timestamp": datetime.now().isoformat() | |
| }) | |
| sessions[project_id]["conversation"].append({ | |
| "role": "manager", | |
| "content": str(response), | |
| "timestamp": datetime.now().isoformat() | |
| }) | |
| # تحديث وقت التعديل | |
| sessions[project_id]["updated_at"] = datetime.now().isoformat() | |
| return { | |
| "status": "success", | |
| "response": str(response), | |
| "conversation_length": len(sessions[project_id]["conversation"]) | |
| } | |
| except Exception as e: | |
| print(f"❌ خطأ في chat_with_manager: {e}") | |
| # استخدم الرد المبسط في حالة الخطأ | |
| return await simple_chat(project_id, request.message) | |
| async def get_conversation(project_id: str): | |
| """الحصول على محادثة المشروع""" | |
| if project_id not in sessions: | |
| raise HTTPException(status_code=404, detail="المشروع غير موجود") | |
| return { | |
| "status": "success", | |
| "project_id": project_id, | |
| "project_name": sessions[project_id].get("name", ""), | |
| "conversation": sessions[project_id]["conversation"], | |
| "total_messages": len(sessions[project_id]["conversation"]) | |
| } | |
| async def get_project_files(project_id: str): | |
| """الحصول على ملفات المشروع""" | |
| if project_id not in sessions: | |
| raise HTTPException(status_code=404, detail="المشروع غير موجود") | |
| project_path = sessions[project_id]["path"] | |
| files = [] | |
| try: | |
| for root, dirs, filenames in os.walk(project_path): | |
| for filename in filenames: | |
| file_path = os.path.join(root, filename) | |
| rel_path = os.path.relpath(file_path, project_path) | |
| # الحصول على حجم الملف | |
| size = os.path.getsize(file_path) | |
| size_mb = size / (1024 * 1024) | |
| # الحصول على امتداد الملف | |
| _, ext = os.path.splitext(filename) | |
| files.append({ | |
| "name": filename, | |
| "path": rel_path, | |
| "size_mb": round(size_mb, 2), | |
| "extension": ext, | |
| "directory": root.replace(project_path, '').lstrip('/') | |
| }) | |
| return { | |
| "status": "success", | |
| "project_id": project_id, | |
| "total_files": len(files), | |
| "files": files[:50] # إرجاع أول 50 ملف فقط | |
| } | |
| except Exception as e: | |
| print(f"❌ خطأ في قراءة الملفات: {e}") | |
| raise HTTPException(status_code=500, detail=f"فشل قراءة الملفات: {str(e)}") | |
| async def analyze_project(project_id: str): | |
| """تحليل المشروع""" | |
| if project_id not in sessions: | |
| raise HTTPException(status_code=404, detail="المشروع غير موجود") | |
| project_path = sessions[project_id]["path"] | |
| try: | |
| # تحليل بسيط للمشروع | |
| file_count = 0 | |
| file_types = {} | |
| total_size = 0 | |
| for root, dirs, filenames in os.walk(project_path): | |
| file_count += len(filenames) | |
| for filename in filenames: | |
| file_path = os.path.join(root, filename) | |
| total_size += os.path.getsize(file_path) | |
| # تحليل أنواع الملفات | |
| _, ext = os.path.splitext(filename) | |
| file_types[ext] = file_types.get(ext, 0) + 1 | |
| # تحليل هيكل المجلدات | |
| dirs = [] | |
| for item in os.listdir(project_path): | |
| item_path = os.path.join(project_path, item) | |
| if os.path.isdir(item_path): | |
| dirs.append(item) | |
| return { | |
| "status": "success", | |
| "project_id": project_id, | |
| "analysis": { | |
| "file_count": file_count, | |
| "total_size_mb": round(total_size / (1024 * 1024), 2), | |
| "file_types": file_types, | |
| "top_directories": dirs[:10], | |
| "has_readme": os.path.exists(os.path.join(project_path, "README.md")), | |
| "has_requirements": os.path.exists(os.path.join(project_path, "requirements.txt")), | |
| "has_package_json": os.path.exists(os.path.join(project_path, "package.json")), | |
| "has_dockerfile": any(f.lower().startswith('dockerfile') for f in os.listdir(project_path)) | |
| } | |
| } | |
| except Exception as e: | |
| print(f"❌ خطأ في تحليل المشروع: {e}") | |
| raise HTTPException(status_code=500, detail=f"فشل تحليل المشروع: {str(e)}") | |
| async def websocket_endpoint(websocket: WebSocket, project_id: str): | |
| """WebSocket للدردشة المباشرة""" | |
| await manager.connect(websocket, project_id) | |
| if project_id not in sessions: | |
| await websocket.send_text(json.dumps({ | |
| "type": "error", | |
| "message": "المشروع غير موجود" | |
| })) | |
| await websocket.close() | |
| return | |
| try: | |
| # إرسال رسالة ترحيبية | |
| welcome_message = "👋 مرحباً! أنا مدير المشروع الذكي. كيف يمكنني مساعدتك اليوم؟" | |
| await websocket.send_text(json.dumps({ | |
| "type": "manager", | |
| "message": welcome_message | |
| })) | |
| while True: | |
| data = await websocket.receive_text() | |
| message_data = json.loads(data) | |
| if message_data["type"] == "user_message": | |
| user_message = message_data["message"] | |
| # إرسال رد مؤقت | |
| await websocket.send_text(json.dumps({ | |
| "type": "status", | |
| "message": "⚙️ المدير يفكر في رد..." | |
| })) | |
| # الحصول على الرد باستخدام API العادي | |
| try: | |
| # استخدام API العادي للحصول على الرد | |
| import aiohttp | |
| import asyncio | |
| async with aiohttp.ClientSession() as session: | |
| async with session.post( | |
| f"http://localhost:7860/api/chat", | |
| json={"project_id": project_id, "message": user_message}, | |
| timeout=30 | |
| ) as response: | |
| if response.status == 200: | |
| data = await response.json() | |
| response_text = data.get("response", "❌ لم أستطع فهم سؤالك.") | |
| else: | |
| response_text = "❌ حدث خطأ في الخادم." | |
| except: | |
| # استخدام رد بديل إذا فشل الاتصال | |
| response_text = f"💬 رسالتك: '{user_message}'\n\nأنا هنا للمساعدة! يمكنك استخدام واجهة الدردشة العادية." | |
| # إرسال الرد | |
| await websocket.send_text(json.dumps({ | |
| "type": "manager", | |
| "message": response_text | |
| })) | |
| except WebSocketDisconnect: | |
| manager.disconnect(project_id) | |
| except Exception as e: | |
| print(f"❌ خطأ في WebSocket: {e}") | |
| await websocket.send_text(json.dumps({ | |
| "type": "error", | |
| "message": f"حدث خطأ: {str(e)}" | |
| })) | |
| manager.disconnect(project_id) | |
| async def debug(): | |
| """صفحة تصحيح""" | |
| # جمع معلومات النظام | |
| system_info = { | |
| "system": platform.system(), | |
| "release": platform.release(), | |
| "python_version": platform.python_version(), | |
| "processor": platform.processor(), | |
| "memory_total_gb": round(psutil.virtual_memory().total / (1024**3), 2), | |
| "memory_used_gb": round(psutil.virtual_memory().used / (1024**3), 2), | |
| "memory_percent": psutil.virtual_memory().percent, | |
| "cpu_count": psutil.cpu_count(), | |
| "cpu_percent": psutil.cpu_percent(interval=1), | |
| "disk_usage": { | |
| "total_gb": round(psutil.disk_usage('/').total / (1024**3), 2), | |
| "used_gb": round(psutil.disk_usage('/').used / (1024**3), 2), | |
| "free_gb": round(psutil.disk_usage('/').free / (1024**3), 2), | |
| "percent": psutil.disk_usage('/').percent | |
| } | |
| } | |
| # تحليل مجلد المشاريع | |
| upload_dir_info = { | |
| "path": UPLOAD_DIR, | |
| "exists": os.path.exists(UPLOAD_DIR), | |
| "absolute_path": os.path.abspath(UPLOAD_DIR) if os.path.exists(UPLOAD_DIR) else None, | |
| "permissions": oct(os.stat(UPLOAD_DIR).st_mode)[-3:] if os.path.exists(UPLOAD_DIR) else None, | |
| "size_mb": get_dir_size(UPLOAD_DIR) if os.path.exists(UPLOAD_DIR) else 0, | |
| "project_count": len([d for d in os.listdir(UPLOAD_DIR) if os.path.isdir(os.path.join(UPLOAD_DIR, d))]) if os.path.exists(UPLOAD_DIR) else 0, | |
| "projects": [] | |
| } | |
| if os.path.exists(UPLOAD_DIR): | |
| try: | |
| projects = [] | |
| for project_id in os.listdir(UPLOAD_DIR): | |
| project_path = os.path.join(UPLOAD_DIR, project_id) | |
| if os.path.isdir(project_path): | |
| project_info = { | |
| "id": project_id, | |
| "size_mb": round(get_dir_size(project_path), 2), | |
| "file_count": count_files(project_path), | |
| "created": datetime.fromtimestamp(os.path.getctime(project_path)).isoformat() if os.path.exists(project_path) else None, | |
| "modified": datetime.fromtimestamp(os.path.getmtime(project_path)).isoformat() if os.path.exists(project_path) else None | |
| } | |
| projects.append(project_info) | |
| upload_dir_info["projects"] = sorted(projects, key=lambda x: x["size_mb"], reverse=True)[:10] # أول 10 مشاريع | |
| except Exception as e: | |
| upload_dir_info["error"] = str(e) | |
| # تحليل الجلسات النشطة | |
| active_sessions = [] | |
| for pid, session in sessions.items(): | |
| session_info = { | |
| "id": pid, | |
| "name": session.get("name", "غير معروف"), | |
| "status": session.get("status", "غير معروف"), | |
| "conversation_count": len(session.get("conversation", [])), | |
| "has_crew": session.get("crew") is not None, | |
| "path_exists": os.path.exists(session.get("path", "")) if session.get("path") else False, | |
| "size_mb": round(get_dir_size(session.get("path", "")), 2) if session.get("path") and os.path.exists(session.get("path")) else 0 | |
| } | |
| active_sessions.append(session_info) | |
| # تحليل المتغيرات البيئية | |
| env_vars = {} | |
| important_vars = ["HF_TOKEN", "OPENAI_API_KEY", "SERPER_API_KEY", "DEBUG", "PYTHONPATH"] | |
| for var in important_vars: | |
| value = os.getenv(var) | |
| if value: | |
| if var.endswith("_TOKEN") or var.endswith("_KEY"): | |
| env_vars[var] = f"✅ موجود ({len(value)} حرف)" if value else "❌ غير موجود" | |
| else: | |
| env_vars[var] = value | |
| # تحليل الموديولات المثبتة | |
| installed_modules = {} | |
| important_modules = ["fastapi", "uvicorn", "crewai", "langchain", "huggingface_hub", "requests", "websockets"] | |
| for module in important_modules: | |
| try: | |
| import importlib | |
| importlib.import_module(module) | |
| installed_modules[module] = "✅ مثبت" | |
| except ImportError: | |
| installed_modules[module] = "❌ غير مثبت" | |
| # إحصائيات API | |
| api_stats = { | |
| "total_projects": len(sessions), | |
| "active_websockets": len(manager.active_connections), | |
| "total_conversations": sum(len(s.get("conversation", [])) for s in sessions.values()), | |
| "avg_conversation_length": round(sum(len(s.get("conversation", [])) for s in sessions.values()) / len(sessions), 2) if sessions else 0 | |
| } | |
| # تحليل الذاكرة للمشاريع | |
| memory_info = { | |
| "process_memory_mb": round(psutil.Process().memory_info().rss / (1024**2), 2), | |
| "process_memory_percent": psutil.Process().memory_percent(), | |
| "open_files": len(psutil.Process().open_files()), | |
| "threads": psutil.Process().num_threads(), | |
| "cpu_times": str(psutil.Process().cpu_times()) | |
| } | |
| # تحليل الشبكة | |
| try: | |
| net_info = psutil.net_io_counters() | |
| network_info = { | |
| "bytes_sent_mb": round(net_info.bytes_sent / (1024**2), 2), | |
| "bytes_recv_mb": round(net_info.bytes_recv / (1024**2), 2), | |
| "packets_sent": net_info.packets_sent, | |
| "packets_recv": net_info.packets_recv | |
| } | |
| except: | |
| network_info = {"error": "لا يمكن قراءة معلومات الشبكة"} | |
| # معلومات التطبيق | |
| app_info = { | |
| "start_time": datetime.now().isoformat(), | |
| "uptime_seconds": (datetime.now() - app_start_time).total_seconds() if 'app_start_time' in globals() else 0, | |
| "version": "1.0.0", | |
| "endpoints": [ | |
| {"path": "/", "method": "GET", "description": "الواجهة الرئيسية"}, | |
| {"path": "/api/upload-zip", "method": "POST", "description": "رفع مشروع"}, | |
| {"path": "/api/projects", "method": "GET", "description": "قائمة المشاريع"}, | |
| {"path": "/api/chat", "method": "POST", "description": "الدردشة مع المدير"}, | |
| {"path": "/ws/{project_id}", "method": "WebSocket", "description": "دردشة مباشرة"}, | |
| {"path": "/debug", "method": "GET", "description": "صفحة التصحيح"} | |
| ] | |
| } | |
| # تحقق من المشاكل | |
| issues = check_for_issues(upload_dir_info, sessions, env_vars) | |
| # توليد التوصيات | |
| recommendations = generate_recommendations(upload_dir_info, system_info, env_vars) | |
| return { | |
| "status": "active", | |
| "timestamp": datetime.now().isoformat(), | |
| # معلومات النظام | |
| "system": system_info, | |
| # معلومات التطبيق | |
| "application": app_info, | |
| # معلومات التخزين | |
| "storage": { | |
| "upload_directory": upload_dir_info, | |
| "active_sessions": active_sessions | |
| }, | |
| # إحصائيات API | |
| "api_statistics": api_stats, | |
| # معلومات الموديولات | |
| "modules": { | |
| "crewai_available": CREWAI_AVAILABLE, | |
| "important_modules": installed_modules | |
| }, | |
| # المتغيرات البيئية | |
| "environment": { | |
| "important_variables": env_vars, | |
| "python_path": os.getenv("PYTHONPATH", "غير محدد"), | |
| "current_working_dir": os.getcwd(), | |
| "script_location": os.path.abspath(__file__) | |
| }, | |
| # معلومات الذاكرة والأداء | |
| "performance": { | |
| "memory": memory_info, | |
| "network": network_info | |
| }, | |
| # المشاكل المحتملة | |
| "issues": issues, | |
| # التوصيات | |
| "recommendations": recommendations | |
| } |