My-AI-Assistant / app.py
Ahmedoooooo's picture
Rename app.py .py to app.py
01a8a4b verified
Raw
History Blame Contribute Delete
17.5 kB
#!/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)