Docfile commited on
Commit
b0d9b56
·
verified ·
1 Parent(s): f8335fd

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -0
app.py ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ import streamlit.components.v1 as components
9
+
10
+ # Charger les variables d'environnement
11
+ load_dotenv()
12
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))
13
+
14
+ # Paramètres de sécurité
15
+ safety_settings = [
16
+ {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
17
+ {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
18
+ {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
19
+ {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
20
+ ]
21
+
22
+ # Instructions système (inchangées pour brièveté)
23
+ ss = """
24
+ # Prompt System pour Mariam, IA conçu par youssouf
25
+ [...]
26
+ """
27
+
28
+ model = genai.GenerativeModel('gemini-2.0-flash-exp', tools='code_execution',
29
+ safety_settings=safety_settings,
30
+ system_instruction=ss)
31
+
32
+ # Fonctions de recherche web (inchangées)
33
+ def perform_web_search(query):
34
+ conn = http.client.HTTPSConnection("google.serper.dev")
35
+ payload = json.dumps({"q": query})
36
+ headers = {'X-API-KEY': '9b90a274d9e704ff5b21c0367f9ae1161779b573', 'Content-Type': 'application/json'}
37
+ try:
38
+ conn.request("POST", "/search", payload, headers)
39
+ res = conn.getresponse()
40
+ data = json.loads(res.read().decode("utf-8"))
41
+ return data
42
+ except Exception as e:
43
+ st.error(f"Erreur lors de la recherche web : {e}")
44
+ return None
45
+ finally:
46
+ conn.close()
47
+
48
+ def format_search_results(data):
49
+ if not data:
50
+ return "Aucun résultat trouvé"
51
+ result = ""
52
+ if 'knowledgeGraph' in data:
53
+ kg = data['knowledgeGraph']
54
+ result += f"### {kg.get('title', '')}\n*{kg.get('type', '')}*\n\n{kg.get('description', '')}\n\n"
55
+ if 'organic' in data:
56
+ result += "### Résultats principaux:\n"
57
+ for item in data['organic'][:3]:
58
+ result += f"- **{item['title']}**\n {item['snippet']}\n [Lien]({item['link']})\n\n"
59
+ return result
60
+
61
+ # Initialisation de l’état
62
+ if "chat" not in st.session_state:
63
+ st.session_state.chat = model.start_chat(history=[])
64
+ if "web_search" not in st.session_state:
65
+ st.session_state.web_search = False
66
+ if "messages" not in st.session_state:
67
+ st.session_state.messages = []
68
+
69
+ # Fonction pour effacer l’historique
70
+ def clear_chat_history():
71
+ st.session_state.chat = model.start_chat(history=[])
72
+ st.session_state.messages = []
73
+
74
+ # CSS personnalisé pour mobile
75
+ st.markdown("""
76
+ <style>
77
+ .chat-container {
78
+ height: 60vh;
79
+ overflow-y: auto;
80
+ padding: 10px;
81
+ background-color: #f9f9f9;
82
+ border-radius: 10px;
83
+ }
84
+ .user-message {
85
+ background-color: #007bff;
86
+ color: white;
87
+ padding: 10px;
88
+ border-radius: 10px;
89
+ margin: 5px 0;
90
+ max-width: 80%;
91
+ align-self: flex-end;
92
+ }
93
+ .assistant-message {
94
+ background-color: #e9ecef;
95
+ color: black;
96
+ padding: 10px;
97
+ border-radius: 10px;
98
+ margin: 5px 0;
99
+ max-width: 80%;
100
+ }
101
+ .input-container {
102
+ position: fixed;
103
+ bottom: 0;
104
+ width: 100%;
105
+ padding: 10px;
106
+ background-color: white;
107
+ box-shadow: 0 -2px 5px rgba(0,0,0,0.1);
108
+ }
109
+ @media (max-width: 600px) {
110
+ .chat-container {
111
+ height: 70vh;
112
+ }
113
+ .stTextArea textarea {
114
+ height: 80px !important;
115
+ }
116
+ }
117
+ </style>
118
+ """, unsafe_allow_html=True)
119
+
120
+ # Titre
121
+ st.title("Mariam AI")
122
+
123
+ # Section des paramètres (expansible)
124
+ with st.expander("Paramètres"):
125
+ st.session_state.web_search = st.toggle("Activer la recherche web", value=st.session_state.web_search,
126
+ help="Permet des réponses enrichies avec des données du web.")
127
+ if st.button("Effacer l’historique"):
128
+ clear_chat_history()
129
+ st.success("Historique effacé.")
130
+
131
+ # Section de téléchargement
132
+ with st.expander("Télécharger un fichier"):
133
+ st.info("Types acceptés : jpg, jpeg, png, pdf, txt")
134
+ uploaded_file = st.file_uploader("Choisir un fichier", type=['jpg', 'jpeg', 'png', 'pdf', 'txt'])
135
+
136
+ # Zone de conversation
137
+ st.markdown('<div class="chat-container">', unsafe_allow_html=True)
138
+ for message in st.session_state.messages:
139
+ if message["role"] == "user":
140
+ st.markdown(f'<div class="user-message">{message["content"]}</div>', unsafe_allow_html=True)
141
+ else:
142
+ st.markdown(f'<div class="assistant-message">{message["content"]}</div>', unsafe_allow_html=True)
143
+ st.markdown('</div>', unsafe_allow_html=True)
144
+
145
+ # JavaScript pour défiler automatiquement
146
+ components.html("""
147
+ <script>
148
+ const chatContainer = document.querySelector('.chat-container');
149
+ chatContainer.scrollTop = chatContainer.scrollHeight;
150
+ </script>
151
+ """)
152
+
153
+ # Zone de saisie
154
+ st.markdown('<div class="input-container">', unsafe_allow_html=True)
155
+ user_input = st.text_area("Votre message", height=80, key="input")
156
+ col1, col2 = st.columns([3, 1])
157
+ with col1:
158
+ pass
159
+ with col2:
160
+ send_button = st.button("Envoyer")
161
+ st.markdown('</div>', unsafe_allow_html=True)
162
+
163
+ # Traitement de l’entrée utilisateur
164
+ if send_button and user_input:
165
+ st.session_state.messages.append({"role": "user", "content": user_input})
166
+
167
+ # Traitement du fichier
168
+ uploaded_gemini_file = None
169
+ if uploaded_file:
170
+ file_ext = uploaded_file.name.split('.')[-1].lower()
171
+ if file_ext in ['jpg', 'jpeg', 'png', 'pdf', 'txt']:
172
+ with open(os.path.join("temp", uploaded_file.name), "wb") as f:
173
+ f.write(uploaded_file.getbuffer())
174
+ try:
175
+ uploaded_gemini_file = genai.upload_file(os.path.join("temp", uploaded_file.name))
176
+ except Exception as e:
177
+ st.error(f"Erreur lors de l’upload : {e}")
178
+ else:
179
+ st.error("Type de fichier non accepté.")
180
+
181
+ # Recherche web
182
+ web_results = None
183
+ if st.session_state.web_search:
184
+ with st.spinner("Recherche web en cours..."):
185
+ web_results = perform_web_search(user_input)
186
+ if web_results:
187
+ formatted_results = format_search_results(web_results)
188
+ user_input = f"{user_input}\n\nRésultats de recherche :\n{formatted_results}\nAnalyse ces informations et réponds-moi."
189
+
190
+ # Réponse de Mariam
191
+ try:
192
+ if uploaded_gemini_file:
193
+ response = st.session_state.chat.send_message([uploaded_gemini_file, "\n\n", user_input])
194
+ else:
195
+ response = st.session_state.chat.send_message(user_input)
196
+ st.session_state.messages.append({"role": "assistant", "content": response.text})
197
+ except Exception as e:
198
+ st.error(f"Erreur : {e}")
199
+
200
+ # Nettoyage
201
+ if uploaded_file:
202
+ shutil.rmtree("temp", ignore_errors=True)
203
+ os.makedirs("temp", exist_ok=True)
204
+
205
+ # Rafraîchir la page pour afficher la réponse
206
+ st.rerun()