diff --git a/ai_engine.py b/ai_engine.py deleted file mode 100644 index abd0582..0000000 --- a/ai_engine.py +++ /dev/null @@ -1,33 +0,0 @@ -import os -from dotenv import load_dotenv -from openai import OpenAI -from groq import Groq -from google import genai - -# Chargement des variables d'environnement en premier -load_dotenv() - -local_client = OpenAI(base_url="http://192.168.1.20:11434/v1", api_key="ollama") -groq_client = Groq(api_key=os.getenv("GROQ_API_KEY")) -gemini_client = genai.Client(api_key=os.getenv("GEMINI_API_KEY")) - -def interroger_gemini_cascade(prompt): - modeles_a_tester = ["gemini-3.1-pro-preview", "gemini-3.1-pro", "gemini-2.5-pro", "gemini-3.7-flash", "gemini-3.6-flash"] - for nom_modele in modeles_a_tester: - try: - config = {"system_instruction": "Réponds toujours dans la langue de l'utilisateur. Utilise exclusivement le format Markdown pour les tableaux et la mise en forme. N'utilise JAMAIS de code LaTeX."} - chat = gemini_client.chats.create(model=nom_modele, config=config) - ia_response = chat.send_message(prompt) - if not ia_response.text: - raise Exception("Réponse vide de Google.") - return ia_response.text, nom_modele - except Exception: - continue - raise Exception("Tous les modèles Gemini sont indisponibles actuellement.") - -def appeler_groq_api(prompt_text): - resp = groq_client.chat.completions.create( - messages=[{"role": "system", "content": "Réponds dans la langue de l'utilisateur. Tu as accès aux recherches web fournies. Utilise uniquement Markdown. Pas de code LaTeX."}, {"role": "user", "content": prompt_text}], - model="llama-3.3-70b-versatile" - ) - return resp.choices[0].message.content, "#f97316", "GROQ", "llama-3.3-70b" \ No newline at end of file diff --git a/database.py b/database.py deleted file mode 100644 index af98d4a..0000000 --- a/database.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import json -import time - -HISTORY_FILE = "aethas_history.json" - -def load_history(): - if not os.path.exists(HISTORY_FILE): - with open(HISTORY_FILE, 'w', encoding='utf-8') as f: - json.dump({}, f) - with open(HISTORY_FILE, 'r', encoding='utf-8') as f: - return json.load(f) - -def save_history(history): - with open(HISTORY_FILE, 'w', encoding='utf-8') as f: - json.dump(history, f, indent=4) - -def nettoyer_vieilles_discussions(): - history = load_history() - now = time.time() - un_an_en_secondes = 365 * 24 * 60 * 60 - a_supprimer = [cid for cid, data in history.items() if not data.get('pinned', False) and now - data.get('timestamp', now) > un_an_en_secondes] - if a_supprimer: - for cid in a_supprimer: - del history[cid] - save_history(history) - print(f"🧹 AETHAS CORE : Nettoyage auto terminé. {len(a_supprimer)} discussion(s) supprimée(s).") diff --git a/router.py b/router.py deleted file mode 100644 index d11b0c5..0000000 --- a/router.py +++ /dev/null @@ -1,14 +0,0 @@ -import json -import re - -def nettoyer_decision_router(raw_text): - try: - clean = re.sub(r'```(?:json)?\s*', '', raw_text) - clean = clean.replace('```', '').strip() - start = clean.find('{') - end = clean.rfind('}') + 1 - if start != -1 and end != 0: - return json.loads(clean[start:end]) - except Exception: - pass - return {"intention": "Analyse", "fournisseur": "gemini"} diff --git a/scraper.py b/scraper.py deleted file mode 100644 index 386c4a4..0000000 --- a/scraper.py +++ /dev/null @@ -1,42 +0,0 @@ -import requests -from bs4 import BeautifulSoup - -def rechercher_sur_google(requete): - try: - url = f"https://lite.duckduckgo.com/lite/" - data = {'q': requete} - headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} - resp = requests.post(url, data=data, headers=headers, timeout=5) - soup = BeautifulSoup(resp.text, 'html.parser') - - urls = [] - for a in soup.find_all('a', class_='result-link', href=True): - href = a['href'] - if href.startswith('http') and 'duckduckgo' not in href: - urls.append(href) - if len(urls) >= 5: - break - if not urls: - for a in soup.find_all('a', href=True): - href = a['href'] - if href.startswith('http') and 'duckduckgo' not in href and href not in urls: - urls.append(href) - if len(urls) >= 5: - break - return urls[:5] - except Exception as e: - print(f"Erreur recherche web: {e}") - return [] - -def extraire_contenu_urls(urls_list): - scraped_text = "" - for url in urls_list: - try: - resp = requests.get(url, timeout=5, headers={"User-Agent": "Mozilla/5.0"}) - soup = BeautifulSoup(resp.text, 'html.parser') - for script in soup(["script", "style", "nav", "footer"]): - script.extract() - scraped_text += f"\n\n--- Source web extraite ({url}) ---\n{soup.get_text(separator=' ', strip=True)[:3000]}" - except Exception as e: - scraped_text += f"\n\n[Impossible de récupérer {url}: {e}]" - return scraped_text diff --git a/setup_modules.py b/setup_modules.py deleted file mode 100644 index 48d9024..0000000 --- a/setup_modules.py +++ /dev/null @@ -1,190 +0,0 @@ -import os - -# Création du dossier de rangement -os.makedirs("modules", exist_ok=True) - -# 1. database.py -with open("modules/database.py", "w", encoding="utf-8") as f: - f.write('''import os -import json -import time - -HISTORY_FILE = "aethas_history.json" - -def load_history(): - if not os.path.exists(HISTORY_FILE): - with open(HISTORY_FILE, 'w', encoding='utf-8') as f: - json.dump({}, f) - with open(HISTORY_FILE, 'r', encoding='utf-8') as f: - return json.load(f) - -def save_history(history): - with open(HISTORY_FILE, 'w', encoding='utf-8') as f: - json.dump(history, f, indent=4) - -def nettoyer_vieilles_discussions(): - history = load_history() - now = time.time() - un_an_en_secondes = 365 * 24 * 60 * 60 - a_supprimer = [cid for cid, data in history.items() if not data.get('pinned', False) and now - data.get('timestamp', now) > un_an_en_secondes] - if a_supprimer: - for cid in a_supprimer: - del history[cid] - save_history(history) - print(f"🧹 AETHAS CORE : Nettoyage auto terminé. {len(a_supprimer)} discussion(s) supprimée(s).") -''') - -# 2. scraper.py -with open("modules/scraper.py", "w", encoding="utf-8") as f: - f.write('''import requests -from bs4 import BeautifulSoup - -def rechercher_sur_google(requete): - try: - url = "https://lite.duckduckgo.com/lite/" - data = {'q': requete} - headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"} - resp = requests.post(url, data=data, headers=headers, timeout=5) - soup = BeautifulSoup(resp.text, 'html.parser') - - urls = [] - for a in soup.find_all('a', class_='result-link', href=True): - href = a['href'] - if href.startswith('http') and 'duckduckgo' not in href: - urls.append(href) - if len(urls) >= 5: - break - if not urls: - for a in soup.find_all('a', href=True): - href = a['href'] - if href.startswith('http') and 'duckduckgo' not in href and href not in urls: - urls.append(href) - if len(urls) >= 5: - break - return urls[:5] - except Exception as e: - print(f"Erreur recherche web: {e}") - return [] - -def extraire_contenu_urls(urls_list): - scraped_text = "" - for url in urls_list: - try: - resp = requests.get(url, timeout=5, headers={"User-Agent": "Mozilla/5.0"}) - soup = BeautifulSoup(resp.text, 'html.parser') - for script in soup(["script", "style", "nav", "footer"]): - script.extract() - scraped_text += f"\\n\\n--- Source web extraite ({url}) ---\\n{soup.get_text(separator=' ', strip=True)[:3000]}" - except Exception as e: - scraped_text += f"\\n\\n[Impossible de récupérer {url}: {e}]" - return scraped_text -''') - -# 3. ai_engine.py (Contient les clients et l'accès OpenAI) -with open("modules/ai_engine.py", "w", encoding="utf-8") as f: - f.write('''import os -from dotenv import load_dotenv -from openai import OpenAI -from groq import Groq -from google import genai - -load_dotenv() - -local_client = OpenAI(base_url="http://192.168.1.20:11434/v1", api_key="ollama") -groq_client = Groq(api_key=os.getenv("GROQ_API_KEY")) -gemini_client = genai.Client(api_key=os.getenv("GEMINI_API_KEY")) - -def interroger_gemini_cascade(prompt): - modeles_a_tester = ["gemini-3.1-pro-preview", "gemini-3.1-pro", "gemini-2.5-pro", "gemini-3.7-flash", "gemini-3.6-flash"] - for nom_modele in modeles_a_tester: - try: - config = {"system_instruction": "Réponds toujours dans la langue de l'utilisateur. Utilise exclusivement le format Markdown pour les tableaux et la mise en forme. N'utilise JAMAIS de code LaTeX."} - chat = gemini_client.chats.create(model=nom_modele, config=config) - ia_response = chat.send_message(prompt) - if not ia_response.text: - raise Exception("Réponse vide de Google.") - return ia_response.text, nom_modele - except Exception: - continue - raise Exception("Tous les modèles Gemini sont indisponibles actuellement.") - -def appeler_groq_api(prompt_text): - resp = groq_client.chat.completions.create( - messages=[{"role": "system", "content": "Réponds dans la langue de l'utilisateur. Tu as accès aux recherches web fournies. Utilise uniquement Markdown. Pas de code LaTeX."}, {"role": "user", "content": prompt_text}], - model="llama-3.3-70b-versatile" - ) - return resp.choices[0].message.content, "#f97316", "GROQ", "llama-3.3-70b" -''') - -# 4. router.py -with open("modules/router.py", "w", encoding="utf-8") as f: - f.write('''import json -import re - -def nettoyer_decision_router(raw_text): - try: - clean = re.sub(r'```(?:json)?\\s*', '', raw_text) - clean = clean.replace('```', '').strip() - start = clean.find('{') - end = clean.rfind('}') + 1 - if start != -1 and end != 0: - return json.loads(clean[start:end]) - except Exception: - pass - return {"intention": "Analyse", "fournisseur": "gemini"} -''') - -# 5. ui_components.py (Gestion de l'affichage UI et sidebar) -with open("modules/ui_components.py", "w", encoding="utf-8") as f: - f.write('''from fasthtml.common import * -from modules.database import load_history - -def render_user_msg(text): - return Div(P(text, style="margin:0; font-weight:bold; white-space: pre-wrap;"), cls="user-bubble") - -def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur): - encart_info = Div( - Span(f"🎯 {intention}", style="margin-right: 10px; color:#aaa; font-size:0.8em;"), - Span(f"🏢 {nom_fournisseur}", style="margin-right: 10px; color:#aaa; font-size:0.8em;"), - Span(f"🧠 {nom_modele}", style=f"margin-right: 10px; color:{couleur}; font-weight:bold; font-size:0.8em;"), - style="border-bottom: 1px solid #333; padding-bottom: 10px; margin-bottom: 15px; display: flex; flex-wrap: wrap; gap: 5px;" - ) - - actions = Div( - Button("📄 PDF", onclick="downloadResponsePDF(this)", cls="msg-action-btn", title="Télécharger en PDF"), - Button("📘 DOC", onclick="downloadResponseDOC(this)", cls="msg-action-btn", title="Télécharger en Word"), - Button("📝 TXT", onclick="downloadResponseTXT(this)", cls="msg-action-btn", title="Télécharger en Texte"), - Button("🗄️ JSON", onclick="downloadResponseJSON(this)", cls="msg-action-btn", title="Télécharger en JSON"), - Button("💬 WhatsApp", onclick="shareWhatsApp(this)", cls="msg-action-btn", title="Partager sur WhatsApp"), - Button("📧 Email", onclick="shareEmail(this)", cls="msg-action-btn", title="Envoyer par Email"), - cls="msg-actions" - ) - - return Div( - encart_info, - Div(text, cls="markdown-raw", style="display:none;"), - Div(cls="markdown-rendered", style="line-height: 1.6;"), - actions, - cls="ai-bubble", style=f"border-left: 4px solid {couleur};" - ) - -def render_sidebar(): - history = load_history() - sorted_chats = sorted(history.items(), key=lambda x: (not x[1].get('pinned', False), -x[1].get('timestamp', 0))) - - items = [] - for chat_id, data in sorted_chats: - pin_icon = "📌" if data.get('pinned', False) else "📍" - items.append( - Div( - A(Span(data['title'], style="color:inherit;"), href=f"/load_chat/{chat_id}", cls="history-link"), - Button(pin_icon, cls="history-btn", hx_post=f"/pin/{chat_id}", hx_target="#sidebar-content", hx_swap="outerHTML"), - Button("✏️", cls="history-btn", hx_post=f"/rename/{chat_id}", hx_prompt="Nouveau nom :", hx_target="#sidebar-content", hx_swap="outerHTML"), - Button("🗑️", cls="history-btn", hx_post=f"/delete/{chat_id}", hx_target="#sidebar-content", hx_swap="outerHTML"), - cls="history-item", id=f"chat-item-{chat_id}" - ) - ) - return Div(*items, id="sidebar-content", cls="sidebar-history", hx_swap_oob="true") -''') - -print("📁 Dossier 'modules/' et sous-scripts générés avec succès !") \ No newline at end of file