Docfile commited on
Commit
40dc451
·
verified ·
1 Parent(s): 31cc6d0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -52
app.py CHANGED
@@ -4,13 +4,59 @@ from google.genai import types
4
  from PIL import Image
5
  import json
6
  import logging
 
 
 
7
 
8
  # Configuration du logging
9
- logging.basicConfig(level=logging.DEBUG)
 
 
 
 
 
 
 
10
  logger = logging.getLogger(__name__)
11
 
12
- def stream_response(container, response):
13
- """Gère le streaming de la réponse avec un affichage progressif"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  mode = 'starting'
15
  thinking_placeholder = None
16
  answer_placeholder = None
@@ -21,15 +67,40 @@ def stream_response(container, response):
21
  for chunk in response:
22
  logger.debug(f"Chunk reçu: {chunk}")
23
 
 
 
 
 
24
  try:
25
- for part in chunk.candidates[0].content.parts:
26
- if hasattr(part, 'thought') and part.thought:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  if mode != "thinking":
28
  if thinking_placeholder is None:
29
  with container.expander("Voir le raisonnement", expanded=False):
30
  thinking_placeholder = st.empty()
31
  mode = "thinking"
32
- thinking_text += part.text
33
  thinking_placeholder.markdown(thinking_text)
34
  else:
35
  if mode != "answering":
@@ -37,87 +108,85 @@ def stream_response(container, response):
37
  answer_placeholder = container.empty()
38
  container.subheader("Réponse")
39
  mode = "answering"
40
- answer_text += part.text
41
  answer_placeholder.markdown(answer_text)
 
 
 
 
42
  except Exception as e:
43
  logger.error(f"Erreur lors du traitement d'un chunk: {e}")
44
  logger.debug(f"Contenu du chunk problématique: {chunk}")
45
- raise
46
 
47
  except Exception as e:
48
- logger.error(f"Erreur dans le streaming de la réponse: {e}")
 
 
49
  raise
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
  def main():
52
- st.title("Mariam M-0. ")
 
 
 
 
 
 
53
 
54
- # Utilisation des secrets pour la clé API
55
  try:
56
  api_key = st.secrets["GEMINI_API_KEY"]
57
  except Exception as e:
58
- logger.error(f"Erreur dans la récupération des secrets:{e}")
59
  st.error("Erreur: Impossible d'accéder aux secrets de l'application.")
60
  return
61
 
62
  # Initialisation du client
63
  try:
64
- client = genai.Client(
65
- api_key=api_key,
66
- http_options={'api_version':'v1alpha'}
67
- )
68
  except Exception as e:
69
- logger.error(f"Erreur d'initialisation du client: {e}")
70
  st.error(f"Erreur lors de l'initialisation du client Gemini: {e}")
71
  return
72
 
73
- # Upload de l'image
74
- uploaded_file = st.file_uploader("Choisissez une image géométrique", type=['png', 'jpg', 'jpeg'])
 
 
 
 
75
 
76
  if uploaded_file:
77
- try:
78
- # Affichage de l'image
79
- image = Image.open(uploaded_file)
80
  st.image(image, caption="Image téléchargée", use_container_width=True)
81
 
82
- # Configuration du modèle
83
  model_name = "gemini-2.0-flash-thinking-exp-01-21"
 
84
 
85
- if st.button("Analyser l'image"):
86
- # Création d'un conteneur pour la réponse en streaming
87
  response_container = st.container()
88
 
89
  with st.spinner("Analyse en cours..."):
90
  try:
91
- # Préparation de la requête
92
- request = {
93
- "contents": [
94
- {"image": image},
95
- {"text": "Resous cette exercice. ça doit être bien présentable et espacé afin d'être facile à lire. réponds en français."}
96
- ]
97
- }
98
-
99
- logger.debug(f"Envoi de la requête au modèle: {request}")
100
-
101
- # Génération de la réponse en streaming
102
- response = client.models.generate_content_stream(
103
- model=model_name,
104
- config={'thinking_config': {'include_thoughts': True}},
105
- contents=[
106
- image,
107
- "Resous cette exercice. ça doit être bien présentable et espacé afin d'être facile à lire. réponds en français"
108
- ]
109
- )
110
-
111
- # Streaming de la réponse
112
  stream_response(response_container, response)
113
-
114
  except Exception as e:
115
  logger.error(f"Erreur lors de l'analyse: {e}", exc_info=True)
116
- st.error(f"Erreur lors de l'analyse de l'image: {str(e)}")
117
-
118
- except Exception as e:
119
- logger.error(f"Erreur lors du traitement de l'image: {e}", exc_info=True)
120
- st.error(f"Erreur lors du traitement de l'image: {str(e)}")
121
 
122
  if __name__ == "__main__":
123
  main()
 
4
  from PIL import Image
5
  import json
6
  import logging
7
+ from typing import Optional, Generator, Any
8
+ import sys
9
+ from pathlib import Path
10
 
11
  # Configuration du logging
12
+ logging.basicConfig(
13
+ level=logging.DEBUG,
14
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
15
+ handlers=[
16
+ logging.StreamHandler(sys.stdout),
17
+ logging.FileHandler(Path('app.log'))
18
+ ]
19
+ )
20
  logger = logging.getLogger(__name__)
21
 
22
+ class GeminiClient:
23
+ """Classe pour gérer les interactions avec l'API Gemini"""
24
+ def __init__(self, api_key: str):
25
+ self.client = None
26
+ self.init_client(api_key)
27
+
28
+ def init_client(self, api_key: str) -> None:
29
+ """Initialise le client Gemini"""
30
+ try:
31
+ self.client = genai.Client(
32
+ api_key=api_key,
33
+ http_options={'api_version': 'v1alpha'}
34
+ )
35
+ except Exception as e:
36
+ logger.error(f"Erreur d'initialisation du client Gemini: {e}")
37
+ raise RuntimeError(f"Impossible d'initialiser le client Gemini: {e}")
38
+
39
+ def analyze_image(self, image: Image.Image, prompt: str, model_name: str) -> Generator:
40
+ """Analyse une image avec Gemini"""
41
+ if not self.client:
42
+ raise RuntimeError("Client Gemini non initialisé")
43
+
44
+ try:
45
+ response = self.client.models.generate_content_stream(
46
+ model=model_name,
47
+ config={'thinking_config': {'include_thoughts': True}},
48
+ contents=[
49
+ image,
50
+ prompt
51
+ ]
52
+ )
53
+ return response
54
+ except Exception as e:
55
+ logger.error(f"Erreur lors de l'analyse de l'image: {e}")
56
+ raise
57
+
58
+ def stream_response(container, response: Generator) -> None:
59
+ """Gère le streaming de la réponse avec un affichage progressif et une gestion d'erreurs robuste"""
60
  mode = 'starting'
