Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import google.generativeai as genai
|
3 |
+
import os
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
import http.client
|
6 |
+
import json
|
7 |
+
import shutil
|
8 |
+
|
9 |
+
# Charger les variables d'environnement
|
10 |
+
load_dotenv()
|
11 |
+
|
12 |
+
# Configurer la clé API
|
13 |
+
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
|
14 |
+
|
15 |
+
# Paramètres de sécurité
|
16 |
+
safety_settings = [
|
17 |
+
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
|
18 |
+
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
|
19 |
+
{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
|
20 |
+
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
|
21 |
+
]
|
22 |
+
|
23 |
+
# Instructions système pour Mariam (inchangées pour brièveté, mais peuvent être affinées)
|
24 |
+
ss = """
|
25 |
+
# Prompt System pour Mariam, IA conçu par youssouf
|
26 |
+
[...]
|
27 |
+
"""
|
28 |
+
|
29 |
+
# Initialisation du modèle
|
30 |
+
model = genai.GenerativeModel('gemini-2.0-flash-exp', tools='code_execution',
|
31 |
+
safety_settings=safety_settings,
|
32 |
+
system_instruction=ss)
|
33 |
+
|
34 |
+
# Fonction de recherche web
|
35 |
+
def perform_web_search(query):
|
36 |
+
conn = http.client.HTTPSConnection("google.serper.dev")
|
37 |
+
payload = json.dumps({"q": query})
|
38 |
+
headers = {
|
39 |
+
'X-API-KEY': '9b90a274d9e704ff5b21c0367f9ae1161779b573',
|
40 |
+
'Content-Type': 'application/json'
|
41 |
+
}
|
42 |
+
try:
|
43 |
+
conn.request("POST", "/search", payload, headers)
|
44 |
+
res = conn.getresponse()
|
45 |
+
data = json.loads(res.read().decode("utf-8"))
|
46 |
+
return data
|
47 |
+
except Exception as e:
|
48 |
+
st.error(f"Erreur lors de la recherche web : {e}")
|
49 |
+
return None
|
50 |
+
finally:
|
51 |
+
conn.close()
|
52 |
+
|
53 |
+
# Formater les résultats de recherche
|
54 |
+
def format_search_results(data):
|
55 |
+
if not data:
|
56 |
+
return "Aucun résultat trouvé"
|
57 |
+
result = ""
|
58 |
+
if 'knowledgeGraph' in data:
|
59 |
+
kg = data['knowledgeGraph']
|
60 |
+
result += f"### {kg.get('title', '')}\n*{kg.get('type', '')}*\n\n{kg.get('description', '')}\n\n"
|
61 |
+
if 'organic' in data:
|
62 |
+
result += "### Résultats principaux:\n"
|
63 |
+
for item in data['organic'][:3]:
|
64 |
+
result += f"- **{item['title']}**\n {item['snippet']}\n [Lien]({item['link']})\n\n"
|
65 |
+
if 'peopleAlsoAsk' in data:
|
66 |
+
result += "### Questions fréquentes:\n"
|
67 |
+
for item in data['peopleAlsoAsk'][:2]:
|
68 |
+
result += f"- **{item['question']}**\n {item['snippet']}\n\n"
|
69 |
+
return result
|
70 |
+
|
71 |
+
# Convertir les rôles pour Streamlit
|
72 |
+
def role_to_streamlit(role):
|
73 |
+
return "assistant" if role == "model" else role
|
74 |
+
|
75 |
+
# Initialiser l'état de la session
|
76 |
+
if "chat" not in st.session_state:
|
77 |
+
st.session_state.chat = model.start_chat(history=[])
|
78 |
+
if "web_search" not in st.session_state:
|
79 |
+
st.session_state.web_search = False
|
80 |
+
if "messages" not in st.session_state:
|
81 |
+
st.session_state.messages = []
|
82 |
+
|
83 |
+
# Fonction pour effacer l’historique
|
84 |
+
def clear_chat_history():
|
85 |
+
st.session_state.chat = model.start_chat(history=[])
|
86 |
+
st.session_state.messages = []
|
87 |
+
|
88 |
+
# Créer un dossier temporaire
|
89 |
+
os.makedirs("temp", exist_ok=True)
|
90 |
+
|
91 |
+
# Titre de l’application
|
92 |
+
st.title("Mariam AI")
|
93 |
+
|
94 |
+
# Section des paramètres dans la barre latérale
|
95 |
+
with st.sidebar:
|
96 |
+
st.header("Paramètres")
|
97 |
+
st.session_state.web_search = st.toggle("Activer la recherche web", value=st.session_state.web_search,
|
98 |
+
help="Permet d’enrichir les réponses avec des informations du web.")
|
99 |
+
if st.button("Effacer l’historique", on_click=clear_chat_history):
|
100 |
+
st.success("Historique effacé.")
|
101 |
+
|
102 |
+
# Section de téléchargement de fichiers
|
103 |
+
with st.sidebar:
|
104 |
+
st.header("Téléchargement de fichiers")
|
105 |
+
st.info("Types acceptés : jpg, jpeg, png, pdf, txt")
|
106 |
+
uploaded_file = st.file_uploader("Choisir un fichier", type=['jpg', 'jpeg', 'png', 'pdf', 'txt'])
|
107 |
+
|
108 |
+
# Section de conversation
|
109 |
+
st.header("Conversation")
|
110 |
+
chat_container = st.container()
|
111 |
+
|
112 |
+
# Afficher les messages
|
113 |
+
with chat_container:
|
114 |
+
for message in st.session_state.messages:
|
115 |
+
with st.chat_message(message["role"]):
|
116 |
+
st.markdown(message["content"])
|
117 |
+
|
118 |
+
# Champ de saisie multiligne
|
119 |
+
user_input = st.text_area("Votre message", height=100)
|
120 |
+
|
121 |
+
# Bouton pour envoyer
|
122 |
+
if st.button("Envoyer"):
|
123 |
+
if user_input:
|
124 |
+
# Ajouter le message de l’utilisateur
|
125 |
+
st.session_state.messages.append({"role": "user", "content": user_input})
|
126 |
+
|
127 |
+
# Traiter le fichier téléchargé
|
128 |
+
uploaded_gemini_file = None
|
129 |
+
if uploaded_file:
|
130 |
+
file_ext = uploaded_file.name.split('.')[-1].lower()
|
131 |
+
accepted_types = ['jpg', 'jpeg', 'png', 'pdf', 'txt']
|
132 |
+
if file_ext in accepted_types:
|
133 |
+
with open(os.path.join("temp", uploaded_file.name), "wb") as f:
|
134 |
+
f.write(uploaded_file.getbuffer())
|
135 |
+
try:
|
136 |
+
uploaded_gemini_file = genai.upload_file(os.path.join("temp", uploaded_file.name))
|
137 |
+
except Exception as e:
|
138 |
+
st.error(f"Erreur lors de l’upload du fichier : {e}")
|
139 |
+
else:
|
140 |
+
st.error("Type de fichier non accepté. Utilisez jpg, jpeg, png, pdf ou txt.")
|
141 |
+
|
142 |
+
# Recherche web si activée
|
143 |
+
web_results = None
|
144 |
+
if st.session_state.web_search:
|
145 |
+
with st.spinner("Recherche web en cours..."):
|
146 |
+
web_results = perform_web_search(user_input)
|
147 |
+
if web_results:
|
148 |
+
formatted_results = format_search_results(web_results)
|
149 |
+
user_input = f"{user_input}\n\nVoici les résultats de la recherche web. Analyse-les et donne-moi une réponse complète :\n\n{formatted_results}"
|
150 |
+
|
151 |
+
# Envoyer à Gemini
|
152 |
+
try:
|
153 |
+
if uploaded_gemini_file:
|
154 |
+
response = st.session_state.chat.send_message([uploaded_gemini_file, "\n\n", user_input])
|
155 |
+
else:
|
156 |
+
response = st.session_state.chat.send_message(user_input)
|
157 |
+
st.session_state.messages.append({"role": "assistant", "content": response.text})
|
158 |
+
with chat_container:
|
159 |
+
with st.chat_message("assistant"):
|
160 |
+
st.markdown(response.text)
|
161 |
+
except Exception as e:
|
162 |
+
st.error(f"Erreur lors de l’envoi : {e}")
|
163 |
+
|
164 |
+
# Nettoyer les fichiers temporaires
|
165 |
+
if uploaded_file:
|
166 |
+
shutil.rmtree("temp", ignore_errors=True)
|
167 |
+
os.makedirs("temp", exist_ok=True)
|