builder.ai / app.py
1Egyb's picture
Update app.py
982a999 verified
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 الرئيسي ============
@app.get("/")
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 ============
@app.get("/api/test")
async def test_api():
"""اختبار API"""
return {
"status": "success",
"message": "✅ نظام مركز الوكلاء يعمل بنجاح!",
"timestamp": datetime.now().isoformat(),
"crewai_available": CREWAI_AVAILABLE,
"sessions_count": len(sessions)
}
@app.get("/api/test-connection")
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
}
@app.post("/api/upload-zip")
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)}")
@app.get("/api/projects")
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}
@app.get("/api/simple-chat")
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"])
}
@app.post("/api/start-discussion")
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)
@app.post("/api/chat")
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)
@app.get("/api/conversation/{project_id}")
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"])
}
@app.get("/api/project/{project_id}/files")
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)}")
@app.get("/api/project/{project_id}/analyze")
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)}")
@app.websocket("/ws/{project_id}")
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)
@app.get("/debug")
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
}