61
  thinking_placeholder = None
62
  answer_placeholder = None
 
67
  for chunk in response:
68
  logger.debug(f"Chunk reçu: {chunk}")
69
 
70
+ if not isinstance(chunk, (dict, types.GenerateContentResponse)):
71
+ logger.warning(f"Format de chunk invalide reçu: {type(chunk)}")
72
+ continue
73
+
74
  try:
75
+ candidates = getattr(chunk, 'candidates', None)
76
+ if not candidates or not len(candidates):
77
+ logger.warning("Pas de candidats dans le chunk")
78
+ continue
79
+
80
+ content = getattr(candidates[0], 'content', None)
81
+ if not content:
82
+ logger.warning("Pas de contenu dans le premier candidat")
83
+ continue
84
+
85
+ parts = getattr(content, 'parts', [])
86
+ for part in parts:
87
+ has_thought = False
88
+ try:
89
+ has_thought = hasattr(part, 'thought') and part.thought
90
+ except Exception as e:
91
+ logger.warning(f"Erreur lors de la vérification de thought: {e}")
92
+
93
+ text = getattr(part, 'text', '')
94
+ if not text:
95
+ continue
96
+
97
+ if has_thought:
98
  if mode != "thinking":
99
  if thinking_placeholder is None:
100
  with container.expander("Voir le raisonnement", expanded=False):
