Public Access
maj auto 1
This commit is contained in:
1 parent
a8f9ab28ed
commit
d47bd67391
2 files changed
+151
-72
No files matched your search
@@ -3,3 +3,4 @@ venv/
|
|||||||
.env
|
.env
|
||||||
.sesskey
|
.sesskey
|
||||||
aethas_history.json
|
aethas_history.json
|
||||||
|
Echange GEMINY.txt
|
||||||
@@ -7,6 +7,7 @@ import requests
|
|||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from fasthtml.common import *
|
from fasthtml.common import *
|
||||||
|
from starlette.responses import FileResponse
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
from groq import Groq
|
from groq import Groq
|
||||||
from google import genai
|
from google import genai
|
||||||
@@ -25,6 +26,26 @@ def load_history():
|
|||||||
def save_history(history):
|
def save_history(history):
|
||||||
with open(HISTORY_FILE, 'w', encoding='utf-8') as f: json.dump(history, f, indent=4)
|
with open(HISTORY_FILE, 'w', encoding='utf-8') as f: json.dump(history, f, indent=4)
|
||||||
|
|
||||||
|
# --- SCRIPT DE NETTOYAGE AUTO ---
|
||||||
|
def nettoyer_vieilles_discussions():
|
||||||
|
history = load_history()
|
||||||
|
now = time.time()
|
||||||
|
un_an_en_secondes = 365 * 24 * 60 * 60
|
||||||
|
a_supprimer = []
|
||||||
|
|
||||||
|
for chat_id, data in history.items():
|
||||||
|
if not data.get('pinned', False):
|
||||||
|
if now - data.get('timestamp', now) > un_an_en_secondes:
|
||||||
|
a_supprimer.append(chat_id)
|
||||||
|
|
||||||
|
if a_supprimer:
|
||||||
|
for chat_id in a_supprimer:
|
||||||
|
del history[chat_id]
|
||||||
|
save_history(history)
|
||||||
|
print(f"🧹 AETHAS CORE : Nettoyage auto terminé. {len(a_supprimer)} discussion(s) de plus de 365 jours supprimée(s).")
|
||||||
|
|
||||||
|
nettoyer_vieilles_discussions()
|
||||||
|
|
||||||
svg_favicon = "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cstyle%3E@keyframes pulse { 0% { opacity: 1; stroke-width: 5; } 50% { opacity: 0.5; stroke-width: 7; } 100% { opacity: 1; stroke-width: 5; } } .neon { animation: pulse 2s infinite; transform-origin: 50px 50px; transform: rotate(45deg); }%3C/style%3E%3Crect width='100' height='100' fill='%23121212'/%3E%3Cg class='neon'%3E%3Crect x='25' y='25' width='50' height='50' fill='none' stroke='%2300e5ff'/%3E%3Cpath d='M25 50 L75 50 M50 25 L50 75' stroke='%23b300ff'/%3E%3Ccircle cx='50' cy='50' r='8' fill='%23e0e0e0'/%3E%3C/g%3E%3C/svg%3E"
|
svg_favicon = "%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Cstyle%3E@keyframes pulse { 0% { opacity: 1; stroke-width: 5; } 50% { opacity: 0.5; stroke-width: 7; } 100% { opacity: 1; stroke-width: 5; } } .neon { animation: pulse 2s infinite; transform-origin: 50px 50px; transform: rotate(45deg); }%3C/style%3E%3Crect width='100' height='100' fill='%23121212'/%3E%3Cg class='neon'%3E%3Crect x='25' y='25' width='50' height='50' fill='none' stroke='%2300e5ff'/%3E%3Cpath d='M25 50 L75 50 M50 25 L50 75' stroke='%23b300ff'/%3E%3Ccircle cx='50' cy='50' r='8' fill='%23e0e0e0'/%3E%3C/g%3E%3C/svg%3E"
|
||||||
|
|
||||||
theme_hdrs = (
|
theme_hdrs = (
|
||||||
@@ -43,11 +64,10 @@ theme_hdrs = (
|
|||||||
.sidebar-history { flex: 1; overflow-y: auto; margin-top: 15px; margin-bottom: 15px; }
|
.sidebar-history { flex: 1; overflow-y: auto; margin-top: 15px; margin-bottom: 15px; }
|
||||||
.sidebar-footer { flex-shrink: 0; border-top: 1px solid #333; padding-top: 15px; text-align: center; }
|
.sidebar-footer { flex-shrink: 0; border-top: 1px solid #333; padding-top: 15px; text-align: center; }
|
||||||
|
|
||||||
/* CORRECTION: Boutons disposés en colonne proprement */
|
|
||||||
.nav-buttons { display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px; }
|
.nav-buttons { display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px; }
|
||||||
.new-chat-btn { display:block; text-align:center; text-decoration:none; background: transparent; border: 1px solid var(--accent-blue); color: var(--accent-blue); padding: 10px; border-radius: 5px; cursor: pointer; font-weight: bold; transition: 0.2s; }
|
.new-chat-btn { display:block; text-align:center; text-decoration:none; background: transparent; border: 1px solid var(--accent-blue); color: var(--accent-blue); padding: 10px; border-radius: 5px; cursor: pointer; font-weight: bold; transition: 0.2s; }
|
||||||
.new-chat-btn:hover { background: rgba(0, 229, 255, 0.1); }
|
.new-chat-btn:hover { background: rgba(0, 229, 255, 0.1); }
|
||||||
.export-btn { background: #2a2a35; border: 1px solid #444; color: white; padding: 10px; border-radius: 5px; cursor: pointer; transition: 0.2s; font-size: 1em; text-align:center;}
|
.export-btn { background: #2a2a35; border: 1px solid #444; color: white; padding: 10px; border-radius: 5px; cursor: pointer; transition: 0.2s; font-size: 1em; text-align:center; text-decoration:none; display:block;}
|
||||||
.export-btn:hover { background: #444; }
|
.export-btn:hover { background: #444; }
|
||||||
|
|
||||||
.history-item { display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid #222; font-size: 0.9em; transition: 0.2s; }
|
.history-item { display: flex; justify-content: space-between; align-items: center; padding: 10px; border-bottom: 1px solid #222; font-size: 0.9em; transition: 0.2s; }
|
||||||
@@ -86,64 +106,107 @@ theme_hdrs = (
|
|||||||
|
|
||||||
.btn-submit { background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); color: white; border: none; width: 36px !important; height: 36px !important; min-width: 36px !important; max-width: 36px !important; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 1.1em; flex-shrink: 0 !important; box-sizing: border-box; }
|
.btn-submit { background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple)); color: white; border: none; width: 36px !important; height: 36px !important; min-width: 36px !important; max-width: 36px !important; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; font-size: 1.1em; flex-shrink: 0 !important; box-sizing: border-box; }
|
||||||
.btn-submit:active { transform: scale(0.95); }
|
.btn-submit:active { transform: scale(0.95); }
|
||||||
|
|
||||||
|
.htmx-indicator { display: none; }
|
||||||
|
.htmx-request .htmx-indicator { display: flex; }
|
||||||
|
.htmx-request.htmx-indicator { display: flex; }
|
||||||
|
|
||||||
|
.tech-loader-container { display: flex; align-items: center; gap: 15px; color: var(--accent-blue); font-style: italic; background: var(--bg-panel); padding: 15px 20px; border-radius: 15px 15px 15px 0; border: 1px dashed var(--accent-blue); width: fit-content; margin-bottom: 20px; font-size: 0.9em; box-shadow: 0 0 10px rgba(0, 229, 255, 0.1); }
|
||||||
|
.tech-spinner { width: 20px; height: 20px; border: 3px solid rgba(0, 229, 255, 0.2); border-top-color: var(--accent-blue); border-radius: 50%; animation: spin 1s linear infinite; }
|
||||||
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
"""),
|
"""),
|
||||||
|
|
||||||
Script("""
|
Script("""
|
||||||
function renderMarkdown() {
|
function renderMarkdown() {
|
||||||
document.querySelectorAll('.markdown-raw').forEach(el => {
|
try {
|
||||||
let container = el.nextElementSibling;
|
document.querySelectorAll('.markdown-raw').forEach(el => {
|
||||||
container.innerHTML = marked.parse(el.textContent);
|
if (el.dataset.rendered) return;
|
||||||
el.classList.remove('markdown-raw');
|
let container = el.nextElementSibling;
|
||||||
|
|
||||||
container.querySelectorAll('pre').forEach(pre => {
|
if (typeof marked !== 'undefined') {
|
||||||
let code = pre.querySelector('code');
|
container.innerHTML = marked.parse(el.textContent);
|
||||||
let lang = code.className.replace('language-', '') || 'texte';
|
} else {
|
||||||
let header = document.createElement('div');
|
container.innerText = el.textContent;
|
||||||
header.className = 'code-header';
|
}
|
||||||
header.innerHTML = `<span style="color:#00e5ff; font-weight:bold;">${lang.toUpperCase()}</span><div><button onclick="copyCode(this)">📋 Copier</button></div>`;
|
|
||||||
pre.parentNode.insertBefore(header, pre);
|
el.dataset.rendered = "true";
|
||||||
hljs.highlightElement(code);
|
el.classList.remove('markdown-raw');
|
||||||
|
|
||||||
|
container.querySelectorAll('pre').forEach(pre => {
|
||||||
|
try {
|
||||||
|
let code = pre.querySelector('code');
|
||||||
|
if(code) {
|
||||||
|
let rawLang = code.className.replace('language-', '') || 'texte';
|
||||||
|
let lang = rawLang.toUpperCase();
|
||||||
|
let header = document.createElement('div');
|
||||||
|
header.className = 'code-header';
|
||||||
|
header.innerHTML = '<span style="color:#00e5ff; font-weight:bold;">' + lang + '</span><div><button onclick="copyCode(this)">📋 Copier</button></div>';
|
||||||
|
pre.parentNode.insertBefore(header, pre);
|
||||||
|
if (typeof hljs !== 'undefined') hljs.highlightElement(code);
|
||||||
|
}
|
||||||
|
} catch(err) { console.warn("Aethas - Erreur formatage code", err); }
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
let chat = document.getElementById('chat-history');
|
||||||
let chat = document.getElementById('chat-history');
|
if (chat) chat.scrollTop = chat.scrollHeight;
|
||||||
chat.scrollTop = chat.scrollHeight;
|
} catch(globalErr) {
|
||||||
|
console.error("Aethas - Erreur fatale rendu Markdown", globalErr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SCRIPTS D'EXPORTATION ---
|
|
||||||
function downloadResponsePDF(btn) {
|
function downloadResponsePDF(btn) {
|
||||||
let content = btn.closest('.ai-bubble').querySelector('.markdown-rendered');
|
try {
|
||||||
let originalColor = content.style.color;
|
let renderedNode = btn.closest('.ai-bubble').querySelector('.markdown-rendered');
|
||||||
let originalBg = content.style.backgroundColor;
|
let rawNode = btn.closest('.ai-bubble').querySelector('.markdown-raw');
|
||||||
|
let originalContent = (renderedNode && renderedNode.innerHTML.trim() !== "") ? renderedNode : rawNode;
|
||||||
// Forçage du texte en noir sur blanc pour le PDF
|
|
||||||
content.style.color = '#000000';
|
let content = originalContent.cloneNode(true);
|
||||||
content.style.backgroundColor = '#ffffff';
|
let wrapper = document.createElement('div');
|
||||||
content.style.padding = '20px';
|
wrapper.style.backgroundColor = '#ffffff';
|
||||||
|
wrapper.style.padding = '20px';
|
||||||
let opt = {
|
wrapper.style.fontFamily = 'Arial, sans-serif';
|
||||||
margin: 10,
|
wrapper.style.color = '#000000';
|
||||||
filename: 'Aethas38_Reponse.pdf',
|
|
||||||
image: { type: 'jpeg', quality: 0.98 },
|
content.querySelectorAll('*').forEach(el => {
|
||||||
html2canvas: { scale: 2, backgroundColor: '#ffffff' },
|
el.style.color = '#000000';
|
||||||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
|
if (el.tagName === 'PRE' || el.tagName === 'CODE' || el.tagName === 'TABLE' || el.tagName === 'TH' || el.tagName === 'TD') {
|
||||||
};
|
el.style.backgroundColor = '#f4f4f4';
|
||||||
|
el.style.borderColor = '#aaaaaa';
|
||||||
html2pdf().set(opt).from(content).save().then(() => {
|
el.style.borderWidth = '1px';
|
||||||
content.style.color = originalColor;
|
el.style.borderStyle = 'solid';
|
||||||
content.style.backgroundColor = originalBg;
|
el.style.borderCollapse = 'collapse';
|
||||||
content.style.padding = '';
|
} else {
|
||||||
});
|
el.style.backgroundColor = 'transparent';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.appendChild(content);
|
||||||
|
|
||||||
|
let opt = {
|
||||||
|
margin: 10,
|
||||||
|
filename: 'Aethas38_Reponse.pdf',
|
||||||
|
image: { type: 'jpeg', quality: 0.98 },
|
||||||
|
html2canvas: { scale: 2, backgroundColor: '#ffffff', useCORS: true },
|
||||||
|
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
|
||||||
|
};
|
||||||
|
html2pdf().set(opt).from(wrapper).save();
|
||||||
|
} catch (err) {
|
||||||
|
alert("Erreur lors de la génération du PDF.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadResponseTXT(btn) {
|
function downloadResponseTXT(btn) {
|
||||||
let content = btn.closest('.ai-bubble').querySelector('.markdown-rendered');
|
let rendered = btn.closest('.ai-bubble').querySelector('.markdown-rendered').innerText;
|
||||||
let blob = new Blob([content.innerText], {type: 'text/plain;charset=utf-8'});
|
let raw = btn.closest('.ai-bubble').querySelector('.markdown-raw').innerText;
|
||||||
|
let finalContent = rendered ? rendered : raw;
|
||||||
|
let blob = new Blob([finalContent], {type: 'text/plain;charset=utf-8'});
|
||||||
let link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'Aethas38_Reponse.txt';
|
let link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'Aethas38_Reponse.txt';
|
||||||
document.body.appendChild(link); link.click(); document.body.removeChild(link);
|
document.body.appendChild(link); link.click(); document.body.removeChild(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadResponseDOC(btn) {
|
function downloadResponseDOC(btn) {
|
||||||
let content = btn.closest('.ai-bubble').querySelector('.markdown-rendered').innerHTML;
|
let renderedNode = btn.closest('.ai-bubble').querySelector('.markdown-rendered');
|
||||||
|
let content = (renderedNode && renderedNode.innerHTML.trim() !== "") ? renderedNode.innerHTML : btn.closest('.ai-bubble').querySelector('.markdown-raw').innerText;
|
||||||
let preHtml = "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'><title>Aethas Export</title></head><body style='font-family: Arial, sans-serif; color: #000000; background-color: #ffffff;'>";
|
let preHtml = "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'><title>Aethas Export</title></head><body style='font-family: Arial, sans-serif; color: #000000; background-color: #ffffff;'>";
|
||||||
let postHtml = "</body></html>";
|
let postHtml = "</body></html>";
|
||||||
let blob = new Blob(['\\ufeff', preHtml + content + postHtml], { type: 'application/msword' });
|
let blob = new Blob(['\\ufeff', preHtml + content + postHtml], { type: 'application/msword' });
|
||||||
@@ -152,8 +215,10 @@ theme_hdrs = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
function downloadResponseJSON(btn) {
|
function downloadResponseJSON(btn) {
|
||||||
let content = btn.closest('.ai-bubble').querySelector('.markdown-rendered').innerText;
|
let rendered = btn.closest('.ai-bubble').querySelector('.markdown-rendered').innerText;
|
||||||
let data = { "timestamp": new Date().toISOString(), "content": content };
|
let raw = btn.closest('.ai-bubble').querySelector('.markdown-raw').innerText;
|
||||||
|
let finalContent = rendered ? rendered : raw;
|
||||||
|
let data = { "timestamp": new Date().toISOString(), "content": finalContent };
|
||||||
let blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json;charset=utf-8'});
|
let blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json;charset=utf-8'});
|
||||||
let link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'Aethas38_Reponse.json';
|
let link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'Aethas38_Reponse.json';
|
||||||
document.body.appendChild(link); link.click(); document.body.removeChild(link);
|
document.body.appendChild(link); link.click(); document.body.removeChild(link);
|
||||||
@@ -167,29 +232,36 @@ theme_hdrs = (
|
|||||||
setTimeout(() => btn.innerText = "📋 Copier", 2000);
|
setTimeout(() => btn.innerText = "📋 Copier", 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
function exportChat() {
|
|
||||||
let chat = document.getElementById('chat-history');
|
|
||||||
if(!chat) return;
|
|
||||||
let blob = new Blob([chat.innerText], {type: 'text/plain;charset=utf-8'});
|
|
||||||
let link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'Aethas38_Discussion_Globale.txt';
|
|
||||||
document.body.appendChild(link); link.click(); document.body.removeChild(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFileSelection(input) {
|
function handleFileSelection(input) {
|
||||||
let previewList = document.getElementById('file-preview-list');
|
let previewList = document.getElementById('file-preview-list');
|
||||||
previewList.innerHTML = '';
|
previewList.innerHTML = '';
|
||||||
if (input.files) {
|
if (input.files) {
|
||||||
for (let i = 0; i < input.files.length; i++) {
|
for (let i = 0; i < input.files.length; i++) {
|
||||||
let pill = document.createElement('div'); pill.className = 'file-pill'; pill.innerHTML = `📎 ${input.files[i].name}`;
|
let pill = document.createElement('div'); pill.className = 'file-pill'; pill.innerHTML = '📎 ' + input.files[i].name;
|
||||||
previewList.appendChild(pill);
|
previewList.appendChild(pill);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function autoResize(textarea) {
|
function autoResize(textarea) {
|
||||||
|
if(!textarea) return;
|
||||||
textarea.style.height = 'auto';
|
textarea.style.height = 'auto';
|
||||||
textarea.style.height = textarea.scrollHeight + 'px';
|
textarea.style.height = textarea.scrollHeight + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shareWhatsApp(btn) {
|
||||||
|
let rendered = btn.closest('.ai-bubble').querySelector('.markdown-rendered').innerText;
|
||||||
|
let raw = btn.closest('.ai-bubble').querySelector('.markdown-raw').innerText;
|
||||||
|
let content = rendered ? rendered : raw;
|
||||||
|
window.open('https://api.whatsapp.com/send?text=' + encodeURIComponent("🤖 *Aethas38 (IA) :*\n\n" + content), '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
function shareEmail(btn) {
|
||||||
|
let rendered = btn.closest('.ai-bubble').querySelector('.markdown-rendered').innerText;
|
||||||
|
let raw = btn.closest('.ai-bubble').querySelector('.markdown-raw').innerText;
|
||||||
|
let content = rendered ? rendered : raw;
|
||||||
|
window.open('mailto:?subject=Réponse Aethas38 IA&body=' + encodeURIComponent(content), '_self');
|
||||||
|
}
|
||||||
""")
|
""")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -206,8 +278,11 @@ 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"]
|
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:
|
for nom_modele in modeles_a_tester:
|
||||||
try:
|
try:
|
||||||
chat = gemini_client.chats.create(model=nom_modele, config={"system_instruction": "Réponds toujours dans la langue de l'utilisateur."})
|
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)
|
ia_response = chat.send_message(prompt)
|
||||||
|
if not ia_response.text:
|
||||||
|
raise Exception("Réponse vide de Google.")
|
||||||
return ia_response.text, nom_modele
|
return ia_response.text, nom_modele
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
@@ -217,7 +292,6 @@ def rechercher_sur_google(requete):
|
|||||||
try:
|
try:
|
||||||
from googlesearch import search
|
from googlesearch import search
|
||||||
urls = []
|
urls = []
|
||||||
# Modification ici : num_results=5 au lieu de 3
|
|
||||||
for url in search(requete, num_results=5, lang="fr"):
|
for url in search(requete, num_results=5, lang="fr"):
|
||||||
urls.append(url)
|
urls.append(url)
|
||||||
return urls
|
return urls
|
||||||
@@ -276,6 +350,8 @@ def render_ai_msg(text, intention, nom_fournisseur, nom_modele, couleur):
|
|||||||
Button("📘 DOC", onclick="downloadResponseDOC(this)", cls="msg-action-btn", title="Télécharger en Word"),
|
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("📝 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("🗄️ 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"
|
cls="msg-actions"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -322,7 +398,7 @@ def get(session):
|
|||||||
messages_ui.append(render_ai_msg(msg['content'], msg.get('intention', ''), msg.get('fournisseur', ''), msg.get('modele', ''), msg.get('couleur', '#333')))
|
messages_ui.append(render_ai_msg(msg['content'], msg.get('intention', ''), msg.get('fournisseur', ''), msg.get('modele', ''), msg.get('couleur', '#333')))
|
||||||
|
|
||||||
if messages_ui:
|
if messages_ui:
|
||||||
messages_ui.append(Script("setTimeout(renderMarkdown, 100);"))
|
messages_ui.append(Script("setTimeout(renderMarkdown, 150);"))
|
||||||
|
|
||||||
return Title("Aethas38 - Mine_AI"), Body(
|
return Title("Aethas38 - Mine_AI"), Body(
|
||||||
Div(
|
Div(
|
||||||
@@ -330,7 +406,7 @@ def get(session):
|
|||||||
Div(H2("AETHAS 38", style="color:#00e5ff; margin:0; text-align:center;"), P("Tech Core", style="font-size:0.7em; color:#888; text-align:center;"), margin_bottom="20px"),
|
Div(H2("AETHAS 38", style="color:#00e5ff; margin:0; text-align:center;"), P("Tech Core", style="font-size:0.7em; color:#888; text-align:center;"), margin_bottom="20px"),
|
||||||
Div(
|
Div(
|
||||||
A("+ Nouvelle discussion", href="/new_chat", cls="new-chat-btn"),
|
A("+ Nouvelle discussion", href="/new_chat", cls="new-chat-btn"),
|
||||||
Button("💾 Exporter JSON", cls="export-btn", onclick="exportChat()", title="Télécharger la conversation Globale"),
|
A("💾 Exporter JSON", href="/export_json", target="_blank", cls="export-btn", title="Télécharger la conversation Globale"),
|
||||||
cls="nav-buttons"
|
cls="nav-buttons"
|
||||||
),
|
),
|
||||||
cls="sidebar-top"
|
cls="sidebar-top"
|
||||||
@@ -344,7 +420,13 @@ def get(session):
|
|||||||
),
|
),
|
||||||
|
|
||||||
Div(
|
Div(
|
||||||
Div(*messages_ui, id="chat-history", cls="chat-container"),
|
Div(*messages_ui, id="chat-history", cls="chat-container", style="padding-bottom: 20px;"),
|
||||||
|
|
||||||
|
Div(
|
||||||
|
Div(Div(cls="tech-spinner"), Span("Aethas Core analyse la requête..."), cls="tech-loader-container"),
|
||||||
|
id="global-loader", cls="htmx-indicator", style="max-width: 1000px; margin: 0 auto; width: 100%; padding: 0 20px; box-sizing: border-box; padding-bottom: 120px;"
|
||||||
|
),
|
||||||
|
|
||||||
Div(
|
Div(
|
||||||
Form(
|
Form(
|
||||||
Input(type="hidden", name="chat_id", value=chat_id),
|
Input(type="hidden", name="chat_id", value=chat_id),
|
||||||
@@ -362,6 +444,7 @@ def get(session):
|
|||||||
hx_post="/chat",
|
hx_post="/chat",
|
||||||
hx_target="#chat-history",
|
hx_target="#chat-history",
|
||||||
hx_swap="beforeend",
|
hx_swap="beforeend",
|
||||||
|
hx_indicator="#global-loader",
|
||||||
hx_on__after_request="this.reset(); document.getElementById('file-preview-list').innerHTML=''; document.getElementById('prompt-input').style.height='auto';",
|
hx_on__after_request="this.reset(); document.getElementById('file-preview-list').innerHTML=''; document.getElementById('prompt-input').style.height='auto';",
|
||||||
enctype="multipart/form-data",
|
enctype="multipart/form-data",
|
||||||
style="width: 100%; display: flex; justify-content: center;"
|
style="width: 100%; display: flex; justify-content: center;"
|
||||||
@@ -389,7 +472,6 @@ async def post(request, session: dict):
|
|||||||
|
|
||||||
history = load_history()
|
history = load_history()
|
||||||
|
|
||||||
# --- 1. INTELLIGENCE DE SÉCURITÉ DU ROUTEUR (Smarter Interceptor) ---
|
|
||||||
security_check_prompt = f"""Tu es l'Aiguilleur du système Aethas38. Ton rôle EXCLUSIF est d'analyser et de router la demande.
|
security_check_prompt = f"""Tu es l'Aiguilleur du système Aethas38. Ton rôle EXCLUSIF est d'analyser et de router la demande.
|
||||||
|
|
||||||
RÈGLE DE SÉCURITÉ ABSOLUE : Les recherches web, les serveurs Minecraft, les modpacks (ex: ATM10) SONT AUTORISÉS.
|
RÈGLE DE SÉCURITÉ ABSOLUE : Les recherches web, les serveurs Minecraft, les modpacks (ex: ATM10) SONT AUTORISÉS.
|
||||||
@@ -411,7 +493,6 @@ async def post(request, session: dict):
|
|||||||
intention = decision.get('intention', '')
|
intention = decision.get('intention', '')
|
||||||
fournisseur = decision.get("fournisseur", "local").lower()
|
fournisseur = decision.get("fournisseur", "local").lower()
|
||||||
|
|
||||||
# Si Qwen détecte un VRAI danger
|
|
||||||
if intention == "Alerte Securite":
|
if intention == "Alerte Securite":
|
||||||
if chat_id not in history:
|
if chat_id not in history:
|
||||||
history[chat_id] = {"title": "Alerte Sécurité...", "pinned": False, "timestamp": time.time(), "messages": []}
|
history[chat_id] = {"title": "Alerte Sécurité...", "pinned": False, "timestamp": time.time(), "messages": []}
|
||||||
@@ -423,7 +504,6 @@ async def post(request, session: dict):
|
|||||||
|
|
||||||
return render_user_msg(prompt), render_ai_msg(reponse_securite, "Alerte Sécurité", "AETHAS CORE", "Cyber-Sec", "#ff0044"), Script("renderMarkdown();"), render_sidebar()
|
return render_user_msg(prompt), render_ai_msg(reponse_securite, "Alerte Sécurité", "AETHAS CORE", "Cyber-Sec", "#ff0044"), Script("renderMarkdown();"), render_sidebar()
|
||||||
|
|
||||||
# --- 2. TRAITEMENT CLASSIQUE ---
|
|
||||||
urls_to_scrape = re.findall(r'https?://[^\s]+', prompt)
|
urls_to_scrape = re.findall(r'https?://[^\s]+', prompt)
|
||||||
mots_cles = ["cherche sur", "recherche sur", "sur internet", "sur le web", "sur le net"]
|
mots_cles = ["cherche sur", "recherche sur", "sur internet", "sur le web", "sur le net"]
|
||||||
if any(mot in prompt.lower() for mot in mots_cles):
|
if any(mot in prompt.lower() for mot in mots_cles):
|
||||||
@@ -451,25 +531,18 @@ async def post(request, session: dict):
|
|||||||
if web_content:
|
if web_content:
|
||||||
full_prompt += f"\n\n[INSTRUCTION SYSTÈME STRICTE : Ne dis JAMAIS que tu ne peux pas naviguer sur internet ou télécharger des fichiers. Le système a déjà fait la recherche web pour toi. Voici le contenu brut extrait des sites. Utilise-le IMPÉRATIVEMENT pour formuler ta réponse sans mentionner tes propres limitations :]\n{web_content}"
|
full_prompt += f"\n\n[INSTRUCTION SYSTÈME STRICTE : Ne dis JAMAIS que tu ne peux pas naviguer sur internet ou télécharger des fichiers. Le système a déjà fait la recherche web pour toi. Voici le contenu brut extrait des sites. Utilise-le IMPÉRATIVEMENT pour formuler ta réponse sans mentionner tes propres limitations :]\n{web_content}"
|
||||||
|
|
||||||
prompt_tokens = len(full_prompt.split()) * 2
|
|
||||||
completion_tokens = 0
|
|
||||||
|
|
||||||
# Fonction d'appel à Groq centralisée pour éviter la duplication de code
|
|
||||||
def appeler_groq_api(prompt_text):
|
def appeler_groq_api(prompt_text):
|
||||||
resp = groq_client.chat.completions.create(
|
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."}, {"role": "user", "content": prompt_text}],
|
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"
|
model="llama-3.3-70b-versatile"
|
||||||
)
|
)
|
||||||
return resp.choices[0].message.content, "#f97316", "GROQ", "llama-3.3-70b"
|
return resp.choices[0].message.content, "#f97316", "GROQ", "llama-3.3-70b"
|
||||||
|
|
||||||
if fournisseur == "gemini":
|
if fournisseur == "gemini":
|
||||||
try:
|
try:
|
||||||
# On tente Gemini en premier
|
|
||||||
reponse_finale, nom_modele = interroger_gemini_cascade(full_prompt)
|
reponse_finale, nom_modele = interroger_gemini_cascade(full_prompt)
|
||||||
couleur, nom_fournisseur = "#3b82f6", "GOOGLE GEMINI"
|
couleur, nom_fournisseur = "#3b82f6", "GOOGLE GEMINI"
|
||||||
completion_tokens = len(reponse_finale.split()) * 2
|
|
||||||
except Exception as e_gemini:
|
except Exception as e_gemini:
|
||||||
# 🔄 IA DE SECOURS : Si Gemini échoue, on bascule silencieusement sur Groq
|
|
||||||
print(f"⚠️ Alerte système : Gemini indisponible ({e_gemini}). Bascule sur l'IA de secours (Groq)...")
|
print(f"⚠️ Alerte système : Gemini indisponible ({e_gemini}). Bascule sur l'IA de secours (Groq)...")
|
||||||
intention = intention + " (Secours Groq)"
|
intention = intention + " (Secours Groq)"
|
||||||
try:
|
try:
|
||||||
@@ -487,7 +560,7 @@ async def post(request, session: dict):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
ia_response = local_client.chat.completions.create(model="qwen2.5:3b", messages=[{"role": "system", "content": "Réponds dans la langue de l'utilisateur."}, {"role": "user", "content": full_prompt}])
|
ia_response = local_client.chat.completions.create(model="qwen2.5:3b", messages=[{"role": "system", "content": "Réponds dans la langue de l'utilisateur. Utilise le Markdown."}, {"role": "user", "content": full_prompt}])
|
||||||
reponse_finale, couleur, nom_fournisseur, nom_modele = ia_response.choices[0].message.content, "#10b981", "SERVEUR LOCAL", "qwen2.5:3b"
|
reponse_finale, couleur, nom_fournisseur, nom_modele = ia_response.choices[0].message.content, "#10b981", "SERVEUR LOCAL", "qwen2.5:3b"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
reponse_finale, couleur, nom_fournisseur, nom_modele = f"Erreur Serveur Local : {e}", "#ff0000", "ERREUR LOCAL", "Qwen HS"
|
reponse_finale, couleur, nom_fournisseur, nom_modele = f"Erreur Serveur Local : {e}", "#ff0000", "ERREUR LOCAL", "Qwen HS"
|
||||||
@@ -560,5 +633,10 @@ def post(chat_id: str):
|
|||||||
save_history(history)
|
save_history(history)
|
||||||
return render_sidebar()
|
return render_sidebar()
|
||||||
|
|
||||||
|
# --- ROUTE : EXPORT JSON COMPLET ---
|
||||||
|
@rt('/export_json')
|
||||||
|
def get():
|
||||||
|
return FileResponse(HISTORY_FILE, media_type='application/json', filename='Aethas38_Discussion_Globale.json')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
serve(port=5001)
|
serve(port=5001)
|
||||||
Reference in new issue
Block a user