Public Access
maj auto 2
This commit is contained in:
1 parent
d47bd67391
commit
f91b5ae215
11 files changed
+647
-477
No files matched your search
@@ -0,0 +1,32 @@
|
||||
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"
|
||||
@@ -0,0 +1,27 @@
|
||||
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).")
|
||||
@@ -0,0 +1,14 @@
|
||||
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"}
|
||||
@@ -0,0 +1,42 @@
|
||||
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
|
||||
@@ -0,0 +1,49 @@
|
||||
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")
|
||||
Reference in new issue
Block a user