Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| import os | |
| import sys | |
| import asyncio | |
| import secrets | |
| import sqlite3 | |
| import hashlib | |
| import time | |
| import json | |
| from pathlib import Path | |
| from datetime import datetime | |
| from typing import List, Dict, Any, Optional, Tuple | |
| import gradio as gr | |
| import aiohttp | |
| from bs4 import BeautifulSoup | |
| import numpy as np | |
| # ============================================================================= | |
| # 0. محاولة استيراد المكتبات القوية مع fallbacks | |
| # ============================================================================= | |
| GROQ_AVAILABLE = False | |
| CHROMA_AVAILABLE = False | |
| SENTENCE_AVAILABLE = False | |
| CRAWL4AI_AVAILABLE = False | |
| TAVILY_AVAILABLE = False | |
| try: | |
| from groq import Groq, AsyncGroq | |
| GROQ_AVAILABLE = True | |
| except ImportError: | |
| pass | |
| try: | |
| import chromadb | |
| from chromadb.utils import embedding_functions | |
| CHROMA_AVAILABLE = True | |
| except ImportError: | |
| pass | |
| try: | |
| from sentence_transformers import SentenceTransformer | |
| SENTENCE_AVAILABLE = True | |
| except ImportError: | |
| pass | |
| try: | |
| from crawl4ai import AsyncWebCrawler | |
| CRAWL4AI_AVAILABLE = True | |
| except ImportError: | |
| pass | |
| try: | |
| import tavily | |
| TAVILY_AVAILABLE = True | |
| except ImportError: | |
| pass | |
| # ============================================================================= | |
| # 1. التكوين والمفاتيح | |
| # ============================================================================= | |
| OWNER_SECRET = os.environ.get("OWNER_SECRET", "admin_giant_2026") | |
| GROQ_API_KEY = os.environ.get("GROQ_API_KEY", "") | |
| TAVILY_API_KEY = os.environ.get("TAVILY_API_KEY", "") | |
| if GROQ_AVAILABLE and GROQ_API_KEY: | |
| groq_client = Groq(api_key=GROQ_API_KEY) | |
| async_groq = AsyncGroq(api_key=GROQ_API_KEY) | |
| else: | |
| groq_client = None | |
| async_groq = None | |
| # ============================================================================= | |
| # 2. الذاكرة المتجهية (ChromaDB إن أمكن، وإلا بديل بسيط) | |
| # ============================================================================= | |
| class VectorMemory: | |
| def __init__(self): | |
| self.use_chroma = False | |
| self.collection = None | |
| self.embed_fn = None | |
| self.simple_data = [] | |
| self.simple_embedder = None | |
| if CHROMA_AVAILABLE and SENTENCE_AVAILABLE: | |
| try: | |
| self.client = chromadb.PersistentClient(path="./chroma_db") | |
| self.embed_fn = embedding_functions.SentenceTransformerEmbeddingFunction( | |
| model_name="all-MiniLM-L6-v2" | |
| ) | |
| self.collection = self.client.get_or_create_collection( | |
| name="giant_memory", | |
| embedding_function=self.embed_fn | |
| ) | |
| self.use_chroma = True | |
| print("✅ ChromaDB + SentenceTransformer active") | |
| except Exception as e: | |
| print(f"⚠️ ChromaDB init failed: {e}") | |
| if not self.use_chroma and SENTENCE_AVAILABLE: | |
| self.simple_embedder = SentenceTransformer("all-MiniLM-L6-v2") | |
| print("✅ Fallback memory (SentenceTransformer) active") | |
| elif not self.use_chroma: | |
| print("⚠️ No vector memory available, using simple list") | |
| def store(self, text: str, metadata: Dict = None): | |
| if self.use_chroma and self.collection: | |
| doc_id = hashlib.md5(f"{text}{time.time()}".encode()).hexdigest() | |
| self.collection.upsert( | |
| ids=[doc_id], | |
| documents=[text[:2000]], | |
| metadatas=[metadata or {}] | |
| ) | |
| elif self.simple_embedder: | |
| self.simple_data.append({"text": text[:2000], "metadata": metadata or {}}) | |
| else: | |
| self.simple_data.append({"text": text[:2000], "metadata": metadata or {}}) | |
| def retrieve(self, query: str, n: int = 5) -> List[Dict]: | |
| if self.use_chroma and self.collection: | |
| try: | |
| results = self.collection.query(query_texts=[query], n_results=n) | |
| docs = results.get("documents", [[]])[0] | |
| metas = results.get("metadatas", [[]])[0] | |
| dists = results.get("distances", [[]])[0] | |
| return [{"text": d, "metadata": m, "score": 1 - dist} for d, m, dist in zip(docs, metas, dists)] | |
| except: | |
| pass | |
| if self.simple_embedder and self.simple_data: | |
| q_emb = self.simple_embedder.encode(query) | |
| scores = [] | |
| for item in self.simple_data: | |
| emb = self.simple_embedder.encode(item["text"][:500]) | |
| sim = np.dot(q_emb, emb) / (np.linalg.norm(q_emb) * np.linalg.norm(emb) + 1e-8) | |
| scores.append(sim) | |
| indices = np.argsort(scores)[-n:][::-1] | |
| return [{"text": self.simple_data[i]["text"], "metadata": self.simple_data[i]["metadata"], "score": scores[i]} for i in indices] | |
| # آخر بديل: بحث نصي بسيط | |
| if self.simple_data: | |
| return [{"text": item["text"], "metadata": item["metadata"], "score": 0.5} for item in self.simple_data[:n]] | |
| return [] | |
| memory = VectorMemory() | |
| # ============================================================================= | |
| # 3. محرك البحث العميق (Tavily أو DuckDuckGo) | |
| # ============================================================================= | |
| class DeepSearchEngine: | |
| def __init__(self): | |
| self.session = None | |
| self.tavily_key = TAVILY_API_KEY | |
| async def _get_session(self): | |
| if self.session is None or self.session.closed: | |
| self.session = aiohttp.ClientSession() | |
| return self.session | |
| async def search_tavily(self, query: str) -> str: | |
| if not self.tavily_key: | |
| return "" | |
| try: | |
| session = await self._get_session() | |
| async with session.post( | |
| "https://api.tavily.com/search", | |
| json={"api_key": self.tavily_key, "query": query, "max_results": 4} | |
| ) as resp: | |
| if resp.status == 200: | |
| data = await resp.json() | |
| results = data.get("results", []) | |
| output = "🔍 **Tavily نتائج:**\n" | |
| for r in results[:3]: | |
| title = r.get("title", "") | |
| content = r.get("content", "")[:200] | |
| url = r.get("url", "") | |
| output += f"- **{title}**: {content}...\n 🔗 {url}\n" | |
| return output | |
| except Exception as e: | |
| print(f"Tavily error: {e}") | |
| return "" | |
| async def search_duckduckgo(self, query: str) -> str: | |
| try: | |
| session = await self._get_session() | |
| url = "https://html.duckduckgo.com/html/" | |
| async with session.get(url, params={"q": query}) as resp: | |
| if resp.status == 200: | |
| html = await resp.text() | |
| soup = BeautifulSoup(html, "html.parser") | |
| results = [] | |
| for r in soup.select(".result")[:4]: | |
| title = r.select_one(".result__a") | |
| snippet = r.select_one(".result__snippet") | |
| if title: | |
| t = title.get_text(strip=True) | |
| s = snippet.get_text(strip=True) if snippet else "" | |
| results.append(f"- **{t}**: {s[:200]}...") | |
| if results: | |
| return "🔍 **DuckDuckGo نتائج:**\n" + "\n".join(results) | |
| except Exception as e: | |
| print(f"DuckDuckGo error: {e}") | |
| return "" | |
| async def deep_search(self, query: str) -> str: | |
| # حاول Tavily أولاً | |
| res = await self.search_tavily(query) | |
| if res: | |
| return res | |
| # ثم DuckDuckGo | |
| res = await self.search_duckduckgo(query) | |
| if res: | |
| return res | |
| return "⚠️ لم يتم العثور على معلومات حديثة." | |
| deep_search = DeepSearchEngine() | |
| # ============================================================================= | |
| # 4. إدارة السياق | |
| # ============================================================================= | |
| class ConversationContext: | |
| def __init__(self): | |
| self.history = [] | |
| def add(self, user: str, assistant: str): | |
| self.history.append((user, assistant)) | |
| if len(self.history) > 10: | |
| self.history.pop(0) | |
| def get_prompt(self) -> str: | |
| if not self.history: | |
| return "" | |
| context = "\n[سِيَاقُ الْمُحَادَثَةِ]\n" | |
| for i, (u, a) in enumerate(self.history[-5:], 1): | |
| context += f"{i}. المستخدم: {u[:200]}\n المساعد: {a[:200]}\n" | |
| return context + "[/سِيَاقُ الْمُحَادَثَةِ]\n" | |
| context = ConversationContext() | |
| # ============================================================================= | |
| # 5. دوال المالك | |
| # ============================================================================= | |
| def read_local_files() -> str: | |
| count = 0 | |
| for root, _, files in os.walk("."): | |
| if any(x in root for x in ["chroma_db", "__pycache__", ".git", "venv"]): | |
| continue | |
| for f in files: | |
| if f.endswith((".txt", ".md", ".py", ".json", ".csv")): | |
| path = os.path.join(root, f) | |
| try: | |
| with open(path, "r", encoding="utf-8", errors="ignore") as fp: | |
| content = fp.read()[:3000] | |
| memory.store(f"ملف: {path}\n{content}", {"source": path}) | |
| count += 1 | |
| except: | |
| pass | |
| return f"✅ تم قراءة {count} ملف." | |
| async def learn_website(url: str) -> str: | |
| try: | |
| # استخدام Crawl4AI إذا متاح | |
| if CRAWL4AI_AVAILABLE: | |
| async with AsyncWebCrawler() as crawler: | |
| result = await crawler.arun(url=url, bypass_cache=True) | |
| if result and result.markdown: | |
| content = result.markdown[:5000] | |
| memory.store(f"محتوى {url}:\n{content}", {"source": url, "type": "website"}) | |
| return f"✅ تم تعلم الموقع {url} بنجاح (Crawl4AI)." | |
| # بديل: aiohttp + BeautifulSoup | |
| async with aiohttp.ClientSession() as session: | |
| async with session.get(url) as resp: | |
| if resp.status == 200: | |
| html = await resp.text() | |
| soup = BeautifulSoup(html, "html.parser") | |
| for s in soup(["script", "style"]): | |
| s.decompose() | |
| text = soup.get_text(separator="\n", strip=True)[:5000] | |
| memory.store(f"محتوى {url}:\n{text}", {"source": url, "type": "website"}) | |
| return f"✅ تم تعلم الموقع {url} بنجاح." | |
| except Exception as e: | |
| return f"❌ خطأ: {e}" | |
| return f"⚠️ فشل تعلم {url}" | |
| # ============================================================================= | |
| # 6. توليد الرد باستخدام Groq | |
| # ============================================================================= | |
| async def generate_response(user_message: str) -> str: | |
| # ردود سريعة | |
| if any(g in user_message.lower() for g in ["السلام", "اهلا", "مرحبا", "كيف حالك"]): | |
| return "وَعَلَيْكُمُ السَّلَامُ! أَنَا بِخَيْرٍ، شُكْرًا. كَيْفَ يُمْكِنُنِي مُسَاعَدَتُكَ؟" | |
| now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
| ctx = context.get_prompt() | |
| memories = memory.retrieve(user_message, n=3) | |
| local = "\n".join([m["text"][:300] for m in memories]) if memories else "" | |
| needs_search = any(k in user_message.lower() for k in ["سعر", "دولار", "أخبار", "طقس"]) | |
| web_info = await deep_search.deep_search(user_message) if needs_search else "" | |
| if GROQ_AVAILABLE and async_groq: | |
| try: | |
| prompt = f"""الوقت الحالي: {now} | |
| {ctx} | |
| المعرفة المحلية: {local[:1000]} | |
| المعلومات الحديثة: {web_info[:1000]} | |
| سؤال المستخدم: {user_message} | |
| أجب بالعربية بدقة ووضوح، مستخدماً الوقت الحالي والمعلومات الحديثة.""" | |
| response = await async_groq.chat.completions.create( | |
| model="llama-3.3-70b-versatile", | |
| messages=[{"role": "user", "content": prompt}], | |
| temperature=0.7, | |
| max_tokens=1024 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| return f"⚠️ خطأ في Groq: {e}\n\nالسياق: {local[:300]}\n{web_info[:300]}" | |
| else: | |
| return f"الوقت: {now}\nالسياق: {ctx[:300]}\nالذاكرة: {local[:300]}\nالويب: {web_info[:300]}\nسؤالك: {user_message}" | |
| # ============================================================================= | |
| # 7. قاعدة بيانات الدردشة (SQLite) | |
| # ============================================================================= | |
| DB_PATH = Path("./chat_history.db") | |
| SESSION_ID = secrets.token_hex(16) | |
| def init_db(): | |
| conn = sqlite3.connect(str(DB_PATH)) | |
| conn.execute("CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY, role TEXT, content TEXT, session TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)") | |
| conn.close() | |
| def save_message(role: str, content: str): | |
| conn = sqlite3.connect(str(DB_PATH)) | |
| conn.execute("INSERT INTO messages (role, content, session) VALUES (?, ?, ?)", (role, content, SESSION_ID)) | |
| conn.commit() | |
| conn.close() | |
| def load_history(limit: int = 30) -> List[Tuple[str, str]]: | |
| conn = sqlite3.connect(str(DB_PATH)) | |
| cur = conn.execute("SELECT role, content FROM messages WHERE session=? ORDER BY id DESC LIMIT ?", (SESSION_ID, limit)) | |
| rows = cur.fetchall() | |
| conn.close() | |
| return [(r[0], r[1]) for r in reversed(rows)] | |
| def delete_history(): | |
| conn = sqlite3.connect(str(DB_PATH)) | |
| conn.execute("DELETE FROM messages WHERE session=?", (SESSION_ID,)) | |
| conn.commit() | |
| conn.close() | |
| init_db() | |
| # ============================================================================= | |
| # 8. معالج الدردشة | |
| # ============================================================================= | |
| async def chat_response(message: str, history: List[Tuple[str, str]]) -> str: | |
| if message.startswith("/"): | |
| parts = message.strip().split(maxsplit=2) | |
| cmd = parts[0].lower() | |
| if cmd == "/help": | |
| return """📖 **أوامر المالك**: | |
| /delete <المفتاح> - مسح الدردشة | |
| /learn <المفتاح> - قراءة الملفات | |
| /learnsite <المفتاح> <الرابط> - تعلم موقع | |
| /status <المفتاح> - حالة النظام | |
| /help - هذه المساعدة""" | |
| if len(parts) < 2: | |
| return f"⚠️ الأمر {cmd} يحتاج إلى المفتاح" | |
| if parts[1] != OWNER_SECRET: | |
| return "⛔ مفتاح غير صحيح" | |
| if cmd == "/delete": | |
| delete_history() | |
| return "🗑️ تم مسح الدردشة" | |
| elif cmd == "/learn": | |
| return read_local_files() | |
| elif cmd == "/learnsite": | |
| if len(parts) < 3: | |
| return "⚠️ الصيغة: /learnsite <المفتاح> <الرابط>" | |
| return await learn_website(parts[2]) | |
| elif cmd == "/status": | |
| return f"""**حالة النظام**: | |
| - Groq API: {'✅' if GROQ_AVAILABLE else '❌'} | |
| - ChromaDB: {'✅' if CHROMA_AVAILABLE else '❌'} | |
| - SentenceTransformers: {'✅' if SENTENCE_AVAILABLE else '❌'} | |
| - Crawl4AI: {'✅' if CRAWL4AI_AVAILABLE else '❌'} | |
| - Tavily: {'✅' if TAVILY_AVAILABLE else '❌'} | |
| - الوقت: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}""" | |
| else: | |
| return f"⚠️ أمر غير معروف: {cmd}" | |
| save_message("user", message) | |
| answer = await generate_response(message) | |
| save_message("assistant", answer) | |
| context.add(message, answer) | |
| return answer | |
| # ============================================================================= | |
| # 9. واجهة Gradio | |
| # ============================================================================= | |
| def create_interface(): | |
| async def respond(message, history): | |
| return await chat_response(message, history) | |
| demo = gr.ChatInterface( | |
| fn=respond, | |
| title="🦾 GIANT-AI ULTIMATE v52.0 - العملاق الكامل", | |
| description="مساعد ذكي عملاق - يدعم البحث العميق، الذاكرة المتجهية، وتعلم المواقع.\nللمالك: /help", | |
| examples=["السلام عليكم", "كم سعر الدولار اليوم؟", "/learnsite admin_giant_2026 https://example.com", "/status admin_giant_2026"] | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| print("🦾 GIANT-AI ULTIMATE v52.0 - تشغيل...") | |
| print(f"Groq: {'متاح' if GROQ_AVAILABLE else 'غير متاح'}") | |
| print(f"ChromaDB: {'متاح' if CHROMA_AVAILABLE else 'غير متاح'}") | |
| print(f"Crawl4AI: {'متاح' if CRAWL4AI_AVAILABLE else 'غير متاح'}") | |
| demo = create_interface() | |
| demo.queue() | |
| demo.launch(server_name="0.0.0.0", server_port=7860) |