101
  thinking_placeholder = st.empty()
102
  mode = "thinking"
103
+ thinking_text += text
104
  thinking_placeholder.markdown(thinking_text)
105
  else:
106
  if mode != "answering":
 
108
  answer_placeholder = container.empty()
109
  container.subheader("Réponse")
110
  mode = "answering"
111
+ answer_text += text
112
  answer_placeholder.markdown(answer_text)
113
+
114
+ except json.JSONDecodeError as e:
115
+ logger.error(f"Erreur de décodage JSON: {e}")
116
+ continue
117
  except Exception as e:
118
  logger.error(f"Erreur lors du traitement d'un chunk: {e}")
119
  logger.debug(f"Contenu du chunk problématique: {chunk}")
120
+ continue
121
 
122
  except Exception as e:
123
+ logger.error(f"Erreur fatale dans le streaming de la réponse: {e}")
124
+ if not answer_text and not thinking_text:
125
+ container.error("Une erreur est survenue lors de l'analyse de l'image. Veuillez réessayer.")
126
  raise
127
+ finally:
128
+ if not answer_text and not thinking_text:
129
+ container.warning("Aucune réponse n'a pu être générée. Veuillez réessayer.")
130
+
131
+ def validate_image(uploaded_file) -> Optional[Image.Image]:
132
+ """Valide et ouvre une image téléchargée"""
133
+ try:
134
+ image = Image.open(uploaded_file)
135
+ return image
136
+ except Exception as e:
137
+ logger.error(f"Erreur lors de l'ouverture de l'image: {e}")
138
+ st.error("L'image n'a pas pu être ouverte. Veuillez vérifier le format.")
139
+ return None
140
 
141
  def main():
142
+ st.set_page_config(
143
+ page_title="Mariam M-0",
144
+ page_icon="🔍",
145
+ layout="wide"
146
+ )
147
+
148
+ st.title("Mariam M-0")
149
 
150
+ # Récupération de la clé API
151
  try:
152
  api_key = st.secrets["GEMINI_API_KEY"]
153
  except Exception as e:
154
+ logger.error(f"Erreur dans la récupération des secrets: {e}")
155
  st.error("Erreur: Impossible d'accéder aux secrets de l'application.")
156
  return
157
 
158
  # Initialisation du client
159
  try:
160
+ gemini_client = GeminiClient(api_key)
 
 
 
161
  except Exception as e:
 
162
  st.error(f"Erreur lors de l'initialisation du client Gemini: {e}")
163
  return
164
 
165
+ # Interface utilisateur
166
+ uploaded_file = st.file_uploader(
167
+ "Choisissez une image géométrique",
168
+ type=['png', 'jpg', 'jpeg'],
169
+ help="Formats supportés: PNG, JPG, JPEG"
170
+ )
171
 
172
  if uploaded_file:
173
+ image = validate_image(uploaded_file)
174
+ if image:
 
175
  st.image(image, caption="Image téléchargée", use_container_width=True)
176
 
 
177
  model_name = "gemini-2.0-flash-thinking-exp-01-21"
178
+ prompt = "Résous cet exercice. La réponse doit être bien présentée et espacée pour faciliter la lecture. Réponds en français."
179
 
180
+ if st.button("Analyser l'image", type="primary"):
 
181
  response_container = st.container()
182
 
183
  with st.spinner("Analyse en cours..."):
184
  try:
185
+ response = gemini_client.analyze_image(image, prompt, model_name)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  stream_response(response_container, response)
 
187
  except Exception as e:
188
  logger.error(f"Erreur lors de l'analyse: {e}", exc_info=True)
189
+ st.error("Une erreur est survenue lors de l'analyse. Veuillez réessayer.")
 
 
 
 
190
 
191
  if __name__ == "__main__":
192
  main()